keycloak-uncached
Changes
services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissions.java 4(+4 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionManagement.java 4(+3 -1)
Details
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 ffcc0f0..2df4953 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
@@ -294,6 +294,10 @@ class MgmtPermissions implements AdminPermissionEvaluator, AdminPermissionManage
if (identity == null) {
throw new RuntimeException("Identity of admin is not set for permission query");
}
+ return evaluatePermission(resource, scope, resourceServer, identity);
+ }
+
+ public boolean evaluatePermission(Resource resource, Scope scope, ResourceServer resourceServer, Identity identity) {
RealmModel oldRealm = session.getContext().getRealm();
try {
session.getContext().setRealm(realm);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionManagement.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionManagement.java
index d4184ed..d465378 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionManagement.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionManagement.java
@@ -43,5 +43,7 @@ public interface UserPermissionManagement {
Policy mapRolesPermission();
- Policy impersonatePermission();
+ Policy adminImpersonatingPermission();
+
+ Policy userImpersonatedPermission();
}
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 46a9d01..80bf07f 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
@@ -18,12 +18,13 @@ package org.keycloak.services.resources.admin.permissions;
import org.jboss.logging.Logger;
import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.common.UserModelIdentity;
+import org.keycloak.authorization.identity.Identity;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.models.AdminRoles;
-import org.keycloak.models.ClientModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.ImpersonationConstants;
import org.keycloak.models.KeycloakSession;
@@ -47,9 +48,11 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
private static final Logger logger = Logger.getLogger(UserPermissions.class);
public static final String MAP_ROLES_SCOPE="map-roles";
public static final String IMPERSONATE_SCOPE="impersonate";
+ public static final String USER_IMPERSONATED_SCOPE="user-impersonated";
public static final String MANAGE_GROUP_MEMBERSHIP_SCOPE="manage-group-membership";
public static final String MAP_ROLES_PERMISSION_USERS = "map-roles.permission.users";
- public static final String IMPERSONATE_PERMISSION_USERS = "impersonate.permission.users";
+ public static final String ADMIN_IMPERSONATING_PERMISSION = "admin-impersonating.permission.users";
+ public static final String USER_IMPERSONATED_PERMISSION = "user-impersonated.permission.users";
public static final String MANAGE_GROUP_MEMBERSHIP_PERMISSION_USERS = "manage-group-membership.permission.users";
public static final String MANAGE_PERMISSION_USERS = "manage.permission.users";
public static final String VIEW_PERMISSION_USERS = "view.permission.users";
@@ -75,6 +78,7 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
Scope viewScope = root.realmViewScope();
Scope mapRolesScope = root.initializeRealmScope(MAP_ROLES_SCOPE);
Scope impersonateScope = root.initializeRealmScope(IMPERSONATE_SCOPE);
+ Scope userImpersonatedScope = root.initializeRealmScope(USER_IMPERSONATED_SCOPE);
Scope manageGroupMembershipScope = root.initializeRealmScope(MANAGE_GROUP_MEMBERSHIP_SCOPE);
Resource usersResource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
@@ -86,6 +90,7 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
scopeset.add(mapRolesScope);
scopeset.add(impersonateScope);
scopeset.add(manageGroupMembershipScope);
+ scopeset.add(userImpersonatedScope);
usersResource.updateScopes(scopeset);
}
Policy managePermission = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId());
@@ -104,9 +109,13 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
if (membershipPermission == null) {
Helper.addEmptyScopePermission(authz, server, MANAGE_GROUP_MEMBERSHIP_PERMISSION_USERS, usersResource, manageGroupMembershipScope);
}
- Policy impersonatePermission = authz.getStoreFactory().getPolicyStore().findByName(IMPERSONATE_PERMISSION_USERS, server.getId());
+ Policy impersonatePermission = authz.getStoreFactory().getPolicyStore().findByName(ADMIN_IMPERSONATING_PERMISSION, server.getId());
if (impersonatePermission == null) {
- Helper.addEmptyScopePermission(authz, server, IMPERSONATE_PERMISSION_USERS, usersResource, impersonateScope);
+ Helper.addEmptyScopePermission(authz, server, ADMIN_IMPERSONATING_PERMISSION, usersResource, impersonateScope);
+ }
+ impersonatePermission = authz.getStoreFactory().getPolicyStore().findByName(USER_IMPERSONATED_PERMISSION, server.getId());
+ if (impersonatePermission == null) {
+ Helper.addEmptyScopePermission(authz, server, USER_IMPERSONATED_PERMISSION, usersResource, userImpersonatedScope);
}
}
@@ -117,7 +126,7 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
scopes.put(AdminPermissionManagement.VIEW_SCOPE, viewPermission().getId());
scopes.put(MAP_ROLES_SCOPE, mapRolesPermission().getId());
scopes.put(MANAGE_GROUP_MEMBERSHIP_SCOPE, manageGroupMembershipPermission().getId());
- scopes.put(IMPERSONATE_SCOPE, impersonatePermission().getId());
+ scopes.put(IMPERSONATE_SCOPE, adminImpersonatingPermission().getId());
return scopes;
}
@@ -166,7 +175,12 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
authz.getStoreFactory().getPolicyStore().delete(policy.getId());
}
- policy = impersonatePermission();
+ policy = adminImpersonatingPermission();
+ if (policy == null) {
+ authz.getStoreFactory().getPolicyStore().delete(policy.getId());
+
+ }
+ policy = userImpersonatedPermission();
if (policy == null) {
authz.getStoreFactory().getPolicyStore().delete(policy.getId());
@@ -215,9 +229,15 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
@Override
- public Policy impersonatePermission() {
+ public Policy adminImpersonatingPermission() {
+ ResourceServer server = root.realmResourceServer();
+ return authz.getStoreFactory().getPolicyStore().findByName(ADMIN_IMPERSONATING_PERMISSION, server.getId());
+ }
+
+ @Override
+ public Policy userImpersonatedPermission() {
ResourceServer server = root.realmResourceServer();
- return authz.getStoreFactory().getPolicyStore().findByName(IMPERSONATE_PERMISSION_USERS, server.getId());
+ return authz.getStoreFactory().getPolicyStore().findByName(USER_IMPERSONATED_PERMISSION, server.getId());
}
@@ -451,7 +471,34 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
@Override
public boolean canImpersonate(UserModel user) {
- return canImpersonate();
+ if (!canImpersonate()) {
+ return false;
+ }
+
+ Identity userIdentity = new UserModelIdentity(root.realm, user);
+ if (!root.isAdminSameRealm()) {
+ return true;
+ }
+
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return true;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
+ if (resource == null) return true;
+
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(USER_IMPERSONATED_PERMISSION, server.getId());
+ if (policy == null) {
+ return true;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return true;
+ }
+
+ Scope scope = root.realmScope(USER_IMPERSONATED_SCOPE);
+ return root.evaluatePermission(resource, scope, server, userIdentity);
}
@@ -469,7 +516,7 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
if (resource == null) return false;
- Policy policy = authz.getStoreFactory().getPolicyStore().findByName(IMPERSONATE_PERMISSION_USERS, server.getId());
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(ADMIN_IMPERSONATING_PERMISSION, server.getId());
if (policy == null) {
return false;
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java
index b8edcf8..452e81f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java
@@ -25,6 +25,7 @@ import org.keycloak.authorization.model.Resource;
import org.keycloak.models.GroupModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.representations.idm.authorization.Logic;
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
@@ -160,6 +161,19 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
{
permissions.clients().setPermissionsEnabled(client1, true);
}
+ // setup Users impersonate policy
+ {
+ ClientModel realmManagementClient = realm.getClientByClientId("realm-management");
+ RoleModel adminRole = realmManagementClient.getRole(AdminRoles.REALM_ADMIN);
+ permissions.users().setPermissionsEnabled(true);
+ ResourceServer server = permissions.realmResourceServer();
+ Policy adminPolicy = permissions.roles().rolePolicy(server, adminRole);
+ adminPolicy.setLogic(Logic.NEGATIVE);
+ Policy permission = permissions.users().userImpersonatedPermission();
+ permission.addAssociatedPolicy(adminPolicy);
+ permission.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
+ }
+
}
@@ -183,6 +197,11 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
session.userCredentialManager().updateCredential(realm, nomapAdmin, UserCredentialModel.password("password"));
nomapAdmin.grantRole(adminRole);
+ UserModel anotherAdmin = session.users().addUser(realm, "anotherAdmin");
+ anotherAdmin.setEnabled(true);
+ session.userCredentialManager().updateCredential(realm, anotherAdmin, UserCredentialModel.password("password"));
+ anotherAdmin.grantRole(adminRole);
+
UserModel authorizedUser = session.users().addUser(realm, "authorized");
authorizedUser.setEnabled(true);
session.userCredentialManager().updateCredential(realm, authorizedUser, UserCredentialModel.password("password"));
@@ -372,6 +391,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
testingClient.server().run(FineGrainAdminUnitTest::setupUsers);
UserRepresentation user1 = adminClient.realm(TEST).users().search("user1").get(0);
+ UserRepresentation anotherAdmin = adminClient.realm(TEST).users().search("anotherAdmin").get(0);
UserRepresentation groupMember = adminClient.realm(TEST).users().search("groupMember").get(0);
RoleRepresentation realmRole = adminClient.realm(TEST).roles().get("realm-role").toRepresentation();
List<RoleRepresentation> realmRoleSet = new LinkedList<>();
@@ -384,6 +404,24 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
List<RoleRepresentation> clientRoleSet = new LinkedList<>();
clientRoleSet.add(clientRole);
+ // test illegal impersonation
+ {
+ Keycloak realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
+ TEST, "nomap-admin", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
+ realmClient.realm(TEST).users().get(user1.getId()).impersonate();
+ realmClient.close(); // just in case of cookie settings
+ realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
+ TEST, "nomap-admin", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
+ try {
+ realmClient.realm(TEST).users().get(anotherAdmin.getId()).impersonate();
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ }
+
{
Keycloak realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),