Projet

Général

Profil

0003-auth_saml-set-attributes-using-model-67025.patch

Valentin Deniaud, 16 août 2022 14:12

Télécharger (6,75 ko)

Voir les différences:

Subject: [PATCH 3/7] auth_saml: set attributes using model (#67025)

 src/authentic2_auth_saml/adapters.py | 45 +++++++++++++++++-----------
 tests/test_auth_saml.py              | 35 +++++++++++-----------
 2 files changed, 45 insertions(+), 35 deletions(-)
src/authentic2_auth_saml/adapters.py
16 16

  
17 17

  
18 18
import logging
19
from contextlib import contextmanager
19 20

  
20 21
from django.contrib import messages
21 22
from django.core.exceptions import MultipleObjectsReturned
......
53 54
        return self.msg % self.kwargs
54 55

  
55 56

  
57
@contextmanager
58
def wrap_mapping_errors(action):
59
    try:
60
        yield
61
    except MappingError as e:
62
        if action.mandatory:
63
            # it's mandatory, provisionning should fail completely
64
            raise e
65
        logger.warning('auth_saml: mapping action failed: %s', e)
66

  
67

  
56 68
class SamlConditionContextProxy:
57 69
    def __init__(self, saml_attributes):
58 70
        self.saml_attributes = saml_attributes
......
133 145
        if not isinstance(attribute_mapping, list):
134 146
            raise MappingError(_('invalid A2_ATTRIBUTE_MAPPING'))
135 147

  
136
        if self.apply_attribute_mapping(user, idp, saml_attributes, attribute_mapping):
137
            user.save()
148
        self.apply_attribute_mapping(user, idp, saml_attributes, attribute_mapping)
138 149

  
139 150
    def apply_attribute_mapping(self, user, idp, saml_attributes, attribute_mapping):
140 151
        self.rename_attributes(idp, saml_attributes)
152
        self.set_attributes(user, idp, saml_attributes)
141 153

  
142
        user_modified = False
143 154
        for mapping in attribute_mapping:
144 155
            if not isinstance(mapping, dict):
145 156
                raise MappingError(_('invalid mapping action "%(mapping)s"'), mapping=mapping)
......
155 166
                if not method:
156 167
                    raise MappingError(_('invalid action'))
157 168
                logger.debug('auth_saml: applying provisionning mapping %s', mapping)
158
                if method(user, idp, saml_attributes, mapping):
159
                    user_modified = True
169
                method(user, idp, saml_attributes, mapping)
160 170
            except MappingError as e:
161 171
                if mandatory:
162 172
                    # it's mandatory, provisionning should fail completely
163 173
                    raise e
164 174
                logger.warning('auth_saml: mapping action failed: %s', e)
165
        return user_modified
166 175

  
167 176
    def rename_attributes(self, idp, saml_attributes):
168 177
        for action in idp['authenticator'].rename_attribute_actions.all():
169 178
            if action.from_name in saml_attributes:
170 179
                saml_attributes[action.to_name] = saml_attributes[action.from_name]
171 180

  
172
    def action_set_attribute(self, user, idp, saml_attributes, mapping):
173
        attribute = mapping.get('attribute')
174
        if not attribute or not isinstance(attribute, str):
175
            raise MappingError(_('missing attribute key'))
181
    def set_attributes(self, user, idp, saml_attributes):
182
        user_modified = False
183
        for action in idp['authenticator'].set_attribute_actions.all():
184
            with wrap_mapping_errors(action):
185
                user_modified |= self.set_user_attribute(user, action, saml_attributes)
176 186

  
177
        saml_attribute = mapping.get('saml_attribute')
178
        if not saml_attribute or not isinstance(saml_attribute, str):
179
            raise MappingError(_('missing saml_attribute key'))
187
        if user_modified:
188
            user.save()
180 189

  
181
        if saml_attribute not in saml_attributes:
182
            raise MappingError(_('unknown saml_attribute (%s)') % saml_attribute)
183
        value = saml_attributes[saml_attribute]
184
        return self.set_user_attribute(user, attribute, value)
190
    def set_user_attribute(self, user, action, saml_attributes):
191
        if action.saml_attribute not in saml_attributes:
192
            raise MappingError(_('unknown saml_attribute (%s)') % action.saml_attribute)
185 193

  
186
    def set_user_attribute(self, user, attribute, value):
194
        attribute = action.attribute
195
        value = saml_attributes[action.saml_attribute]
187 196
        if isinstance(value, list):
188 197
            if len(value) == 0:
189 198
                raise MappingError(_('no value for attribute "%(attribute)s"'), attribute=attribute)
tests/test_auth_saml.py
28 28
from authentic2.custom_user.models import DeletedUser
29 29
from authentic2.models import Attribute
30 30
from authentic2_auth_saml.adapters import AuthenticAdapter, MappingError
31
from authentic2_auth_saml.models import RenameAttributeAction, SAMLAuthenticator
31
from authentic2_auth_saml.models import RenameAttributeAction, SAMLAuthenticator, SetAttributeAction
32 32

  
33 33
from .utils import login
34 34

  
......
78 78
        enabled=True,
79 79
        metadata='meta1.xml',
80 80
        slug='idp1',
81
        a2_attribute_mapping=[
82
            {
83
                'attribute': 'email',
84
                'saml_attribute': 'mail',
85
                'mandatory': True,
86
            },
87
            {
88
                'attribute': 'title',
89
                'saml_attribute': 'title',
90
            },
91
            {
92
                'attribute': 'first_name',
93
                'saml_attribute': 'first_name',
94
            },
95
        ],
81
    )
82
    SetAttributeAction.objects.create(
83
        authenticator=authenticator,
84
        attribute='email',
85
        saml_attribute='mail',
86
        mandatory=True,
87
    )
88
    SetAttributeAction.objects.create(
89
        authenticator=authenticator,
90
        attribute='title',
91
        saml_attribute='title',
92
    )
93
    SetAttributeAction.objects.create(
94
        authenticator=authenticator,
95
        attribute='first_name',
96
        saml_attribute='first_name',
96 97
    )
97 98
    RenameAttributeAction.objects.create(
98 99
        authenticator=authenticator,
......
157 158
    adapter, idp, saml_attributes, title_attribute, user, rf
158 159
):
159 160
    saml_attributes['http://nice/attribute/givenName'] = []
160
    idp['A2_ATTRIBUTE_MAPPING'][-1]['mandatory'] = True
161
    SetAttributeAction.objects.filter(attribute='first_name').update(mandatory=True)
161 162
    with pytest.raises(MappingError, match='no value'):
162 163
        adapter.apply_attribute_mapping(user, idp, saml_attributes, idp['A2_ATTRIBUTE_MAPPING'])
163 164

  
164
-