Projet

Général

Profil

0001-authentic2_auth_oidc-show-a-warning-message-if-targe.patch

Benjamin Dauvergne, 25 mai 2022 12:11

Télécharger (5,61 ko)

Voir les différences:

Subject: [PATCH] authentic2_auth_oidc: show a warning message if target user
 is already linked to another provider

 src/authentic2_auth_oidc/backends.py | 28 ++++++++++++++++++-----
 src/authentic2_auth_oidc/views.py    |  2 --
 tests/test_auth_oidc.py              | 33 ++++++++++++++++++++++++++++
 3 files changed, 56 insertions(+), 7 deletions(-)
src/authentic2_auth_oidc/backends.py
18 18
import logging
19 19

  
20 20
import requests
21
from django.contrib import messages
21 22
from django.contrib.auth import get_user_model
22 23
from django.contrib.auth.backends import ModelBackend
23
from django.db.transaction import atomic
24
from django.db import IntegrityError
25
from django.db.transaction import atomic, set_rollback
24 26
from django.utils.timezone import now
27
from django.utils.translation import gettext as _
25 28
from jwcrypto.jwk import JWK
26 29
from jwcrypto.jwt import JWT
27 30

  
......
37 40
class OIDCBackend(ModelBackend):
38 41
    # pylint: disable=arguments-renamed
39 42
    def authenticate(self, request, access_token=None, id_token=None, nonce=None, provider=None):
40
        with atomic(savepoint=False):
43
        with atomic():
41 44
            return self._authenticate(
42 45
                request, access_token=access_token, id_token=id_token, nonce=nonce, provider=provider
43 46
            )
......
285 288
                    user = User.objects.create(ou=provider.ou, email=email or '')
286 289
                    user.set_unusable_password()
287 290
                    created_user = True
288
                oidc_account, created = models.OIDCAccount.objects.get_or_create(
289
                    provider=provider, user=user, defaults={'sub': id_token.sub}
290
                )
291
                try:
292
                    oidc_account, created = models.OIDCAccount.objects.get_or_create(
293
                        provider=provider, user=user, defaults={'sub': id_token.sub}
294
                    )
295
                except IntegrityError:
296
                    set_rollback(True)
297
                    if request:
298
                        logger.warning('auth_oidc: email %s is already linked to another provider.', email)
299
                        messages.warning(
300
                            request,
301
                            _(
302
                                'Your email is already linked to another SSO account, please contact an administrator'
303
                            ),
304
                        )
305
                        return None
306

  
291 307
                if not created and oidc_account.sub != id_token.sub:
292 308
                    logger.info(
293 309
                        'auth_oidc: changed user %s sub from %s to %s (issuer %s)',
......
299 315
                    oidc_account.sub = id_token.sub
300 316
                    oidc_account.save()
301 317
            else:
318
                if request:
319
                    messages.warning(request, _('No user found'))
302 320
                logger.warning(
303 321
                    'auth_oidc: cannot create user for sub %r as issuer %r does not allow it',
304 322
                    id_token.sub,
src/authentic2_auth_oidc/views.py
298 298
                    'provider_pk': provider.pk,
299 299
                }
300 300
            )
301
        else:
302
            messages.warning(request, _('No user found'))
303 301
        return self.continue_to_next_url(request)
304 302

  
305 303
    errors = {
tests/test_auth_oidc.py
1141 1141
    assert second_authenticator.claim_mappings.get().pk == second_provider_claim_mapping.pk
1142 1142
    assert second_authenticator.accounts.count() == 1
1143 1143
    assert second_authenticator.accounts.get().pk == second_provider_account.pk
1144

  
1145

  
1146
def test_double_link(app, caplog, code, simple_user, oidc_provider_jwkset):
1147
    ou = get_default_ou()
1148
    ou.email_is_unique = True
1149
    ou.save()
1150
    provider1 = make_oidc_provider(name='provider1', jwkset=oidc_provider_jwkset)
1151
    provider2 = make_oidc_provider(name='provider2', jwkset=oidc_provider_jwkset)
1152

  
1153
    OIDCAccount.objects.create(provider=provider2, sub='1234', user=simple_user)
1154

  
1155
    response = app.get('/').maybe_follow()
1156
    response = response.click('provider1')
1157
    location = urllib.parse.urlparse(response.location)
1158
    query = QueryDict(location.query)
1159
    state = query['state']
1160
    nonce = query['nonce']
1161

  
1162
    # sub=john.doe
1163
    with utils.check_log(caplog, 'auth_oidc: email user@example.net is already linked'):
1164
        with oidc_provider_mock(
1165
            provider1,
1166
            oidc_provider_jwkset,
1167
            code,
1168
            nonce=nonce,
1169
            extra_id_token={'email': simple_user.email},
1170
            extra_user_info={'email': simple_user.email},
1171
        ):
1172
            response = app.get(login_callback_url(provider1), params={'code': code, 'state': state})
1173
        response = response.maybe_follow()
1174
    warnings = response.pyquery('.warning')
1175
    assert len(warnings) == 1
1176
    assert 'Your email is already linked' in warnings.text()
1144
-