Coverage for distro_tracker/signon/distro_tracker.py: 77%
33 statements
« prev ^ index » next coverage.py v6.5.0, created at 2025-01-12 09:15 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2025-01-12 09:15 +0000
1# Copyright 2024 The Debusine Developers
2# See the COPYRIGHT file at the top-level directory of this distribution
3#
4# This file is part of Distro Tracker. It is subject to the license terms
5# in the LICENSE file found in the top-level directory of this
6# distribution and at https://deb.li/DTLicense. No part of Distro Tracker,
7# including this file, may be copied, modified, propagated, or distributed
8# except according to the terms contained in the LICENSE file.
9"""Distro Tracker extensions to Signon."""
11import logging
13from django_email_accounts.models import User, UserEmail
14from django.core.exceptions import ValidationError
15from django.contrib.auth.hashers import make_password
17from distro_tracker.signon.models import Identity
18from distro_tracker.signon.signon import Signon
19from distro_tracker.signon.utils import split_full_name
21log = logging.getLogger(__name__)
24class DistroTrackerSignon(Signon):
25 """
26 Distro Tracker specific extension to Signon.
28 Activate it in django settings with::
30 SIGNON_CLASS = "distro_tracker.signon.distro_tracker.DistroTrackerSignon" # noqa: E501
31 """
33 def _lookup_user_from_identity(self, identity: Identity) -> User | None:
34 """Lookup an existing user from claims in an Identity."""
35 try:
36 user_email = UserEmail.objects.get(
37 email__iexact=identity.claims["email"]
38 )
39 return user_email.user
40 except UserEmail.DoesNotExist:
41 return None
43 def create_user_from_identity(self, identity: Identity) -> User | None:
44 email = identity.claims["email"]
46 user_email, _ = UserEmail.objects.get_or_create(
47 email__iexact=email, defaults={'email': email}
48 )
49 if not user_email.user:
50 first_name, last_name = split_full_name(identity.claims["name"])
52 # Django does not run validators on create_user, so garbage in the
53 # claims can either create garbage users, or cause database
54 # transaction errors that will invalidate the current transaction.
55 #
56 # See: https://stackoverflow.com/questions/67442439/why-django-does-not-validate-email-in-customuser-model # noqa: E501
58 # Instead of calling create_user, I instead have to replicate what
59 # it does here and call validation explicitly before save.
61 # This is the equivalent of the following, with validation:
62 # user = User.objects.create_user(
63 # main_email=email,
64 # first_name=first_name,
65 # last_name=last_name,
66 # )
67 user = User(
68 main_email=email,
69 first_name=first_name,
70 last_name=last_name,
71 is_active=True,
72 )
73 user.password = make_password(None)
74 try:
75 user.clean_fields()
76 except ValidationError as e:
77 log.warning(
78 "%s: cannot create a local user",
79 identity,
80 exc_info=e,
81 )
82 return None
83 user.save()
84 user_email.user = user
85 user_email.save()
86 else:
87 user = user_email.user
89 return user