From ad6154e92ca8a6a4b694fea9c94a629d4f2e3cb6 Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Wed, 15 Mar 2023 13:27:35 +1300 Subject: [PATCH 01/11] new: examples/filter_attributes: enforce controlled vocabulary for eduPerson*Affiliation attributes --- .../plugins/microservices/filter_attributes.yaml.example | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/example/plugins/microservices/filter_attributes.yaml.example b/example/plugins/microservices/filter_attributes.yaml.example index f368493b5..f8ae2fb0a 100644 --- a/example/plugins/microservices/filter_attributes.yaml.example +++ b/example/plugins/microservices/filter_attributes.yaml.example @@ -2,6 +2,15 @@ module: satosa.micro_services.attribute_modifications.FilterAttributeValues name: AttributeFilter config: attribute_filters: + # default rules for any IdentityProvider + "": + # default rules for any requester + "": + # enforce controlled vocabulary + eduPersonAffiliation: "^(faculty|student|staff|alum|member|affiliate|employee|library-walk-in)$" + eduPersonPrimaryAffiliation: "^(faculty|student|staff|alum|member|affiliate|employee|library-walk-in)$" + eduPersonScopedAffiliation: "^(faculty|student|staff|alum|member|affiliate|employee|library-walk-in)@" + target_provider1: requester1: attr1: "^foo:bar$" From 1b58acd6b94179c81ad023edff2877b5426e20a3 Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Wed, 15 Mar 2023 13:43:42 +1300 Subject: [PATCH 02/11] new: FilterAttributeValues: allow more filter types besides regexp Introduce an extended filter notation where for each attribute, instead of a single value with a regexp, the filter can be a dict indexed by filter type, with (optional) filter value. Define `regexp` filter matching existing behaviour. Support existing syntax (regexp as direct filter value) by mapping it to a dict explictily pointing to regexp filter. --- .../micro_services/attribute_modifications.py | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/satosa/micro_services/attribute_modifications.py b/src/satosa/micro_services/attribute_modifications.py index 67633af27..447d42e8e 100644 --- a/src/satosa/micro_services/attribute_modifications.py +++ b/src/satosa/micro_services/attribute_modifications.py @@ -1,6 +1,7 @@ import re from .base import ResponseMicroService +from ..exception import SATOSAError class AddStaticAttributes(ResponseMicroService): @@ -40,17 +41,27 @@ def process(self, context, data): def _apply_requester_filters(self, attributes, provider_filters, requester): # apply default requester filters default_requester_filters = provider_filters.get("", {}) - self._apply_filter(attributes, default_requester_filters) + self._apply_filters(attributes, default_requester_filters) # apply requester specific filters requester_filters = provider_filters.get(requester, {}) - self._apply_filter(attributes, requester_filters) - - def _apply_filter(self, attributes, attribute_filters): - for attribute_name, attribute_filter in attribute_filters.items(): - regex = re.compile(attribute_filter) - if attribute_name == "": # default filter for all attributes - for attribute, values in attributes.items(): - attributes[attribute] = list(filter(regex.search, attributes[attribute])) - elif attribute_name in attributes: - attributes[attribute_name] = list(filter(regex.search, attributes[attribute_name])) + self._apply_filters(attributes, requester_filters) + + def _apply_filters(self, attributes, attribute_filters): + for attribute_name, attribute_filters in attribute_filters.items(): + if type(attribute_filters) == str: + # convert simple notation to filter list + attribute_filters = {'regexp': attribute_filters} + + for filter_type, filter_value in attribute_filters.items(): + + if filter_type == "regexp": + filter_func = re.compile(filter_value).search + else: + raise SATOSAError("Unknown filter type") + + if attribute_name == "": # default filter for all attributes + for attribute, values in attributes.items(): + attributes[attribute] = list(filter(filter_func, attributes[attribute])) + elif attribute_name in attributes: + attributes[attribute_name] = list(filter(filter_func, attributes[attribute_name])) From df563efbd7534e95f007efffac338d286f5c2b31 Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Wed, 15 Mar 2023 13:48:45 +1300 Subject: [PATCH 03/11] new: FilterAttributeValues: pass context and target_provider through _apply_requester_filters and _apply_filters ... for use by specific filters --- .../micro_services/attribute_modifications.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/satosa/micro_services/attribute_modifications.py b/src/satosa/micro_services/attribute_modifications.py index 447d42e8e..3ed063161 100644 --- a/src/satosa/micro_services/attribute_modifications.py +++ b/src/satosa/micro_services/attribute_modifications.py @@ -30,24 +30,24 @@ def __init__(self, config, *args, **kwargs): def process(self, context, data): # apply default filters provider_filters = self.attribute_filters.get("", {}) - self._apply_requester_filters(data.attributes, provider_filters, data.requester) + target_provider = data.auth_info.issuer + self._apply_requester_filters(data.attributes, provider_filters, data.requester, context, target_provider) # apply target provider specific filters - target_provider = data.auth_info.issuer provider_filters = self.attribute_filters.get(target_provider, {}) - self._apply_requester_filters(data.attributes, provider_filters, data.requester) + self._apply_requester_filters(data.attributes, provider_filters, data.requester, context, target_provider) return super().process(context, data) - def _apply_requester_filters(self, attributes, provider_filters, requester): + def _apply_requester_filters(self, attributes, provider_filters, requester, context, target_provider): # apply default requester filters default_requester_filters = provider_filters.get("", {}) - self._apply_filters(attributes, default_requester_filters) + self._apply_filters(attributes, default_requester_filters, context, target_provider) # apply requester specific filters requester_filters = provider_filters.get(requester, {}) - self._apply_filters(attributes, requester_filters) + self._apply_filters(attributes, requester_filters, context, target_provider) - def _apply_filters(self, attributes, attribute_filters): + def _apply_filters(self, attributes, attribute_filters, context, target_provider): for attribute_name, attribute_filters in attribute_filters.items(): if type(attribute_filters) == str: # convert simple notation to filter list From c14f0a0b60ad524947b6f74a9a00d76f161725c8 Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Wed, 15 Mar 2023 13:50:58 +1300 Subject: [PATCH 04/11] new: FilterAttributeValues: add new filter types shibmdscope_match_scope and shibmdscope_match_value Equivalent to ScopeMatchesShibMDScope and ValueMatchesShibMDScope from the Shibboleth project. --- .../micro_services/attribute_modifications.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/satosa/micro_services/attribute_modifications.py b/src/satosa/micro_services/attribute_modifications.py index 3ed063161..29ca7298c 100644 --- a/src/satosa/micro_services/attribute_modifications.py +++ b/src/satosa/micro_services/attribute_modifications.py @@ -1,8 +1,11 @@ import re +import logging from .base import ResponseMicroService +from ..context import Context from ..exception import SATOSAError +logger = logging.getLogger(__name__) class AddStaticAttributes(ResponseMicroService): """ @@ -57,6 +60,14 @@ def _apply_filters(self, attributes, attribute_filters, context, target_provider if filter_type == "regexp": filter_func = re.compile(filter_value).search + elif filter_type == "shibmdscope_match_scope": + mdstore = context.get_decoration(Context.KEY_METADATA_STORE) + md_scopes = list(mdstore.shibmd_scopes(target_provider,"idpsso_descriptor")) + filter_func = lambda v: self._shibmdscope_match_scope(v, md_scopes) + elif filter_type == "shibmdscope_match_value": + mdstore = context.get_decoration(Context.KEY_METADATA_STORE) + md_scopes = list(mdstore.shibmd_scopes(target_provider,"idpsso_descriptor")) + filter_func = lambda v: self._shibmdscope_match_value(v, md_scopes) else: raise SATOSAError("Unknown filter type") @@ -65,3 +76,19 @@ def _apply_filters(self, attributes, attribute_filters, context, target_provider attributes[attribute] = list(filter(filter_func, attributes[attribute])) elif attribute_name in attributes: attributes[attribute_name] = list(filter(filter_func, attributes[attribute_name])) + + def _shibmdscope_match_value(self, value, md_scopes): + for md_scope in md_scopes: + if not md_scope['regexp'] and md_scope['text'] == value: + return True + elif md_scope['regexp'] and re.compile(md_scope['text']).match(value): + return True + return False + + def _shibmdscope_match_scope(self, value, md_scopes): + split_value = value.split('@') + if len(split_value) != 2: + logger.info(f"Discarding invalid scoped value {value}") + return False + value_scope = split_value[1] + return self._shibmdscope_match_value(value_scope, md_scopes) From f7fcadff16ae9fcdf6a3aaead3182720a54fb2a6 Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Wed, 15 Mar 2023 13:53:28 +1300 Subject: [PATCH 05/11] new: examples/filter_attributes: enforce scope on scoped attributes (and also enforce scoping rules on schacHomeOrganization value) --- .../microservices/filter_attributes.yaml.example | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/example/plugins/microservices/filter_attributes.yaml.example b/example/plugins/microservices/filter_attributes.yaml.example index f8ae2fb0a..9d445765c 100644 --- a/example/plugins/microservices/filter_attributes.yaml.example +++ b/example/plugins/microservices/filter_attributes.yaml.example @@ -6,10 +6,20 @@ config: "": # default rules for any requester "": - # enforce controlled vocabulary + # enforce controlled vocabulary (via simple notation) eduPersonAffiliation: "^(faculty|student|staff|alum|member|affiliate|employee|library-walk-in)$" eduPersonPrimaryAffiliation: "^(faculty|student|staff|alum|member|affiliate|employee|library-walk-in)$" - eduPersonScopedAffiliation: "^(faculty|student|staff|alum|member|affiliate|employee|library-walk-in)@" + eduPersonScopedAffiliation: + # enforce controlled vocabulary (via extended notation) + regexp: "^(faculty|student|staff|alum|member|affiliate|employee|library-walk-in)@" + # enforce correct scope + shibmdscope_match_scope: + eduPersonPrincipalName: + # enforce correct scope + shibmdscope_match_scope: + schacHomeOrganization: + # enforce scoping rule on attribute value + shibmdscope_match_value: target_provider1: requester1: From fcbd4ddf42f4bf15d38ed3333b090b13b71144a8 Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Fri, 17 Mar 2023 15:59:29 +1300 Subject: [PATCH 06/11] fix: FilterAttributeValues: use re.fullmatch, remove unnecessary compile ... as per review in #432 --- src/satosa/micro_services/attribute_modifications.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/satosa/micro_services/attribute_modifications.py b/src/satosa/micro_services/attribute_modifications.py index 29ca7298c..6fe6dfa4a 100644 --- a/src/satosa/micro_services/attribute_modifications.py +++ b/src/satosa/micro_services/attribute_modifications.py @@ -81,7 +81,7 @@ def _shibmdscope_match_value(self, value, md_scopes): for md_scope in md_scopes: if not md_scope['regexp'] and md_scope['text'] == value: return True - elif md_scope['regexp'] and re.compile(md_scope['text']).match(value): + elif md_scope['regexp'] and re.fullmatch(md_scope['text'], value): return True return False From a7491502d88d7f2196ac3ef211e902276e03a19e Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Fri, 17 Mar 2023 16:00:58 +1300 Subject: [PATCH 07/11] fix: FilterAttributeValues: call mdstore only if available --- src/satosa/micro_services/attribute_modifications.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/satosa/micro_services/attribute_modifications.py b/src/satosa/micro_services/attribute_modifications.py index 6fe6dfa4a..bb00761b4 100644 --- a/src/satosa/micro_services/attribute_modifications.py +++ b/src/satosa/micro_services/attribute_modifications.py @@ -62,11 +62,11 @@ def _apply_filters(self, attributes, attribute_filters, context, target_provider filter_func = re.compile(filter_value).search elif filter_type == "shibmdscope_match_scope": mdstore = context.get_decoration(Context.KEY_METADATA_STORE) - md_scopes = list(mdstore.shibmd_scopes(target_provider,"idpsso_descriptor")) + md_scopes = list(mdstore.shibmd_scopes(target_provider,"idpsso_descriptor")) if mdstore else [] filter_func = lambda v: self._shibmdscope_match_scope(v, md_scopes) elif filter_type == "shibmdscope_match_value": mdstore = context.get_decoration(Context.KEY_METADATA_STORE) - md_scopes = list(mdstore.shibmd_scopes(target_provider,"idpsso_descriptor")) + md_scopes = list(mdstore.shibmd_scopes(target_provider,"idpsso_descriptor")) if mdstore else [] filter_func = lambda v: self._shibmdscope_match_value(v, md_scopes) else: raise SATOSAError("Unknown filter type") From e5a67cdad638621d5c552087b14fee53bba776df Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Fri, 17 Mar 2023 16:04:49 +1300 Subject: [PATCH 08/11] new: FilterAttributeValues: add tests for new filter notation Test regexp filter via new notation, test invalid filter type. --- .../test_attribute_modifications.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/satosa/micro_services/test_attribute_modifications.py b/tests/satosa/micro_services/test_attribute_modifications.py index 0efaec43e..3e3b1d815 100644 --- a/tests/satosa/micro_services/test_attribute_modifications.py +++ b/tests/satosa/micro_services/test_attribute_modifications.py @@ -1,3 +1,5 @@ +import pytest +from satosa.exception import SATOSAError from satosa.internal import AuthenticationInformation from satosa.internal import InternalData from satosa.micro_services.attribute_modifications import FilterAttributeValues @@ -116,3 +118,43 @@ def test_filter_one_attribute_for_one_target_provider_for_one_requester(self): } filtered = filter_service.process(None, resp) assert filtered.attributes == {"a1": ["1:foo:bar:2"]} + + def test_filter_one_attribute_from_all_target_providers_for_all_requesters_in_extended_notation(self): + attribute_filters = { + "": { + "": { + "a2": { + "regexp": "^foo:bar$" + } + } + } + } + filter_service = self.create_filter_service(attribute_filters) + + resp = InternalData(AuthenticationInformation()) + resp.attributes = { + "a1": ["abc:xyz"], + "a2": ["foo:bar", "1:foo:bar:2"], + } + filtered = filter_service.process(None, resp) + assert filtered.attributes == {"a1": ["abc:xyz"], "a2": ["foo:bar"]} + + def test_invalid_filter_type(self): + attribute_filters = { + "": { + "": { + "a2": { + "invalid_filter": None + } + } + } + } + filter_service = self.create_filter_service(attribute_filters) + + resp = InternalData(AuthenticationInformation()) + resp.attributes = { + "a1": ["abc:xyz"], + "a2": ["foo:bar", "1:foo:bar:2"], + } + with pytest.raises(SATOSAError): + filtered = filter_service.process(None, resp) From 92b9dc7576070887cf9f1540beb69fb53e582c39 Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Fri, 17 Mar 2023 16:05:47 +1300 Subject: [PATCH 09/11] new: FilterAttributeValues: add tests for shibmdscope_match_scope and shibmdscope_match_value filters --- .../test_attribute_modifications.py | 240 ++++++++++++++++++ 1 file changed, 240 insertions(+) diff --git a/tests/satosa/micro_services/test_attribute_modifications.py b/tests/satosa/micro_services/test_attribute_modifications.py index 3e3b1d815..2bd1db0fc 100644 --- a/tests/satosa/micro_services/test_attribute_modifications.py +++ b/tests/satosa/micro_services/test_attribute_modifications.py @@ -1,4 +1,8 @@ import pytest +from tests.util import FakeIdP, create_metadata_from_config_dict, FakeSP +from saml2.mdstore import MetadataStore +from saml2.config import Config +from satosa.context import Context from satosa.exception import SATOSAError from satosa.internal import AuthenticationInformation from satosa.internal import InternalData @@ -12,6 +16,22 @@ def create_filter_service(self, attribute_filters): filter_service.next = lambda ctx, data: data return filter_service + def create_idp_metadata_conf_with_shibmd_scopes(self, idp_entityid, shibmd_scopes): + idp_conf = { + "entityid": idp_entityid, + "service": { + "idp":{} + } + } + + if shibmd_scopes is not None: + idp_conf["service"]["idp"]["scope"] = shibmd_scopes + + metadata_conf = { + "inline": [create_metadata_from_config_dict(idp_conf)] + } + return metadata_conf + def test_filter_all_attributes_from_all_target_providers_for_all_requesters(self): attribute_filters = { "": { # all providers @@ -158,3 +178,223 @@ def test_invalid_filter_type(self): } with pytest.raises(SATOSAError): filtered = filter_service.process(None, resp) + + def test_shibmdscope_match_value_filter_with_no_md_store_in_context(self): + attribute_filters = { + "": { + "": { + "a2": { + "shibmdscope_match_value": None + } + } + } + } + filter_service = self.create_filter_service(attribute_filters) + + resp = InternalData(AuthenticationInformation()) + resp.attributes = { + "a1": ["abc:xyz"], + "a2": ["foo:bar", "1:foo:bar:2"], + } + ctx = Context() + filtered = filter_service.process(ctx, resp) + assert filtered.attributes == {"a1": ["abc:xyz"], "a2": []} + + def test_shibmdscope_match_value_filter_with_empty_md_store_in_context(self): + attribute_filters = { + "": { + "": { + "a2": { + "shibmdscope_match_value": None + } + } + } + } + filter_service = self.create_filter_service(attribute_filters) + + resp = InternalData(AuthenticationInformation()) + resp.attributes = { + "a1": ["abc:xyz"], + "a2": ["foo:bar", "1:foo:bar:2"], + } + ctx = Context() + mdstore = MetadataStore(None, None) + ctx.decorate(Context.KEY_METADATA_STORE, mdstore) + filtered = filter_service.process(ctx, resp) + assert filtered.attributes == {"a1": ["abc:xyz"], "a2": []} + + def test_shibmdscope_match_value_filter_with_idp_md_with_no_scope(self): + attribute_filters = { + "": { + "": { + "a2": { + "shibmdscope_match_value": None + } + } + } + } + filter_service = self.create_filter_service(attribute_filters) + + resp = InternalData(AuthenticationInformation()) + resp.attributes = { + "a1": ["abc:xyz"], + "a2": ["foo.bar", "1.foo.bar.2"], + } + + idp_entityid = 'https://idp.example.org/' + resp.auth_info.issuer = idp_entityid + + mdstore = MetadataStore(None, Config()) + mdstore.imp(self.create_idp_metadata_conf_with_shibmd_scopes(idp_entityid, None)) + ctx = Context() + ctx.decorate(Context.KEY_METADATA_STORE, mdstore) + + filtered = filter_service.process(ctx, resp) + assert filtered.attributes == {"a1": ["abc:xyz"], "a2": []} + + def test_shibmdscope_match_value_filter_with_idp_md_with_single_scope(self): + attribute_filters = { + "": { + "": { + "a2": { + "shibmdscope_match_value": None + } + } + } + } + filter_service = self.create_filter_service(attribute_filters) + + resp = InternalData(AuthenticationInformation()) + resp.attributes = { + "a1": ["abc:xyz"], + "a2": ["foo.bar", "1.foo.bar.2"], + } + + idp_entityid = 'https://idp.example.org/' + resp.auth_info.issuer = idp_entityid + + mdstore = MetadataStore(None, Config()) + mdstore.imp(self.create_idp_metadata_conf_with_shibmd_scopes(idp_entityid, ["foo.bar"])) + ctx = Context() + ctx.decorate(Context.KEY_METADATA_STORE, mdstore) + + filtered = filter_service.process(ctx, resp) + assert filtered.attributes == {"a1": ["abc:xyz"], "a2": ["foo.bar"]} + + def test_shibmdscope_match_value_filter_with_idp_md_with_single_regexp_scope(self): + attribute_filters = { + "": { + "": { + "a2": { + "shibmdscope_match_value": None + } + } + } + } + filter_service = self.create_filter_service(attribute_filters) + + resp = InternalData(AuthenticationInformation()) + resp.attributes = { + "a1": ["abc:xyz"], + "a2": ["test.foo.bar", "1.foo.bar.2"], + } + + idp_entityid = 'https://idp.example.org/' + resp.auth_info.issuer = idp_entityid + + mdstore = MetadataStore(None, Config()) + mdstore.imp(self.create_idp_metadata_conf_with_shibmd_scopes(idp_entityid, ["[^.]*\.foo\.bar$"])) + mdstore[idp_entityid]['idpsso_descriptor'][0]['extensions']['extension_elements'][0]['regexp'] = 'true' + ctx = Context() + ctx.decorate(Context.KEY_METADATA_STORE, mdstore) + + filtered = filter_service.process(ctx, resp) + assert filtered.attributes == {"a1": ["abc:xyz"], "a2": ["test.foo.bar"]} + + def test_shibmdscope_match_value_filter_with_idp_md_with_multiple_scopes(self): + attribute_filters = { + "": { + "": { + "a2": { + "shibmdscope_match_value": None + } + } + } + } + filter_service = self.create_filter_service(attribute_filters) + + resp = InternalData(AuthenticationInformation()) + resp.attributes = { + "a1": ["abc:xyz"], + "a2": ["foo.bar", "1.foo.bar.2", "foo.baz", "foo.baz.com"], + } + + idp_entityid = 'https://idp.example.org/' + resp.auth_info.issuer = idp_entityid + + mdstore = MetadataStore(None, Config()) + mdstore.imp(self.create_idp_metadata_conf_with_shibmd_scopes(idp_entityid, ["foo.bar", "foo.baz"])) + ctx = Context() + ctx.decorate(Context.KEY_METADATA_STORE, mdstore) + + filtered = filter_service.process(ctx, resp) + assert filtered.attributes == {"a1": ["abc:xyz"], "a2": ["foo.bar", "foo.baz"]} + + def test_shibmdscope_match_scope_filter_with_single_scope(self): + attribute_filters = { + "": { + "": { + "a2": { + "shibmdscope_match_scope": None + } + } + } + } + filter_service = self.create_filter_service(attribute_filters) + + resp = InternalData(AuthenticationInformation()) + resp.attributes = { + "a1": ["abc:xyz"], + "a2": ["foo.bar", "value@foo.bar", "1.foo.bar.2", "value@foo.bar.2", "value@extra@foo.bar"], + } + + idp_entityid = 'https://idp.example.org/' + resp.auth_info.issuer = idp_entityid + + mdstore = MetadataStore(None, Config()) + mdstore.imp(self.create_idp_metadata_conf_with_shibmd_scopes(idp_entityid, ["foo.bar"])) + ctx = Context() + ctx.decorate(Context.KEY_METADATA_STORE, mdstore) + + filtered = filter_service.process(ctx, resp) + assert filtered.attributes == {"a1": ["abc:xyz"], "a2": ["value@foo.bar"]} + + def test_multiple_filters_for_single_attribute(self): + attribute_filters = { + "": { + "": { + "a2": { + "regexp": "^value1@", + "shibmdscope_match_scope": None + } + } + } + } + filter_service = self.create_filter_service(attribute_filters) + + resp = InternalData(AuthenticationInformation()) + resp.attributes = { + "a1": ["abc:xyz"], + "a2": ["foo.bar", "value1@foo.bar", "value2@foo.bar", "1.foo.bar.2", "value@foo.bar.2", "value@extra@foo.bar"], + } + + idp_entityid = 'https://idp.example.org/' + resp.auth_info.issuer = idp_entityid + + mdstore = MetadataStore(None, Config()) + mdstore.imp(self.create_idp_metadata_conf_with_shibmd_scopes(idp_entityid, ["foo.bar"])) + ctx = Context() + ctx.decorate(Context.KEY_METADATA_STORE, mdstore) + + filtered = filter_service.process(ctx, resp) + assert filtered.attributes == {"a1": ["abc:xyz"], "a2": ["value1@foo.bar"]} From cfda9cedaa137ae9eb8d0759089cd2f166a8ed87 Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Mon, 20 Mar 2023 10:29:55 +1300 Subject: [PATCH 10/11] new: examples/filter_attributes: add sample rules for saml-subject-id and saml-pairwise-id --- .../microservices/filter_attributes.yaml.example | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/example/plugins/microservices/filter_attributes.yaml.example b/example/plugins/microservices/filter_attributes.yaml.example index 9d445765c..185f2dec0 100644 --- a/example/plugins/microservices/filter_attributes.yaml.example +++ b/example/plugins/microservices/filter_attributes.yaml.example @@ -17,6 +17,16 @@ config: eduPersonPrincipalName: # enforce correct scope shibmdscope_match_scope: + subject-id: + # enforce attribute syntax + regexp: "^[0-9A-Za-z][-=0-9A-Za-z]{0,126}@[0-9A-Za-z][-.0-9A-Za-z]{0,126}\\Z" + # enforce correct scope + shibmdscope_match_scope: + pairwise-id: + # enforce attribute syntax + regexp: "^[0-9A-Za-z][-=0-9A-Za-z]{0,126}@[0-9A-Za-z][-.0-9A-Za-z]{0,126}\\Z" + # enforce correct scope + shibmdscope_match_scope: schacHomeOrganization: # enforce scoping rule on attribute value shibmdscope_match_value: From f8529f158620e49eb9ebc05db8d1205dfc286b2d Mon Sep 17 00:00:00 2001 From: Vlad Mencl Date: Mon, 20 Mar 2023 10:32:35 +1300 Subject: [PATCH 11/11] nfc: FilterAttributeValues: add clarifying comment to shibmdscope_match_scope test --- tests/satosa/micro_services/test_attribute_modifications.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/satosa/micro_services/test_attribute_modifications.py b/tests/satosa/micro_services/test_attribute_modifications.py index 2bd1db0fc..aa1fcb8d5 100644 --- a/tests/satosa/micro_services/test_attribute_modifications.py +++ b/tests/satosa/micro_services/test_attribute_modifications.py @@ -304,6 +304,7 @@ def test_shibmdscope_match_value_filter_with_idp_md_with_single_regexp_scope(sel mdstore = MetadataStore(None, Config()) mdstore.imp(self.create_idp_metadata_conf_with_shibmd_scopes(idp_entityid, ["[^.]*\.foo\.bar$"])) + # mark scope as regexp (cannot be done via pysaml2 YAML config) mdstore[idp_entityid]['idpsso_descriptor'][0]['extensions']['extension_elements'][0]['regexp'] = 'true' ctx = Context() ctx.decorate(Context.KEY_METADATA_STORE, mdstore)