diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissions.java index 54834860357..d9bbf9bc5f2 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissions.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissions.java @@ -74,7 +74,7 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme protected final KeycloakSession session; private final AuthorizationProvider authz; protected final MgmtPermissions root; - private final PolicyStore policyStore; + protected final PolicyStore policyStore; protected final ResourceStore resourceStore; private boolean grantIfNoPermission = false; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionsV2.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionsV2.java index 75df7599bfa..3aa8e4b0bb8 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionsV2.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionsV2.java @@ -117,6 +117,10 @@ class UserPermissionsV2 extends UserPermissions { Resource resource = user == null ? null : resourceStore.findByName(server, user.getId()); if (resource == null) { + // check if there is permission for "all-users". If so, load its resource and proceed with evaluation + if (policyStore.findByName(server, AdminPermissionsSchema.USERS_RESOURCE_TYPE) == null) { + return false; + } resource = resourceStore.findByName(server, AdminPermissionsSchema.USERS_RESOURCE_TYPE, server.getId()); } @@ -127,9 +131,11 @@ class UserPermissionsV2 extends UserPermissions { List expectedScopes = Arrays.asList(scopes); for (Permission permission : permissions) { - for (String scope : permission.getScopes()) { - if (expectedScopes.contains(scope)) { - return true; + if (permission.getResourceId().equals(resource.getId())) { + for (String scope : permission.getScopes()) { + if (expectedScopes.contains(scope)) { + return true; + } } } } diff --git a/tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/UserResourceTypeEvaluationTest.java b/tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/UserResourceTypeEvaluationTest.java index d59de08fbb1..9186e1e7fd0 100644 --- a/tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/UserResourceTypeEvaluationTest.java +++ b/tests/base/src/test/java/org/keycloak/tests/admin/authz/fgap/UserResourceTypeEvaluationTest.java @@ -79,6 +79,17 @@ public class UserResourceTypeEvaluationTest extends AbstractPermissionTest { realm.admin().users().search(newUserUsername).forEach(user -> realm.admin().users().get(user.getId()).remove()); } + @Test + public void testSingleUserPermission() { + UserRepresentation myadmin = realm.admin().users().search("myadmin").get(0); + UserPolicyRepresentation allowMyAdminPermission = createUserPolicy("Only My Admin User Policy", myadmin.getId()); + // allow my admin to see alice only + createUserPermission(userAlice.admin().toRepresentation(), Set.of(VIEW), allowMyAdminPermission); + List search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1); + assertEquals(1, search.size()); + assertEquals(userAlice.getUsername(), search.get(0).getUsername()); + } + @Test public void testImpersonatePermission() { // myadmin shouldn't be able to impersonate user just yet