From 8d6b4653e3374f1988f0e7e43cb380e4bc6dae1f Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Thu, 29 Oct 2020 16:11:19 +0100 Subject: [PATCH] auth_saml: reorganize and fix tests (#48117) --- tests/test_auth_saml.py | 205 +++++++++++++++++++++------------------- 1 file changed, 110 insertions(+), 95 deletions(-) diff --git a/tests/test_auth_saml.py b/tests/test_auth_saml.py index f69a405f..0494b337 100644 --- a/tests/test_auth_saml.py +++ b/tests/test_auth_saml.py @@ -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') +@pytest.fixture +def idp(): - user = User.objects.create() - 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'], - } - 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() - - # 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'] = [] - 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) +@pytest.fixture +def title_attribute(db): + return Attribute.objects.create(kind='title', name='title', label='title') -@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, - }, - }, - 'condition': "roles == 'A'", - } - ] - } - - saml_attributes = { +@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'], } - 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() +@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 - 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() + assert user.email == 'john.doe@example.com' + assert user.attributes.title == 'Mr.' + assert user.first_name == 'John' + assert user.attributes.title == 'Mr.' + assert user.ou.default is True - 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 +def test_lookup_user_missing_mandatory_attribute(adapter, idp, saml_attributes, title_attribute): del saml_attributes['mail'] + + 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']) - # simulate no attribute value - saml_attributes['first_name'] = [] - attribute_mapping = [ - { - 'mandatory': True, - 'attribute': 'first_name', - 'saml_attribute': 'first_name', + +@pytest.mark.parametrize('action_name', ['add-role', 'toggle-role']) +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'", + } + ] } - ] - # 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): -- 2.29.1