Projet

Général

Profil

0001-auth_oidc-show-a-warning-message-if-target-user-is-a.patch

Benjamin Dauvergne, 01 décembre 2022 10:56

Télécharger (5,5 ko)

Voir les différences:

Subject: [PATCH] auth_oidc: show a warning message if target user is already
 linked to another provider (#65692)

 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
19 19

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

  
......
39 42
class OIDCBackend(ModelBackend):
40 43
    # pylint: disable=arguments-renamed
41 44
    def authenticate(self, request, access_token=None, id_token=None, nonce=None, provider=None):
42
        with atomic(savepoint=False):
45
        with atomic():
43 46
            return self._authenticate(
44 47
                request, access_token=access_token, id_token=id_token, nonce=nonce, provider=provider
45 48
            )
......
313 316
                    user = User.objects.create(ou=provider.ou, email=email or '')
314 317
                    user.set_unusable_password()
315 318
                    created_user = True
316
                oidc_account, created = models.OIDCAccount.objects.get_or_create(
317
                    provider=provider, user=user, defaults={'sub': id_token.sub}
318
                )
319
                try:
320
                    oidc_account, created = models.OIDCAccount.objects.get_or_create(
321
                        provider=provider, user=user, defaults={'sub': id_token.sub}
322
                    )
323
                except IntegrityError:
324
                    set_rollback(True)
325
                    logger.warning('auth_oidc: email %s is already linked to another provider.', email)
326
                    if request:
327
                        messages.warning(
328
                            request,
329
                            _(
330
                                'Your email is already linked to another SSO account, please contact an administrator.'
331
                            ),
332
                        )
333
                        return None
334

  
319 335
                if not created and oidc_account.sub != id_token.sub:
320 336
                    logger.info(
321 337
                        'auth_oidc: changed user %s sub from %s to %s (issuer %s)',
......
327 343
                    oidc_account.sub = id_token.sub
328 344
                    oidc_account.save()
329 345
            else:
346
                if request:
347
                    messages.warning(request, _('No user found'))
330 348
                logger.warning(
331 349
                    'auth_oidc: cannot create user for sub %r as issuer %r does not allow it',
332 350
                    id_token.sub,
src/authentic2_auth_oidc/views.py
295 295
                    'provider_pk': provider.pk,
296 296
                }
297 297
            )
298
        else:
299
            messages.warning(request, _('No user found'))
300 298
        return self.continue_to_next_url(request)
301 299

  
302 300
    errors = {
tests/test_auth_oidc.py
1361 1361
    with pytest.raises(IntegrityError):
1362 1362
        with transaction.atomic():
1363 1363
            OIDCProvider.objects.create(issuer='test', slug='d')
1364

  
1365

  
1366
def test_double_link(app, caplog, code, simple_user, oidc_provider_jwkset):
1367
    ou = get_default_ou()
1368
    ou.email_is_unique = True
1369
    ou.save()
1370
    provider1 = make_oidc_provider(name='provider1', jwkset=oidc_provider_jwkset)
1371
    provider2 = make_oidc_provider(name='provider2', jwkset=oidc_provider_jwkset)
1372

  
1373
    OIDCAccount.objects.create(provider=provider2, sub='1234', user=simple_user)
1374

  
1375
    response = app.get('/').maybe_follow()
1376
    response = response.click('provider1')
1377
    location = urllib.parse.urlparse(response.location)
1378
    query = QueryDict(location.query)
1379
    state = query['state']
1380
    nonce = query['nonce']
1381

  
1382
    # sub=john.doe
1383
    with utils.check_log(caplog, 'auth_oidc: email user@example.net is already linked'):
1384
        with oidc_provider_mock(
1385
            provider1,
1386
            oidc_provider_jwkset,
1387
            code,
1388
            nonce=nonce,
1389
            extra_id_token={'email': simple_user.email},
1390
            extra_user_info={'email': simple_user.email},
1391
        ):
1392
            response = app.get(login_callback_url(provider1), params={'code': code, 'state': state})
1393
        response = response.maybe_follow()
1394
    warnings = response.pyquery('.warning')
1395
    assert len(warnings) == 1
1396
    assert 'Your email is already linked' in warnings.text()
1364
-