🐛(backend) fix create document for user when sub does not match
When creating a document on behalf of a user via the server-to-server API, a special edge case was broken that should should never happen but happens in our OIDC federation because one of the provider modifies the users "sub" each time they login. We end-up with existing users for who the email matches but not the sub. They were not correctly handled. I made a few additional fixes and improvements to the endpoint.
This commit is contained in:
committed by
Samuel Paccoud
parent
96bb99d6ec
commit
610948cd16
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
Declare and configure the models for the impress core application
|
||||
"""
|
||||
# pylint: disable=too-many-lines
|
||||
|
||||
import hashlib
|
||||
import smtplib
|
||||
@@ -89,6 +90,16 @@ class LinkReachChoices(models.TextChoices):
|
||||
PUBLIC = "public", _("Public") # Even anonymous users can access the document
|
||||
|
||||
|
||||
class DuplicateEmailError(Exception):
|
||||
"""Raised when an email is already associated with a pre-existing user."""
|
||||
|
||||
def __init__(self, message=None, email=None):
|
||||
"""Set message and email to describe the exception."""
|
||||
self.message = message
|
||||
self.email = email
|
||||
super().__init__(self.message)
|
||||
|
||||
|
||||
class BaseModel(models.Model):
|
||||
"""
|
||||
Serves as an abstract base model for other models, ensuring that records are validated
|
||||
@@ -126,6 +137,35 @@ class BaseModel(models.Model):
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class UserManager(auth_models.UserManager):
|
||||
"""Custom manager for User model with additional methods."""
|
||||
|
||||
def get_user_by_sub_or_email(self, sub, email):
|
||||
"""Fetch existing user by sub or email."""
|
||||
try:
|
||||
return self.get(sub=sub)
|
||||
except self.model.DoesNotExist as err:
|
||||
if not email:
|
||||
return None
|
||||
|
||||
if settings.OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION:
|
||||
try:
|
||||
return self.get(email=email)
|
||||
except self.model.DoesNotExist:
|
||||
pass
|
||||
elif (
|
||||
self.filter(email=email).exists()
|
||||
and not settings.OIDC_ALLOW_DUPLICATE_EMAILS
|
||||
):
|
||||
raise DuplicateEmailError(
|
||||
_(
|
||||
"We couldn't find a user with this sub but the email is already "
|
||||
"associated with a registered user."
|
||||
)
|
||||
) from err
|
||||
return None
|
||||
|
||||
|
||||
class User(AbstractBaseUser, BaseModel, auth_models.PermissionsMixin):
|
||||
"""User model to work with OIDC only authentication."""
|
||||
|
||||
@@ -192,7 +232,7 @@ class User(AbstractBaseUser, BaseModel, auth_models.PermissionsMixin):
|
||||
),
|
||||
)
|
||||
|
||||
objects = auth_models.UserManager()
|
||||
objects = UserManager()
|
||||
|
||||
USERNAME_FIELD = "admin_email"
|
||||
REQUIRED_FIELDS = []
|
||||
@@ -939,7 +979,10 @@ class Invitation(BaseModel):
|
||||
super().clean()
|
||||
|
||||
# Check if an identity already exists for the provided email
|
||||
if User.objects.filter(email=self.email).exists():
|
||||
if (
|
||||
User.objects.filter(email=self.email).exists()
|
||||
and not settings.OIDC_ALLOW_DUPLICATE_EMAILS
|
||||
):
|
||||
raise exceptions.ValidationError(
|
||||
{"email": _("This email is already associated to a registered user.")}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user