fix(directory-sync): parse SCIM PATCH op case-insensitively so Entra group changes aren't dropped - #4070
fix(directory-sync): parse SCIM PATCH op case-insensitively so Entra group changes aren't dropped#4070byrmsh wants to merge 1 commit into
Conversation
…group changes aren't dropped
📝 WalkthroughWalkthroughSCIM group and user PATCH parsers now normalize operation verbs to lowercase. Tests cover PascalCase and lowercase operations, removal sentinels, replacement values, and malformed group operations. ChangesSCIM patch parsing
Estimated code review effort: 2 (Simple) | ~10 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
|
|
Thanks @byrmsh, the team will review your PR. |
Problem
parseGroupOperationandparseUserPatchRequest(npm/src/directory-sync/scim/utils.ts) compare the SCIM PATCHopagainst lowercase literals (op === 'add','remove','replace').Microsoft Entra ID, unless the tenant enables the
aadOptscim062020feature flag, sends PascalCaseopvalues. Microsoft documents this with verbatim request samples, e.g. a group member add:and a member remove uses
"op": "Remove". See "Known issues with SCIM 2.0 protocol compliance" (Microsoft Learn): https://learn.microsoft.com/en-us/entra/identity/app-provisioning/application-provisioning-config-problem-scim-compatibilityBecause
"Add" !== "add", group operations fall through to{ action: 'unknown' }. InDirectoryGroups.patch()(npm/src/directory-sync/scim/DirectoryGroups.ts) anunknownaction matches none of theaddGroupMember/removeGroupMember/updateGroupNamebranches, so it is silently discarded: the request still returns200, nogroup.user_addedwebhook is emitted, and the membership is never recorded. Group provisioning from a default-configured Entra tenant simply does not work.The same mismatch affects user PATCH:
parseUserPatchRequestgates attribute removal onop === 'remove', so Entra's"op": "Remove"is not recognized as a removal and is instead applied as a set.The
aadOptscim062020flag that emits lowercase ops is opt-in and, per the same doc, "currently doesn't work with on-demand provisioning", so real Entra directories hit this by default.Fix
Normalize
opto lowercase before comparison in both functions. This is loss-less: every valid SCIM (RFC 7644) / JSON Patch (RFC 6902) op token is already lowercase, so normalizing can only broaden matching, never reinterpret a valid op. Optional chaining (operation.op?.toLowerCase()) keeps a malformed op-less operation degrading to its existing default ({ action: 'unknown' }/ an attribute set) instead of throwing.Added a unit test in
npm/test/dsync/scim-utils.test.tscovering PascalCase and lowercase group ops (plus the op-less case) and PascalCase user removals.