Skip to content

Add an IdP config option to control IdP admin role escalation - #52652

Open
tkyjovsk wants to merge 21 commits into
keycloak:mainfrom
tkyjovsk:CVE-2026-12388
Open

tkyjovsk wants to merge 21 commits into
keycloak:mainfrom
tkyjovsk:CVE-2026-12388

Conversation

@tkyjovsk

Copy link
Copy Markdown
Contributor

Closes: #50444.

@tkyjovsk tkyjovsk changed the title Add a IdP config option to control IdP admin role escalation Add an IdP config option to control IdP admin role escalation Sep 10, 2026
@keycloak-github-bot

Copy link
Copy Markdown

Unreported flaky test detected

If the flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR.

org.keycloak.testsuite.forms.BruteForceTest#testCacheExpiryForTemporaryLockout

Keycloak CI - Forms IT (firefox)

org.opentest4j.AssertionFailedError: cache entry should have expired ==> expected: <null> but was: <org.keycloak.loginfailures.jpa.UserLoginFailureAdapter@c27c372>
	at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
	at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
	at org.junit.jupiter.api.AssertNull.failNotNull(AssertNull.java:50)
	at org.junit.jupiter.api.AssertNull.assertNull(AssertNull.java:35)
...

Report flaky test

@keycloak-github-bot keycloak-github-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unreported flaky test detected, please review

@tkyjovsk
tkyjovsk force-pushed the CVE-2026-12388 branch 3 times, most recently from a241c4a to 438642b Compare September 15, 2026 12:45
@tkyjovsk
tkyjovsk marked this pull request as ready for review September 15, 2026 12:47
@tkyjovsk
tkyjovsk requested review from a team as code owners September 15, 2026 12:47
Copilot AI balanced review requested due to automatic review settings September 15, 2026 12:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved critical and moderate findings affect default security behavior, composite and group mapper enforcement, mapper consistency, and null-configuration handling.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds a per-IdP allowAdminRoleMapping option to control administrative role escalation through identity-provider mappers.

Changes:

  • Adds model, API, UI, and translation support.
  • Enforces mapper restrictions during administration and broker login.
  • Adds RBAC tests for escalation scenarios.
File summaries
File Reviewed changes and final findings
tests/base/src/test/java/org/keycloak/tests/admin/authz/rbac/RealmAdminAccessTest.java Adds RBAC coverage for mapper escalation controls.
services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java Applies creation-time configuration restrictions. Critical (2 votes): absent configuration defaults to enabled, contrary to the documented disabled-by-default behavior.
services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java Validates IdP and mapper updates. Critical (1 vote): group mappers can bypass equivalent admin-role validation. Moderate (1 vote): realm-admin-created mappers can be persisted while runtime enforcement rejects them. Moderate (1 vote): null mapper configuration can cause a NullPointerException.
services/src/main/java/org/keycloak/broker/saml/mappers/AbstractAttributeToRoleMapper.java Applies runtime mapper restrictions for SAML mappings.
services/src/main/java/org/keycloak/broker/provider/HardcodedRoleMapper.java Applies runtime restrictions for hardcoded roles.
services/src/main/java/org/keycloak/broker/oidc/mappers/AbstractClaimToRoleMapper.java Applies runtime restrictions for OIDC claim mappings.
server-spi/src/main/java/org/keycloak/models/IdentityProviderModel.java Defines the new IdP configuration option.
server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java Exposes the configuration in representations.
server-spi-private/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderMapper.java Provides shared runtime validation. Critical (3 votes): direct-role-only checking allows mappers targeting composites containing admin roles to bypass the setting.
js/apps/admin-ui/src/identity-providers/add/AdvancedSettings.tsx Adds the administration-console setting.
js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties Adds labels and help text for the setting.
Review details

Suppressed comments (3)

