auth_saml: update error message on user creation failure (#62930)
This commit is contained in:
parent
b6ab4b32aa
commit
968f4fae4f
|
@ -21,6 +21,7 @@ from django.contrib import messages
|
|||
from django.core.exceptions import MultipleObjectsReturned
|
||||
from django.db.transaction import atomic
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_noop as N_
|
||||
from mellon.adapters import DefaultAdapter, UserCreationError
|
||||
from mellon.utils import get_setting
|
||||
|
||||
|
@ -37,15 +38,17 @@ logger = logging.getLogger('authentic2.auth_saml')
|
|||
class MappingError(Exception):
|
||||
details = None
|
||||
|
||||
def __init__(self, message, details=None):
|
||||
self.details = details or {}
|
||||
super().__init__(message)
|
||||
def __init__(self, msg, *args, **kwargs):
|
||||
assert msg % kwargs
|
||||
self.msg = msg
|
||||
self.kwargs = kwargs
|
||||
super().__init__(*args)
|
||||
|
||||
def message(self):
|
||||
return _(self.msg) % self.kwargs
|
||||
|
||||
def __str__(self):
|
||||
s = str(self.args[0])
|
||||
if self.details:
|
||||
s += ' ' + repr(self.details)
|
||||
return s
|
||||
return N_(self.msg) % self.kwargs
|
||||
|
||||
|
||||
class SamlConditionContextProxy:
|
||||
|
@ -76,7 +79,7 @@ class AuthenticAdapter(DefaultAdapter):
|
|||
except MappingError as e:
|
||||
logger.warning('auth_saml: user creation failed on a mandatory mapping action, %s', e)
|
||||
if self.request:
|
||||
messages.error(self.request, _('user creation failed on a mandatory mapping action: %s') % e)
|
||||
messages.error(self.request, _('User creation failed: %s.') % e.message())
|
||||
raise UserCreationError
|
||||
if not user.ou:
|
||||
user.ou = get_default_ou()
|
||||
|
@ -121,7 +124,7 @@ class AuthenticAdapter(DefaultAdapter):
|
|||
user_modified = False
|
||||
for mapping in attribute_mapping:
|
||||
if not isinstance(mapping, dict):
|
||||
raise MappingError('invalid mapping action', details={'mapping': mapping})
|
||||
raise MappingError('invalid mapping action "%(mapping)s"', mapping=mapping)
|
||||
action = mapping.get('action', 'set-attribute')
|
||||
mandatory = mapping.get('mandatory', False) is True
|
||||
method = None
|
||||
|
@ -137,7 +140,6 @@ class AuthenticAdapter(DefaultAdapter):
|
|||
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
|
||||
|
@ -171,9 +173,13 @@ class AuthenticAdapter(DefaultAdapter):
|
|||
def set_user_attribute(self, user, attribute, value):
|
||||
if isinstance(value, list):
|
||||
if len(value) == 0:
|
||||
raise MappingError('no value')
|
||||
raise MappingError('no value for attribute "%(attribute)s"', attribute=attribute)
|
||||
if len(value) > 1:
|
||||
raise MappingError('too many values', details={'value': value})
|
||||
raise MappingError(
|
||||
'too many values for attribute "%(attribute)s": %(value)s',
|
||||
attribute=attribute,
|
||||
value=value,
|
||||
)
|
||||
value = value[0]
|
||||
if attribute in ('first_name', 'last_name', 'email', 'username'):
|
||||
if getattr(user, attribute) != value:
|
||||
|
@ -201,14 +207,14 @@ class AuthenticAdapter(DefaultAdapter):
|
|||
try:
|
||||
return OU.objects.get(slug=slug)
|
||||
except OU.DoesNotExist:
|
||||
raise MappingError('unknown ou', details={'slug': slug})
|
||||
raise MappingError('unknown ou: "%(slug)s"', slug=slug)
|
||||
elif name:
|
||||
if not isinstance(name, str):
|
||||
raise MappingError('invalid ou.slug in ou description')
|
||||
try:
|
||||
return OU.objects.get(name=name)
|
||||
except OU.DoesNotExist:
|
||||
raise MappingError('unknown ou', details={'name': name})
|
||||
raise MappingError('unknown ou: "%(name)s"', name=name)
|
||||
else:
|
||||
raise MappingError('invalid ou description')
|
||||
|
||||
|
@ -226,11 +232,11 @@ class AuthenticAdapter(DefaultAdapter):
|
|||
|
||||
if slug:
|
||||
if not isinstance(slug, str):
|
||||
raise MappingError('invalid role slug', details={'slug': slug})
|
||||
raise MappingError('invalid role slug: "%(slug)s"', slug=slug)
|
||||
kwargs['slug'] = slug
|
||||
elif name:
|
||||
if not isinstance(name, str):
|
||||
raise MappingError('invalid role name', details={'name': name})
|
||||
raise MappingError('invalid role name: "%(name)s"', name=name)
|
||||
kwargs['name'] = name
|
||||
else:
|
||||
raise MappingError('invalid role description')
|
||||
|
@ -238,9 +244,9 @@ class AuthenticAdapter(DefaultAdapter):
|
|||
try:
|
||||
return Role.objects.get(**kwargs)
|
||||
except Role.DoesNotExist:
|
||||
raise MappingError('unknown role', details=kwargs)
|
||||
raise MappingError('unknown role: %(kwargs)s', kwargs=kwargs)
|
||||
except MultipleObjectsReturned:
|
||||
raise MappingError('ambiuous role description', details=kwargs)
|
||||
raise MappingError('ambiugous role description: %(kwargs)s', kwargs=kwargs)
|
||||
|
||||
def evaluate_condition(self, user, saml_attributes, mapping):
|
||||
condition = mapping.get('condition')
|
||||
|
@ -255,7 +261,7 @@ class AuthenticAdapter(DefaultAdapter):
|
|||
logger.debug('auth_saml: condition %r is %s', condition, value, extra={'user': user})
|
||||
return value
|
||||
except Exception as e:
|
||||
raise MappingError('condition evaluation failed', details={'error': str(e)})
|
||||
raise MappingError('condition evaluation failed: %(message)s', message=e)
|
||||
|
||||
def action_add_role(self, user, idp, saml_attributes, mapping):
|
||||
role = self.get_role(mapping)
|
||||
|
|
|
@ -21,6 +21,7 @@ from unittest import mock
|
|||
import lasso
|
||||
import pytest
|
||||
from django.contrib.auth import get_user_model
|
||||
from mellon.adapters import UserCreationError
|
||||
from mellon.models import Issuer, UserSAMLIdentifier
|
||||
|
||||
from authentic2.custom_user.models import DeletedUser
|
||||
|
@ -138,13 +139,22 @@ def test_apply_attribute_mapping_missing_attribute_logged(
|
|||
|
||||
|
||||
def test_apply_attribute_mapping_missing_attribute_exception(
|
||||
adapter, idp, saml_attributes, title_attribute, user
|
||||
adapter, idp, saml_attributes, title_attribute, user, rf
|
||||
):
|
||||
saml_attributes['http://fucking/attribute/givenName'] = []
|
||||
idp['A2_ATTRIBUTE_MAPPING'][-1]['mandatory'] = True
|
||||
with pytest.raises(MappingError, match='no value'):
|
||||
adapter.apply_attribute_mapping(user, idp, saml_attributes, idp['A2_ATTRIBUTE_MAPPING'])
|
||||
|
||||
request = rf.get('/')
|
||||
request._messages = mock.Mock()
|
||||
adapter.request = request
|
||||
with pytest.raises(UserCreationError):
|
||||
adapter.finish_create_user(idp, saml_attributes, user)
|
||||
request._messages.add.assert_called_once_with(
|
||||
40, 'User creation failed: no value for attribute "first_name".', ''
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('action_name', ['add-role', 'toggle-role'])
|
||||
class TestAddRole:
|
||||
|
|
Loading…
Reference in New Issue