23 |
23 |
|
24 |
24 |
from django.contrib.auth import get_user_model
|
25 |
25 |
from authentic2.models import Attribute
|
26 |
|
from authentic2_auth_saml.adapters import MappingError
|
|
26 |
from authentic2_auth_saml.adapters import AuthenticAdapter, MappingError
|
|
27 |
|
|
28 |
User = get_user_model()
|
27 |
29 |
|
28 |
30 |
|
29 |
31 |
def test_providers_on_login_page(db, app, settings):
|
... | ... | |
52 |
54 |
assert response.pyquery('button[name="login-saml-1"]')
|
53 |
55 |
|
54 |
56 |
|
55 |
|
def test_provision_attributes(db, caplog):
|
56 |
|
from authentic2_auth_saml.adapters import AuthenticAdapter
|
|
57 |
@pytest.fixture
|
|
58 |
def adapter():
|
|
59 |
return AuthenticAdapter()
|
|
60 |
|
57 |
61 |
|
58 |
|
adapter = AuthenticAdapter()
|
59 |
|
User = get_user_model()
|
60 |
|
Attribute.objects.create(kind='title', name='title', label='title')
|
|
62 |
@pytest.fixture
|
|
63 |
def idp():
|
61 |
64 |
|
62 |
|
user = User.objects.create()
|
63 |
|
idp = {
|
|
65 |
return {
|
64 |
66 |
'A2_ATTRIBUTE_MAPPING': [
|
65 |
67 |
{
|
66 |
68 |
'attribute': 'email',
|
... | ... | |
83 |
85 |
]
|
84 |
86 |
}
|
85 |
87 |
|
86 |
|
saml_attributes = {
|
87 |
|
u'issuer': 'https://idp.com/',
|
88 |
|
u'name_id_content': 'xxx',
|
89 |
|
u'name_id_format': lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT,
|
90 |
|
u'mail': [u'john.doe@example.com'],
|
91 |
|
u'title': [u'Mr.'],
|
92 |
|
u'http://fucking/attribute/givenName': ['John'],
|
93 |
|
}
|
94 |
|
user = adapter.lookup_user(idp, saml_attributes)
|
95 |
|
user.refresh_from_db()
|
96 |
|
assert user.email == 'john.doe@example.com'
|
97 |
|
assert user.attributes.title == 'Mr.'
|
98 |
|
assert user.first_name == 'John'
|
99 |
|
user.delete()
|
100 |
|
|
101 |
|
# on missing mandatory attribute, no user is created
|
102 |
|
del saml_attributes['mail']
|
103 |
|
assert adapter.lookup_user(idp, saml_attributes) is None
|
104 |
88 |
|
105 |
|
# simulate no attribute value
|
106 |
|
saml_attributes['first_name'] = []
|
107 |
|
mapping = {
|
108 |
|
'attribute': 'first_name',
|
109 |
|
'saml_attribute': 'first_name',
|
110 |
|
}
|
111 |
|
with pytest.raises(MappingError, match='no value for first_name'):
|
112 |
|
adapter.action_set_attribute(user, idp, saml_attributes, mapping)
|
|
89 |
@pytest.fixture
|
|
90 |
def title_attribute(db):
|
|
91 |
return Attribute.objects.create(kind='title', name='title', label='title')
|
113 |
92 |
|
114 |
93 |
|
115 |
|
@pytest.mark.parametrize('action_name', ['add-role', 'toggle-role'])
|
116 |
|
def test_provision_add_role(db, simple_role, action_name):
|
117 |
|
from authentic2_auth_saml.adapters import AuthenticAdapter
|
118 |
|
|
119 |
|
adapter = AuthenticAdapter()
|
120 |
|
User = get_user_model()
|
121 |
|
user = User.objects.create()
|
122 |
|
idp = {
|
123 |
|
'A2_ATTRIBUTE_MAPPING': [
|
124 |
|
{
|
125 |
|
'action': action_name,
|
126 |
|
'role': {
|
127 |
|
'name': simple_role.name,
|
128 |
|
'ou': {
|
129 |
|
'name': simple_role.ou.name,
|
130 |
|
},
|
131 |
|
},
|
132 |
|
'condition': "roles == 'A'",
|
133 |
|
}
|
134 |
|
]
|
135 |
|
}
|
136 |
|
|
137 |
|
saml_attributes = {
|
|
94 |
@pytest.fixture
|
|
95 |
def saml_attributes():
|
|
96 |
return {
|
138 |
97 |
'issuer': 'https://idp.com/',
|
139 |
98 |
'name_id_content': 'xxx',
|
140 |
99 |
'name_id_format': lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT,
|
|
100 |
'mail': ['john.doe@example.com'],
|
|
101 |
'title': ['Mr.'],
|
|
102 |
'http://fucking/attribute/givenName': ['John'],
|
141 |
103 |
}
|
142 |
|
user = adapter.lookup_user(idp, saml_attributes)
|
143 |
|
user.refresh_from_db()
|
144 |
|
assert simple_role not in user.roles.all()
|
145 |
|
assert user.ou.default is True
|
146 |
|
user.delete()
|
147 |
104 |
|
148 |
|
# if a toggle-role is mandatory, failure to evaluate condition block user creation
|
149 |
|
assert idp['A2_ATTRIBUTE_MAPPING'][-1]['action'] == action_name
|
150 |
|
idp['A2_ATTRIBUTE_MAPPING'][-1]['mandatory'] = True
|
151 |
|
assert adapter.lookup_user(idp, saml_attributes) is None
|
152 |
105 |
|
153 |
|
saml_attributes['roles'] = ['A']
|
154 |
|
user = adapter.lookup_user(idp, saml_attributes)
|
155 |
|
user.refresh_from_db()
|
156 |
|
assert simple_role in user.roles.all()
|
157 |
|
user.delete()
|
|
106 |
@pytest.fixture
|
|
107 |
def user(db):
|
|
108 |
return User.objects.create()
|
|
109 |
|
|
110 |
|
|
111 |
def test_lookup_user_ok(adapter, idp, saml_attributes, title_attribute):
|
|
112 |
assert User.objects.count() == 0
|
158 |
113 |
|
159 |
|
idp['A2_ATTRIBUTE_MAPPING'][-1]['condition'] = "'A' in roles__list"
|
160 |
114 |
user = adapter.lookup_user(idp, saml_attributes)
|
161 |
115 |
user.refresh_from_db()
|
162 |
|
assert simple_role in user.roles.all()
|
|
116 |
assert user.email == 'john.doe@example.com'
|
|
117 |
assert user.attributes.title == 'Mr.'
|
|
118 |
assert user.first_name == 'John'
|
|
119 |
assert user.attributes.title == 'Mr.'
|
|
120 |
assert user.ou.default is True
|
163 |
121 |
|
164 |
|
saml_attributes['roles'] = []
|
165 |
|
adapter.provision(user, idp, saml_attributes)
|
166 |
|
# condition failed, so role should be removed
|
167 |
|
assert simple_role not in user.roles.all()
|
168 |
|
user.delete()
|
169 |
122 |
|
170 |
|
# on missing mandatory attribute, no user is created
|
|
123 |
def test_lookup_user_missing_mandatory_attribute(adapter, idp, saml_attributes, title_attribute):
|
171 |
124 |
del saml_attributes['mail']
|
|
125 |
|
|
126 |
assert User.objects.count() == 0
|
172 |
127 |
assert adapter.lookup_user(idp, saml_attributes) is None
|
|
128 |
assert User.objects.count() == 0
|
|
129 |
|
|
130 |
|
|
131 |
def test_apply_attribute_mapping_missing_attribute_logged(caplog, adapter, idp, saml_attributes, title_attribute, user):
|
|
132 |
caplog.set_level('WARNING')
|
|
133 |
saml_attributes['http://fucking/attribute/givenName'] = []
|
|
134 |
adapter.apply_attribute_mapping(user, idp, saml_attributes, idp['A2_ATTRIBUTE_MAPPING'])
|
|
135 |
assert re.match('.*no value.*first_name', caplog.records[-1].message)
|
|
136 |
|
|
137 |
|
|
138 |
def test_apply_attribute_mapping_missing_attribute_exception(adapter, idp, saml_attributes, title_attribute, user):
|
|
139 |
saml_attributes['http://fucking/attribute/givenName'] = []
|
|
140 |
idp['A2_ATTRIBUTE_MAPPING'][-1]['mandatory'] = True
|
|
141 |
with pytest.raises(MappingError, match='no value'):
|
|
142 |
adapter.apply_attribute_mapping(user, idp, saml_attributes, idp['A2_ATTRIBUTE_MAPPING'])
|
173 |
143 |
|
174 |
|
# simulate no attribute value
|
175 |
|
saml_attributes['first_name'] = []
|
176 |
|
attribute_mapping = [
|
177 |
|
{
|
178 |
|
'mandatory': True,
|
179 |
|
'attribute': 'first_name',
|
180 |
|
'saml_attribute': 'first_name',
|
|
144 |
|
|
145 |
@pytest.mark.parametrize('action_name', ['add-role', 'toggle-role'])
|
|
146 |
class TestAddRole:
|
|
147 |
@pytest.fixture
|
|
148 |
def idp(self, action_name, simple_role):
|
|
149 |
return {
|
|
150 |
'A2_ATTRIBUTE_MAPPING': [
|
|
151 |
{
|
|
152 |
'action': action_name,
|
|
153 |
'role': {
|
|
154 |
'name': simple_role.name,
|
|
155 |
'ou': {
|
|
156 |
'name': simple_role.ou.name,
|
|
157 |
},
|
|
158 |
},
|
|
159 |
'condition': "roles == 'A'",
|
|
160 |
}
|
|
161 |
]
|
181 |
162 |
}
|
182 |
|
]
|
183 |
163 |
|
184 |
|
# fail fast
|
185 |
|
with pytest.raises(MappingError, match='no value.*first_name'):
|
186 |
|
adapter.apply_attribute_mapping(user, idp, saml_attributes, attribute_mapping)
|
|
164 |
@pytest.fixture
|
|
165 |
def saml_attributes(self):
|
|
166 |
return {
|
|
167 |
'issuer': 'https://idp.com/',
|
|
168 |
'name_id_content': 'xxx',
|
|
169 |
'name_id_format': lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT,
|
|
170 |
}
|
187 |
171 |
|
188 |
|
# or log a warning
|
189 |
|
caplog.clear()
|
190 |
|
del attribute_mapping[0]['mandatory']
|
191 |
|
adapter.apply_attribute_mapping(user, idp, saml_attributes, attribute_mapping)
|
192 |
|
assert re.match('.*no value.*first_name', caplog.records[0].message)
|
|
172 |
def test_lookup_user_condition_fails(self, adapter, simple_role, idp, saml_attributes):
|
|
173 |
user = adapter.lookup_user(idp, saml_attributes)
|
|
174 |
assert simple_role not in user.roles.all()
|
|
175 |
|
|
176 |
def test_lookup_user_condition_success(self, adapter, simple_role, idp, saml_attributes):
|
|
177 |
saml_attributes['roles'] = ['A']
|
|
178 |
user = adapter.lookup_user(idp, saml_attributes)
|
|
179 |
assert simple_role in user.roles.all()
|
|
180 |
|
|
181 |
def test_lookup_user_mandatory_condition(self, adapter, simple_role, idp, saml_attributes):
|
|
182 |
# if a toggle-role is mandatory, failure to evaluate condition block user creation
|
|
183 |
idp['A2_ATTRIBUTE_MAPPING'][0]['mandatory'] = True
|
|
184 |
assert adapter.lookup_user(idp, saml_attributes) is None
|
|
185 |
|
|
186 |
def test_lookup_user_mandatory(self, adapter, simple_role, idp, saml_attributes):
|
|
187 |
idp['A2_ATTRIBUTE_MAPPING'][0]['mandatory'] = True
|
|
188 |
saml_attributes['roles'] = ['A']
|
|
189 |
user = adapter.lookup_user(idp, saml_attributes)
|
|
190 |
assert simple_role in user.roles.all()
|
|
191 |
|
|
192 |
def test_lookup_user_use_list(self, adapter, simple_role, idp, saml_attributes):
|
|
193 |
idp['A2_ATTRIBUTE_MAPPING'][0]['condition'] = "'A' in roles__list"
|
|
194 |
saml_attributes['roles'] = ['A']
|
|
195 |
user = adapter.lookup_user(idp, saml_attributes)
|
|
196 |
assert simple_role in user.roles.all()
|
|
197 |
|
|
198 |
def test_lookup_user_add_and_remove(self, adapter, simple_role, idp, saml_attributes, caplog):
|
|
199 |
saml_attributes['roles'] = ['A']
|
|
200 |
user = adapter.lookup_user(idp, saml_attributes)
|
|
201 |
assert simple_role in user.roles.all()
|
|
202 |
|
|
203 |
saml_attributes['roles'] = []
|
|
204 |
adapter.provision(user, idp, saml_attributes)
|
|
205 |
# condition failed, so role should be removed
|
|
206 |
user.refresh_from_db()
|
|
207 |
assert simple_role not in user.roles.all()
|
193 |
208 |
|
194 |
209 |
|
195 |
210 |
def test_login_with_conditionnal_authenticators(db, app, settings, caplog):
|
196 |
|
-
|