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 | |
... | ... | |
38 | 41 |
class OIDCBackend(ModelBackend): |
39 | 42 |
# pylint: disable=arguments-renamed |
40 | 43 |
def authenticate(self, request, access_token=None, id_token=None, nonce=None, provider=None): |
41 |
with atomic(savepoint=False):
|
|
44 |
with atomic(): |
|
42 | 45 |
return self._authenticate( |
43 | 46 |
request, access_token=access_token, id_token=id_token, nonce=nonce, provider=provider |
44 | 47 |
) |
... | ... | |
312 | 315 |
user = User.objects.create(ou=provider.ou, email=email or '') |
313 | 316 |
user.set_unusable_password() |
314 | 317 |
created_user = True |
315 |
oidc_account, created = models.OIDCAccount.objects.get_or_create( |
|
316 |
provider=provider, user=user, defaults={'sub': id_token.sub} |
|
317 |
) |
|
318 |
try: |
|
319 |
oidc_account, created = models.OIDCAccount.objects.get_or_create( |
|
320 |
provider=provider, user=user, defaults={'sub': id_token.sub} |
|
321 |
) |
|
322 |
except IntegrityError: |
|
323 |
set_rollback(True) |
|
324 |
logger.warning('auth_oidc: email %s is already linked to another provider.', email) |
|
325 |
if request: |
|
326 |
messages.warning( |
|
327 |
request, |
|
328 |
_( |
|
329 |
'Your email is already linked to another SSO account, please contact an administrator.' |
|
330 |
), |
|
331 |
) |
|
332 |
return None |
|
333 | ||
318 | 334 |
if not created and oidc_account.sub != id_token.sub: |
319 | 335 |
logger.info( |
320 | 336 |
'auth_oidc: changed user %s sub from %s to %s (issuer %s)', |
... | ... | |
326 | 342 |
oidc_account.sub = id_token.sub |
327 | 343 |
oidc_account.save() |
328 | 344 |
else: |
345 |
if request: |
|
346 |
messages.warning(request, _('No user found')) |
|
329 | 347 |
logger.warning( |
330 | 348 |
'auth_oidc: cannot create user for sub %r as issuer %r does not allow it', |
331 | 349 |
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 | ||
---|---|---|
1337 | 1337 | |
1338 | 1338 |
user = User.objects.get() |
1339 | 1339 |
assert simple_role in user.roles.all() |
1340 | ||
1341 | ||
1342 |
def test_double_link(app, caplog, code, simple_user, oidc_provider_jwkset): |
|
1343 |
ou = get_default_ou() |
|
1344 |
ou.email_is_unique = True |
|
1345 |
ou.save() |
|
1346 |
provider1 = make_oidc_provider(name='provider1', jwkset=oidc_provider_jwkset) |
|
1347 |
provider2 = make_oidc_provider(name='provider2', jwkset=oidc_provider_jwkset) |
|
1348 | ||
1349 |
OIDCAccount.objects.create(provider=provider2, sub='1234', user=simple_user) |
|
1350 | ||
1351 |
response = app.get('/').maybe_follow() |
|
1352 |
response = response.click('provider1') |
|
1353 |
location = urllib.parse.urlparse(response.location) |
|
1354 |
query = QueryDict(location.query) |
|
1355 |
state = query['state'] |
|
1356 |
nonce = query['nonce'] |
|
1357 | ||
1358 |
# sub=john.doe |
|
1359 |
with utils.check_log(caplog, 'auth_oidc: email user@example.net is already linked'): |
|
1360 |
with oidc_provider_mock( |
|
1361 |
provider1, |
|
1362 |
oidc_provider_jwkset, |
|
1363 |
code, |
|
1364 |
nonce=nonce, |
|
1365 |
extra_id_token={'email': simple_user.email}, |
|
1366 |
extra_user_info={'email': simple_user.email}, |
|
1367 |
): |
|
1368 |
response = app.get(login_callback_url(provider1), params={'code': code, 'state': state}) |
|
1369 |
response = response.maybe_follow() |
|
1370 |
warnings = response.pyquery('.warning') |
|
1371 |
assert len(warnings) == 1 |
|
1372 |
assert 'Your email is already linked' in warnings.text() |
|
1340 |
- |