Repository navigation
SSO role sync strips existing users' roles when a mapped role has "Can edit users" off (SAML and OIDC) #38050
Description
Problem Statement
When an IdP group is mapped to a dotCMS role whose "Can edit users" flag (editUsers) is off, the role assignment throws during SSO login and the whole login fails. Because both SSO flows remove all of the user's roles before re-adding them, a user who already exists is left with their roles stripped. That includes their personal role, which is where directly granted permissions hang off. This happens with both OIDC (dotAuth OAuth) and SAML.
Impact:
- Any admin can trigger it. Unticking "Can edit users" on a role that a group mapping points to silently strips the roles of every existing SSO user the next time each of them logs in. Under SAML they lose all roles. Under OIDC with "Sync all" they keep only the system and default roles. The roles come back only after the role is fixed and the user logs in again.
- No useful error for the user. OIDC redirects to
/?error=oauth+callback+failed, which renders a 404 page. SAML returns HTTP 400 and shows raw JSON in the browser:{"message":"Cannot alter users on this role. Name:<role>, id:<role id>"}. - New users are half-provisioned. The user row is created before the role step fails, and the user cannot log in until the role is fixed.
- Hard to diagnose. Nothing reaches the error log for the existing-user case. It is recorded only as an INFO line from the security logger (
DotBadRequestExceptionMapper : Cannot alter users on this role, orOAuthWebInterceptor - OAuth callback failedfor OIDC).
Root cause:
- The throwing call.
RoleAPIImpl.addRoleToUserthrowsDotStateException("Cannot alter users on this role…")wheneditUsers=false. - OIDC path.
OAuthHelper.applyBuildRolesStrategycallsremoveAllRolesFromUser, thenapplySystemRoles,applyExtraRolesandapplyProviderGroups, which leads toaddRoleByKeyand thenaddRoleToUser. Nothing catches the exception, so the callback fails after the removal. - SAML path.
SAMLHelper.addRolescallsremoveAllRolesFromUser, thenhandleRoles,addRolesFromIDPandaddIDPRoles, which leads toUserHelper.addRoleand thenaddRoleToUser.addRolesonly catchesDotDataException, andDotSamlResourcehas no handler, so the exception reaches the genericDotStateExceptionmapper (400). SAML adds the IdP roles first, so it fails before re-adding anything.
Steps to Reproduce
Reproduced locally on dotcms/dotcms:26.10.12-01 with Keycloak 26.6.1 as both the OIDC and SAML IdP.
- In dotCMS, create a role (for example key
keycloak-editors) with Can edit users ON. - Configure dotAuth SSO on SYSTEM_HOST (OIDC or SAML) with role behavior Sync all, and a group mapping from an IdP group (for example
dotcms-editors) to that role key. For SAML the role key must equal the group name, because SAML's default strategy maps group name to role key. - Log in through SSO as an IdP user in that group. This works; the user gets
DOTCMS_BACK_END_USER, the mapped role and their personal role. - Edit the role and turn Can edit users OFF.
- Log in again as the same user.
Actual:
| Flow | Browser | User's roles afterwards |
|---|---|---|
| OIDC, existing user | /?error=oauth+callback+failed (404 page) |
only DOTCMS_BACK_END_USER |
| SAML, existing user | HTTP 400, raw JSON "Cannot alter users on this role…" | none (GET /api/v1/roles/users/<email> returns []) |
| SAML, new user | HTTP 400, raw JSON | user row created, no roles |
Expected: the login succeeds, and the unassignable role is skipped with a WARN that names the role and the reason. The user keeps every role that could be assigned.
Acceptance Criteria
- An IdP-mapped role that cannot be assigned (
editUsers=false) is skipped with a WARN naming the role key and the reason, and the login completes, for both OIDC and SAML. - A failure while re-applying roles never leaves an existing user with fewer roles than the strategy would assign. Either the new role set is computed before anything is removed, or the remove and re-add run atomically.
- The user's personal role (role key = user ID) is never removed by either flow's role sync.
- A new user whose role assignment fails is not left half-provisioned: either the user is created with the roles that could be assigned, or creation is rolled back.
- SAML login errors show a user-facing error page, not raw JSON from the ACS endpoint.
- Tests cover OIDC and SAML with an existing user, a new user, and a mapped role with
editUsers=false, under "Sync all" and "IdP groups only".
dotCMS Version
Reproduced on dotcms/dotcms:26.10.12-01 (Evergreen). The same code paths are present on main as of 2026-10-15.
Severity
High - Major functionality broken
Links
NA
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsNew
Activity