Skip to content

fix: reject PATCH /Groups operations targeting nonexistent users#108

Open
nicolamacoir wants to merge 1 commit into
Metatavu:developfrom
nicolamacoir:fix/group-patch-invalid-member-ref
Open

fix: reject PATCH /Groups operations targeting nonexistent users#108
nicolamacoir wants to merge 1 commit into
Metatavu:developfrom
nicolamacoir:fix/group-patch-invalid-member-ref

Conversation

@nicolamacoir
Copy link
Copy Markdown
Contributor

Summary

  • Fixes PATCH /Groups silently 200s when add/remove/replace targets a nonexistent user (REPLACE also wipes membership) #107PATCH /Groups add/remove/replace operations against a nonexistent member ID silently returned HTTP 200, and the replace variant additionally wiped the existing membership before the (failing) lookup.
  • Introduces InvalidGroupMemberReference (checked exception) and throws it from every member lookup site in GroupsController.patchGroup, replacing the silent if (user != null) no-op.
  • For REPLACE, all member IDs are now resolved up front; the existing membership is only cleared if every reference is valid. A bad ID inside a REPLACE no longer wipes the group.
  • RealmScimServer.patchGroup catches the new exception and maps it to HTTP 400, matching the existing handling of UnsupportedGroupPath / UnsupportedPatchOperation.

Changes

  • groups/InvalidGroupMemberReference.java — new checked exception carrying the offending member ID.
  • groups/GroupsController.java
    • patchGroup throws InvalidGroupMemberReference.
    • ADD / REPLACE resolve all member IDs up front (List<UserModel> resolved) and throw on any unresolved ID before mutating membership.
    • REMOVE (filter form and value-list form) throws when the referenced member does not exist.
  • realm/RealmScimServer.java — new catch (InvalidGroupMemberReference e) branch returning 400 Bad Request with the offending ID.
  • RealmGroupPatchTestsIT — three new regression tests:
    • testAddNonexistentGroupMemberReturnsBadRequest
    • testRemoveNonexistentGroupMemberReturnsBadRequest
    • testReplaceMembersWithNonexistentUserDoesNotWipeMembership — seeds a real member, sends replace with a bad ID, asserts 400 and that the original member is still in the group.

The organization-scope OrganizationScimServer.patchGroup currently returns 501 Not Implemented, so no parallel change is needed there.

Test plan

  • ./gradlew test --tests "fi.metatavu.keycloak.scim.server.test.tests.functional.RealmGroupPatchTestsIT" passes (existing happy-path tests + three new regressions)
  • Manually verify add/remove/replace with a bogus member ID returns 400
  • Manually verify replace with a bogus ID leaves existing membership intact

Notes

Returns plain-text "Invalid group member reference: <id>" to match the existing pattern in RealmScimServer.patchGroup. If you'd prefer a structured SCIM error body ({"schemas":[...], "status":"400", "scimType":"invalidValue", "detail":"..."}), happy to switch over in this PR or in a follow-up that uniformly upgrades the error responses.

GroupsController.patchGroup silently no-op'd add/remove/replace
operations whose member IDs did not resolve to a user — every site
ran getUserById(...) followed by an "if (user != null)" that just
dropped the request. SCIM clients then received HTTP 200 with the
group unchanged, indistinguishable from a successful op.

The replace variant additionally cleared the existing membership
before the lookup loop, so a single bad ID in a replace silently
wiped the group's members.

- Introduce InvalidGroupMemberReference (checked exception) and
  throw it from every member lookup site instead of the silent
  null check.
- For REPLACE, resolve every member ID up front and only swap
  membership if all references are valid — preserves existing
  members when one of the new IDs is bogus.
- Map the new exception to HTTP 400 in RealmScimServer.patchGroup,
  matching how UnsupportedGroupPath / UnsupportedPatchOperation
  are already handled.

Adds regression tests covering the add, remove (filter form), and
replace cases, plus a specific assertion that REPLACE with a bad
ID does not wipe existing members.

Closes Metatavu#107
@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PATCH /Groups silently 200s when add/remove/replace targets a nonexistent user (REPLACE also wipes membership)

1 participant