0009-auth_oidc-use-generic-related-object-code-53442.patch
src/authentic2_auth_oidc/forms.py | ||
---|---|---|
16 | 16 | |
17 | 17 |
from django import forms |
18 | 18 | |
19 |
from authentic2.forms.fields import RoleChoiceField |
|
19 | 20 |
from authentic2.forms.widgets import DatalistTextInput, SelectAttributeWidget |
20 | 21 | |
21 | 22 |
from .models import OIDCClaimMapping, OIDCProvider |
... | ... | |
80 | 81 |
widgets = { |
81 | 82 |
'claim': OIDCClaimTextInput, |
82 | 83 |
} |
84 | ||
85 | ||
86 |
class OIDCRelatedObjectForm(forms.ModelForm): |
|
87 |
class Meta: |
|
88 |
exclude = ('authenticator',) |
|
89 |
field_classes = {'role': RoleChoiceField} |
|
90 |
widgets = { |
|
91 |
'claim': OIDCClaimTextInput, |
|
92 |
'attribute': SelectAttributeWidget, |
|
93 |
} |
src/authentic2_auth_oidc/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 OIDCProvider |
|
23 | ||
24 | ||
25 |
class OIDCClaimMappingEvents(EventTypeDefinition): |
|
26 |
@classmethod |
|
27 |
def record(cls, *, user, session, claim, data=None): |
|
28 |
data = data or {} |
|
29 |
data.update({'claim_id': claim.pk}) |
|
30 |
super().record(user=user, session=session, references=[claim.provider], data=data) |
|
31 | ||
32 | ||
33 |
class OIDCClaimMappingCreation(OIDCClaimMappingEvents): |
|
34 |
name = 'authenticator.oidc.claim.creation' |
|
35 |
label = _('OIDC provider claim creation') |
|
36 | ||
37 |
@classmethod |
|
38 |
def get_message(cls, event, context): |
|
39 |
(provider,) = event.get_typed_references(OIDCProvider) |
|
40 |
claim_id = event.get_data('claim_id') |
|
41 |
if context != provider: |
|
42 |
return _('creation of claim ({claim_id}) in provider "{provider}"').format( |
|
43 |
claim_id=claim_id, provider=provider |
|
44 |
) |
|
45 |
else: |
|
46 |
return _('creation of claim (%s)') % claim_id |
|
47 | ||
48 | ||
49 |
class OIDCClaimMappingEdit(OIDCClaimMappingEvents): |
|
50 |
name = 'authenticator.oidc.claim.edit' |
|
51 |
label = _('OIDC provider claim edit') |
|
52 | ||
53 |
@classmethod |
|
54 |
def record(cls, *, user, session, form): |
|
55 |
super().record( |
|
56 |
user=user, |
|
57 |
session=session, |
|
58 |
claim=form.instance, |
|
59 |
data=form_to_old_new(form), |
|
60 |
) |
|
61 | ||
62 |
@classmethod |
|
63 |
def get_message(cls, event, context): |
|
64 |
(provider,) = event.get_typed_references(OIDCProvider) |
|
65 |
claim_id = event.get_data('claim_id') |
|
66 |
new = event.get_data('new') or {} |
|
67 |
edited_attributes = ', '.join(new) or '' |
|
68 |
if context != provider: |
|
69 |
return _('edit claim ({claim_id}) in provider "{provider}" ({change})').format( |
|
70 |
claim_id=claim_id, |
|
71 |
provider=provider, |
|
72 |
change=edited_attributes, |
|
73 |
) |
|
74 |
else: |
|
75 |
return _('edit claim ({claim_id}) ({change})').format(claim_id=claim_id, change=edited_attributes) |
|
76 | ||
77 | ||
78 |
class OIDCClaimMappingDeletion(OIDCClaimMappingEvents): |
|
79 |
name = 'authenticator.oidc.claim.deletion' |
|
80 |
label = _('OIDC provider claim deletion') |
|
81 | ||
82 |
@classmethod |
|
83 |
def get_message(cls, event, context): |
|
84 |
(provider,) = event.get_typed_references(OIDCProvider) |
|
85 |
claim_id = event.get_data('claim_id') |
|
86 |
if context != provider: |
|
87 |
return _('deletion of claim ({claim_id}) in provider "{provider}"').format( |
|
88 |
claim_id=claim_id, provider=provider |
|
89 |
) |
|
90 |
else: |
|
91 |
return _('deletion of claim %s') % claim_id |
src/authentic2_auth_oidc/migrations/0001_initial.py | ||
---|---|---|
52 | 52 |
('created', models.DateTimeField(auto_now_add=True, verbose_name='created')), |
53 | 53 |
('modified', models.DateTimeField(auto_now=True, verbose_name='modified')), |
54 | 54 |
], |
55 |
options={ |
|
56 |
'verbose_name': 'Claim', |
|
57 |
'verbose_name_plural': 'Claims', |
|
58 |
'default_related_name': 'claim_mappings', |
|
59 |
}, |
|
55 | 60 |
), |
56 | 61 |
migrations.CreateModel( |
57 | 62 |
name='OIDCProvider', |
src/authentic2_auth_oidc/migrations/0014_auto_20220920_1614.py | ||
---|---|---|
1 |
# Generated by Django 2.2.26 on 2022-09-20 14:14 |
|
2 | ||
3 |
import django |
|
4 |
from django.db import migrations, models |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('authentic2_auth_oidc', '0013_auto_20220726_1714'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.RenameField( |
|
15 |
model_name='oidcclaimmapping', |
|
16 |
old_name='provider', |
|
17 |
new_name='authenticator', |
|
18 |
), |
|
19 |
migrations.AlterField( |
|
20 |
model_name='oidcclaimmapping', |
|
21 |
name='authenticator', |
|
22 |
field=models.ForeignKey( |
|
23 |
on_delete=django.db.models.deletion.CASCADE, |
|
24 |
related_name='claim_mappings', |
|
25 |
to='authenticators.BaseAuthenticator', |
|
26 |
), |
|
27 |
), |
|
28 |
] |
src/authentic2_auth_oidc/models.py | ||
---|---|---|
26 | 26 |
from jwcrypto.jwk import InvalidJWKValue, JWKSet |
27 | 27 | |
28 | 28 |
from authentic2.a2_rbac.utils import get_default_ou |
29 |
from authentic2.apps.authenticators.models import BaseAuthenticator |
|
29 |
from authentic2.apps.authenticators.models import AuthenticatorRelatedObjectBase, BaseAuthenticator
|
|
30 | 30 |
from authentic2.utils.misc import make_url, redirect_to_login |
31 | 31 |
from authentic2.utils.template import validate_template |
32 | 32 | |
... | ... | |
115 | 115 | |
116 | 116 |
type = 'oidc' |
117 | 117 |
how = ['oidc'] |
118 |
manager_view_template_name = 'authentic2_auth_oidc/authenticator_detail.html' |
|
119 | 118 |
description_fields = ['show_condition', 'issuer', 'scopes', 'strategy', 'created', 'modified'] |
120 | 119 | |
121 | 120 |
class Meta: |
... | ... | |
127 | 126 | |
128 | 127 |
return OIDCProviderEditForm |
129 | 128 | |
129 |
@property |
|
130 |
def related_object_form_class(self): |
|
131 |
from .forms import OIDCRelatedObjectForm |
|
132 | ||
133 |
return OIDCRelatedObjectForm |
|
134 | ||
135 |
@property |
|
136 |
def related_models(self): |
|
137 |
return { |
|
138 |
OIDCClaimMapping: self.claim_mappings.all(), |
|
139 |
} |
|
140 | ||
130 | 141 |
@property |
131 | 142 |
def jwkset(self): |
132 | 143 |
if self.jwkset_json: |
... | ... | |
215 | 226 |
return render(request, template_names, context) |
216 | 227 | |
217 | 228 | |
218 |
class OIDCClaimMapping(models.Model):
|
|
229 |
class OIDCClaimMapping(AuthenticatorRelatedObjectBase):
|
|
219 | 230 |
NOT_VERIFIED = 0 |
220 | 231 |
VERIFIED_CLAIM = 1 |
221 | 232 |
ALWAYS_VERIFIED = 2 |
... | ... | |
225 | 236 |
(ALWAYS_VERIFIED, _('always verified')), |
226 | 237 |
] |
227 | 238 | |
228 |
provider = models.ForeignKey( |
|
229 |
to='OIDCProvider', verbose_name=_('provider'), related_name='claim_mappings', on_delete=models.CASCADE |
|
230 |
) |
|
231 | 239 |
claim = models.CharField(max_length=128, verbose_name=_('claim'), validators=[validate_template]) |
232 | 240 |
attribute = models.CharField(max_length=64, verbose_name=_('attribute')) |
233 | 241 |
verified = models.PositiveIntegerField( |
... | ... | |
240 | 248 | |
241 | 249 |
objects = managers.OIDCClaimMappingManager() |
242 | 250 | |
251 |
class Meta: |
|
252 |
default_related_name = 'claim_mappings' |
|
253 |
verbose_name = _('Claim') |
|
254 |
verbose_name_plural = _('Claims') |
|
255 | ||
243 | 256 |
def natural_key(self): |
244 | 257 |
return (self.claim, self.attribute, self.verified, self.required) |
245 | 258 | |
... | ... | |
262 | 275 |
return '<OIDCClaimMapping %r:%r on provider %r verified:%s required:%s >' % ( |
263 | 276 |
self.claim, |
264 | 277 |
self.attribute, |
265 |
self.provider and self.provider.issuer,
|
|
278 |
self.authenticator,
|
|
266 | 279 |
self.verified, |
267 | 280 |
self.required, |
268 | 281 |
) |
src/authentic2_auth_oidc/templates/authentic2_auth_oidc/authenticator_detail.html | ||
---|---|---|
1 |
{% extends 'authentic2/authenticators/authenticator_detail.html' %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block extra-tab-buttons %} |
|
5 |
<button aria-controls="panel-claims" aria-selected="false" id="tab-claims" role="tab" tabindex="-1">{% trans "Claims" %}</button> |
|
6 |
{% endblock %} |
|
7 | ||
8 |
{% block extra-tab-list %} |
|
9 |
<div aria-labelledby="tab-claims" hidden="" id="panel-claims" role="tabpanel" tabindex="0"> |
|
10 |
<ul class="objects-list single-links"> |
|
11 |
{% for claim in object.claim_mappings.all %} |
|
12 |
<li> |
|
13 |
<a rel="popup" href="{% url 'a2-manager-oidc-edit-claim' authenticator_pk=object.pk pk=claim.pk %}">{{ claim }}</a> |
|
14 |
<a rel="popup" class="delete" href="{% url 'a2-manager-oidc-delete-claim' authenticator_pk=object.pk pk=claim.pk %}">{% trans "Remove" %}</a> |
|
15 |
</li> |
|
16 |
{% endfor %} |
|
17 |
<li><a class="add" rel="popup" href="{% url 'a2-manager-oidc-add-claim' authenticator_pk=object.pk %}">{% trans 'Add' %}</a></li> |
|
18 |
</ul> |
|
19 |
</div> |
|
20 |
{% endblock %} |
src/authentic2_auth_oidc/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 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 | 18 | |
23 | 19 |
from . import views |
24 | 20 | |
... | ... | |
27 | 23 |
url(r'^accounts/oidc/login/$', views.login_initiate, name='oidc-login-initiate'), |
28 | 24 |
url(r'^accounts/oidc/callback/$', views.login_callback, name='oidc-login-callback'), |
29 | 25 |
] |
30 | ||
31 |
urlpatterns += required( |
|
32 |
superuser_login_required, |
|
33 |
[ |
|
34 |
path( |
|
35 |
'authenticators/<int:authenticator_pk>/claim/add/', |
|
36 |
views.add_claim, |
|
37 |
name='a2-manager-oidc-add-claim', |
|
38 |
), |
|
39 |
path( |
|
40 |
'authenticators/<int:authenticator_pk>/claim/<int:pk>/edit/', |
|
41 |
views.edit_claim, |
|
42 |
name='a2-manager-oidc-edit-claim', |
|
43 |
), |
|
44 |
path( |
|
45 |
'authenticators/<int:authenticator_pk>/claim/<int:pk>/delete/', |
|
46 |
views.delete_claim, |
|
47 |
name='a2-manager-oidc-delete-claim', |
|
48 |
), |
|
49 |
], |
|
50 |
) |
src/authentic2_auth_oidc/views.py | ||
---|---|---|
24 | 24 |
from django.contrib import messages |
25 | 25 |
from django.contrib.auth import REDIRECT_FIELD_NAME |
26 | 26 |
from django.http import HttpResponseBadRequest |
27 |
from django.shortcuts import get_object_or_404 |
|
28 | 27 |
from django.urls import reverse |
29 | 28 |
from django.utils.translation import get_language |
30 | 29 |
from django.utils.translation import ugettext as _ |
31 |
from django.views.generic import CreateView, DeleteView, UpdateView |
|
32 | 30 |
from django.views.generic.base import View |
33 | 31 | |
34 |
from authentic2.manager.views import MediaMixin, TitleMixin |
|
35 | 32 |
from authentic2.utils import crypto |
36 | 33 |
from authentic2.utils.misc import authenticate, good_next_url, login, redirect |
37 | 34 | |
38 |
from .forms import OIDCClaimMappingForm |
|
39 |
from .models import OIDCClaimMapping, OIDCProvider |
|
35 |
from .models import OIDCProvider |
|
40 | 36 |
from .utils import get_provider, get_provider_by_issuer |
41 | 37 | |
42 | 38 |
logger = logging.getLogger(__name__) |
... | ... | |
350 | 346 | |
351 | 347 | |
352 | 348 |
login_callback = LoginCallback.as_view() |
353 | ||
354 | ||
355 |
class OIDCProviderMixin(MediaMixin, TitleMixin): |
|
356 |
model = OIDCClaimMapping |
|
357 | ||
358 |
def dispatch(self, request, *args, **kwargs): |
|
359 |
self.provider = get_object_or_404(OIDCProvider, pk=kwargs.get('authenticator_pk')) |
|
360 |
return super().dispatch(request, *args, **kwargs) |
|
361 | ||
362 |
def get_form_kwargs(self): |
|
363 |
kwargs = super().get_form_kwargs() |
|
364 |
if not kwargs.get('instance'): |
|
365 |
kwargs['instance'] = self.model() |
|
366 |
kwargs['instance'].provider = self.provider |
|
367 |
return kwargs |
|
368 | ||
369 |
def get_success_url(self): |
|
370 |
return reverse('a2-manager-authenticator-detail', kwargs={'pk': self.provider.pk}) + '#open:claims' |
|
371 | ||
372 | ||
373 |
class OIDCClaimMappingAddView(OIDCProviderMixin, CreateView): |
|
374 |
template_name = 'authentic2/manager/form.html' |
|
375 |
title = _('New claim') |
|
376 |
form_class = OIDCClaimMappingForm |
|
377 | ||
378 |
def form_valid(self, form): |
|
379 |
resp = super().form_valid(form) |
|
380 |
self.request.journal.record('authenticator.oidc.claim.creation', claim=form.instance) |
|
381 |
return resp |
|
382 | ||
383 | ||
384 |
add_claim = OIDCClaimMappingAddView.as_view() |
|
385 | ||
386 | ||
387 |
class OIDCClaimMappingEditView(OIDCProviderMixin, UpdateView): |
|
388 |
template_name = 'authentic2/manager/form.html' |
|
389 |
title = _('Edit claim') |
|
390 |
form_class = OIDCClaimMappingForm |
|
391 | ||
392 |
def form_valid(self, form): |
|
393 |
resp = super().form_valid(form) |
|
394 |
self.request.journal.record('authenticator.oidc.claim.edit', form=form) |
|
395 |
return resp |
|
396 | ||
397 | ||
398 |
edit_claim = OIDCClaimMappingEditView.as_view() |
|
399 | ||
400 | ||
401 |
class OIDCClaimMappingDeleteView(OIDCProviderMixin, DeleteView): |
|
402 |
template_name = 'authentic2/authenticators/authenticator_delete_form.html' |
|
403 | ||
404 |
def delete(self, *args, **kwargs): |
|
405 |
self.request.journal.record('authenticator.oidc.claim.deletion', claim=self.get_object()) |
|
406 |
return super().delete(*args, **kwargs) |
|
407 | ||
408 | ||
409 |
delete_claim = OIDCClaimMappingDeleteView.as_view() |
tests/test_auth_oidc.py | ||
---|---|---|
184 | 184 |
button_label=name, |
185 | 185 |
) |
186 | 186 |
provider.full_clean() |
187 |
OIDCClaimMapping.objects.create(provider=provider, claim='sub', attribute='username', idtoken_claim=True) |
|
188 |
OIDCClaimMapping.objects.create(provider=provider, claim='email', attribute='email') |
|
189 |
OIDCClaimMapping.objects.create(provider=provider, claim='email', required=True, attribute='email') |
|
190 | 187 |
OIDCClaimMapping.objects.create( |
191 |
provider=provider, |
|
188 |
authenticator=provider, claim='sub', attribute='username', idtoken_claim=True |
|
189 |
) |
|
190 |
OIDCClaimMapping.objects.create(authenticator=provider, claim='email', attribute='email') |
|
191 |
OIDCClaimMapping.objects.create(authenticator=provider, claim='email', required=True, attribute='email') |
|
192 |
OIDCClaimMapping.objects.create( |
|
193 |
authenticator=provider, |
|
192 | 194 |
claim='given_name', |
193 | 195 |
required=True, |
194 | 196 |
verified=OIDCClaimMapping.ALWAYS_VERIFIED, |
195 | 197 |
attribute='first_name', |
196 | 198 |
) |
197 | 199 |
OIDCClaimMapping.objects.create( |
198 |
provider=provider,
|
|
200 |
authenticator=provider,
|
|
199 | 201 |
claim='family_name', |
200 | 202 |
required=True, |
201 | 203 |
verified=OIDCClaimMapping.VERIFIED_CLAIM, |
202 | 204 |
attribute='last_name', |
203 | 205 |
) |
204 |
OIDCClaimMapping.objects.create(provider=provider, claim='ou', attribute='ou__slug')
|
|
206 |
OIDCClaimMapping.objects.create(authenticator=provider, claim='ou', attribute='ou__slug')
|
|
205 | 207 |
return provider |
206 | 208 | |
207 | 209 | |
... | ... | |
672 | 674 |
def test_strategy_find_email(app, caplog, code, oidc_provider, oidc_provider_jwkset, simple_user): |
673 | 675 |
OIDCClaimMapping.objects.all().delete() |
674 | 676 |
OIDCClaimMapping.objects.create( |
675 |
provider=oidc_provider,
|
|
677 |
authenticator=oidc_provider,
|
|
676 | 678 |
claim='email', |
677 | 679 |
attribute='email', |
678 | 680 |
idtoken_claim=False, # served by user_info endpoint |
... | ... | |
729 | 731 |
): |
730 | 732 |
OIDCClaimMapping.objects.all().delete() |
731 | 733 |
OIDCClaimMapping.objects.create( |
732 |
provider=oidc_provider,
|
|
734 |
authenticator=oidc_provider,
|
|
733 | 735 |
claim='email', |
734 | 736 |
attribute='email', |
735 | 737 |
idtoken_claim=False, # served by user_info endpoint |
... | ... | |
908 | 910 |
OIDCClaimMapping.objects.all().delete() |
909 | 911 | |
910 | 912 |
OIDCClaimMapping.objects.create( |
911 |
provider=oidc_provider,
|
|
913 |
authenticator=oidc_provider,
|
|
912 | 914 |
attribute='username', |
913 | 915 |
idtoken_claim=False, |
914 | 916 |
claim='{{ given_name }} "{{ nickname }}" {{ family_name }}', |
915 | 917 |
) |
916 | 918 |
OIDCClaimMapping.objects.create( |
917 |
provider=oidc_provider,
|
|
919 |
authenticator=oidc_provider,
|
|
918 | 920 |
attribute='pro_phone', |
919 | 921 |
idtoken_claim=False, |
920 | 922 |
claim='(prefix +33) {{ phone_number }}', |
921 | 923 |
) |
922 | 924 |
OIDCClaimMapping.objects.create( |
923 |
provider=oidc_provider,
|
|
925 |
authenticator=oidc_provider,
|
|
924 | 926 |
attribute='email', |
925 | 927 |
idtoken_claim=False, |
926 | 928 |
claim='{{ given_name }}@foo.bar', |
927 | 929 |
) |
928 | 930 |
# last one, with an idtoken claim |
929 | 931 |
OIDCClaimMapping.objects.create( |
930 |
provider=oidc_provider,
|
|
932 |
authenticator=oidc_provider,
|
|
931 | 933 |
attribute='last_name', |
932 | 934 |
idtoken_claim=True, |
933 | 935 |
claim='{{ name|upper }}', |
934 | 936 |
) |
935 | 937 |
# typo in template string |
936 | 938 |
OIDCClaimMapping.objects.create( |
937 |
provider=oidc_provider,
|
|
939 |
authenticator=oidc_provider,
|
|
938 | 940 |
attribute='first_name', |
939 | 941 |
idtoken_claim=True, |
940 | 942 |
claim='{{ given_name', |
tests/test_manager_authenticators.py | ||
---|---|---|
198 | 198 |
authenticator = OIDCProvider.objects.create(slug='idp1') |
199 | 199 |
resp = login(app, superuser, path=authenticator.get_absolute_url()) |
200 | 200 | |
201 |
resp = resp.click('Add') |
|
201 |
resp = resp.click('Add', href='claim')
|
|
202 | 202 |
resp.form['claim'] = 'email' |
203 | 203 |
resp.form['attribute'].select(text='Email address (email)') |
204 | 204 |
resp.form['verified'].select(text='verified claim') |
205 | 205 |
resp.form['required'] = True |
206 | 206 |
resp.form['idtoken_claim'] = True |
207 | 207 |
resp = resp.form.submit() |
208 |
assert_event('authenticator.oidc.claim.creation', user=superuser, session=app.session)
|
|
209 |
assert '#open:claims' in resp.location
|
|
208 |
assert_event('authenticator.related_object.creation', user=superuser, session=app.session)
|
|
209 |
assert '#open:oidcclaimmapping' in resp.location
|
|
210 | 210 | |
211 | 211 |
resp = resp.follow() |
212 | 212 |
assert 'email → Email address (email), verified, required, idtoken' in resp.text |
... | ... | |
215 | 215 |
resp.form['attribute'].select(text='First name (first_name)') |
216 | 216 |
resp = resp.form.submit().follow() |
217 | 217 |
assert 'email → First name (first_name), verified, required, idtoken' in resp.text |
218 |
assert_event('authenticator.oidc.claim.edit', user=superuser, session=app.session)
|
|
218 |
assert_event('authenticator.related_object.edit', user=superuser, session=app.session)
|
|
219 | 219 | |
220 | 220 |
resp = resp.click('Remove') |
221 | 221 |
resp = resp.form.submit().follow() |
222 | 222 |
assert 'email' not in resp.text |
223 |
assert_event('authenticator.oidc.claim.deletion', user=superuser, session=app.session)
|
|
223 |
assert_event('authenticator.related_object.deletion', user=superuser, session=app.session)
|
|
224 | 224 | |
225 | 225 | |
226 | 226 |
def test_authenticators_fc(app, superuser): |
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_oidc.models import OIDCClaimMapping, OIDCProvider |
|
32 | 31 |
from authentic2_auth_saml.models import SAMLAuthenticator, SetAttributeAction |
33 | 32 | |
34 | 33 |
from .utils import login, logout, text_content |
... | ... | |
62 | 61 |
authenticator = LoginPasswordAuthenticator.objects.create(slug='test') |
63 | 62 |
saml_authenticator = SAMLAuthenticator.objects.create(slug='saml') |
64 | 63 |
set_attribute_action = SetAttributeAction.objects.create(authenticator=saml_authenticator) |
65 |
oidc_provider = OIDCProvider.objects.create(slug='oidc') |
|
66 |
oidc_claim_mapping = OIDCClaimMapping.objects.create(provider=oidc_provider) |
|
67 | 64 | |
68 | 65 |
class EventFactory: |
69 | 66 |
date = make_aware(datetime.datetime(2020, 1, 1)) |
... | ... | |
325 | 322 |
session=session2, |
326 | 323 |
related_object=set_attribute_action, |
327 | 324 |
) |
328 |
make('authenticator.oidc.claim.creation', user=agent, session=session2, claim=oidc_claim_mapping) |
|
329 |
claim_edit_form = mock.Mock(spec=['instance', 'initial', 'changed_data', 'cleaned_data']) |
|
330 |
claim_edit_form.instance = oidc_claim_mapping |
|
331 |
claim_edit_form.initial = {'claim': 'email'} |
|
332 |
claim_edit_form.changed_data = ['claim'] |
|
333 |
claim_edit_form.cleaned_data = {'claim': 'first_name'} |
|
334 |
make('authenticator.oidc.claim.edit', user=agent, session=session2, form=claim_edit_form) |
|
335 |
make('authenticator.oidc.claim.deletion', user=agent, session=session2, claim=oidc_claim_mapping) |
|
336 | 325 | |
337 | 326 |
# verify we created at least one event for each type |
338 | 327 |
assert set(Event.objects.values_list("type__name", flat=True)) == set(_registry) |
... | ... | |
370 | 359 |
def test_global_journal(app, superuser, events): |
371 | 360 |
response = login(app, user=superuser, path="/manage/") |
372 | 361 |
set_attribute_action = SetAttributeAction.objects.get() |
373 |
oidc_claim_mapping = OIDCClaimMapping.objects.get() |
|
374 | 362 | |
375 | 363 |
# remove event about admin login |
376 | 364 |
Event.objects.filter(user=superuser).delete() |
... | ... | |
747 | 735 |
'type': 'authenticator.related_object.deletion', |
748 | 736 |
'user': 'agent', |
749 | 737 |
}, |
750 |
{ |
|
751 |
'message': 'creation of claim (%s) in provider "OpenIDConnect"' % oidc_claim_mapping.pk, |
|
752 |
'timestamp': 'Jan. 3, 2020, 11 a.m.', |
|
753 |
'type': 'authenticator.oidc.claim.creation', |
|
754 |
'user': 'agent', |
|
755 |
}, |
|
756 |
{ |
|
757 |
'message': 'edit claim (%s) in provider "OpenIDConnect" (claim)' % oidc_claim_mapping.pk, |
|
758 |
'timestamp': 'Jan. 3, 2020, noon', |
|
759 |
'type': 'authenticator.oidc.claim.edit', |
|
760 |
'user': 'agent', |
|
761 |
}, |
|
762 |
{ |
|
763 |
'message': 'deletion of claim (%s) in provider "OpenIDConnect"' % oidc_claim_mapping.pk, |
|
764 |
'timestamp': 'Jan. 3, 2020, 1 p.m.', |
|
765 |
'type': 'authenticator.oidc.claim.deletion', |
|
766 |
'user': 'agent', |
|
767 |
}, |
|
768 | 738 |
] |
769 | 739 | |
770 | 740 |
agent_page = response.click('agent', index=1) |
771 |
- |