From 5da02971ebcb52a5e447e7a5a80d6ac4cad9ce56 Mon Sep 17 00:00:00 2001 From: Massimo Fierro Date: Tue, 14 Jan 2025 07:50:41 +0900 Subject: [PATCH] sources/scim: fix user creation (duplicate userName) (#12547) * sources/scim: fix user creation (duplicate userName) * sources/scim: add test case (duplicate username) * Formatting * simplify query with Q Signed-off-by: Jens Langhammer --------- Signed-off-by: Jens Langhammer Co-authored-by: Jens Langhammer --- authentik/sources/scim/tests/test_users.py | 49 ++++++++++++++++++++++ authentik/sources/scim/views/v2/users.py | 6 ++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/authentik/sources/scim/tests/test_users.py b/authentik/sources/scim/tests/test_users.py index 45730ce5c5f1..fe025a97f005 100644 --- a/authentik/sources/scim/tests/test_users.py +++ b/authentik/sources/scim/tests/test_users.py @@ -88,6 +88,55 @@ def test_user_create(self): ).exists() ) + def test_user_create_duplicate_by_username(self): + """Test user create""" + user = create_test_user() + username = generate_id() + obj1 = { + "userName": username, + "externalId": generate_id(), + "emails": [ + { + "primary": True, + "value": user.email, + } + ], + } + obj2 = obj1.copy() + obj2.update({"externalId": generate_id()}) + response = self.client.post( + reverse( + "authentik_sources_scim:v2-users", + kwargs={ + "source_slug": self.source.slug, + }, + ), + data=dumps(obj1), + content_type=SCIM_CONTENT_TYPE, + HTTP_AUTHORIZATION=f"Bearer {self.source.token.key}", + ) + self.assertEqual(response.status_code, 201) + self.assertTrue( + SCIMSourceUser.objects.filter(source=self.source, user__username=username).exists() + ) + self.assertTrue( + Event.objects.filter( + action=EventAction.MODEL_CREATED, user__username=self.source.token.user.username + ).exists() + ) + response = self.client.post( + reverse( + "authentik_sources_scim:v2-users", + kwargs={ + "source_slug": self.source.slug, + }, + ), + data=dumps(obj2), + content_type=SCIM_CONTENT_TYPE, + HTTP_AUTHORIZATION=f"Bearer {self.source.token.key}", + ) + self.assertEqual(response.status_code, 409) + def test_user_property_mappings(self): """Test user property_mappings""" self.source.user_property_mappings.set( diff --git a/authentik/sources/scim/views/v2/users.py b/authentik/sources/scim/views/v2/users.py index d2c6cf73b8bf..93761a3ac589 100644 --- a/authentik/sources/scim/views/v2/users.py +++ b/authentik/sources/scim/views/v2/users.py @@ -2,6 +2,7 @@ from uuid import uuid4 +from django.db.models import Q from django.db.transaction import atomic from django.http import Http404, QueryDict from django.urls import reverse @@ -113,8 +114,11 @@ def update_user(self, connection: SCIMSourceUser | None, data: QueryDict): def post(self, request: Request, **kwargs) -> Response: """Create user handler""" connection = SCIMSourceUser.objects.filter( + Q( + Q(user__uuid=request.data.get("id")) + | Q(user__username=request.data.get("userName")) + ), source=self.source, - user__uuid=request.data.get("id"), ).first() if connection: self.logger.debug("Found existing user")