mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
Allow mapping Admin roles by server administrator only
Closes #39956 Signed-off-by: vramik <vramik@redhat.com>
This commit is contained in:
parent
a712692535
commit
9590221ef8
@ -18,12 +18,9 @@
|
||||
package org.keycloak.services.resources.admin.fgap;
|
||||
|
||||
import static org.keycloak.authorization.fgap.AdminPermissionsSchema.ROLES_RESOURCE_TYPE;
|
||||
import static org.keycloak.models.utils.KeycloakModelUtils.getMasterRealmAdminManagementClientId;
|
||||
import static org.keycloak.services.managers.RealmManager.isAdministrationRealm;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authorization.fgap.AdminPermissionsSchema;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
@ -34,13 +31,11 @@ import org.keycloak.models.AdminRoles;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.services.resources.admin.fgap.ModelRecord.RoleModelRecord;
|
||||
|
||||
class RolePermissionsV2 extends RolePermissions {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(RolePermissionsV2.class);
|
||||
private final FineGrainedAdminPermissionEvaluator eval;
|
||||
|
||||
RolePermissionsV2(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) {
|
||||
@ -48,9 +43,20 @@ class RolePermissionsV2 extends RolePermissions {
|
||||
this.eval = new FineGrainedAdminPermissionEvaluator(session, root, resourceStore, policyStore);
|
||||
}
|
||||
|
||||
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 canMapRole(RoleModel role) {
|
||||
if (!canMapAdminRole(role)) return false;
|
||||
if (AdminRoles.ALL_ROLES.contains(role.getName()) && !hasMasterAdminRole()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (root.hasOneAdminRole(AdminRoles.MANAGE_USERS)) {
|
||||
return true;
|
||||
@ -67,7 +73,9 @@ class RolePermissionsV2 extends RolePermissions {
|
||||
|
||||
@Override
|
||||
public boolean canMapComposite(RoleModel role) {
|
||||
if (!canMapAdminRole(role)) return false;
|
||||
if (AdminRoles.ALL_ROLES.contains(role.getName()) && !hasMasterAdminRole()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (role.getContainer() instanceof ClientModel clientModel) {
|
||||
if (root.hasOneAdminRole(AdminRoles.MANAGE_CLIENTS)) {
|
||||
@ -108,90 +116,6 @@ class RolePermissionsV2 extends RolePermissions {
|
||||
return eval.getIdsByScope(ROLES_RESOURCE_TYPE, scope);
|
||||
}
|
||||
|
||||
boolean canMapAdminRole(RoleModel role) {
|
||||
if (!AdminRoles.ALL_ROLES.contains(role.getName())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (root.admin().hasRole(role)) return true;
|
||||
|
||||
if (!role.isClientRole()) { //realm role
|
||||
// if realm name is master realm, than we know this is a admin role ("admin" or "create-realm") in master realm.
|
||||
return isAdministrationRealm((RealmModel) role.getContainer()) ? adminConflictMessage(role) : true;
|
||||
} else {
|
||||
// management client in master realm
|
||||
ClientModel client = (ClientModel)role.getContainer();
|
||||
if (isAdministrationRealm(client.getRealm()) && client.getClientId().equals(getMasterRealmAdminManagementClientId(client.getRealm().getName()))) {
|
||||
return adminConflictMessage(role);
|
||||
}
|
||||
|
||||
switch (role.getName()) {
|
||||
case AdminRoles.REALM_ADMIN:
|
||||
// check to see if we have masterRealm.admin role. Otherwise abort
|
||||
if (root.adminsRealm() == null || !root.adminsRealm().getName().equals(Config.getAdminRealm())) {
|
||||
return adminConflictMessage(role);
|
||||
}
|
||||
|
||||
RealmModel masterRealm = root.adminsRealm();
|
||||
RoleModel adminRole = masterRealm.getRole(AdminRoles.ADMIN);
|
||||
return root.admin().hasRole(adminRole) ? true : adminConflictMessage(role);
|
||||
|
||||
case AdminRoles.QUERY_CLIENTS:
|
||||
case AdminRoles.QUERY_GROUPS:
|
||||
case AdminRoles.QUERY_REALMS:
|
||||
case AdminRoles.QUERY_USERS:
|
||||
return true;
|
||||
|
||||
case AdminRoles.MANAGE_CLIENTS:
|
||||
case AdminRoles.CREATE_CLIENT:
|
||||
return root.clients().canManage() ? true : adminConflictMessage(role);
|
||||
case AdminRoles.VIEW_CLIENTS:
|
||||
return root.clients().canView() ? true : adminConflictMessage(role);
|
||||
|
||||
case AdminRoles.MANAGE_USERS:
|
||||
return root.users().canManage() ? true : adminConflictMessage(role);
|
||||
case AdminRoles.VIEW_USERS:
|
||||
return root.users().canView() ? true : adminConflictMessage(role);
|
||||
case AdminRoles.IMPERSONATION:
|
||||
return root.users().canImpersonate() ? true : adminConflictMessage(role);
|
||||
|
||||
case AdminRoles.MANAGE_REALM:
|
||||
return root.realm().canManageRealm() ? true : adminConflictMessage(role);
|
||||
case AdminRoles.VIEW_REALM:
|
||||
return root.realm().canViewRealm() ? true : adminConflictMessage(role);
|
||||
case AdminRoles.MANAGE_AUTHORIZATION:
|
||||
return root.realm().canManageAuthorization(getResourceServer(role)) ? true : adminConflictMessage(role);
|
||||
case AdminRoles.VIEW_AUTHORIZATION:
|
||||
return root.realm().canViewAuthorization(getResourceServer(role)) ? true : adminConflictMessage(role);
|
||||
case AdminRoles.MANAGE_EVENTS:
|
||||
return root.realm().canManageEvents() ? true : adminConflictMessage(role);
|
||||
case AdminRoles.VIEW_EVENTS:
|
||||
return root.realm().canViewEvents() ? true : adminConflictMessage(role);
|
||||
case AdminRoles.MANAGE_IDENTITY_PROVIDERS:
|
||||
return root.realm().canManageIdentityProviders() ? true : adminConflictMessage(role);
|
||||
case AdminRoles.VIEW_IDENTITY_PROVIDERS:
|
||||
return root.realm().canViewIdentityProviders() ? true : adminConflictMessage(role);
|
||||
|
||||
default:
|
||||
return adminConflictMessage(role);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean adminConflictMessage(RoleModel role) {
|
||||
logger.debugf("Trying to assign admin privileges of role: %s but admin doesn't have same privilege", role.getName());
|
||||
return false;
|
||||
}
|
||||
|
||||
private ResourceServer getResourceServer(RoleModel role) {
|
||||
ResourceServer resourceServer = null;
|
||||
if (role.isClientRole()) {
|
||||
RoleContainerModel container = role.getContainer();
|
||||
resourceServer = session.getProvider(AuthorizationProvider.class).getStoreFactory().getResourceServerStore().findById(container.getId());
|
||||
}
|
||||
return resourceServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPermissionsEnabled(RoleModel role) {
|
||||
throw new UnsupportedOperationException("Not supported in V2");
|
||||
|
||||
@ -46,6 +46,7 @@ import static org.keycloak.authorization.fgap.AdminPermissionsSchema.MAP_ROLES;
|
||||
import static org.keycloak.authorization.fgap.AdminPermissionsSchema.MAP_ROLE_CLIENT_SCOPE;
|
||||
import static org.keycloak.authorization.fgap.AdminPermissionsSchema.MAP_ROLE_COMPOSITE;
|
||||
import static org.keycloak.authorization.fgap.AdminPermissionsSchema.VIEW;
|
||||
import org.keycloak.models.AdminRoles;
|
||||
|
||||
@KeycloakIntegrationTest
|
||||
public class RoleResourceTypeEvaluationTest extends AbstractPermissionTest {
|
||||
@ -162,4 +163,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user