diff --git a/example/plugins/microservices/metadata_attributes.yaml.example b/example/plugins/microservices/metadata_attributes.yaml.example new file mode 100644 index 000000000..773d68cca --- /dev/null +++ b/example/plugins/microservices/metadata_attributes.yaml.example @@ -0,0 +1,21 @@ +module: satosa.micro_services.attribute_modifications.AddMetadataAttributes +name: AddMetadataAttributes +config: + attribute_mapping: + - name: shibmd_scopes + type: shibmd_scopes + - name: contact_person_data + type: contact_person_data + - name: assurance_certifications + type: assurance_certifications + - name: registration_info + type: registration_info + - name: registration_authority + type: registration_authority + - name: entity_categories + type: entity_categories + - name: supported_entity_categories + type: supported_entity_categories + - name: entity_attributes + type: entity_attributes + diff --git a/src/satosa/micro_services/attribute_modifications.py b/src/satosa/micro_services/attribute_modifications.py index bb00761b4..f11422a05 100644 --- a/src/satosa/micro_services/attribute_modifications.py +++ b/src/satosa/micro_services/attribute_modifications.py @@ -7,6 +7,7 @@ logger = logging.getLogger(__name__) + class AddStaticAttributes(ResponseMicroService): """ Add static attributes to the responses. @@ -21,6 +22,64 @@ def process(self, context, data): return super().process(context, data) +class AddMetadataAttributes(ResponseMicroService): + """ + Add metadata-derived attributes to the responses. + """ + + def __init__(self, config, *args, **kwargs): + super().__init__(*args, **kwargs) + self.attribute_mapping = config["attribute_mapping"] + + def process(self, context, data): + data.attributes.update(self.get_attribute_values(context, data.auth_info.issuer, self.attribute_mapping)) + return super().process(context, data) + + def get_attribute_values(self, context, target_provider, attribute_mapping): + attribute_values = {} + mdstore = context.get_decoration(Context.KEY_METADATA_STORE) + if not mdstore: + return attribute_values # empty: nothing we can do without an mdstore + for am in attribute_mapping: + value = None + if am['type'] == "shibmd_scopes": + scopes = mdstore.shibmd_scopes(target_provider, "idpsso_descriptor") + + # saml2.MDStore.shibmd_scopes returns compiled RE Pattern objects, but these are not serializable. + # Replace them back with the original pattern text. + # And wrap the resulting list in a dict, as mod_auth_openidc does not accept lists of JSON objects. + value = {"scopes": + [ + {"regexp": scope['regexp'], "text": scope['text'].pattern if scope['regexp'] else scope['text']} + for scope in scopes + ]} + elif am['type'] == "contact_person_data": + # Convert tuple to a list to make it serializable. + # And wrap it in a dict, as mod_auth_openidc does not accept lists of JSON objects. + value = {"contacts": list(mdstore.contact_person_data(target_provider))} + elif am['type'] == "assurance_certifications": + # Convert tuple to a list to make it serializable. + value = list(mdstore.assurance_certifications(target_provider)) + elif am['type'] == "registration_info": + value = mdstore.registration_info(target_provider) + elif am['type'] == "registration_authority": + registration_info = mdstore.registration_info(target_provider) + if registration_info and 'registration_authority' in registration_info: + value = registration_info['registration_authority'] + elif am['type'] == "entity_categories": + value = mdstore.entity_categories(target_provider) + elif am['type'] == "supported_entity_categories": + value = mdstore.supported_entity_categories(target_provider) + elif am['type'] == "entity_attributes": + value = mdstore.entity_attributes(target_provider) + else: + raise SATOSAError("Unknown SAML metadata attribute type") + + if value: + attribute_values[am['name']] = value + return attribute_values + + class FilterAttributeValues(ResponseMicroService): """ Filter attribute values, only preserving those matching the given regex.