Skip to content

Commit 497aa9c

Browse files
Merge pull request #432 from REANNZ/filter_attributes_mdscope
Filter attributes by shibmd_scope
2 parents 4e8d27c + f8529f1 commit 497aa9c

File tree

3 files changed

+365
-15
lines changed

3 files changed

+365
-15
lines changed

example/plugins/microservices/filter_attributes.yaml.example

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,35 @@ module: satosa.micro_services.attribute_modifications.FilterAttributeValues
22
name: AttributeFilter
33
config:
44
attribute_filters:
5+
# default rules for any IdentityProvider
6+
"":
7+
# default rules for any requester
8+
"":
9+
# enforce controlled vocabulary (via simple notation)
10+
eduPersonAffiliation: "^(faculty|student|staff|alum|member|affiliate|employee|library-walk-in)$"
11+
eduPersonPrimaryAffiliation: "^(faculty|student|staff|alum|member|affiliate|employee|library-walk-in)$"
12+
eduPersonScopedAffiliation:
13+
# enforce controlled vocabulary (via extended notation)
14+
regexp: "^(faculty|student|staff|alum|member|affiliate|employee|library-walk-in)@"
15+
# enforce correct scope
16+
shibmdscope_match_scope:
17+
eduPersonPrincipalName:
18+
# enforce correct scope
19+
shibmdscope_match_scope:
20+
subject-id:
21+
# enforce attribute syntax
22+
regexp: "^[0-9A-Za-z][-=0-9A-Za-z]{0,126}@[0-9A-Za-z][-.0-9A-Za-z]{0,126}\\Z"
23+
# enforce correct scope
24+
shibmdscope_match_scope:
25+
pairwise-id:
26+
# enforce attribute syntax
27+
regexp: "^[0-9A-Za-z][-=0-9A-Za-z]{0,126}@[0-9A-Za-z][-.0-9A-Za-z]{0,126}\\Z"
28+
# enforce correct scope
29+
shibmdscope_match_scope:
30+
schacHomeOrganization:
31+
# enforce scoping rule on attribute value
32+
shibmdscope_match_value:
33+
534
target_provider1:
635
requester1:
736
attr1: "^foo:bar$"

src/satosa/micro_services/attribute_modifications.py

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import re
2+
import logging
23

34
from .base import ResponseMicroService
5+
from ..context import Context
6+
from ..exception import SATOSAError
47

8+
logger = logging.getLogger(__name__)
59

610
class AddStaticAttributes(ResponseMicroService):
711
"""
@@ -29,28 +33,62 @@ def __init__(self, config, *args, **kwargs):
2933
def process(self, context, data):
3034
# apply default filters
3135
provider_filters = self.attribute_filters.get("", {})
32-
self._apply_requester_filters(data.attributes, provider_filters, data.requester)
36+
target_provider = data.auth_info.issuer
37+
self._apply_requester_filters(data.attributes, provider_filters, data.requester, context, target_provider)
3338

3439
# apply target provider specific filters
35-
target_provider = data.auth_info.issuer
3640
provider_filters = self.attribute_filters.get(target_provider, {})
37-
self._apply_requester_filters(data.attributes, provider_filters, data.requester)
41+
self._apply_requester_filters(data.attributes, provider_filters, data.requester, context, target_provider)
3842
return super().process(context, data)
3943

40-
def _apply_requester_filters(self, attributes, provider_filters, requester):
44+
def _apply_requester_filters(self, attributes, provider_filters, requester, context, target_provider):
4145
# apply default requester filters
4246
default_requester_filters = provider_filters.get("", {})
43-
self._apply_filter(attributes, default_requester_filters)
47+
self._apply_filters(attributes, default_requester_filters, context, target_provider)
4448

4549
# apply requester specific filters
4650
requester_filters = provider_filters.get(requester, {})
47-
self._apply_filter(attributes, requester_filters)
48-
49-
def _apply_filter(self, attributes, attribute_filters):
50-
for attribute_name, attribute_filter in attribute_filters.items():
51-
regex = re.compile(attribute_filter)
52-
if attribute_name == "": # default filter for all attributes
53-
for attribute, values in attributes.items():
54-
attributes[attribute] = list(filter(regex.search, attributes[attribute]))
55-
elif attribute_name in attributes:
56-
attributes[attribute_name] = list(filter(regex.search, attributes[attribute_name]))
51+
self._apply_filters(attributes, requester_filters, context, target_provider)
52+
53+
def _apply_filters(self, attributes, attribute_filters, context, target_provider):
54+
for attribute_name, attribute_filters in attribute_filters.items():
55+
if type(attribute_filters) == str:
56+
# convert simple notation to filter list
57+
attribute_filters = {'regexp': attribute_filters}
58+
59+
for filter_type, filter_value in attribute_filters.items():
60+
61+
if filter_type == "regexp":
62+
filter_func = re.compile(filter_value).search
63+
elif filter_type == "shibmdscope_match_scope":
64+
mdstore = context.get_decoration(Context.KEY_METADATA_STORE)
65+
md_scopes = list(mdstore.shibmd_scopes(target_provider,"idpsso_descriptor")) if mdstore else []
66+
filter_func = lambda v: self._shibmdscope_match_scope(v, md_scopes)
67+
elif filter_type == "shibmdscope_match_value":
68+
mdstore = context.get_decoration(Context.KEY_METADATA_STORE)
69+
md_scopes = list(mdstore.shibmd_scopes(target_provider,"idpsso_descriptor")) if mdstore else []
70+
filter_func = lambda v: self._shibmdscope_match_value(v, md_scopes)
71+
else:
72+
raise SATOSAError("Unknown filter type")
73+
74+
if attribute_name == "": # default filter for all attributes
75+
for attribute, values in attributes.items():
76+
attributes[attribute] = list(filter(filter_func, attributes[attribute]))
77+
elif attribute_name in attributes:
78+
attributes[attribute_name] = list(filter(filter_func, attributes[attribute_name]))
79+
80+
def _shibmdscope_match_value(self, value, md_scopes):
81+
for md_scope in md_scopes:
82+
if not md_scope['regexp'] and md_scope['text'] == value:
83+
return True
84+
elif md_scope['regexp'] and re.fullmatch(md_scope['text'], value):
85+
return True
86+
return False
87+
88+
def _shibmdscope_match_scope(self, value, md_scopes):
89+
split_value = value.split('@')
90+
if len(split_value) != 2:
91+
logger.info(f"Discarding invalid scoped value {value}")
92+
return False
93+
value_scope = split_value[1]
94+
return self._shibmdscope_match_value(value_scope, md_scopes)

0 commit comments

Comments
 (0)