From 536a57a514609670394b0f107e70fc1f8afa5475 Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Mon, 5 Jun 2017 19:52:51 -0400 Subject: [PATCH] ui for permission reference --- .../resources/admin/ClientResource.java | 55 ++++++++++++ .../resources/admin/ClientsResource.java | 2 +- .../resources/admin/GroupResource.java | 55 ++++++++++++ .../ClientPermissionManagement.java | 17 ++++ .../admin/permissions/ClientPermissions.java | 89 ++++++++++++++----- .../GroupPermissionManagement.java | 5 ++ .../admin/permissions/GroupPermissions.java | 36 ++++++-- .../admin/permissions/RolePermissions.java | 6 +- .../testsuite/AbstractKeycloakTest.java | 6 +- .../admin/FineGrainAdminUnitTest.java | 21 +++-- .../messages/admin-messages_en.properties | 17 +++- .../admin/resources/js/authz/authz-app.js | 24 +++++ .../resources/js/authz/authz-controller.js | 32 +++++++ .../resources/js/authz/authz-services.js | 48 +++++++++- .../theme/base/admin/resources/js/services.js | 22 ----- .../authz/mgmt/client-permissions.html | 39 ++++++++ .../authz/mgmt/client-role-permissions.html | 3 +- .../authz/mgmt/group-permissions.html | 39 ++++++++ .../authz/mgmt/realm-role-permissions.html | 2 +- .../authz/mgmt/users-permissions.html | 2 +- .../resources/templates/kc-tabs-client.html | 4 + .../resources/templates/kc-tabs-group.html | 4 + 22 files changed, 456 insertions(+), 72 deletions(-) create mode 100644 themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/client-permissions.html create mode 100644 themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/group-permissions.html diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java index 58ae6231b8..36362cb67b 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java @@ -31,6 +31,7 @@ import org.keycloak.models.Constants; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; @@ -41,6 +42,7 @@ import org.keycloak.protocol.ClientInstallationProvider; import org.keycloak.representations.adapters.action.GlobalRequestResult; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.representations.idm.ManagementPermissionReference; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserSessionRepresentation; import org.keycloak.services.ErrorResponse; @@ -52,6 +54,8 @@ import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.ResourceAdminManager; import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; +import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement; +import org.keycloak.services.resources.admin.permissions.AdminPermissions; import org.keycloak.services.validation.ClientValidator; import org.keycloak.services.validation.PairwiseClientValidator; import org.keycloak.services.validation.ValidationMessages; @@ -536,4 +540,55 @@ public class ClientResource { return resource; } + + /** + * Return object stating whether client Authorization permissions have been initialized or not and a reference + * + * @return + */ + @Path("management/permissions") + @GET + @Produces(MediaType.APPLICATION_JSON) + @NoCache + public ManagementPermissionReference getManagementPermissions() { + auth.roles().requireView(client); + + AdminPermissionManagement permissions = AdminPermissions.management(session, realm); + if (!permissions.clients().isPermissionsEnabled(client)) { + return new ManagementPermissionReference(); + } + return toMgmtRef(client, permissions); + } + + public static ManagementPermissionReference toMgmtRef(ClientModel client, AdminPermissionManagement permissions) { + ManagementPermissionReference ref = new ManagementPermissionReference(); + ref.setEnabled(true); + ref.setResource(permissions.clients().resource(client).getId()); + ref.setScopePermissions(permissions.clients().getPermissions(client)); + return ref; + } + + + /** + * Return object stating whether client Authorization permissions have been initialized or not and a reference + * + * + * @return initialized manage permissions reference + */ + @Path("management/permissions") + @PUT + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @NoCache + public ManagementPermissionReference setManagementPermissionsEnabled(ManagementPermissionReference ref) { + auth.clients().requireManage(client); + if (ref.isEnabled()) { + AdminPermissionManagement permissions = AdminPermissions.management(session, realm); + permissions.clients().setPermissionsEnabled(client, ref.isEnabled()); + return toMgmtRef(client, permissions); + } else { + return new ManagementPermissionReference(); + } + } + } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java index d62056014b..41b91befdf 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java @@ -207,7 +207,7 @@ public class ClientsResource { ClientModel clientModel = realm.getClientById(id); if (clientModel == null) { // we do this to make sure somebody can't phish ids - if (!auth.clients().canList()) throw new NotFoundException("Could not find client"); + if (auth.clients().canList()) throw new NotFoundException("Could not find client"); else throw new ForbiddenException(); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java b/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java index a529a4895e..a5b7a6c77b 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java @@ -21,6 +21,7 @@ import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.ResourceType; +import org.keycloak.models.ClientModel; import org.keycloak.models.Constants; import org.keycloak.models.GroupModel; import org.keycloak.models.KeycloakSession; @@ -28,6 +29,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.representations.idm.GroupRepresentation; +import org.keycloak.representations.idm.ManagementPermissionReference; import org.keycloak.representations.idm.UserRepresentation; import javax.ws.rs.Consumes; @@ -50,6 +52,8 @@ import java.util.Map; import java.util.Set; import org.keycloak.services.ErrorResponse; import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; +import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement; +import org.keycloak.services.resources.admin.permissions.AdminPermissions; /** * @resource Groups @@ -214,4 +218,55 @@ public class GroupResource { return results; } + /** + * Return object stating whether client Authorization permissions have been initialized or not and a reference + * + * @return + */ + @Path("management/permissions") + @GET + @Produces(MediaType.APPLICATION_JSON) + @NoCache + public ManagementPermissionReference getManagementPermissions() { + auth.groups().requireView(group); + + AdminPermissionManagement permissions = AdminPermissions.management(session, realm); + if (!permissions.groups().isPermissionsEnabled(group)) { + return new ManagementPermissionReference(); + } + return toMgmtRef(group, permissions); + } + + public static ManagementPermissionReference toMgmtRef(GroupModel group, AdminPermissionManagement permissions) { + ManagementPermissionReference ref = new ManagementPermissionReference(); + ref.setEnabled(true); + ref.setResource(permissions.groups().resource(group).getId()); + ref.setScopePermissions(permissions.groups().getPermissions(group)); + return ref; + } + + + /** + * Return object stating whether client Authorization permissions have been initialized or not and a reference + * + * + * @return initialized manage permissions reference + */ + @Path("management/permissions") + @PUT + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @NoCache + public ManagementPermissionReference setManagementPermissionsEnabled(ManagementPermissionReference ref) { + auth.groups().requireManage(group); + if (ref.isEnabled()) { + AdminPermissionManagement permissions = AdminPermissions.management(session, realm); + permissions.groups().setPermissionsEnabled(group, ref.isEnabled()); + return toMgmtRef(group, permissions); + } else { + return new ManagementPermissionReference(); + } + } + } + diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionManagement.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionManagement.java index 26ae9514d8..d967443264 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionManagement.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionManagement.java @@ -17,21 +17,38 @@ package org.keycloak.services.resources.admin.permissions; import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.ResourceServer; import org.keycloak.models.ClientModel; +import java.util.Map; + /** * @author Bill Burke * @version $Revision: 1 $ */ public interface ClientPermissionManagement { public static final String MAP_ROLES_SCOPE = "map-roles"; + public static final String MAP_ROLES_CLIENT_SCOPE = "map-roles-client-scope"; + public static final String MAP_ROLES_COMPOSITE_SCOPE = "map-roles-composite"; boolean isPermissionsEnabled(ClientModel client); void setPermissionsEnabled(ClientModel client, boolean enable); + Resource resource(ClientModel client); + + Map getPermissions(ClientModel client); + Policy mapRolesPermission(ClientModel client); + Policy mapRolesClientScopePermission(ClientModel client); + + Policy mapRolesCompositePermission(ClientModel client); + + Policy managePermission(ClientModel client); + + Policy viewPermission(ClientModel client); + ResourceServer resourceServer(ClientModel client); } 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 3118f7a0f3..06fa5d822e 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 @@ -30,7 +30,9 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.services.ForbiddenException; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; /** @@ -67,11 +69,11 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa private String getMapRolesPermissionName(ClientModel client) { return MAP_ROLES_SCOPE + ".permission.client." + client.getId(); } - private String getMapRoleClientScopePermissionName(ClientModel client) { - return RolePermissionManagement.MAP_ROLE_CLIENT_SCOPE_SCOPE + ".permission.client." + client.getId(); + private String getMapRolesClientScopePermissionName(ClientModel client) { + return MAP_ROLES_CLIENT_SCOPE + ".permission.client." + client.getId(); } - private String getMapRoleCompositePermissionName(ClientModel client) { - return RolePermissionManagement.MAP_ROLE_COMPOSITE_SCOPE + ".permission.client." + client.getId(); + private String getMapRolesCompositePermissionName(ClientModel client) { + return MAP_ROLES_COMPOSITE_SCOPE + ".permission.client." + client.getId(); } private void initialize(ClientModel client) { @@ -88,13 +90,13 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa if (mapRoleScope == null) { mapRoleScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLES_SCOPE, server); } - Scope mapRoleClientScope = mapRoleClientScope(server); + Scope mapRoleClientScope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_CLIENT_SCOPE, server.getId()); if (mapRoleClientScope == null) { - mapRoleClientScope = authz.getStoreFactory().getScopeStore().create(RolePermissionManagement.MAP_ROLE_CLIENT_SCOPE_SCOPE, server); + mapRoleClientScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLES_CLIENT_SCOPE, server); } - Scope mapRoleCompositeScope = mapRoleCompositeScope(server); + Scope mapRoleCompositeScope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_COMPOSITE_SCOPE, server.getId()); if (mapRoleCompositeScope == null) { - mapRoleCompositeScope = authz.getStoreFactory().getScopeStore().create(RolePermissionManagement.MAP_ROLE_COMPOSITE_SCOPE, server); + mapRoleCompositeScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLES_COMPOSITE_SCOPE, server); } String resourceName = getResourceName(client); @@ -129,12 +131,12 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa if (mapRolePermission == null) { Helper.addEmptyScopePermission(authz, server, mapRolePermissionName, resource, mapRoleScope); } - String mapRoleClientScopePermissionName = getMapRoleClientScopePermissionName(client); + String mapRoleClientScopePermissionName = getMapRolesClientScopePermissionName(client); Policy mapRoleClientScopePermission = authz.getStoreFactory().getPolicyStore().findByName(mapRoleClientScopePermissionName, server.getId()); if (mapRoleClientScopePermission == null) { Helper.addEmptyScopePermission(authz, server, mapRoleClientScopePermissionName, resource, mapRoleClientScope); } - String mapRoleCompositePermissionName = getMapRoleCompositePermissionName(client); + String mapRoleCompositePermissionName = getMapRolesCompositePermissionName(client); Policy mapRoleCompositePermission = authz.getStoreFactory().getPolicyStore().findByName(mapRoleCompositePermissionName, server.getId()); if (mapRoleCompositePermission == null) { Helper.addEmptyScopePermission(authz, server, mapRoleCompositePermissionName, resource, mapRoleCompositeScope); @@ -155,8 +157,8 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa deletePolicy(getManagePermissionName(client), client, server); deletePolicy(getViewPermissionName(client), client, server); deletePolicy(getMapRolesPermissionName(client), client, server); - deletePolicy(getMapRoleClientScopePermissionName(client), client, server); - deletePolicy(getMapRoleCompositePermissionName(client), client, server); + deletePolicy(getMapRolesClientScopePermissionName(client), client, server); + deletePolicy(getMapRolesCompositePermissionName(client), client, server); Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());; if (resource != null) authz.getStoreFactory().getResourceStore().delete(resource.getId()); } @@ -190,12 +192,6 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa private Scope mapRolesScope(ResourceServer server) { return authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_SCOPE, server.getId()); } - private Scope mapRoleClientScope(ResourceServer server) { - return authz.getStoreFactory().getScopeStore().findByName(RolePermissionManagement.MAP_ROLE_CLIENT_SCOPE_SCOPE, server.getId()); - } - private Scope mapRoleCompositeScope(ResourceServer server) { - return authz.getStoreFactory().getScopeStore().findByName(RolePermissionManagement.MAP_ROLE_COMPOSITE_SCOPE, server.getId()); - } @Override public boolean canList() { @@ -250,6 +246,27 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa } } + @Override + public Resource resource(ClientModel client) { + ResourceServer server = resourceServer(client); + if (server == null) return null; + Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId()); + if (resource == null) return null; + return resource; + } + + @Override + public Map getPermissions(ClientModel client) { + Map scopes = new HashMap<>(); + scopes.put(MAP_ROLES_SCOPE, mapRolesPermission(client).getId()); + scopes.put(MAP_ROLES_CLIENT_SCOPE, mapRolesClientScopePermission(client).getId()); + scopes.put(MAP_ROLES_COMPOSITE_SCOPE, mapRolesCompositePermission(client).getId()); + scopes.put(AdminPermissionManagement.VIEW_SCOPE, viewPermission(client).getId()); + scopes.put(AdminPermissionManagement.MANAGE_SCOPE, managePermission(client).getId()); + return scopes; + } + + @Override public boolean canManage(ClientModel client) { if (!root.isAdminSameRealm()) { @@ -397,6 +414,34 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa return authz.getStoreFactory().getPolicyStore().findByName(getMapRolesPermissionName(client), server.getId()); } + @Override + public Policy mapRolesClientScopePermission(ClientModel client) { + ResourceServer server = resourceServer(client); + if (server == null) return null; + return authz.getStoreFactory().getPolicyStore().findByName(getMapRolesClientScopePermissionName(client), server.getId()); + } + + @Override + public Policy mapRolesCompositePermission(ClientModel client) { + ResourceServer server = resourceServer(client); + if (server == null) return null; + return authz.getStoreFactory().getPolicyStore().findByName(getMapRolesCompositePermissionName(client), server.getId()); + } + + @Override + public Policy managePermission(ClientModel client) { + ResourceServer server = resourceServer(client); + if (server == null) return null; + return authz.getStoreFactory().getPolicyStore().findByName(getManagePermissionName(client), server.getId()); + } + + @Override + public Policy viewPermission(ClientModel client) { + ResourceServer server = resourceServer(client); + if (server == null) return null; + return authz.getStoreFactory().getPolicyStore().findByName(getViewPermissionName(client), server.getId()); + } + @Override public ResourceServer resourceServer(ClientModel client) { return authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); @@ -410,7 +455,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId()); if (resource == null) return false; - Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRoleCompositePermissionName(client), server.getId()); + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRolesCompositePermissionName(client), server.getId()); if (policy == null) { return false; } @@ -421,7 +466,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa return false; } - Scope scope = mapRoleCompositeScope(server); + Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_COMPOSITE_SCOPE, server.getId()); return root.evaluatePermission(resource, scope, server); } @Override @@ -432,7 +477,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId()); if (resource == null) return false; - Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRoleClientScopePermissionName(client), server.getId()); + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRolesClientScopePermissionName(client), server.getId()); if (policy == null) { return false; } @@ -443,7 +488,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa return false; } - Scope scope = mapRoleClientScope(server); + Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_CLIENT_SCOPE, server.getId()); return root.evaluatePermission(resource, scope, server); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionManagement.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionManagement.java index 5f4097be27..a3277ba316 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionManagement.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionManagement.java @@ -17,6 +17,7 @@ package org.keycloak.services.resources.admin.permissions; import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.Resource; import org.keycloak.models.GroupModel; import org.keycloak.models.RoleModel; @@ -34,4 +35,8 @@ public interface GroupPermissionManagement { Policy manageMembersPermission(GroupModel group); Policy viewPermissionGroup(GroupModel group); Policy managePermissionGroup(GroupModel group); + + Resource resource(GroupModel group); + + Map getPermissions(GroupModel group); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissions.java index e1c035622e..acf80c13d1 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissions.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissions.java @@ -23,13 +23,16 @@ 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.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.services.ForbiddenException; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; /** @@ -54,16 +57,16 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag } private static String getGroupResourceName(GroupModel group) { - return "group.resource." + getGroupSuffix(group); + return "group.resource." + group.getId(); } public static String getManagePermissionGroup(GroupModel group) { - return "manage.permission.group." + getGroupSuffix(group); + return "manage.permission.group." + group.getId(); } public static String getManageMembersPermissionGroup(GroupModel group) { - return "manage.members.permission.group." + getGroupSuffix(group); + return "manage.members.permission.group." + group.getId(); } public static String getGroupSuffix(GroupModel group) { @@ -71,11 +74,11 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag } public static String getViewPermissionGroup(GroupModel group) { - return "view.permission.group." + getGroupSuffix(group); + return "view.permission.group." + group.getId(); } public static String getViewMembersPermissionGroup(GroupModel group) { - return "view.members.permission.group." + getGroupSuffix(group); + return "view.members.permission.group." + group.getId(); } private void initialize(GroupModel group) { @@ -102,7 +105,7 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag Policy manageUsersPolicy = root.roles().manageUsersPolicy(server); Helper.addScopePermission(authz, server, managePermissionName, groupResource, manageScope, manageUsersPolicy); } - String viewPermissionName = getManagePermissionGroup(group); + String viewPermissionName = getViewPermissionGroup(group); Policy viewPermission = authz.getStoreFactory().getPolicyStore().findByName(viewPermissionName, server.getId()); if (viewPermission == null) { Policy viewUsersPolicy = root.roles().viewUsersPolicy(server); @@ -213,6 +216,27 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag return authz.getStoreFactory().getPolicyStore().findByName(managePermissionName, server.getId()); } + @Override + public Resource resource(GroupModel group) { + ResourceServer server = root.realmResourceServer(); + if (server == null) return null; + Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId()); + if (resource == null) return null; + return resource; + } + + @Override + public Map getPermissions(GroupModel group) { + Map scopes = new HashMap<>(); + scopes.put(AdminPermissionManagement.VIEW_SCOPE, viewPermissionGroup(group).getId()); + scopes.put(AdminPermissionManagement.MANAGE_SCOPE, managePermissionGroup(group).getId()); + scopes.put(MANAGE_MEMBERS_SCOPE, manageMembersPermission(group).getId()); + scopes.put(VIEW_MEMBERS_SCOPE, viewMembersPermission(group).getId()); + return scopes; + } + + + @Override public boolean canManage(GroupModel group) { 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 5f666c66b0..83838de94b 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 @@ -141,19 +141,19 @@ class RolePermissions implements RolePermissionEvaluator, RolePermissionManageme @Override public boolean canMapRole(RoleModel role) { if (!root.isAdminSameRealm()) { - return root.users().canManage(); + return root.users().canManageDefault(); } if (role.getContainer() instanceof ClientModel) { if (root.clients().canMapRoles((ClientModel)role.getContainer())) return true; } if (!isPermissionsEnabled(role)){ - return root.users().canManage(); + return root.users().canManageDefault(); } ResourceServer resourceServer = getResourceServer(role); Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRolePermissionName(role), resourceServer.getId()); if (policy.getAssociatedPolicies().isEmpty()) { - return root.users().canManage(); // if no policies applied, just do default + return root.users().canManageDefault(); // if no policies applied, just do default } Resource roleResource = resource(role); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java index 1601189c40..d6d2ad8e15 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java @@ -175,7 +175,11 @@ public abstract class AbstractKeycloakTest { // Cleanup objects for (TestCleanup cleanup : testContext.getCleanups().values()) { - if (cleanup != null) cleanup.executeCleanup(); + try { + if (cleanup != null) cleanup.executeCleanup(); + } catch (Exception e) { + throw new RuntimeException(e); + } } testContext.getCleanups().clear(); } 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 5d413d0abb..72f3df93af 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 @@ -282,7 +282,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { } - //@Test + @Test public void testUI() throws Exception { testingClient.server().run(FineGrainAdminUnitTest::setupPolices); testingClient.server().run(FineGrainAdminUnitTest::setupUsers); @@ -308,7 +308,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { realmRoleSet.add(realmRole); RoleRepresentation realmRole2 = adminClient.realm(TEST).roles().get("realm-role2").toRepresentation(); List realmRole2Set = new LinkedList<>(); - realmRole2Set.add(realmRole); + realmRole2Set.add(realmRole2); ClientRepresentation client = adminClient.realm(TEST).clients().findByClientId("role-namespace").get(0); RoleRepresentation clientRole = adminClient.realm(TEST).clients().get(client.getId()).roles().get("client-role").toRepresentation(); List clientRoleSet = new LinkedList<>(); @@ -395,18 +395,18 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { TEST, "groupManager", "password", Constants.ADMIN_CLI_CLIENT_ID, null); List roles = null; realmClient.realm(TEST).users().get(groupMember.getId()).roles().clientLevel(client.getId()).add(clientRoleSet); - roles = realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).listAll(); + roles = realmClient.realm(TEST).users().get(groupMember.getId()).roles().clientLevel(client.getId()).listAll(); Assert.assertTrue(roles.stream().anyMatch((r) -> { return r.getName().equals("client-role"); })); - realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).remove(clientRoleSet); + realmClient.realm(TEST).users().get(groupMember.getId()).roles().clientLevel(client.getId()).remove(clientRoleSet); - roles = realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().listAvailable(); + roles = realmClient.realm(TEST).users().get(groupMember.getId()).roles().realmLevel().listAvailable(); Assert.assertEquals(roles.size(), 1); - realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().add(realmRoleSet); - realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().remove(realmRoleSet); + realmClient.realm(TEST).users().get(groupMember.getId()).roles().realmLevel().add(realmRoleSet); + realmClient.realm(TEST).users().get(groupMember.getId()).roles().realmLevel().remove(realmRoleSet); try { - realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().add(realmRole2Set); + realmClient.realm(TEST).users().get(groupMember.getId()).roles().realmLevel().add(realmRole2Set); Assert.fail("should fail with forbidden exception"); } catch (ClientErrorException e) { Assert.assertEquals(e.getResponse().getStatus(), 403); @@ -428,6 +428,8 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { Keycloak realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(), TEST, "clientMapper", "password", Constants.ADMIN_CLI_CLIENT_ID, null); List roles = null; + roles = realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).listAll(); + Assert.assertTrue(roles.isEmpty()); realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).add(clientRoleSet); roles = realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).listAll(); Assert.assertTrue(roles.stream().anyMatch((r) -> { @@ -455,9 +457,6 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { testingClient.server().run(FineGrainAdminUnitTest::setupUsers); UserRepresentation user1 = adminClient.realm(TEST).users().search("user1").get(0); - UserRepresentation user2 = adminClient.realm(TEST).users().search("user2").get(0); - UserRepresentation user3 = adminClient.realm(TEST).users().search("user3").get(0); - UserRepresentation user4 = adminClient.realm(TEST).users().search("user4").get(0); RoleRepresentation realmRole = adminClient.realm(TEST).roles().get("realm-role").toRepresentation(); List realmRoleSet = new LinkedList<>(); realmRoleSet.add(realmRole); diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index 1dd53fa6af..11fe2c5c06 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -1306,7 +1306,8 @@ credential-reset-actions-timeout.tooltip=Maximum time before the action permit e ldap-mappers=LDAP Mappers create-ldap-mapper=Create LDAP mapper map-role-mgmt-scope-description=Policies that decide if an admin can map this role to a user or group -manage-mgmt-scope-description=Policies that decide if an admin can manage this resource or resources +manage-authz-users-scope-description=Policies that decide if an admin can manage all users in the realm +view-authz-users-scope-description=Policies that decide if an admin can view all users in realm permissions-enabled-role=Permissions Enabled permissions-enabled-role.tooltip=Whether or not to enable fine grain permissions for managing this role. Disabling will delete all current permissions that have been set up. manage-permissions-role.tooltip=Fine grain permissions for managing roles. For example, you can define different policies for who is allowed to map a role. @@ -1314,6 +1315,20 @@ lookup=Lookup manage-permissions-users.tooltip=Fine grain permssions for managing all users in realm. You can define different policies for who is allowed to manage users in the realm. permissions-enabled-users=Permissions Enabled permissions-enabled-users.tooltip=Whether or not to enable fine grain permissions for managing users. Disabling will delete all current permissions that have been set up. +manage-permissions-client.tooltip=Fine grain permssions for admins that want to manage this client or apply roles defined by this client. +manage-permissions-group.tooltip=Fine grain permssions for admins that want to manage this group or the members of this group. +manage-authz-group-scope-description=Policies that decide if an admin can manage this group +view-authz-group-scope-description=Policies that decide if an admin can view this group +view.members-authz-group-scope-description=Policies that decide if an admin can manage the members of this group +manage.members-authz-group-scope-description=Policies that decide if an admin can view the members of this group +manage-authz-client-scope-description=Policies that decide if an admin can manage this client +view-authz-client-scope-description=Policies that decide if an admin can view this client +map-roles-authz-client-scope-description=Policies that decide if an admin can map roles defined by this client +map-roles-client-scope-authz-client-scope-description=Policies that decide if an admin can apply roles defined by this client to the client scope of another client +map-roles-composite-authz-client-scope-description=Policies that decide if an admin can apply roles defined by this client as a composite to another role +map-role-authz-role-scope-description=Policies that decide if an admin can map role this role to a user or group +map-role-client-scope-authz-role-scope-description=Policies that decide if an admin can apply this role to the client scope of a client +map-role-composite-authz-role-scope-description=Policies that decide if an admin can apply this role as a composite to another role diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-app.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-app.js index 455891dc5e..047fa492d4 100644 --- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-app.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-app.js @@ -424,6 +424,30 @@ module.config(['$routeProvider', function ($routeProvider) { }, controller : 'UsersPermissionsCtrl' }) + .when('/realms/:realm/clients/:client/permissions', { + templateUrl : resourceUrl + '/partials/authz/mgmt/client-permissions.html', + resolve : { + realm : function(RealmLoader) { + return RealmLoader(); + }, + client : function(ClientLoader) { + return ClientLoader(); + } + }, + controller : 'ClientPermissionsCtrl' + }) + .when('/realms/:realm/groups/:group/permissions', { + templateUrl : resourceUrl + '/partials/authz/mgmt/group-permissions.html', + resolve : { + realm : function(RealmLoader) { + return RealmLoader(); + }, + group : function(GroupLoader) { + return GroupLoader(); + } + }, + controller : 'GroupPermissionsCtrl' + }) ; }]); diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js index 5eb0ba2d88..37b4984dfe 100644 --- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js @@ -2419,3 +2419,35 @@ module.controller('UsersPermissionsCtrl', function($scope, $http, $route, $locat }); +module.controller('ClientPermissionsCtrl', function($scope, $http, $route, $location, realm, client, ClientManagementPermissions, Notifications) { + $scope.client = client; + $scope.realm = realm; + ClientManagementPermissions.get({realm: realm.realm, client: client.id}, function(data) { + $scope.permissions = data; + }); + $scope.setEnabled = function() { + var param = { enabled: $scope.permissions.enabled}; + $scope.permissions = ClientManagementPermissions.update({realm: realm.realm, client: client.id}, param); + }; + + +}); + +module.controller('GroupPermissionsCtrl', function($scope, $http, $route, $location, realm, group, GroupManagementPermissions, Client, Notifications) { + $scope.group = group; + $scope.realm = realm; + Client.query({realm: realm.realm, clientId: getManageClientId(realm)}, function(data) { + $scope.realmManagementClientId = data[0].id; + }); + GroupManagementPermissions.get({realm: realm.realm, group: group.id}, function(data) { + $scope.permissions = data; + }); + $scope.setEnabled = function() { + var param = { enabled: $scope.permissions.enabled}; + $scope.permissions = GroupManagementPermissions.update({realm: realm.realm, group: group.id}, param); + }; + + +}); + + diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js index 92219bb570..f56ca8f695 100644 --- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js @@ -144,4 +144,50 @@ module.service('AuthzDialog', function($modal) { } return dialog; -}); \ No newline at end of file +}); + +module.factory('RoleManagementPermissions', function($resource) { + return $resource(authUrl + '/admin/realms/:realm/roles-by-id/:role/management/permissions', { + realm : '@realm', + role : '@role' + }, { + update: { + method: 'PUT' + } + }); +}); + +module.factory('UsersManagementPermissions', function($resource) { + return $resource(authUrl + '/admin/realms/:realm/users-management-permissions', { + realm : '@realm' + }, { + update: { + method: 'PUT' + } + }); +}); + +module.factory('ClientManagementPermissions', function($resource) { + return $resource(authUrl + '/admin/realms/:realm/clients/:client/management/permissions', { + realm : '@realm', + client : '@client' + }, { + update: { + method: 'PUT' + } + }); +}); + +module.factory('GroupManagementPermissions', function($resource) { + return $resource(authUrl + '/admin/realms/:realm/groups/:group/management/permissions', { + realm : '@realm', + group : '@group' + }, { + update: { + method: 'PUT' + } + }); +}); + + + diff --git a/themes/src/main/resources/theme/base/admin/resources/js/services.js b/themes/src/main/resources/theme/base/admin/resources/js/services.js index 50b50aba91..e850b3bc4b 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/services.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/services.js @@ -842,28 +842,6 @@ module.factory('Role', function($resource) { }); }); -module.factory('RoleManagementPermissions', function($resource) { - return $resource(authUrl + '/admin/realms/:realm/roles-by-id/:role/management/permissions', { - realm : '@realm', - role : '@role' - }, { - update: { - method: 'PUT' - } - }); -}); - -module.factory('UsersManagementPermissions', function($resource) { - return $resource(authUrl + '/admin/realms/:realm/users-management-permissions', { - realm : '@realm' - }, { - update: { - method: 'PUT' - } - }); -}); - - module.factory('RoleById', function($resource) { return $resource(authUrl + '/admin/realms/:realm/roles-by-id/:role', { realm : '@realm', diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/client-permissions.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/client-permissions.html new file mode 100644 index 0000000000..50241b2139 --- /dev/null +++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/client-permissions.html @@ -0,0 +1,39 @@ +
+ + + + +
+
+
+ +
+ +
+ {{:: 'permissions-enabled-role.tooltip' | translate}} +
+
+
+ + + + + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/client-role-permissions.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/client-role-permissions.html index d2d6c140e1..988c1fa384 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/client-role-permissions.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/client-role-permissions.html @@ -2,7 +2,6 @@ @@ -30,7 +29,7 @@ {{scopeName}} - + {{:: 'edit' | translate}} diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/group-permissions.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/group-permissions.html new file mode 100644 index 0000000000..61770c91cf --- /dev/null +++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/group-permissions.html @@ -0,0 +1,39 @@ +
+ + + + +
+
+
+ +
+ +
+ {{:: 'permissions-enabled-role.tooltip' | translate}} +
+
+
+ + + + + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/realm-role-permissions.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/realm-role-permissions.html index 82c841343b..9c03333397 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/realm-role-permissions.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/realm-role-permissions.html @@ -28,7 +28,7 @@ {{scopeName}} - + {{:: 'edit' | translate}} diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/users-permissions.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/users-permissions.html index 92a90678dc..4a5661f28b 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/users-permissions.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/users-permissions.html @@ -24,7 +24,7 @@ {{scopeName}} - + {{:: 'edit' | translate}} diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html index 2a3be322bb..a18c31fbd1 100755 --- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html +++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html @@ -43,5 +43,9 @@ {{:: 'service-account-roles' | translate}} {{:: 'service-account-roles.tooltip' | translate}} +
  • + {{:: 'authz-permissions' | translate}} + {{:: 'manage-permissions-client.tooltip' | translate}} +
  • \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-group.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-group.html index c2423363ba..3aa12bc2f0 100755 --- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-group.html +++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-group.html @@ -9,5 +9,9 @@
  • {{:: 'attributes' | translate}}
  • {{:: 'role-mappings' | translate}}
  • {{:: 'members' | translate}}
  • +
  • + {{:: 'authz-permissions' | translate}} + {{:: 'manage-permissions-group.tooltip' | translate}} +
  • \ No newline at end of file