From 31975329e536d7bf88105fbc03ae3a3b8de2f69c Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Thu, 28 Jul 2022 15:32:34 +0200 Subject: [PATCH 3/8] auth_saml: set attributes using model (#67025) --- src/authentic2_auth_saml/adapters.py | 38 +++++++++++++++------------- tests/test_auth_saml.py | 35 ++++++++++++------------- 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/src/authentic2_auth_saml/adapters.py b/src/authentic2_auth_saml/adapters.py index cff0c40d..a2bc2cc9 100644 --- a/src/authentic2_auth_saml/adapters.py +++ b/src/authentic2_auth_saml/adapters.py @@ -133,13 +133,12 @@ class AuthenticAdapter(DefaultAdapter): if not isinstance(attribute_mapping, list): raise MappingError(_('invalid A2_ATTRIBUTE_MAPPING')) - if self.apply_attribute_mapping(user, idp, saml_attributes, attribute_mapping): - user.save() + self.apply_attribute_mapping(user, idp, saml_attributes, attribute_mapping) def apply_attribute_mapping(self, user, idp, saml_attributes, attribute_mapping): self.rename_attributes(idp, saml_attributes) + self.set_attributes(user, idp, saml_attributes) - user_modified = False for mapping in attribute_mapping: if not isinstance(mapping, dict): raise MappingError(_('invalid mapping action "%(mapping)s"'), mapping=mapping) @@ -155,35 +154,38 @@ class AuthenticAdapter(DefaultAdapter): if not method: raise MappingError(_('invalid action')) logger.debug('auth_saml: applying provisionning mapping %s', mapping) - if method(user, idp, saml_attributes, mapping): - user_modified = True + method(user, idp, saml_attributes, mapping) except MappingError as e: if mandatory: # it's mandatory, provisionning should fail completely raise e logger.warning('auth_saml: mapping action failed: %s', e) - return user_modified def rename_attributes(self, idp, saml_attributes): for action in idp['authenticator'].rename_attribute_actions.all(): if action.from_name in saml_attributes: saml_attributes[action.to_name] = saml_attributes[action.from_name] - def action_set_attribute(self, user, idp, saml_attributes, mapping): - attribute = mapping.get('attribute') - if not attribute or not isinstance(attribute, str): - raise MappingError(_('missing attribute key')) + def set_attributes(self, user, idp, saml_attributes): + user_modified = False + for action in idp['authenticator'].set_attribute_actions.all(): + try: + user_modified |= self.set_user_attribute(user, action, saml_attributes) + except MappingError as e: + logger.warning('auth_saml: mapping action failed: %s', e) + if action.mandatory: + # it's mandatory, provisionning should fail completely + raise e - saml_attribute = mapping.get('saml_attribute') - if not saml_attribute or not isinstance(saml_attribute, str): - raise MappingError(_('missing saml_attribute key')) + if user_modified: + user.save() - if saml_attribute not in saml_attributes: - raise MappingError(_('unknown saml_attribute (%s)') % saml_attribute) - value = saml_attributes[saml_attribute] - return self.set_user_attribute(user, attribute, value) + def set_user_attribute(self, user, action, saml_attributes): + if action.saml_attribute not in saml_attributes: + raise MappingError(_('unknown saml_attribute (%s)') % action.saml_attribute) - def set_user_attribute(self, user, attribute, value): + attribute = action.attribute + value = saml_attributes[action.saml_attribute] if isinstance(value, list): if len(value) == 0: raise MappingError(_('no value for attribute "%(attribute)s"'), attribute=attribute) diff --git a/tests/test_auth_saml.py b/tests/test_auth_saml.py index e6925b4d..3040dfef 100644 --- a/tests/test_auth_saml.py +++ b/tests/test_auth_saml.py @@ -28,7 +28,7 @@ from authentic2.apps.authenticators.models import LoginPasswordAuthenticator from authentic2.custom_user.models import DeletedUser from authentic2.models import Attribute from authentic2_auth_saml.adapters import AuthenticAdapter, MappingError -from authentic2_auth_saml.models import RenameAttributeAction, SAMLAuthenticator +from authentic2_auth_saml.models import RenameAttributeAction, SAMLAuthenticator, SetAttributeAction from .utils import login @@ -78,21 +78,22 @@ def idp(db): enabled=True, metadata='meta1.xml', slug='idp1', - a2_attribute_mapping=[ - { - 'attribute': 'email', - 'saml_attribute': 'mail', - 'mandatory': True, - }, - { - 'attribute': 'title', - 'saml_attribute': 'title', - }, - { - 'attribute': 'first_name', - 'saml_attribute': 'first_name', - }, - ], + ) + SetAttributeAction.objects.create( + authenticator=authenticator, + attribute='email', + saml_attribute='mail', + mandatory=True, + ) + SetAttributeAction.objects.create( + authenticator=authenticator, + attribute='title', + saml_attribute='title', + ) + SetAttributeAction.objects.create( + authenticator=authenticator, + attribute='first_name', + saml_attribute='first_name', ) RenameAttributeAction.objects.create( authenticator=authenticator, @@ -157,7 +158,7 @@ def test_apply_attribute_mapping_missing_attribute_exception( adapter, idp, saml_attributes, title_attribute, user, rf ): saml_attributes['http://nice/attribute/givenName'] = [] - idp['A2_ATTRIBUTE_MAPPING'][-1]['mandatory'] = True + SetAttributeAction.objects.filter(attribute='first_name').update(mandatory=True) with pytest.raises(MappingError, match='no value'): adapter.apply_attribute_mapping(user, idp, saml_attributes, idp['A2_ATTRIBUTE_MAPPING']) -- 2.30.2