from django.conf import settings from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager from django.contrib.auth.models import PermissionsMixin from django.contrib.auth.validators import UnicodeUsernameValidator from django.core.mail import send_mail from django.db import models from django.utils import timezone from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ from djeveric.fields import ConfirmationField from djeveric.models import ConfirmableModelMixin from userausfall.emails import TrustBridgeConfirmationEmail from userausfall.ldap import LDAPManager class PasswordMismatch(Exception): """The given password does not match the user's password.""" pass class UserManager(BaseUserManager): use_in_migrations = True def _create_user(self, username, password, **extra_fields): """ Create and save a user with the given username, email, and password. """ # email = self.normalize_email(email) username = self.model.normalize_username(username) user = self.model(username=username, **extra_fields) user.set_password(password) user.save(using=self._db) return user def create_user(self, username, password=None, **extra_fields): extra_fields.setdefault("is_staff", False) extra_fields.setdefault("is_superuser", False) return self._create_user(username, password, **extra_fields) def create_superuser(self, email, password, **extra_fields): extra_fields.setdefault("is_staff", True) extra_fields.setdefault("is_superuser", True) if extra_fields.get("is_staff") is not True: raise ValueError("Superuser must have is_staff=True.") if extra_fields.get("is_superuser") is not True: raise ValueError("Superuser must have is_superuser=True.") return self._create_user(email, password, **extra_fields) class User(PermissionsMixin, AbstractBaseUser): username_validator = UnicodeUsernameValidator() username = models.CharField( _("username"), max_length=150, help_text=_("Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."), validators=[username_validator], error_messages={"unique": _("A user with that username already exists.")}, unique=True, blank=True, ) is_staff = models.BooleanField( _("staff status"), default=False, help_text=_("Designates whether the user can log into this admin site."), ) date_joined = models.DateTimeField(_("date joined"), default=timezone.now) objects = UserManager() # EMAIL_FIELD = "email" USERNAME_FIELD = "username" REQUIRED_FIELDS = [] class Meta: verbose_name = _("user") verbose_name_plural = _("users") def clean(self): super().clean() self.email = self.__class__.objects.normalize_email(self.email) def create_ldap_account(self, raw_password): """Create the LDAP account which corresponds to this user.""" if not self.check_password(raw_password): raise PasswordMismatch("The given password does not match the user's password.") return self._ldap.create_account(self.username, raw_password) def email_user(self, subject, message, from_email=None, **kwargs): """Send an email to this user.""" send_mail(subject, message, from_email, [self.email], **kwargs) def get_primary_email(self): """Returns the primary email address for this user.""" return f"{self.username}@{settings.USERAUSFALL['PRIMARY_EMAIL_DOMAIN']}" def has_ldap_account(self): """Returns True if an ldap account exists for the user's username.""" return self._ldap.has_account(self.username) @cached_property def _ldap(self): return LDAPManager() class TrustBridge(ConfirmableModelMixin, models.Model): is_trusted = ConfirmationField(email_class=TrustBridgeConfirmationEmail) trust_giver = models.ForeignKey("User", on_delete=models.SET_NULL, null=True) trust_taker = models.OneToOneField("User", on_delete=models.CASCADE, related_name="trust_bridge") def get_confirmation_email_recipient(self) -> str: return self.trust_giver.get_primary_email() def _has_confirmation_recipient(self): return self.trust_giver is not None