0001-auth_oidc-show-a-warning-message-if-target-user-is-a.patch
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 |
- |