fix: surface duplicate-username and duplicate-email conflicts on POST /Users#110
Merged
nicolamacoir merged 4 commits intoJun 1, 2026
Conversation
… /Users
Two related problems on POST /Users when the request collides with an
existing user:
- The realm-scope createUser pre-checked only username, returning a
generic "User already exists" body that did not identify the offending
field. A duplicate email was not pre-checked, so the create reached
UserModel.setEmail and surfaced as Keycloak's generic
{"error":"unknown_error"} response.
- The organization-scope createUser had no pre-check at all — both
collisions surfaced as unknown_error.
Add a getUserByUsername + getUserByEmail pre-check on both scopes and
return a 409 with a body that identifies the duplicated field and value:
User already exists with username: <X>
User already exists with email: <X>
The email pre-check is gated on RealmModel.isDuplicateEmailsAllowed so
realms configured to permit duplicate emails still create successfully.
Adds regression tests for both the duplicate-username and the
duplicate-email cases on both scopes; existing duplicate-username tests
are strengthened to assert the message identifies the field.
Out of scope: Keycloak's user-profile email validator can still reject
duplicates even when the realm flag is on. That path would need a
ModelDuplicateException catch in the controller layer and is left for a
follow-up.
Closes Metatavu#109
…nScimServer Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The duplicate-username and duplicate-email conflict tests added in 02a11e9 used assertTrue without importing it, breaking compileTestJava. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
POST /Usersreturned{\"error\":\"unknown_error\"}for duplicate-email collisions (no SCIM-layer pre-check; Keycloak's generic error handler surfaced the model exception). The realm-scope's existing duplicate-username message also didn't identify which field collided. The organization-scope had no pre-check at all.getUserByUsername+getUserByEmailpre-check on both scopes. The 409 body now identifies the duplicated field and value:User already exists with username: <X>/User already exists with email: <X>.RealmModel.isDuplicateEmailsAllowed()so realms configured to allow duplicate emails still create successfully.Changes
RealmScimServer.createUser— distinct message on the existing username conflict, plus a new email pre-check gated on the realm flag.OrganizationScimServer.createUser— adds both pre-checks symmetric to the realm scope (previously had neither).RealmUserCreateTestsIT—testCreateDuplicateUserReturnsConflictstrengthened to assert the message namesusernameand the offending value; newtestCreateDuplicateEmailReturnsConflict.OrganizationUserCreateTestsIT— same upgrade and new email test on the org scope.Test plan
./gradlew test --tests \"fi.metatavu.keycloak.scim.server.test.tests.functional.RealmUserCreateTestsIT\"passes./gradlew test --tests \"fi.metatavu.keycloak.scim.server.test.tests.functional.OrganizationUserCreateTestsIT\"passesunknown_errorNotes / follow-ups
setEmailwill throwModelDuplicateException, which still surfaces asunknown_error. Catching that exception in the controller layer is intentionally out of scope here — happy to follow up if you'd like it bundled.ScimErrorsbecomes the canonical SCIM error builder, these twoResponse.status(...).entity(...).build()calls should migrate toScimErrors.conflict(...). Trivial follow-up.