Skip to content

Release 1.6.0#99

Open
nicolamacoir wants to merge 53 commits into
masterfrom
develop
Open

Release 1.6.0#99
nicolamacoir wants to merge 53 commits into
masterfrom
develop

Conversation

@nicolamacoir
Copy link
Copy Markdown
Contributor

Merging recent change from develop to master in order to create a next release

xgp and others added 30 commits September 5, 2025 16:18
…s. refactored the code to simplify/share a bit.
- Introduced SCIM_IDENTITY_PROVIDER_ALIAS.
- Updated kc-test.json to include unmanagedAttributePolicy and custom attributes.
- Enhanced MetadataController to handle custom attributes based on identity provider mappers.
- Updated tests to validate provisioning and patching of custom attributes.
docs: update installation instructions in README.md for GitHub Release
Added basic auth for okta compatibility
# Conflicts:
#	src/main/java/fi/metatavu/keycloak/scim/server/organization/OrganizationController.java
…dundant session storage

- Remove organizationType from ScimResources, ScimRealmResourceProvider,
  and ScimRealmResourceProviderFactory. The default OrganizationScimServerProvider
  is resolved via session.getProvider() without a provider ID. Implementors
  can override using KC_SPI_ORGANIZATION_SCIM_SERVER_PROVIDER__PROVIDER_DEFAULT.
- Lazy-initialize OrganizationScimServer in ScimResources to avoid failing
  all SCIM endpoints (including realm-level) when the org provider is missing
  or misconfigured. Includes null guard and try/catch with warning.
- Remove redundant session field from KeycloakOrganizationScimServerProvider
  (was stored but never read, since createScimContext is static).
- Remove redundant session field from OrganizationScimServer (methods use
  scimContext.getSession() instead).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: phasetwo orgs implementation
dnl555 and others added 4 commits April 23, 2026 21:17
RFC 7644 §3.5.2 allows PATCH operations to omit 'path' and carry the
attribute changes inside a map-valued 'value'. Okta's Deactivate User
flow uses this exact shape:
  {"op":"replace","value":{"active":false}}

UsersController.patchUser previously called findByScimPath(null) on this
shape, got null back, and threw UnsupportedUserPath, returning HTTP 500
without applying any state change.

This change handles the path-less shape by iterating the map-valued
'value' and applying each entry as a separate attribute update, sharing
the write logic with the with-path branch via a new private
applyPatchValue helper.

Test: RealmUserPatchTestsIT#testDeactivateUserPathLessPatchOp covers
both deactivate and reactivate through the path-less shape.

Fixes #95
Handle path-less PatchOp shape (Okta Deactivate User)
Three closely-related fixes that together let Okta-driven SCIM Group
Push and User provisioning round-trip through the realm and
organization-scoped endpoints.

1. Path-less PatchOp on Groups. RFC 7644 §3.5.2 says that when "path"
   is omitted on a PatchOp, "value" carries a map of attribute -> value
   to apply to the resource. Okta's Group Push (add / remove members)
   emits this shape:

     {"op":"replace","value":{"members":[{"value":"<user-id>"}]}}

   The previous code called findByScimPath(null), got null, and threw
   UnsupportedGroupPath, breaking every Okta Group Push. A path-less
   branch in patchGroup expands the map into one logical operation per
   entry.

2. Valid JSON error bodies on every patch / create / update / delete
   path. Per RFC 7644 §3.12 a SCIM Service Provider error response is
   a JSON object with schemas, status, and detail. Several handlers
   were returning plain-text strings, which Okta surfaces in its
   System Log as a malformed JSON parsing error and which other SCIM
   clients reject before showing the operator anything useful. A new
   ScimErrors helper centralises the envelope and is wired into the
   Groups, Users, OrganizationUser, RealmScimServer, and
   OrganizationScimServer entry points.

3. Silently ignore read-only / structural attrs on PATCH. Per RFC 7644
   §3.5.2 and the SCIM core schema (RFC 7643 §3.1), servers MUST not
   error on read-only / structural attributes appearing in PATCH
   payloads. Okta and other clients echo "id", "meta", and "schemas"
   inside the PatchOp value when it is constructed from a prior GET.
   isReadOnlyOrStructural lives on AbstractController and is
   consulted in both path-less and path-based branches before
   attribute resolution. externalId is intentionally NOT in the list
   per RFC 7643 §3.1 (client-settable).

Test assertions in *ListTestsIT switched from assertEquals on the
exact pre-change plain-text error string to assertTrue substring on
"Invalid filter", so they remain correct regardless of whether the
body is plain text or JSON.
@xgp
Copy link
Copy Markdown
Contributor

xgp commented May 18, 2026

Would ❤️ to see a release that includes the recent updates. Please let me know if there's anything I can do to help.

nicolamacoir and others added 14 commits May 19, 2026 11:29
GroupsController.dispatchGroupMembershipLeaveEvent passed
user.getEmail() directly into Map.of(), which rejects null values
and threw NullPointerException whenever the removed member had no
email set in Keycloak. PATCH /Groups remove-member then returned
HTTP 500 to the client.

The sibling join event already coalesces null to "" — apply the
same guard to the leave event so the two paths stay symmetric.

Adds a regression test that creates a user, clears their email
via the Keycloak admin client, then removes them from a group
via SCIM PATCH and asserts success.

Closes #103
… /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 #109
…email

fix: guard null email in group membership leave admin event
… on USER_PROFILE attrs

Promote UsersController.applyPatchValue to protected and delete the
near-identical OrganizationUserController.applyOrgPatchValue, routing
both controllers through the single shared method.

Add UserAttribute.clear(UserModel) with an optional Consumer<UserModel>
remover registered at construction time. USER_PROFILE-backed attributes
(externalId, displayName, etc.) register user -> user.removeAttribute(name)
so REMOVE operations no longer pass null into List.of(value), which threw
NPE on Java 9+ and returned HTTP 500.
# Conflicts:
#	src/test/java/fi/metatavu/keycloak/scim/server/test/tests/functional/RealmGroupPatchTestsIT.java
…ttributes

Add Support for Custom User Attributes in SCIM Provisioning
@sonarqubecloud
Copy link
Copy Markdown

Handle path-less PatchOp on Groups + return valid JSON errors
@sonarqubecloud
Copy link
Copy Markdown

nicolamacoir and others added 4 commits May 28, 2026 17:23
…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>
…rrors

fix: surface duplicate-username and duplicate-email conflicts on POST /Users
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jun 1, 2026

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.

4 participants