auth_saml: always add mapping as MappingError details (#47760)
This commit is contained in:
parent
d47bc8e1ad
commit
6cd84ac407
|
@ -38,8 +38,7 @@ class MappingError(Exception):
|
|||
details = None
|
||||
|
||||
def __init__(self, message, details=None):
|
||||
if details:
|
||||
self.details = details
|
||||
self.details = details or {}
|
||||
super(MappingError, self).__init__(message)
|
||||
|
||||
def __str__(self):
|
||||
|
@ -109,6 +108,10 @@ 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()
|
||||
|
||||
def apply_attribute_mapping(self, user, idp, saml_attributes, attribute_mapping):
|
||||
user_modified = False
|
||||
for mapping in attribute_mapping:
|
||||
if not isinstance(mapping, dict):
|
||||
|
@ -121,21 +124,20 @@ class AuthenticAdapter(DefaultAdapter):
|
|||
method = getattr(self, 'action_' + action.replace('-', '_'))
|
||||
except AttributeError:
|
||||
pass
|
||||
if not method:
|
||||
raise MappingError('invalid action %r' % action)
|
||||
try:
|
||||
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
|
||||
except MappingError as e:
|
||||
e.details['mapping'] = mapping
|
||||
if mandatory:
|
||||
# it's mandatory, provisionning should fail completely
|
||||
raise e
|
||||
else:
|
||||
logger.debug('auth_saml: action mapping %r failed: %s', mapping, e)
|
||||
|
||||
if user_modified:
|
||||
user.save()
|
||||
logger.warning('auth_saml: mapping action failed: %s', e)
|
||||
return user_modified
|
||||
|
||||
def action_rename(self, user, idp, saml_attributes, mapping):
|
||||
from_name = mapping.get('from')
|
||||
|
@ -157,16 +159,16 @@ class AuthenticAdapter(DefaultAdapter):
|
|||
raise MappingError('missing saml_attribute key')
|
||||
|
||||
if saml_attribute not in saml_attributes:
|
||||
raise MappingError('unknown saml_attribute', details={'saml_attribute': saml_attribute})
|
||||
raise MappingError('unknown saml_attribute')
|
||||
value = saml_attributes[saml_attribute]
|
||||
return self.set_user_attribute(user, attribute, value)
|
||||
|
||||
def set_user_attribute(self, user, attribute, value):
|
||||
if isinstance(value, list):
|
||||
if len(value) == 0:
|
||||
raise MappingError('no value for %s' % attribute, details={'attribute': attribute})
|
||||
raise MappingError('no value')
|
||||
if len(value) > 1:
|
||||
raise MappingError('too many values for %s' % attribute, details={'attribute': attribute})
|
||||
raise MappingError('too many values', details={'value': value})
|
||||
value = value[0]
|
||||
if attribute in ('first_name', 'last_name', 'email', 'username'):
|
||||
if getattr(user, attribute) != value:
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import logging
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -165,6 +165,31 @@ def test_provision_add_role(db, simple_role, action_name):
|
|||
adapter.provision(user, idp, saml_attributes)
|
||||
# condition failed, so role should be removed
|
||||
assert simple_role not in user.roles.all()
|
||||
user.delete()
|
||||
|
||||
# on missing mandatory attribute, no user is created
|
||||
del saml_attributes['mail']
|
||||
assert adapter.lookup_user(idp, saml_attributes) is None
|
||||
|
||||
# simulate no attribute value
|
||||
saml_attributes['first_name'] = []
|
||||
attribute_mapping = [
|
||||
{
|
||||
'mandatory': True,
|
||||
'attribute': 'first_name',
|
||||
'saml_attribute': 'first_name',
|
||||
}
|
||||
]
|
||||
|
||||
# fail fast
|
||||
with pytest.raises(MappingError, match='no value.*first_name'):
|
||||
adapter.apply_attribute_mapping(user, idp, saml_attributes, attribute_mapping)
|
||||
|
||||
# or log a warning
|
||||
caplog.clear()
|
||||
del attribute_mapping[0]['mandatory']
|
||||
adapter.apply_attribute_mapping(user, idp, saml_attributes, attribute_mapping)
|
||||
assert re.match('.*no value.*first_name', caplog.records[0].message)
|
||||
|
||||
|
||||
def test_login_with_conditionnal_authenticators(db, app, settings, caplog):
|
||||
|
|
Loading…
Reference in New Issue