diff --git a/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/AvailableRoleMappingResource.java b/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/AvailableRoleMappingResource.java index 7512aa8abb1..39574d5b592 100644 --- a/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/AvailableRoleMappingResource.java +++ b/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/AvailableRoleMappingResource.java @@ -1,5 +1,6 @@ package org.keycloak.admin.ui.rest; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Set; @@ -21,7 +22,9 @@ import org.eclipse.microprofile.openapi.annotations.media.Schema; import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; import org.keycloak.admin.ui.rest.model.ClientRole; import org.keycloak.admin.ui.rest.model.RoleMapper; +import org.keycloak.authorization.AdminPermissionsSchema; import org.keycloak.common.Profile; +import org.keycloak.models.AdminRoles; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientScopeModel; import org.keycloak.models.GroupModel; @@ -32,12 +35,12 @@ import org.keycloak.models.UserModel; import org.keycloak.models.UserProvider; import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; -import static org.keycloak.services.resources.admin.permissions.ClientPermissionManagement.MAP_ROLES_CLIENT_SCOPE; -import static org.keycloak.services.resources.admin.permissions.ClientPermissionManagement.MAP_ROLES_COMPOSITE_SCOPE; -import static org.keycloak.services.resources.admin.permissions.ClientPermissionManagement.MAP_ROLES_SCOPE; -import static org.keycloak.services.resources.admin.permissions.RolePermissionManagement.MAP_ROLE_CLIENT_SCOPE_SCOPE; -import static org.keycloak.services.resources.admin.permissions.RolePermissionManagement.MAP_ROLE_COMPOSITE_SCOPE; -import static org.keycloak.services.resources.admin.permissions.RolePermissionManagement.MAP_ROLE_SCOPE; +import static org.keycloak.authorization.AdminPermissionsSchema.MAP_ROLE; +import static org.keycloak.authorization.AdminPermissionsSchema.MAP_ROLE_CLIENT_SCOPE; +import static org.keycloak.authorization.AdminPermissionsSchema.MAP_ROLE_COMPOSITE; +import static org.keycloak.authorization.AdminPermissionsSchema.MAP_ROLES; +import static org.keycloak.authorization.AdminPermissionsSchema.MAP_ROLES_CLIENT_SCOPE; +import static org.keycloak.authorization.AdminPermissionsSchema.MAP_ROLES_COMPOSITE; public class AvailableRoleMappingResource extends RoleMappingResource { public AvailableRoleMappingResource(KeycloakSession session, RealmModel realm, AdminPermissionEvaluator auth) { @@ -66,18 +69,20 @@ public class AvailableRoleMappingResource extends RoleMappingResource { @DefaultValue("0") int first, @QueryParam("max") @DefaultValue("10") int max, @QueryParam("search") @DefaultValue("") String search) { ClientScopeModel scopeModel = this.realm.getClientScopeById(id); if (scopeModel == null) { - throw new NotFoundException("Could not find client scope"); + if (auth.clients().canListClientScopes()) throw new NotFoundException("Could not find client scope"); + else throw new ForbiddenException(); } else { - if(this.auth.clients().canManage() || !Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ)) { - this.auth.clients().requireManage(); + if (auth.hasOneAdminRole(AdminRoles.MANAGE_CLIENTS)) { Stream excludedRoleIds = scopeModel.getScopeMappingsStream().filter(RoleModel::isClientRole).map(RoleModel::getId); return searchForClientRolesByExcludedIds(realm, search, first, max, excludedRoleIds); - } else { - this.auth.clients().requireView(scopeModel); - Set roleIds = getRoleIdsWithPermissions(MAP_ROLE_CLIENT_SCOPE_SCOPE, MAP_ROLES_CLIENT_SCOPE); + } + if (AdminPermissionsSchema.SCHEMA.isAdminPermissionsEnabled(realm) || Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ)) { + auth.clients().requireView(scopeModel); + Set roleIds = getRoleIdsWithPermissions(MAP_ROLE_CLIENT_SCOPE, MAP_ROLES_CLIENT_SCOPE); scopeModel.getScopeMappingsStream().forEach(role -> roleIds.remove(role.getId())); return searchForClientRolesByIds(realm, roleIds.stream(), search, first, max); } + return Collections.emptyList(); } } @@ -103,18 +108,20 @@ public class AvailableRoleMappingResource extends RoleMappingResource { @DefaultValue("0") int first, @QueryParam("max") @DefaultValue("10") int max, @QueryParam("search") @DefaultValue("") String search) { ClientModel client = this.realm.getClientById(id); if (client == null) { - throw new NotFoundException("Could not find client"); + if (auth.clients().canList()) throw new NotFoundException("Could not find client"); + else throw new ForbiddenException(); } else { - if(this.auth.clients().canManage() || !Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ)) { - this.auth.clients().requireManage(); + if (auth.hasOneAdminRole(AdminRoles.MANAGE_CLIENTS)) { Stream excludedRoleIds = Stream.concat(client.getScopeMappingsStream(), client.getRolesStream()).filter(RoleModel::isClientRole).map(RoleModel::getId); return searchForClientRolesByExcludedIds(realm, search, first, max, excludedRoleIds); - } else { - this.auth.clients().requireView(client); - Set roleIds = getRoleIdsWithPermissions(MAP_ROLE_CLIENT_SCOPE_SCOPE, MAP_ROLES_CLIENT_SCOPE); + } + if (AdminPermissionsSchema.SCHEMA.isAdminPermissionsEnabled(realm) || Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ)) { + auth.clients().requireView(client); + Set roleIds = getRoleIdsWithPermissions(MAP_ROLE_CLIENT_SCOPE, MAP_ROLES_CLIENT_SCOPE); Stream.concat(client.getScopeMappingsStream(), client.getRolesStream()).forEach(role -> roleIds.remove(role.getId())); return searchForClientRolesByIds(realm, roleIds.stream(), search, first, max); } + return Collections.emptyList(); } } @@ -140,18 +147,20 @@ public class AvailableRoleMappingResource extends RoleMappingResource { @DefaultValue("0") int first, @QueryParam("max") @DefaultValue("10") int max, @QueryParam("search") @DefaultValue("") String search) { GroupModel group = this.realm.getGroupById(id); if (group == null) { - throw new NotFoundException("Could not find group"); + if (auth.groups().canList()) throw new NotFoundException("Could not find group"); + else throw new ForbiddenException(); } else { - if(this.auth.users().canManage() || !Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ)) { - this.auth.users().requireManage(); + if (auth.hasOneAdminRole(AdminRoles.MANAGE_USERS)) { Stream excludedRoleIds = group.getRoleMappingsStream().filter(RoleModel::isClientRole).map(RoleModel::getId); return searchForClientRolesByExcludedIds(realm, search, first, max, excludedRoleIds); - } else { - this.auth.groups().requireView(group); - Set roleIds = getRoleIdsWithPermissions(MAP_ROLE_SCOPE, MAP_ROLES_SCOPE); + } + if (AdminPermissionsSchema.SCHEMA.isAdminPermissionsEnabled(realm) || Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ)) { + auth.groups().requireView(group); + Set roleIds = getRoleIdsWithPermissions(MAP_ROLE, MAP_ROLES); group.getRoleMappingsStream().forEach(role -> roleIds.remove(role.getId())); return searchForClientRolesByIds(realm, roleIds.stream(), search, first, max); } + return Collections.emptyList(); } } @@ -181,16 +190,20 @@ public class AvailableRoleMappingResource extends RoleMappingResource { if (auth.users().canQuery()) throw new NotFoundException("User not found"); else throw new ForbiddenException(); } else { - if (this.auth.users().canManage() || !Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ)) { - this.auth.users().requireManage(); + if (auth.hasOneAdminRole(AdminRoles.MANAGE_USERS)) { Stream excludedRoleIds = userModel.getRoleMappingsStream().filter(RoleModel::isClientRole).map(RoleModel::getId); return searchForClientRolesByExcludedIds(realm, search, first, max, excludedRoleIds); - } else { - this.auth.users().requireView(userModel); - Set roleIds = getRoleIdsWithPermissions(MAP_ROLE_SCOPE, MAP_ROLES_SCOPE); + } + if (AdminPermissionsSchema.SCHEMA.isAdminPermissionsEnabled(realm) || Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ)) { + auth.users().requireView(userModel); + if (!auth.users().canMapRoles(userModel)) { + return Collections.emptyList(); + } + Set roleIds = getRoleIdsWithPermissions(MAP_ROLE, MAP_ROLES); userModel.getRoleMappingsStream().forEach(role -> roleIds.remove(role.getId())); return searchForClientRolesByIds(realm, roleIds.stream(), search, first, max); } + return Collections.emptyList(); } } @@ -214,19 +227,20 @@ public class AvailableRoleMappingResource extends RoleMappingResource { ) public final List listAvailableRoleMappings(@PathParam("id") String id, @QueryParam("first") @DefaultValue("0") int first, @QueryParam("max") @DefaultValue("10") int max, @QueryParam("search") @DefaultValue("") String search) { - if (this.auth.users().canManage() || !Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ)) { - this.auth.users().requireManage(); + if (auth.hasOneAdminRole(AdminRoles.MANAGE_USERS)) { return searchForClientRolesByExcludedIds(realm, search, first, max, Stream.of(id)); - } else { - Set roleIds = getRoleIdsWithPermissions(MAP_ROLE_COMPOSITE_SCOPE, MAP_ROLES_COMPOSITE_SCOPE); + } + if (AdminPermissionsSchema.SCHEMA.isAdminPermissionsEnabled(realm) || Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ)) { + Set roleIds = getRoleIdsWithPermissions(MAP_ROLE_COMPOSITE, MAP_ROLES_COMPOSITE); roleIds.remove(id); return searchForClientRolesByIds(realm, roleIds.stream(), search, first, max); } + return Collections.emptyList(); } private Set getRoleIdsWithPermissions(String roleResourceScope, String clientResourceScope) { - Set roleIds = this.auth.roles().getRoleIdsWithViewPermission(roleResourceScope); - Set clientIds = this.auth.clients().getClientIdsWithViewPermission(clientResourceScope); + Set roleIds = this.auth.roles().getRoleIdsByScope(roleResourceScope); + Set clientIds = this.auth.clients().getClientIdsByScope(clientResourceScope); clientIds.stream().flatMap(cid -> realm.getClientById(cid).getRolesStream()).forEach(role -> roleIds.add(role.getId())); return roleIds; } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionEvaluator.java index 19abaf4f93a..d42ad2e8091 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionEvaluator.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionEvaluator.java @@ -26,6 +26,7 @@ public interface AdminPermissionEvaluator { RealmPermissionEvaluator realm(); void requireAnyAdminRole(); + boolean hasOneAdminRole(String... adminRoles); AdminAuth adminAuth(); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java index 46d94044b66..826eb5302b8 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java @@ -196,9 +196,9 @@ public interface ClientPermissionEvaluator { Map getAccess(ClientModel client); /** - * Returns the IDs of the clients that the current user can view.. + * Returns the IDs of the clients that the current user can perform based on {@code scope}. * - * @return Stream of IDs of clients with view permission. + * @return Stream of IDs of clients with {@code scope} permission. */ - Set getClientIdsWithViewPermission(String scope); + Set getClientIdsByScope(String scope); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissions.java index 23d6169e296..4ee82b58b53 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissions.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissions.java @@ -671,7 +671,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionM } @Override - public Set getClientIdsWithViewPermission(String scope) { + public Set getClientIdsByScope(String scope) { if (!root.isAdminSameRealm()) { return Collections.emptySet(); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionsV2.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionsV2.java index 1dbf48a2594..1e5ebaf1331 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionsV2.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionsV2.java @@ -129,7 +129,7 @@ class ClientPermissionsV2 extends ClientPermissions { } @Override - public Set getClientIdsWithViewPermission(String scope) { + public Set getClientIdsByScope(String scope) { if (!root.isAdminSameRealm()) { return Collections.emptySet(); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissions.java index 97dbe2a8f56..b4c58b858f6 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissions.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissions.java @@ -147,6 +147,7 @@ class MgmtPermissions implements AdminPermissionEvaluator, AdminPermissionManage return hasOneAdminRole(realm, AdminRoles.ALL_REALM_ROLES); } + @Override public boolean hasOneAdminRole(String... adminRoles) { return hasOneAdminRole(realm, adminRoles); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionEvaluator.java index b9ba658b44a..7730dfb7a74 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionEvaluator.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionEvaluator.java @@ -142,9 +142,9 @@ public interface RolePermissionEvaluator { void requireView(RoleContainerModel container); /** - * Returns the IDs of the roles that the current user can view.. + * Returns the IDs of the roles that the current user can perform based on {@code scope}. * - * @return Stream of IDs of roles with view permission. + * @return Stream of IDs of roles with {@code scope} permission. */ - Set getRoleIdsWithViewPermission(String scope); + Set getRoleIdsByScope(String scope); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissions.java index 9627772f4b1..1db6e5bdbd1 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissions.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissions.java @@ -546,7 +546,7 @@ class RolePermissions implements RolePermissionEvaluator, RolePermissionManageme } @Override - public Set getRoleIdsWithViewPermission(String scope) { + public Set getRoleIdsByScope(String scope) { if (!root.isAdminSameRealm()) { return Collections.emptySet(); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionsV2.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionsV2.java index 6015d69efb1..ee5e0fb02c3 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionsV2.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionsV2.java @@ -83,7 +83,7 @@ public class RolePermissionsV2 extends RolePermissions { } @Override - public Set getRoleIdsWithViewPermission(String scope) { + public Set getRoleIdsByScope(String scope) { if (!root.isAdminSameRealm()) { return Collections.emptySet(); }