services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java:534

  • AdminRoles.isAdminRole only recognizes a role whose own name is in ALL_ROLES; it does not recognize a custom composite containing, for example, realm-management.manage-users. With the flag disabled, a non-realm-admin can therefore create or update a mapper for that composite and receive the admin child transitively. Use the composite-aware AdminRoles.isAdminRoleOrComposite predicate here.
        RoleModel role = KeycloakModelUtils.getRoleFromString(session, realm, roleName);
        if (role == null || !AdminRoles.isAdminRole(role)) {
            return;

services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java:524

  • This early return lets a realm administrator create an admin-role mapper while the setting is disabled, but isAdminRoleGrantAllowed later rejects that mapper based only on the IdP flag. The API can therefore report a successful create/update for a mapper that never grants its configured role during broker login; either require the setting for realm-admin-created mappers too or persist an explicit trusted exemption that the runtime check honors.
        if (auth.hasOneAdminRole(AdminRoles.MANAGE_REALM)) {
            return;

services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java:527

  • IdentityProviderMapperRepresentation permits a null config and RepresentationToModel.removeEmptyString preserves it as null. Because every add/update mapper request now reaches this method, .get can throw a NullPointerException and return 500 for such a request; guard the null config before reading roleName.
        String roleName = mapperModel.getConfig().get(ConfigConstants.ROLE);
  • Files reviewed: 11/11 changed files
  • Comments generated: 3
  • Review effort level: Lite (auto)

Note

Copilot is running an experiment and ran this review at Lite.


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Copilot AI review requested due to automatic review settings September 17, 2026 12:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Composite and group-based escalation checks, plus the creation default, have unresolved issues.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (3)

server-spi-private/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderMapper.java:109

  • This direct-role check misses composite roles that contain an admin role. Because user role checks resolve composites, a mapper for a custom composite containing a realm-management role can still grant admin privileges while this setting is disabled; use the composite-aware AdminRoles.isAdminRoleOrComposite predicate here and in the admin endpoint validation.
        if (role == null || !AdminRoles.isAdminRole(role)) {

services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java:347

  • This validation only inspects config.role. A user with manage-identity-providers can instead add a hardcoded or claim-to-group mapper that joins users to a group carrying a built-in admin role; DefaultPermissions.isAdminUser resolves group-inherited admin roles, so this still escalates while the setting is disabled. Extend the protection to admin-bearing target groups (including inherited/composite roles) and enforce it when the group mapper is applied.
        IdentityProviderMapperModel model = RepresentationToModel.toModel(mapper);
        validateMapperAdminRoleMapping(model);

services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java:293

  • When a realm administrator creates an IdP without this key, this branch is skipped and isAllowAdminRoleMapping() defaults to true, so the new IdP is enabled by default through the REST API despite the new help text saying it is disabled by default. Track whether the key was supplied and persist false for omitted values while retaining explicit values.
            if (!auth.hasOneAdminRole(AdminRoles.MANAGE_REALM)) {
                if (identityProvider.getConfig().containsKey(IdentityProviderModel.ALLOW_ADMIN_ROLE_MAPPING)
                        && identityProvider.isAllowAdminRoleMapping()) {
                    throw ErrorResponse.error("Only users with '" + AdminRoles.MANAGE_REALM
                            + "' role can enable the '" + IdentityProviderModel.ALLOW_ADMIN_ROLE_MAPPING + "' setting.",
                            Response.Status.FORBIDDEN);
                }
                identityProvider.setAllowAdminRoleMapping(false);
            }
  • Files reviewed: 11/11 changed files
  • Comments generated: 1
  • Review effort level: Lite (auto)

Note

Copilot is running an experiment and ran this review at Lite.

Copilot AI review requested due to automatic review settings September 17, 2026 14:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Group, composite-role, cross-IdP, and default-value paths can bypass the intended escalation protection.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (3)

services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java:529

  • This validator only inspects the role property, so group mappers are always accepted. A manager can map users into a group (or child group) carrying an admin role and bypass the new switch; validate ConfigConstants.GROUP with AdminRoles.groupHasAdminRoles and enforce the same rule in the OIDC/SAML group mapper runtime paths.
        String roleName = mapperModel.getConfig().get(ConfigConstants.ROLE);
        if (roleName == null || roleName.trim().isEmpty()) {
            return;

services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java:534

  • A custom composite role containing realm-management.realm-admin is not itself recognized by isAdminRole, so it can be submitted while this switch is disabled and still confer its admin subroles. Use the composite-aware helper already provided by AdminRoles.
        RoleModel role = KeycloakModelUtils.getRoleFromString(session, realm, roleName);
        if (role == null || !AdminRoles.isAdminRole(role)) {
            return;

server-spi-private/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderMapper.java:110

  • The runtime guard also treats a composite containing admin roles as non-admin, so pre-existing/imported mappers can still grant such a composite while the switch is disabled. Use the composite-aware check here as well.
    protected static boolean isAdminRoleGrantAllowed(KeycloakSession session, RealmModel realm, RoleModel role, IdentityProviderMapperModel mapperModel) {
        if (role == null || !AdminRoles.isAdminRole(role)) {
            return true;
  • Files reviewed: 11/11 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Copilot AI review requested due to automatic review settings September 18, 2026 14:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Mapper ownership, secure defaults, and composite admin roles currently permit security controls to be bypassed.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java:410

  • The mapper fetched using the path ID is discarded, while validation uses the path IdP and persistence uses the body-controlled ID and alias. This permits updating a mapper belonging to a disabled IdP through an enabled IdP's URL; verify ownership and make the path ID/alias authoritative before validation.
        IdentityProviderMapperModel model = session.identityProviders().getMapperById(id);
        if (model == null) throw new NotFoundException("Model not found");
        model = RepresentationToModel.toModel(rep);
        validateMapperAdminRoleMapping(model);
  • Files reviewed: 13/13 changed files
  • Comments generated: 5
  • Review effort level: Balanced

Copilot AI review requested due to automatic review settings September 18, 2026 15:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Missing configuration is treated as enabled on several new-provider creation paths, undermining the documented secure default.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 13/13 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread server-spi/src/main/java/org/keycloak/models/IdentityProviderModel.java Outdated
Copilot AI review requested due to automatic review settings September 21, 2026 15:31
…enable `allowAdminRoleMapping` switch.

Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
…kward compatibility for existing IDPs.

Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
…e migration class.

Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
…per id or idp alias.

Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
…and existing IDPs.

Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
…even when the setting was off.

Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
…mapper) method.

Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
…a admin-role-granting mapper.

Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
Copilot AI review requested due to automatic review settings September 23, 2026 21:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Comment thread js/apps/admin-ui/src/identity-providers/add/AdvancedSettings.tsx
Only an administrator with the `manage-realm` permission can enable the setting.

After upgrading, if you rely on identity provider mappers to assign administrative roles or to add brokered users to groups that carry administrative roles, an administrator with the `manage-realm` permission must enable *Allow granting admin roles via mappers* on each affected identity provider.
Otherwise, those mappings stop taking effect and the affected users no longer receive the administrative roles on their next login.
Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
Copilot AI review requested due to automatic review settings September 23, 2026 22:16
Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

The Admin UI currently fails compilation, and mapper validation has unresolved alias, organization-group, and documentation issues.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 3 High severity · 1 Low severity

Open (4)
Resolved since last review (2)

Comment on lines +565 to +572
private boolean mapperJoinsAdminGroup(IdentityProviderMapperModel mapperModel) {
String groupPath = mapperModel.getConfig().get(ConfigConstants.GROUP);
if (groupPath == null || groupPath.trim().isEmpty()) {
return false;
}
GroupModel group = KeycloakModelUtils.findGroupByPath(session, realm, groupPath);
return group != null && AdminRoles.groupHasAdminRoles(group);
}
Copilot AI review requested due to automatic review settings September 23, 2026 22:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

Missing aliases can orphan mappers, and runtime coverage and upgrade guidance remain incomplete.

Review effort: Balanced
Findings: 2 High severity · 1 Low severity

Open (3)
Resolved since last review (1)
Previously missed (2)

In code that hasn't changed since last review

Medium severity Add broker-login coverage for disabled admin-group mapping

server-spi-private/​src/​main/​java/​org/​keycloak/​broker/​provider/​AbstractIdentityProviderMapper.java:126

The new broker-login test only exercises direct OIDC hardcoded-role mapping, so this security-sensitive group gate is not executed. Add a login test with an existing hardcoded admin-group mapper proving that disabled blocks the join and enabled permits it.

Medium severity Add SAML broker coverage for the admin-role guard

services/​src/​main/​java/​org/​keycloak/​broker/​saml/​mappers/​AbstractAttributeToRoleMapper.java:110

No added broker-login test enters this SAML runtime guard; the new test uses the separate OIDC hardcoded mapper. Add SAML broker coverage for an admin-role mapper with the setting disabled and enabled so this CVE fix cannot regress only on SAML providers.

Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
Copilot AI review requested due to automatic review settings September 23, 2026 23:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

The upgrade guide incorrectly promises automatic revocation of persisted administrative role assignments.

Review effort: Balanced
Findings: 1 High severity · 1 Low severity

Open (2)
Resolved since last review (1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[CVE-2026-12388] IdP mapper admin role escalation

2 participants