Skip to content

feat(scim): Restructure SCIM package into discovery/users/groups modules with shared response and filter utilities - #4842

Open
ravindu439 wants to merge 3 commits into
thunder-id:feature/scim-supportfrom
ravindu439:SCIM-current-bugs-fix3
Open

feat(scim): Restructure SCIM package into discovery/users/groups modules with shared response and filter utilities#4842
ravindu439 wants to merge 3 commits into
thunder-id:feature/scim-supportfrom
ravindu439:SCIM-current-bugs-fix3

Conversation

@ravindu439

@ravindu439 ravindu439 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Purpose

This PR restructures the backend/internal/scim package from a small set of generic, monolithic files into resource-scoped modules (discovery, users, groups) that share common response/error/filter utilities. It also normalizes license headers across the whole SCIM package (main + integration tests) from the WSO2 Apache boilerplate to the ThunderID SPDX header, and fixes several bugs found while doing this split: swallowed credential-lookup errors, filter errors bypassing the SCIM error/i18n pipeline, CORS not being applied to unsupported-route handlers, and /Me GET/PUT ignoring attributes/excludedAttributes.

SCIM wire-level behavior is unchanged: HTTP paths, methods, status codes, and JSON payload shapes stay the same for existing supported operations. The breaking surface is internal Go package structure (file layout, renamed/unexported functions, changed function signatures) plus stricter server-side error handling in a couple of edge cases.


⚠️ Breaking Changes

🔧 Summary of Breaking Changes

1. License header normalization
All 44 files under backend/internal/scim/ and tests/integration/scim/ had their header changed from the WSO2 Apache License boilerplate to:

// Copyright 2025-2026 The ThunderID Authors
// SPDX-License-Identifier: Apache-2.0

2. File restructuring

  • handler.go split into discovery_handler.go (ServiceProviderConfig/Schemas/ResourceTypes handlers) and response.go (shared response/error helpers).
  • handler_test.godiscovery_handler_test.go; new response_test.go added.
  • service.godiscovery_service.go; service_test.godiscovery_service_test.go.
  • group_model.gogroups_model.go, group_resource.gogroups_resource.go, group_resource_test.gogroups_resource_test.go, group_service.gogroups_service.go, group_service_test.gogroups_service_test.go, group_handler_test.gogroups_handler_test.go.
  • group_handler.go removed, replaced by groups_handler.go.
  • group_patch_validator.go / group_patch_validator_test.go removed; their logic merged into scim_validator.go / scim_validator_test.go.
  • New scim_filter.go / scim_filter_test.go — filter parsing extracted out of users_handler.go.
  • Every group/user/discovery file renamed group*groups* where applicable, for naming consistency with the existing users_*.go files.
  • scimService / scimHandler renamed to scimDiscoveryService / scimDiscoveryHandler (and their constructors newSCIMService/newSCIMHandlernewSCIMDiscoveryService/newSCIMDiscoveryHandler) to match the discovery_*.go file names.

3. Go API surface changes (package-internal)

  • ValidateSCIMUserRequest (exported) renamed to validateSCIMUserRequest (unexported).
  • reverseMapCoreAttrsForSchema(coreAttrs, schema) now returns (map[string]json.RawMessage, error) — dropped the consumedCoreKeys return value.
  • undeclaredAttrs(extensionAttrs, schema) — dropped the now-unused coreAttrs and consumedCoreKeys parameters.
  • scimUsersService.getCredentialKeys(ctx, canonicalName) now returns (map[string]struct{}, *tidcommon.ServiceError) instead of silently swallowing the lookup error and returning an empty map. ListUsers, GetUser, CreateUser, and ReplaceUser now propagate this error as ErrorInternalServer instead of silently returning users with unstripped/incorrectly-stripped credential fields.
  • parseSCIMFilterForEq now returns *tidcommon.ServiceError instead of a plain error.
  • scimDiscoveryService (formerly scimService) dropped its unused userService dependency — it never called into it; only scimUsersService needs a user service.

4. Filter errors now go through the standard SCIM error pipeline
filter parse failures on GET /Users and POST /Users/.search previously built a raw SCIMErrorResponse inline with a hardcoded, un-translated Detail string, bypassing i18n and the standard mapSCIMError mapping. This is now a proper ServiceError (SCIM-1033, ErrorInvalidFilterSyntax) with i18n keys error.scim.invalid_filter_syntax / error.scim.invalid_filter_syntax_description added to defaults.go. Response Detail text for these errors may now differ from before.

5. CORS now applied to "unsupported request" routes
init.go previously registered unsupported route stubs directly with mux.HandleFunc, bypassing CORS middleware. They're now wrapped with middleware.WithCORS, consistent with every other SCIM route.

6. /Me GET and PUT now honor attributes/excludedAttributes
HandleMeGetRequest and HandleMeReplaceRequest previously ignored these query params and always returned the full resource. They now apply attribute projection, matching GET/PUT /Users/{id} behavior.

7. FilterMaxResults source changed
backend/internal/scim/config/config.go: FilterMaxResults was a hardcoded 200, now derived from serverconst.MaxPageSize.


Approach

