Allow mapping Admin roles by server administrator only

Closes #39956


# Conflicts:
#	services/src/main/java/org/keycloak/services/resources/admin/fgap/RolePermissionsV2.java
#	tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/RoleResourceTypeEvaluationTest.java

Signed-off-by: vramik <vramik@redhat.com>
This commit is contained in:
vramik 2025-05-26 12:45:16 +02:00 committed by Alexander Schwartz
parent e36fff1287
commit e5a2d3789d
2 changed files with 64 additions and 7 deletions

View File

@ -19,6 +19,7 @@ package org.keycloak.services.resources.admin.permissions;
import static org.keycloak.authorization.AdminPermissionsSchema.ROLES_RESOURCE_TYPE;
import org.keycloak.Config;
import org.keycloak.authorization.AdminPermissionsSchema;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
@ -49,12 +50,28 @@ public class RolePermissionsV2 extends RolePermissions {
super(session, realm, authz, root);
}
private boolean hasMasterAdminRole() {
RealmModel masterRealm = root.adminsRealm().getName().equals(Config.getAdminRealm()) ?
root.adminsRealm():
session.realms().getRealmByName(Config.getAdminRealm());
RoleModel adminRole = masterRealm.getRole(AdminRoles.ADMIN);
return root.admin().hasRole(adminRole);
}
@Override
public boolean canMapClientScope(RoleModel role) {
if (root.clients().canManageClientsDefault()) return true;
if (role.getContainer() instanceof ClientModel clientModel) {
if (root.clients().canMapClientScopeRoles(clientModel)) return true;
if (root.hasOneAdminRole(AdminRoles.MANAGE_CLIENTS)) {
return true;
}
if (root.clients().canMapClientScopeRoles(clientModel)) {
return true;
}
} else {
if (root.hasOneAdminRole(AdminRoles.MANAGE_REALM)) {
return true;
}
}
return hasPermission(role, MAP_ROLE_CLIENT_SCOPE_SCOPE);
@ -62,24 +79,32 @@ public class RolePermissionsV2 extends RolePermissions {
@Override
public boolean canMapComposite(RoleModel role) {
if (canManageDefault(role)) return checkAdminRoles(role);
if (AdminRoles.ALL_ROLES.contains(role.getName()) && !hasMasterAdminRole()) {
return false;
}
if (role.getContainer() instanceof ClientModel clientModel) {
if (root.clients().canMapCompositeRoles(clientModel)) return true;
}
return hasPermission(role, MAP_ROLE_COMPOSITE_SCOPE) && checkAdminRoles(role);
return hasPermission(role, MAP_ROLE_COMPOSITE_SCOPE);
}
@Override
public boolean canMapRole(RoleModel role) {
if (root.hasOneAdminRole(AdminRoles.MANAGE_USERS)) return checkAdminRoles(role);
if (AdminRoles.ALL_ROLES.contains(role.getName()) && !hasMasterAdminRole()) {
return false;
}
if (root.hasOneAdminRole(AdminRoles.MANAGE_USERS)) {
return true;
}
if (role.getContainer() instanceof ClientModel clientModel) {
if (root.clients().canMapRoles(clientModel)) return true;
}
return hasPermission(role, MAP_ROLE_SCOPE) && checkAdminRoles(role);
return hasPermission(role, MAP_ROLE_SCOPE);
}
@Override

View File

@ -25,6 +25,7 @@ import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.ClientScopeResource;
import org.keycloak.admin.client.resource.ScopePermissionsResource;
import org.keycloak.authorization.AdminPermissionsSchema;
import org.keycloak.models.AdminRoles;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
@ -171,4 +172,35 @@ public class RoleResourceTypeEvaluationTest extends AbstractPermissionTest {
assertThat(ex, instanceOf(ForbiddenException.class));
}
}
@Test
public void testMappingAdminRoles() {
UserRepresentation myadmin = realm.admin().users().search("myadmin").get(0);
ClientRepresentation realmManagement = realm.admin().clients().findByClientId("realm-management").get(0);
RoleRepresentation createClientRole = realm.admin().clients().get(realmManagement.getId()).roles().get(AdminRoles.CREATE_CLIENT).toRepresentation();
// create permission to map roles from all clients and to all users
UserPolicyRepresentation onlyMyAdminUserPolicy = createUserPolicy(realm, client, "Only My Admin User Policy", myadmin.getId());
createAllPermission(client, AdminPermissionsSchema.CLIENTS_RESOURCE_TYPE, onlyMyAdminUserPolicy, Set.of(MAP_ROLES));
createAllPermission(client, AdminPermissionsSchema.USERS_RESOURCE_TYPE, onlyMyAdminUserPolicy, Set.of(MAP_ROLES));
// create a role
RoleRepresentation role = new RoleRepresentation();
role.setName("myRole");
ClientRepresentation myclient = realm.admin().clients().findByClientId("myclient").get(0);
realm.admin().clients().get(myclient.getId()).roles().create(role);
role = realm.admin().clients().get(myclient.getId()).roles().get("myRole").toRepresentation();
// should pass
realmAdminClient.realm(realm.getName()).users().get(myadmin.getId()).roles().clientLevel(myclient.getId()).add(List.of(role));
// should fail as it is admin role and myadmin does not have master realm admin role assigned
try {
realmAdminClient.realm(realm.getName()).users().get(myadmin.getId()).roles().clientLevel(realmManagement.getId())
.add(List.of(createClientRole));
fail("Expected exception wasn't thrown.");
} catch (Exception ex) {
assertThat(ex, instanceOf(ForbiddenException.class));
}
}
}