Skip to content

WPB-23434: Support SCIM PATCH of multi-valued emails attribute - #5419

Open
blackheaven wants to merge 12 commits into
developfrom
gdifolco/WPB-23434-scim-patch
Open

WPB-23434: Support SCIM PATCH of multi-valued emails attribute#5419
blackheaven wants to merge 12 commits into
developfrom
gdifolco/WPB-23434-scim-patch

Conversation

@blackheaven

Copy link
Copy Markdown
Contributor

https://wearezeta.atlassian.net/browse/WPB-23434

Checklist

  • Add a new entry in an appropriate subdirectory of changelog.d
  • Read and follow the PR guidelines

@blackheaven
blackheaven requested review from a team as code owners August 5, 2026 06:56
@zebot zebot added the ok-to-test Approved for running tests in CI, overrides not-ok-to-test if both labels exist label Aug 5, 2026
Implement value-path PATCH (e.g. `emails[type eq "work"].value`) for the
`emails` multi-valued attribute in hscim's applyUserOperation, which Entra
and other IdPs use to provision user emails. Any value-path target was
previously rejected with "can not lens into multi-valued attributes yet".
Supports the value/type/primary sub-attributes and whole-entry paths;
other multi-valued attributes remain unsupported.

Also fix email propagation in Spar: an email-only PATCH does not change
externalId, so updateVsuUref (the only update-path route to validateEmail
-> Brig.updateEmail, gated on externalId change) never ran, and GET
reconstructs the email from Brig -- so the change was a silent no-op. Add
an explicit email-diff validateEmail in updateValidScimUser, gated on
externalId being unchanged to avoid double-validation with updateVsuUref.
@blackheaven
blackheaven force-pushed the gdifolco/WPB-23434-scim-patch branch from a0fb7f7 to e4acc08 Compare August 5, 2026 07:37

@akshaymankar akshaymankar left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall. Two things I would request:

  1. Document somewhere that Entra is acting wrong or fix applyUserOperation so it doesn't rewrite Add to Replace in case of multi-values and complex attributes.
  2. The integration test should be in the new suite.

Other comments are nits.

Comment thread libs/hscim/src/Web/Scim/Schema/User.hs Outdated
Comment thread libs/hscim/src/Web/Scim/Schema/User.hs Outdated
Comment thread libs/hscim/test/Test/Schema/UserSpec.hs Outdated
Comment thread services/spar/test-integration/Test/Spar/Scim/UserSpec.hs Outdated
Handle Add on emails[...] value-path explicitly instead of rewriting to Replace: whole-entry Add appends (concat semantics) while sub-attribute Add delegates to the Replace path. Give ValuePath named record fields and drop the redundant valuePathFilter helper. Correct the create-on-absent NOTE to cite Entra's documented Add behaviour. Move the end-to-end email PATCH test to the new integration suite (patchScimUser helper + testSparPatchEmailValuePath) and remove the deprecated copy. Add hscim unit tests for the Add behaviour.
@blackheaven
blackheaven force-pushed the gdifolco/WPB-23434-scim-patch branch from 1715cd6 to 709b260 Compare August 5, 2026 10:52
spar never persists the SCIM email 'type': synthesizeScimUser returns every email with typ=Nothing, so a value-path filter emails[type eq "work"] can never match an existing email. The original test created a user WITH an email, so the Add hit create-on-absent and appended a second entry; scimEmailsToEmailAddress (pickFirst) then returned the old email, so no email diff fired and Brig was never updated. Mirror the proven deprecated test instead: create an email-less SAML user, disable validateSAMLemails so the provisioned email auto-activates, and assert the Add on emails[type eq "work"].value provisions a work email that propagates to Brig. (Add delegates to the Replace path for the .value sub-attribute, so this is behaviour-equivalent to the prior Replace test.)
scimEmailsToEmailAddress now returns Either, rejecting (Left) when more than one email is marked primary (RFC 7643 §2.4 violation). spar's validateScimUser' turns the Left into a SCIM 400, covering POST/PUT/PATCH (PATCH re-validates via putUser). Previously spar silently first-wins, masking client-side misconfiguration. Other inputs (zero or one primary) are unchanged.
synthesizeScimUser now echoes type = Just "work" instead of Nothing. spar/brig store a single address with no type, but Entra PATCHes with a value-path filter emails[type eq "work"]; without an echoed type the filter never matched and such PATCHes appended a duplicate email rather than updating the address in place. Echoing the canonical type (RFC 7643 sec 4.1.2) at the single synthesis point makes the filter match across create/GET/PATCH.

Adds testSparPatchEmailValuePathInPlace (in-place update), corrects the create-on-absent test comment, and adds a changelog fragment.
Follow-up to 1406a78 (synthesizeScimUser now echoes type = Just "work" instead of Nothing): update all SCIM email test-expectations to expect type = "work", since every GET/PUT/PATCH response now materializes it.

integration framework (Test/Spar.hs): add a scimWorkEmail helper and route the 13 full-email-object assertions/inputs that compare to a response through it. PUT inputs never round-tripped to a response are left typeless, preserving coverage that the server synthesizes the type when omitted.

spar test-integration: flip the two expected-email constructions (Util/Scim.hs setDefaultRoleAndEmailsIfEmpty, APISpec.hs scim-user-search) from Nothing to Just "work".

Test-only; no production code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ok-to-test Approved for running tests in CI, overrides not-ok-to-test if both labels exist

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants