0008-auth_saml-add-views-to-configure-related-objects-670.patch
src/authentic2/apps/authenticators/models.py | ||
---|---|---|
75 | 75 | |
76 | 76 |
type = '' |
77 | 77 |
manager_form_class = None |
78 |
manager_view_template_name = 'authentic2/authenticators/authenticator_detail.html' |
|
78 | 79 |
unique = False |
79 | 80 |
protected = False |
80 | 81 |
description_fields = ['show_condition'] |
src/authentic2/apps/authenticators/templates/authentic2/authenticators/authenticator_detail.html | ||
---|---|---|
31 | 31 |
</div> |
32 | 32 |
{% endif %} |
33 | 33 | |
34 |
<div class='placeholder'> |
|
35 |
{% for line in object.get_full_description %} |
|
36 |
<p>{{ line }}</p> |
|
37 |
{% endfor %} |
|
34 |
<div class='section authenticator-detail'> |
|
35 |
<div class='pk-tabs'> |
|
36 |
<div class="pk-tabs--tab-list" role="tablist"> |
|
37 |
<button aria-controls="panel-description" aria-selected="true" id="tab-description" role="tab" tabindex="0">{% trans "Description" %}</button> |
|
38 |
{% block extra-tab-buttons %} |
|
39 |
{% endblock %} |
|
40 |
</div> |
|
41 | ||
42 |
<div class="pk-tabs--container"> |
|
43 |
<div aria-labelledby="tab-description" id="panel-description" role="tabpanel" tabindex="0"> |
|
44 |
<ul> |
|
45 |
{% for line in object.get_full_description %} |
|
46 |
<li>{{ line }}</li> |
|
47 |
{% endfor %} |
|
48 |
</ul> |
|
49 |
</div> |
|
50 |
{% block extra-tab-list %} |
|
51 |
{% endblock %} |
|
52 |
</div> |
|
53 |
</div> |
|
38 | 54 |
</div> |
39 | 55 |
{% endblock %} |
src/authentic2/apps/authenticators/views.py | ||
---|---|---|
64 | 64 | |
65 | 65 | |
66 | 66 |
class AuthenticatorDetailView(AuthenticatorsMixin, DetailView): |
67 |
template_name = 'authentic2/authenticators/authenticator_detail.html' |
|
67 |
def get_template_names(self): |
|
68 |
return self.object.manager_view_template_name |
|
68 | 69 | |
69 | 70 |
@property |
70 | 71 |
def title(self): |
src/authentic2_auth_saml/forms.py | ||
---|---|---|
16 | 16 | |
17 | 17 |
from django import forms |
18 | 18 | |
19 |
from authentic2.a2_rbac.models import Role |
|
20 |
from authentic2.manager.utils import label_from_role |
|
21 | ||
19 | 22 |
from .models import SAMLAuthenticator |
20 | 23 | |
21 | 24 | |
... | ... | |
23 | 26 |
class Meta: |
24 | 27 |
model = SAMLAuthenticator |
25 | 28 |
exclude = ('ou',) |
29 | ||
30 | ||
31 |
class RoleChoiceField(forms.ModelChoiceField): |
|
32 |
def __init__(self, *args, **kwargs): |
|
33 |
super().__init__(*args, **kwargs) |
|
34 |
self.queryset = Role.objects.exclude(slug__startswith='_') |
|
35 | ||
36 |
def label_from_instance(self, obj): |
|
37 |
return label_from_role(obj) |
src/authentic2_auth_saml/journal_event_types.py | ||
---|---|---|
1 |
# authentic2 - versatile identity manager |
|
2 |
# Copyright (C) 2010-2022 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.utils.translation import gettext_lazy as _ |
|
18 | ||
19 |
from authentic2.apps.journal.models import EventTypeDefinition |
|
20 |
from authentic2.apps.journal.utils import form_to_old_new |
|
21 | ||
22 |
from .models import SAMLAuthenticator |
|
23 | ||
24 | ||
25 |
class SAMLAuthenticatorEvents(EventTypeDefinition): |
|
26 |
@classmethod |
|
27 |
def record(cls, *, user, session, related_object, data=None): |
|
28 |
data = data or {} |
|
29 |
data.update({'related_object': repr(related_object)}) |
|
30 |
super().record(user=user, session=session, references=[related_object.authenticator], data=data) |
|
31 | ||
32 | ||
33 |
class SAMLAuthenticatorRelatedObjectCreation(SAMLAuthenticatorEvents): |
|
34 |
name = 'authenticator.saml.related_object.creation' |
|
35 |
label = _('SAML authenticator related object creation') |
|
36 | ||
37 |
@classmethod |
|
38 |
def get_message(cls, event, context): |
|
39 |
(authenticator,) = event.get_typed_references(SAMLAuthenticator) |
|
40 |
related_object = event.get_data('related_object') |
|
41 |
if context != authenticator: |
|
42 |
return _('creation of {related_object} in authenticator "{authenticator}"').format( |
|
43 |
related_object=related_object, authenticator=authenticator |
|
44 |
) |
|
45 |
else: |
|
46 |
return _('creation of %s') % related_object |
|
47 | ||
48 | ||
49 |
class SAMLAuthenticatorRelatedObjectEdit(SAMLAuthenticatorEvents): |
|
50 |
name = 'authenticator.saml.related_object.edit' |
|
51 |
label = _('SAML authenticator related object edit') |
|
52 | ||
53 |
@classmethod |
|
54 |
def record(cls, *, user, session, form): |
|
55 |
super().record( |
|
56 |
user=user, |
|
57 |
session=session, |
|
58 |
related_object=form.instance, |
|
59 |
data=form_to_old_new(form), |
|
60 |
) |
|
61 | ||
62 |
@classmethod |
|
63 |
def get_message(cls, event, context): |
|
64 |
(authenticator,) = event.get_typed_references(SAMLAuthenticator) |
|
65 |
related_object = event.get_data('related_object') |
|
66 |
new = event.get_data('new') or {} |
|
67 |
edited_attributes = ', '.join(new) or '' |
|
68 |
if context != authenticator: |
|
69 |
return _('edit {related_object} in authenticator "{authenticator}" ({change})').format( |
|
70 |
related_object=related_object, |
|
71 |
authenticator=authenticator, |
|
72 |
change=edited_attributes, |
|
73 |
) |
|
74 |
else: |
|
75 |
return _('edit {related_object} ({change})').format( |
|
76 |
related_object=related_object, change=edited_attributes |
|
77 |
) |
|
78 | ||
79 | ||
80 |
class SAMLAuthenticatorRelatedObjectDeletion(SAMLAuthenticatorEvents): |
|
81 |
name = 'authenticator.saml.related_object.deletion' |
|
82 |
label = _('SAML authenticator related object deletion') |
|
83 | ||
84 |
@classmethod |
|
85 |
def get_message(cls, event, context): |
|
86 |
(authenticator,) = event.get_typed_references(SAMLAuthenticator) |
|
87 |
related_object = event.get_data('related_object') |
|
88 |
if context != authenticator: |
|
89 |
return _('deletion of {related_object} in authenticator "{authenticator}"').format( |
|
90 |
related_object=related_object, authenticator=authenticator |
|
91 |
) |
|
92 |
else: |
|
93 |
return _('deletion of %s') % related_object |
src/authentic2_auth_saml/models.py | ||
---|---|---|
21 | 21 | |
22 | 22 |
from authentic2.a2_rbac.models import Role |
23 | 23 |
from authentic2.apps.authenticators.models import BaseAuthenticator |
24 |
from authentic2.manager.utils import label_from_role |
|
24 | 25 |
from authentic2.utils.misc import redirect_to_login |
25 | 26 | |
26 |
from . import views |
|
27 | ||
28 | 27 | |
29 | 28 |
class SAMLAuthenticator(BaseAuthenticator): |
30 | 29 |
metadata_url = models.URLField(_('Metadata URL'), max_length=300, blank=True) |
... | ... | |
147 | 146 | |
148 | 147 |
type = 'saml' |
149 | 148 |
how = ['saml'] |
149 |
manager_view_template_name = 'authentic2_auth_saml/authenticator_detail.html' |
|
150 | 150 |
description_fields = [ |
151 | 151 |
'show_condition', |
152 | 152 |
'metadata_url', |
... | ... | |
195 | 195 |
) |
196 | 196 | |
197 | 197 |
def login(self, request, *args, **kwargs): |
198 |
from . import views |
|
199 | ||
198 | 200 |
return views.login(request, self, *args, **kwargs) |
199 | 201 | |
200 | 202 |
def profile(self, request, *args, **kwargs): |
203 |
from . import views |
|
204 | ||
201 | 205 |
return views.profile(request, *args, **kwargs) |
202 | 206 | |
203 | 207 | |
... | ... | |
219 | 223 |
default_related_name = 'rename_attribute_actions' |
220 | 224 |
verbose_name = _('Rename an attribute') |
221 | 225 | |
226 |
def __str__(self): |
|
227 |
return '%s → %s' % (self.from_name, self.to_name) |
|
228 | ||
222 | 229 | |
223 | 230 |
class SAMLAttributeLookup(SAMLRelatedObjectBase): |
224 | 231 |
user_field = models.CharField(_('User field'), max_length=256) |
... | ... | |
229 | 236 |
default_related_name = 'attribute_lookups' |
230 | 237 |
verbose_name = _('Attribute lookup') |
231 | 238 | |
239 |
def __str__(self): |
|
240 |
label = _('"%(saml_attribute)s" (from "%(user_field)s")') % { |
|
241 |
'saml_attribute': self.saml_attribute, |
|
242 |
'user_field': self.user_field, |
|
243 |
} |
|
244 |
if self.ignore_case: |
|
245 |
label = '%s, %s' % (label, _('case insensitive')) |
|
246 |
return label |
|
247 | ||
232 | 248 |
def as_dict(self): |
233 | 249 |
return { |
234 | 250 |
'user_field': self.user_field, |
... | ... | |
246 | 262 |
default_related_name = 'set_attribute_actions' |
247 | 263 |
verbose_name = _('Set an attribute') |
248 | 264 | |
265 |
def __str__(self): |
|
266 |
label = _('"%(attribute)s" from "%(saml_attribute)s"') % { |
|
267 |
'attribute': self.attribute, |
|
268 |
'saml_attribute': self.saml_attribute, |
|
269 |
} |
|
270 |
if self.mandatory: |
|
271 |
label = '%s (%s)' % (label, _('mandatory')) |
|
272 |
return label |
|
273 | ||
249 | 274 | |
250 | 275 |
class AddRoleAction(SAMLRelatedObjectBase): |
251 | 276 |
role = models.ForeignKey(Role, verbose_name=_('Role'), on_delete=models.CASCADE) |
... | ... | |
255 | 280 |
class Meta: |
256 | 281 |
default_related_name = 'add_role_actions' |
257 | 282 |
verbose_name = _('Add a role') |
283 | ||
284 |
def __str__(self): |
|
285 |
return label_from_role(self.role) |
src/authentic2_auth_saml/templates/authentic2_auth_saml/authenticator_detail.html | ||
---|---|---|
1 |
{% extends 'authentic2/authenticators/authenticator_detail.html' %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block extra-tab-buttons %} |
|
5 |
<button aria-controls="panel-samlattributelookup" aria-selected="false" id="tab-samlattributelookup" role="tab" tabindex="-1">{% trans "Lookup by attributes" %}</button> |
|
6 |
<button aria-controls="panel-renameattributeaction" aria-selected="false" id="tab-renameattributeaction" role="tab" tabindex="-1">{% trans "Rename attributes" %}</button> |
|
7 |
<button aria-controls="panel-setattributeaction" aria-selected="false" id="tab-setattributeaction" role="tab" tabindex="-1">{% trans "Set attributes" %}</button> |
|
8 |
<button aria-controls="panel-addroleaction" aria-selected="false" id="tab-addroleaction" role="tab" tabindex="-1">{% trans "Add roles" %}</button> |
|
9 |
{% endblock %} |
|
10 | ||
11 |
{% block extra-tab-list %} |
|
12 |
<div aria-labelledby="tab-samlattributelookup" hidden="" id="panel-samlattributelookup" role="tabpanel" tabindex="0"> |
|
13 |
{% include 'authentic2_auth_saml/related_object_list.html' with object_list=object.attribute_lookups.all model_name='samlattributelookup' %} |
|
14 |
</div> |
|
15 | ||
16 |
<div aria-labelledby="tab-renameattributeaction" hidden="" id="panel-renameattributeaction" role="tabpanel" tabindex="0"> |
|
17 |
{% include 'authentic2_auth_saml/related_object_list.html' with object_list=object.rename_attribute_actions.all model_name='renameattributeaction' %} |
|
18 |
</div> |
|
19 | ||
20 |
<div aria-labelledby="tab-setattributeaction" hidden="" id="panel-setattributeaction" role="tabpanel" tabindex="0"> |
|
21 |
{% include 'authentic2_auth_saml/related_object_list.html' with object_list=object.set_attribute_actions.all model_name='setattributeaction' %} |
|
22 |
</div> |
|
23 | ||
24 |
<div aria-labelledby="tab-addroleaction" hidden="" id="panel-addroleaction" role="tabpanel" tabindex="0"> |
|
25 |
{% include 'authentic2_auth_saml/related_object_list.html' with object_list=object.add_role_actions.all model_name='addroleaction' %} |
|
26 |
</div> |
|
27 |
{% endblock %} |
src/authentic2_auth_saml/templates/authentic2_auth_saml/related_object_list.html | ||
---|---|---|
1 |
{% load i18n %} |
|
2 | ||
3 |
<ul class="objects-list single-links"> |
|
4 |
{% for related_object in object_list %} |
|
5 |
<li> |
|
6 |
<a rel="popup" href="{% url 'a2-manager-saml-edit-related-object' authenticator_pk=object.pk model_name=model_name pk=related_object.pk %}">{{ related_object }}</a> |
|
7 |
<a rel="popup" class="delete" href="{% url 'a2-manager-saml-delete-related-object' authenticator_pk=object.pk model_name=model_name pk=related_object.pk %}">{% trans "Remove" %}</a> |
|
8 |
</li> |
|
9 |
{% endfor %} |
|
10 |
<li><a class="add" rel="popup" href="{% url 'a2-manager-saml-add-related-object' authenticator_pk=object.pk model_name=model_name %}">{% trans 'Add' %}</a></li> |
|
11 |
</ul> |
src/authentic2_auth_saml/urls.py | ||
---|---|---|
15 | 15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 | 17 |
from django.conf.urls import include, url |
18 |
from django.urls import path |
|
19 | ||
20 |
from authentic2.apps.authenticators.manager_urls import superuser_login_required |
|
21 |
from authentic2.decorators import required |
|
22 | ||
23 |
from . import views |
|
18 | 24 | |
19 | 25 |
urlpatterns = [ |
20 | 26 |
url(r'^accounts/saml/', include('mellon.urls'), kwargs={'template_base': 'authentic2/base.html'}) |
21 | 27 |
] |
28 | ||
29 |
urlpatterns += required( |
|
30 |
superuser_login_required, |
|
31 |
[ |
|
32 |
path( |
|
33 |
'authenticators/<int:authenticator_pk>/<slug:model_name>/add/', |
|
34 |
views.add_related_object, |
|
35 |
name='a2-manager-saml-add-related-object', |
|
36 |
), |
|
37 |
path( |
|
38 |
'authenticators/<int:authenticator_pk>/<slug:model_name>/<int:pk>/edit/', |
|
39 |
views.edit_related_object, |
|
40 |
name='a2-manager-saml-edit-related-object', |
|
41 |
), |
|
42 |
path( |
|
43 |
'authenticators/<int:authenticator_pk>/<slug:model_name>/<int:pk>/delete/', |
|
44 |
views.delete_related_object, |
|
45 |
name='a2-manager-saml-delete-related-object', |
|
46 |
), |
|
47 |
], |
|
48 |
) |
src/authentic2_auth_saml/views.py | ||
---|---|---|
1 |
from django.shortcuts import render |
|
1 |
from django.apps import apps |
|
2 |
from django.forms.models import modelform_factory |
|
3 |
from django.http import Http404 |
|
4 |
from django.shortcuts import get_object_or_404, render |
|
2 | 5 |
from django.template.loader import render_to_string |
6 |
from django.urls import reverse |
|
7 |
from django.views.generic import CreateView, DeleteView, UpdateView |
|
3 | 8 |
from mellon.utils import get_idp |
4 | 9 | |
10 |
from authentic2.manager.views import MediaMixin, TitleMixin |
|
5 | 11 |
from authentic2.utils.misc import redirect_to_login |
6 | 12 | |
13 |
from .forms import RoleChoiceField |
|
14 |
from .models import ( |
|
15 |
AddRoleAction, |
|
16 |
RenameAttributeAction, |
|
17 |
SAMLAttributeLookup, |
|
18 |
SAMLAuthenticator, |
|
19 |
SetAttributeAction, |
|
20 |
) |
|
21 | ||
7 | 22 | |
8 | 23 |
def login(request, authenticator, *args, **kwargs): |
9 | 24 |
context = kwargs.pop('context', {}).copy() |
... | ... | |
34 | 49 |
user_saml_identifier.idp = get_idp(user_saml_identifier.issuer.entity_id) |
35 | 50 |
context['user_saml_identifiers'] = user_saml_identifiers |
36 | 51 |
return render_to_string('authentic2_auth_saml/profile.html', context, request=request) |
52 | ||
53 | ||
54 |
class SAMLAuthenticatorMixin(MediaMixin, TitleMixin): |
|
55 |
allowed_models = (SAMLAttributeLookup, RenameAttributeAction, SetAttributeAction, AddRoleAction) |
|
56 | ||
57 |
def dispatch(self, request, *args, **kwargs): |
|
58 |
self.authenticator = get_object_or_404(SAMLAuthenticator, pk=kwargs.get('authenticator_pk')) |
|
59 | ||
60 |
model_name = kwargs.get('model_name') |
|
61 |
if model_name not in (x._meta.model_name for x in self.allowed_models): |
|
62 |
raise Http404() |
|
63 |
self.model = apps.get_model('authentic2_auth_saml', model_name) |
|
64 | ||
65 |
return super().dispatch(request, *args, **kwargs) |
|
66 | ||
67 |
def get_form_class(self): |
|
68 |
return modelform_factory( |
|
69 |
self.model, exclude=('authenticator',), field_classes={'role': RoleChoiceField} |
|
70 |
) |
|
71 | ||
72 |
def get_form_kwargs(self): |
|
73 |
kwargs = super().get_form_kwargs() |
|
74 |
if not kwargs.get('instance'): |
|
75 |
kwargs['instance'] = self.model() |
|
76 |
kwargs['instance'].authenticator = self.authenticator |
|
77 |
return kwargs |
|
78 | ||
79 |
def get_success_url(self): |
|
80 |
return ( |
|
81 |
reverse('a2-manager-authenticator-detail', kwargs={'pk': self.authenticator.pk}) |
|
82 |
+ '#open:%s' % self.model._meta.model_name |
|
83 |
) |
|
84 | ||
85 |
@property |
|
86 |
def title(self): |
|
87 |
return self.model._meta.verbose_name |
|
88 | ||
89 | ||
90 |
class RelatedObjectAddView(SAMLAuthenticatorMixin, CreateView): |
|
91 |
template_name = 'authentic2/manager/form.html' |
|
92 | ||
93 |
def form_valid(self, form): |
|
94 |
resp = super().form_valid(form) |
|
95 |
self.request.journal.record( |
|
96 |
'authenticator.saml.related_object.creation', related_object=form.instance |
|
97 |
) |
|
98 |
return resp |
|
99 | ||
100 | ||
101 |
add_related_object = RelatedObjectAddView.as_view() |
|
102 | ||
103 | ||
104 |
class RelatedObjectEditView(SAMLAuthenticatorMixin, UpdateView): |
|
105 |
template_name = 'authentic2/manager/form.html' |
|
106 | ||
107 |
def form_valid(self, form): |
|
108 |
resp = super().form_valid(form) |
|
109 |
self.request.journal.record('authenticator.saml.related_object.edit', form=form) |
|
110 |
return resp |
|
111 | ||
112 | ||
113 |
edit_related_object = RelatedObjectEditView.as_view() |
|
114 | ||
115 | ||
116 |
class RelatedObjectDeleteView(SAMLAuthenticatorMixin, DeleteView): |
|
117 |
template_name = 'authentic2/authenticators/authenticator_delete_form.html' |
|
118 |
title = '' |
|
119 | ||
120 |
def delete(self, *args, **kwargs): |
|
121 |
self.request.journal.record( |
|
122 |
'authenticator.saml.related_object.deletion', related_object=self.get_object() |
|
123 |
) |
|
124 |
return super().delete(*args, **kwargs) |
|
125 | ||
126 | ||
127 |
delete_related_object = RelatedObjectDeleteView.as_view() |
tests/test_manager_authenticators.py | ||
---|---|---|
16 | 16 | |
17 | 17 |
import pytest |
18 | 18 |
from django import VERSION as DJ_VERSION |
19 |
from django.utils.html import escape |
|
19 | 20 | |
20 | 21 |
from authentic2.a2_rbac.utils import get_default_ou |
21 | 22 |
from authentic2.apps.authenticators.models import BaseAuthenticator, LoginPasswordAuthenticator |
... | ... | |
279 | 280 |
assert 'Authenticator has been enabled.' in resp.text |
280 | 281 | |
281 | 282 | |
283 |
def test_authenticators_saml_attribute_lookup(app, superuser): |
|
284 |
authenticator = SAMLAuthenticator.objects.create(metadata='meta1.xml', slug='idp1') |
|
285 |
resp = login(app, superuser, path=authenticator.get_absolute_url()) |
|
286 | ||
287 |
resp = resp.click('Add', href='samlattributelookup') |
|
288 |
resp.form['user_field'] = 'email' |
|
289 |
resp.form['saml_attribute'] = 'mail' |
|
290 |
resp = resp.form.submit() |
|
291 |
assert_event('authenticator.saml.related_object.creation', user=superuser, session=app.session) |
|
292 |
assert '#open:samlattributelookup' in resp.location |
|
293 | ||
294 |
resp = resp.follow() |
|
295 |
assert escape('"mail" (from "email")') in resp.text |
|
296 | ||
297 |
resp = resp.click('mail') |
|
298 |
resp.form['ignore_case'] = True |
|
299 |
resp = resp.form.submit().follow() |
|
300 |
assert escape('"mail" (from "email"), case insensitive') in resp.text |
|
301 |
assert_event('authenticator.saml.related_object.edit', user=superuser, session=app.session) |
|
302 | ||
303 |
resp = resp.click('Remove', href='samlattributelookup') |
|
304 |
resp = resp.form.submit().follow() |
|
305 |
assert 'mail' not in resp.text |
|
306 |
assert_event('authenticator.saml.related_object.deletion', user=superuser, session=app.session) |
|
307 | ||
308 | ||
309 |
def test_authenticators_saml_rename_attribute(app, superuser): |
|
310 |
authenticator = SAMLAuthenticator.objects.create(metadata='meta1.xml', slug='idp1') |
|
311 |
resp = login(app, superuser, path=authenticator.get_absolute_url()) |
|
312 | ||
313 |
resp = resp.click('Add', href='renameattributeaction') |
|
314 |
resp.form['from_name'] = 'a' |
|
315 |
resp.form['to_name'] = 'b' |
|
316 |
resp = resp.form.submit().follow() |
|
317 |
assert 'a → b' in resp.text |
|
318 | ||
319 | ||
320 |
def test_authenticators_saml_set_attribute(app, superuser): |
|
321 |
authenticator = SAMLAuthenticator.objects.create(metadata='meta1.xml', slug='idp1') |
|
322 |
resp = login(app, superuser, path=authenticator.get_absolute_url()) |
|
323 | ||
324 |
resp = resp.click('Add', href='setattributeaction') |
|
325 |
resp.form['attribute'] = 'email' |
|
326 |
resp.form['saml_attribute'] = 'mail' |
|
327 |
resp = resp.form.submit().follow() |
|
328 |
assert escape('"email" from "mail"') in resp.text |
|
329 | ||
330 |
resp = resp.click('mail') |
|
331 |
resp.form['mandatory'] = True |
|
332 |
resp = resp.form.submit().follow() |
|
333 |
assert escape('"email" from "mail" (mandatory)') in resp.text |
|
334 | ||
335 | ||
336 |
def test_authenticators_saml_add_role(app, superuser, role_ou1, role_ou2): |
|
337 |
authenticator = SAMLAuthenticator.objects.create(metadata='meta1.xml', slug='idp1') |
|
338 |
resp = login(app, superuser, path=authenticator.get_absolute_url()) |
|
339 | ||
340 |
resp = resp.click('Add', href='addroleaction') |
|
341 |
assert [x[2] for x in resp.form['role'].options] == ['---------', 'OU1 - role_ou1', 'OU2 - role_ou2'] |
|
342 | ||
343 |
resp.form['role'] = role_ou1.pk |
|
344 |
resp = resp.form.submit().follow() |
|
345 |
assert 'role_ou1' in resp.text |
|
346 | ||
347 |
resp = resp.click('role_ou1') |
|
348 |
resp.form['role'] = role_ou2.pk |
|
349 |
resp = resp.form.submit().follow() |
|
350 |
assert 'role_ou1' not in resp.text |
|
351 |
assert 'role_ou2' in resp.text |
|
352 | ||
353 | ||
282 | 354 |
def test_authenticators_order(app, superuser): |
283 | 355 |
resp = login(app, superuser, path='/manage/authenticators/') |
284 | 356 |
tests/test_manager_journal.py | ||
---|---|---|
28 | 28 |
from authentic2.custom_user.models import Profile, ProfileType, User |
29 | 29 |
from authentic2.journal import journal |
30 | 30 |
from authentic2.models import Service |
31 |
from authentic2_auth_saml.models import RenameAttributeAction, SAMLAuthenticator |
|
31 | 32 | |
32 | 33 |
from .utils import login, logout, text_content |
33 | 34 | |
... | ... | |
58 | 59 |
role_agent = Role.objects.create(name="role2", ou=ou) |
59 | 60 |
service = Service.objects.create(name="service") |
60 | 61 |
authenticator = LoginPasswordAuthenticator.objects.create(slug='test') |
62 |
saml_authenticator = SAMLAuthenticator.objects.create(slug='saml') |
|
63 |
rename_attribute_action = RenameAttributeAction.objects.create(authenticator=saml_authenticator) |
|
61 | 64 | |
62 | 65 |
class EventFactory: |
63 | 66 |
date = make_aware(datetime.datetime(2020, 1, 1)) |
... | ... | |
301 | 304 |
make('authenticator.enable', user=agent, session=session2, authenticator=authenticator) |
302 | 305 |
make('authenticator.disable', user=agent, session=session2, authenticator=authenticator) |
303 | 306 |
make('authenticator.deletion', user=agent, session=session2, authenticator=authenticator) |
307 |
make( |
|
308 |
'authenticator.saml.related_object.creation', |
|
309 |
user=agent, |
|
310 |
session=session2, |
|
311 |
related_object=rename_attribute_action, |
|
312 |
) |
|
313 |
action_edit_form = mock.Mock(spec=['instance', 'initial', 'changed_data', 'cleaned_data']) |
|
314 |
action_edit_form.instance = rename_attribute_action |
|
315 |
action_edit_form.initial = {'from_name': 'old'} |
|
316 |
action_edit_form.changed_data = ['from_name'] |
|
317 |
action_edit_form.cleaned_data = {'from_name': 'new'} |
|
318 |
make('authenticator.saml.related_object.edit', user=agent, session=session2, form=action_edit_form) |
|
319 |
make( |
|
320 |
'authenticator.saml.related_object.deletion', |
|
321 |
user=agent, |
|
322 |
session=session2, |
|
323 |
related_object=rename_attribute_action, |
|
324 |
) |
|
304 | 325 | |
305 | 326 |
# verify we created at least one event for each type |
306 | 327 |
assert set(Event.objects.values_list("type__name", flat=True)) == set(_registry) |
... | ... | |
337 | 358 | |
338 | 359 |
def test_global_journal(app, superuser, events): |
339 | 360 |
response = login(app, user=superuser, path="/manage/") |
361 |
rename_attribute_action = RenameAttributeAction.objects.get() |
|
340 | 362 | |
341 | 363 |
# remove event about admin login |
342 | 364 |
Event.objects.filter(user=superuser).delete() |
... | ... | |
692 | 714 |
'type': 'authenticator.deletion', |
693 | 715 |
'user': 'agent', |
694 | 716 |
}, |
717 |
{ |
|
718 |
'message': 'creation of RenameAttributeAction (%s) in authenticator "SAML"' |
|
719 |
% rename_attribute_action.pk, |
|
720 |
'timestamp': 'Jan. 3, 2020, 8 a.m.', |
|
721 |
'type': 'authenticator.saml.related_object.creation', |
|
722 |
'user': 'agent', |
|
723 |
}, |
|
724 |
{ |
|
725 |
'message': 'edit RenameAttributeAction (%s) in authenticator "SAML" (from_name)' |
|
726 |
% rename_attribute_action.pk, |
|
727 |
'timestamp': 'Jan. 3, 2020, 9 a.m.', |
|
728 |
'type': 'authenticator.saml.related_object.edit', |
|
729 |
'user': 'agent', |
|
730 |
}, |
|
731 |
{ |
|
732 |
'message': 'deletion of RenameAttributeAction (%s) in authenticator "SAML"' |
|
733 |
% rename_attribute_action.pk, |
|
734 |
'timestamp': 'Jan. 3, 2020, 10 a.m.', |
|
735 |
'type': 'authenticator.saml.related_object.deletion', |
|
736 |
'user': 'agent', |
|
737 |
}, |
|
695 | 738 |
] |
696 | 739 | |
697 | 740 |
agent_page = response.click('agent', index=1) |
698 |
- |