License headers
Normalized all 44 SCIM main-package and integration-test files from the WSO2 boilerplate to the 2-line ThunderID SPDX header, ahead of the structural refactor below.

Package restructuring

  • Split handler.go / service.go (ServiceProviderConfig, Schemas, ResourceTypes) into discovery_handler.go / discovery_service.go, matching the existing users_*.go naming convention. The shared response/error helpers that used to live in handler.go were pulled out into response.go.
  • Renamed the group implementation from singular (group_*.go) to plural (groups_*.go) to match users_*.go, and reworked scimGroupsHandler/scimGroupsService to delegate to the same shared response.go helpers as the users handler (previously groups and users had near-duplicate but subtly different response/error-mapping code).
  • Extracted filter parsing out of users_handler.go into scim_filter.go, since filtering is a cross-resource concern (used by both GET /Users and POST /Users/.search).
  • Consolidated scim_validator.go into clearly-sectioned Users vs. Groups validation (folding in the removed group_patch_validator.go), adding a shared hasSchemaURN helper that replaces three separate inline "does schemas[] contain URN X" checks with one function.
  • Renamed scimService/scimHandler to scimDiscoveryService/scimDiscoveryHandler for naming consistency with discovery_*.go, and dropped the unused userService field from scimDiscoveryService (dead dependency — the discovery service only ever used entityTypeService).

Bug fixes found during the split

  • getCredentialKeys error swallowing (Breaking Changes 3).
  • filter errors bypassing the standard SCIM error pipeline (Breaking Changes 4).
  • CORS not applied on unsupported-route stubs (Breaking Changes 5).
  • /Me GET/PUT ignoring attribute projection query params (Breaking Changes 6).
  • mapUserServiceErrorToSCIM switched from magic string error codes ("USR-1003", "USR-1014", etc.) to referencing user.ErrorUserNotFound.Code and friends directly.
  • isCanonicalAddrSubAttr (core_attr_mapper.go) rewritten from an O(n) linear scan over coreAttrRules per call to a precomputed map built once at package init.

Other included changes

  • api/scim.yaml: regenerated OpenAPI spec reflecting the above.
  • backend/internal/system/i18n/core/defaults.go: added the two i18n keys backing SCIM-1033.

Related Issues

  • N/A

Related PRs

  • N/A

Checklist

  • Followed the contribution guidelines.
  • Manual test round performed and verified.
  • Documentation provided. (Add links if there are any)
    • Ran Vale and fixed all errors and warnings
  • Tests provided.
    • Unit Tests
    • Integration Tests
  • Breaking changes. (Fill if applicable)
    • Breaking changes section filled.
    • breaking change label added.

Security checks

  • Followed secure coding standards in WSO2 Secure Coding Guidelines
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • main

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2c840714-5ac6-4988-b6f4-713d2dd7375e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ravindu439
ravindu439 force-pushed the SCIM-current-bugs-fix3 branch 2 times, most recently from 0ffc00d to fe25dfe Compare August 11, 2026 11:40
…rtise pagination support

Allow POST /Users to omit the ThunderID extension schema URN when the
request carries only core SCIM attributes. CreateUser then defaults to
the sole configured user type via resolveDefaultEntityTypeName, erroring
if zero or more than one type is configured. ReplaceUser (and Me PUT)
still require the extension URN explicitly; no fallback on update.

Also advertise the RFC 9865 pagination attribute on
GET /ServiceProviderConfig: index-based pagination supported, cursor
based pagination not implemented.
@ravindu439
ravindu439 force-pushed the SCIM-current-bugs-fix3 branch 2 times, most recently from d6789dc to e3d242b Compare August 12, 2026 05:01
@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

@ravindu439
ravindu439 force-pushed the SCIM-current-bugs-fix3 branch 2 times, most recently from 45f6c60 to c27eb6e Compare August 13, 2026 06:16
Split the generic handler.go/service.go into discovery_handler.go and
discovery_service.go, and rename group_*.go to groups_*.go to match the
existing users_*.go convention. Extract shared response/error handling
into response.go and filter parsing into scim_filter.go so all three
resource handlers use the same code paths instead of duplicating them.

Normalize license headers across the SCIM package and its integration
tests from the WSO2 Apache boilerplate to the ThunderID SPDX header.

Fix bugs found while doing the split:
- getCredentialKeys silently swallowed entity-type lookup failures and
  returned an empty credential set instead of surfacing an error
- filter parse errors on GET /Users and POST /Users/.search bypassed
  the SCIM error/i18n pipeline with a hardcoded, untranslated response
- unsupported SCIM route stubs were registered without CORS middleware
- /Me GET and PUT ignored the attributes/excludedAttributes query
  parameters and always returned the full resource

Rename scimService/scimHandler to scimDiscoveryService/scimDiscoveryHandler
to match the discovery_*.go file names, and drop the unused userService
dependency from the discovery service, since only scimUsersService needs it.

Regenerate api/scim.yaml to reflect the above.
@ravindu439
ravindu439 force-pushed the SCIM-current-bugs-fix3 branch from c27eb6e to 6a0cbd2 Compare August 14, 2026 04:43
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.

1 participant