auth_saml: reorganize and fix tests (#48117)

This commit is contained in:
Benjamin Dauvergne 2020-10-29 16:11:19 +01:00
parent 7b002f861f
commit 8d6b4653e3
1 changed files with 114 additions and 99 deletions

View File

@ -23,7 +23,9 @@ import lasso
from django.contrib.auth import get_user_model
from authentic2.models import Attribute
from authentic2_auth_saml.adapters import MappingError
from authentic2_auth_saml.adapters import AuthenticAdapter, MappingError
User = get_user_model()
def test_providers_on_login_page(db, app, settings):
@ -52,15 +54,15 @@ def test_providers_on_login_page(db, app, settings):
assert response.pyquery('button[name="login-saml-1"]')
def test_provision_attributes(db, caplog):
from authentic2_auth_saml.adapters import AuthenticAdapter
@pytest.fixture
def adapter():
return AuthenticAdapter()
adapter = AuthenticAdapter()
User = get_user_model()
Attribute.objects.create(kind='title', name='title', label='title')
user = User.objects.create()
idp = {
@pytest.fixture
def idp():
return {
'A2_ATTRIBUTE_MAPPING': [
{
'attribute': 'email',
@ -83,113 +85,126 @@ def test_provision_attributes(db, caplog):
]
}
saml_attributes = {
u'issuer': 'https://idp.com/',
u'name_id_content': 'xxx',
u'name_id_format': lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT,
u'mail': [u'john.doe@example.com'],
u'title': [u'Mr.'],
u'http://fucking/attribute/givenName': ['John'],
@pytest.fixture
def title_attribute(db):
return Attribute.objects.create(kind='title', name='title', label='title')
@pytest.fixture
def saml_attributes():
return {
'issuer': 'https://idp.com/',
'name_id_content': 'xxx',
'name_id_format': lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT,
'mail': ['john.doe@example.com'],
'title': ['Mr.'],
'http://fucking/attribute/givenName': ['John'],
}
@pytest.fixture
def user(db):
return User.objects.create()
def test_lookup_user_ok(adapter, idp, saml_attributes, title_attribute):
assert User.objects.count() == 0
user = adapter.lookup_user(idp, saml_attributes)
user.refresh_from_db()
assert user.email == 'john.doe@example.com'
assert user.attributes.title == 'Mr.'
assert user.first_name == 'John'
user.delete()
assert user.attributes.title == 'Mr.'
assert user.ou.default is True
# on missing mandatory attribute, no user is created
def test_lookup_user_missing_mandatory_attribute(adapter, idp, saml_attributes, title_attribute):
del saml_attributes['mail']
assert adapter.lookup_user(idp, saml_attributes) is None
# simulate no attribute value
saml_attributes['first_name'] = []
mapping = {
'attribute': 'first_name',
'saml_attribute': 'first_name',
}
with pytest.raises(MappingError, match='no value for first_name'):
adapter.action_set_attribute(user, idp, saml_attributes, mapping)
assert User.objects.count() == 0
assert adapter.lookup_user(idp, saml_attributes) is None
assert User.objects.count() == 0
def test_apply_attribute_mapping_missing_attribute_logged(caplog, adapter, idp, saml_attributes, title_attribute, user):
caplog.set_level('WARNING')
saml_attributes['http://fucking/attribute/givenName'] = []
adapter.apply_attribute_mapping(user, idp, saml_attributes, idp['A2_ATTRIBUTE_MAPPING'])
assert re.match('.*no value.*first_name', caplog.records[-1].message)
def test_apply_attribute_mapping_missing_attribute_exception(adapter, idp, saml_attributes, title_attribute, user):
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'])
@pytest.mark.parametrize('action_name', ['add-role', 'toggle-role'])
def test_provision_add_role(db, simple_role, action_name):
from authentic2_auth_saml.adapters import AuthenticAdapter
adapter = AuthenticAdapter()
User = get_user_model()
user = User.objects.create()
idp = {
'A2_ATTRIBUTE_MAPPING': [
{
'action': action_name,
'role': {
'name': simple_role.name,
'ou': {
'name': simple_role.ou.name,
class TestAddRole:
@pytest.fixture
def idp(self, action_name, simple_role):
return {
'A2_ATTRIBUTE_MAPPING': [
{
'action': action_name,
'role': {
'name': simple_role.name,
'ou': {
'name': simple_role.ou.name,
},
},
},
'condition': "roles == 'A'",
}
]
}
saml_attributes = {
'issuer': 'https://idp.com/',
'name_id_content': 'xxx',
'name_id_format': lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT,
}
user = adapter.lookup_user(idp, saml_attributes)
user.refresh_from_db()
assert simple_role not in user.roles.all()
assert user.ou.default is True
user.delete()
# if a toggle-role is mandatory, failure to evaluate condition block user creation
assert idp['A2_ATTRIBUTE_MAPPING'][-1]['action'] == action_name
idp['A2_ATTRIBUTE_MAPPING'][-1]['mandatory'] = True
assert adapter.lookup_user(idp, saml_attributes) is None
saml_attributes['roles'] = ['A']
user = adapter.lookup_user(idp, saml_attributes)
user.refresh_from_db()
assert simple_role in user.roles.all()
user.delete()
idp['A2_ATTRIBUTE_MAPPING'][-1]['condition'] = "'A' in roles__list"
user = adapter.lookup_user(idp, saml_attributes)
user.refresh_from_db()
assert simple_role in user.roles.all()
saml_attributes['roles'] = []
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',
'condition': "roles == 'A'",
}
]
}
]
# fail fast
with pytest.raises(MappingError, match='no value.*first_name'):
adapter.apply_attribute_mapping(user, idp, saml_attributes, attribute_mapping)
@pytest.fixture
def saml_attributes(self):
return {
'issuer': 'https://idp.com/',
'name_id_content': 'xxx',
'name_id_format': lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT,
}
# 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_lookup_user_condition_fails(self, adapter, simple_role, idp, saml_attributes):
user = adapter.lookup_user(idp, saml_attributes)
assert simple_role not in user.roles.all()
def test_lookup_user_condition_success(self, adapter, simple_role, idp, saml_attributes):
saml_attributes['roles'] = ['A']
user = adapter.lookup_user(idp, saml_attributes)
assert simple_role in user.roles.all()
def test_lookup_user_mandatory_condition(self, adapter, simple_role, idp, saml_attributes):
# if a toggle-role is mandatory, failure to evaluate condition block user creation
idp['A2_ATTRIBUTE_MAPPING'][0]['mandatory'] = True
assert adapter.lookup_user(idp, saml_attributes) is None
def test_lookup_user_mandatory(self, adapter, simple_role, idp, saml_attributes):
idp['A2_ATTRIBUTE_MAPPING'][0]['mandatory'] = True
saml_attributes['roles'] = ['A']
user = adapter.lookup_user(idp, saml_attributes)
assert simple_role in user.roles.all()
def test_lookup_user_use_list(self, adapter, simple_role, idp, saml_attributes):
idp['A2_ATTRIBUTE_MAPPING'][0]['condition'] = "'A' in roles__list"
saml_attributes['roles'] = ['A']
user = adapter.lookup_user(idp, saml_attributes)
assert simple_role in user.roles.all()
def test_lookup_user_add_and_remove(self, adapter, simple_role, idp, saml_attributes, caplog):
saml_attributes['roles'] = ['A']
user = adapter.lookup_user(idp, saml_attributes)
assert simple_role in user.roles.all()
saml_attributes['roles'] = []
adapter.provision(user, idp, saml_attributes)
# condition failed, so role should be removed
user.refresh_from_db()
assert simple_role not in user.roles.all()
def test_login_with_conditionnal_authenticators(db, app, settings, caplog):