diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java index cc9e54b7ea..a7ca83e1a6 100755 --- a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java @@ -65,6 +65,7 @@ public class ClientRepresentation { private Boolean useTemplateScope; private Boolean useTemplateMappers; private ResourceServerRepresentation authorizationSettings; + private Map access; public String getId() { @@ -366,4 +367,12 @@ public class ClientRepresentation { public void setAuthorizationSettings(ResourceServerRepresentation authorizationSettings) { this.authorizationSettings = authorizationSettings; } + + public Map getAccess() { + return access; + } + + public void setAccess(Map access) { + this.access = access; + } } diff --git a/core/src/main/java/org/keycloak/representations/idm/GroupRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/GroupRepresentation.java index 180db64677..9c96970970 100755 --- a/core/src/main/java/org/keycloak/representations/idm/GroupRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/GroupRepresentation.java @@ -34,6 +34,7 @@ public class GroupRepresentation { protected List realmRoles; protected Map> clientRoles; protected List subGroups; + private Map access; public String getId() { return id; @@ -97,4 +98,12 @@ public class GroupRepresentation { public void setSubGroups(List subGroups) { this.subGroups = subGroups; } + + public Map getAccess() { + return access; + } + + public void setAccess(Map access) { + this.access = access; + } } diff --git a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java index 2a4ec62836..4dcea953df 100755 --- a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java @@ -62,6 +62,7 @@ public class UserRepresentation { protected List socialLinks; protected List groups; + private Map access; public String getSelf() { return self; @@ -264,4 +265,12 @@ public class UserRepresentation { public void setDisableableCredentialTypes(Set disableableCredentialTypes) { this.disableableCredentialTypes = disableableCredentialTypes; } + + public Map getAccess() { + return access; + } + + public void setAccess(Map access) { + this.access = access; + } } diff --git a/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java b/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java index 1a36d7a853..4b620bc561 100755 --- a/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java +++ b/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java @@ -34,6 +34,7 @@ import org.keycloak.migration.migrators.MigrateTo2_3_0; import org.keycloak.migration.migrators.MigrateTo2_5_0; import org.keycloak.migration.migrators.MigrateTo3_0_0; import org.keycloak.migration.migrators.MigrateTo3_1_0; +import org.keycloak.migration.migrators.MigrateTo3_2_0; import org.keycloak.migration.migrators.Migration; import org.keycloak.models.KeycloakSession; @@ -60,7 +61,8 @@ public class MigrationModelManager { new MigrateTo2_3_0(), new MigrateTo2_5_0(), new MigrateTo3_0_0(), - new MigrateTo3_1_0() + new MigrateTo3_1_0(), + new MigrateTo3_2_0() }; public static void migrate(KeycloakSession session) { diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_2_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_2_0.java new file mode 100644 index 0000000000..0cb3eb6244 --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_2_0.java @@ -0,0 +1,70 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.migration.migrators; + +import org.keycloak.migration.ModelVersion; +import org.keycloak.models.AdminRoles; +import org.keycloak.models.ClientModel; +import org.keycloak.models.Constants; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class MigrateTo3_2_0 implements Migration { + public static final ModelVersion VERSION = new ModelVersion("3.1.0"); + + @Override + public void migrate(KeycloakSession session) { + for (RealmModel realm : session.realms().getRealms()) { + ClientModel realmAccess = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID); + if (realmAccess != null) { + addRoles(realmAccess); + } + ClientModel masterAdminClient = realm.getMasterAdminClient(); + if (masterAdminClient != null) { + addRoles(masterAdminClient); + + } + + } + } + + public void addRoles(ClientModel realmAccess) { + RoleModel queryClients = realmAccess.addRole(AdminRoles.QUERY_CLIENTS); + RoleModel queryUsers = realmAccess.addRole(AdminRoles.QUERY_USERS); + RoleModel queryGroups = realmAccess.addRole(AdminRoles.QUERY_GROUPS); + + RoleModel viewClients = realmAccess.getRole(AdminRoles.VIEW_CLIENTS); + if (viewClients != null) { + viewClients.addCompositeRole(queryClients); + } + RoleModel viewUsers = realmAccess.getRole(AdminRoles.VIEW_USERS); + if (viewUsers != null) { + viewUsers.addCompositeRole(queryUsers); + viewUsers.addCompositeRole(queryGroups); + } + } + + @Override + public ModelVersion getVersion() { + return VERSION; + } +} diff --git a/server-spi-private/src/main/java/org/keycloak/models/AdminRoles.java b/server-spi-private/src/main/java/org/keycloak/models/AdminRoles.java index 24455b8bfd..cf605d16c5 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/AdminRoles.java +++ b/server-spi-private/src/main/java/org/keycloak/models/AdminRoles.java @@ -17,6 +17,8 @@ package org.keycloak.models; +import java.util.Set; + /** * @author Stian Thorgersen */ @@ -46,6 +48,11 @@ public class AdminRoles { public static String MANAGE_EVENTS = "manage-events"; public static String MANAGE_AUTHORIZATION = "manage-authorization"; - public static String[] ALL_REALM_ROLES = {CREATE_CLIENT, VIEW_REALM, VIEW_USERS, VIEW_CLIENTS, VIEW_EVENTS, VIEW_IDENTITY_PROVIDERS, VIEW_AUTHORIZATION, MANAGE_REALM, MANAGE_USERS, MANAGE_CLIENTS, MANAGE_EVENTS, MANAGE_IDENTITY_PROVIDERS, MANAGE_AUTHORIZATION}; + public static String QUERY_USERS = "query-users"; + public static String QUERY_CLIENTS = "query-clients"; + public static String QUERY_REALMS = "query-realms"; + public static String QUERY_GROUPS = "query-groups"; + + public static String[] ALL_REALM_ROLES = {CREATE_CLIENT, VIEW_REALM, VIEW_USERS, VIEW_CLIENTS, VIEW_EVENTS, VIEW_IDENTITY_PROVIDERS, VIEW_AUTHORIZATION, MANAGE_REALM, MANAGE_USERS, MANAGE_CLIENTS, MANAGE_EVENTS, MANAGE_IDENTITY_PROVIDERS, MANAGE_AUTHORIZATION, QUERY_USERS, QUERY_CLIENTS, QUERY_REALMS, QUERY_GROUPS}; } diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java index 6637bd8393..5ae590444e 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java +++ b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java @@ -168,7 +168,6 @@ public class ModelToRepresentation { return rep; } - public static UserRepresentation toRepresentation(KeycloakSession session, RealmModel realm, UserModel user) { UserRepresentation rep = new UserRepresentation(); rep.setId(user.getId()); diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java index e94ff3c00f..21df21f91b 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -198,6 +198,18 @@ public class RealmManager { } } + public void addQueryCompositeRoles(ClientModel realmAccess) { + RoleModel queryClients = realmAccess.getRole(AdminRoles.QUERY_CLIENTS); + RoleModel queryUsers = realmAccess.getRole(AdminRoles.QUERY_USERS); + RoleModel queryGroups = realmAccess.getRole(AdminRoles.QUERY_GROUPS); + + RoleModel viewClients = realmAccess.getRole(AdminRoles.VIEW_CLIENTS); + viewClients.addCompositeRole(queryClients); + RoleModel viewUsers = realmAccess.getRole(AdminRoles.VIEW_USERS); + viewUsers.addCompositeRole(queryUsers); + viewUsers.addCompositeRole(queryGroups); + } + public String getRealmAdminClientId(RealmModel realm) { return Constants.REALM_MANAGEMENT_CLIENT_ID; @@ -327,6 +339,7 @@ public class RealmManager { role.setScopeParamRequired(false); adminRole.addCompositeRole(role); } + addQueryCompositeRoles(realmAdminApp); } private void checkMasterAdminManagementRoles(RealmModel realm) { @@ -340,6 +353,7 @@ public class RealmManager { addAndSetAdminRole(r, masterAdminClient, adminRole); } } + addQueryCompositeRoles(masterAdminClient); } @@ -362,6 +376,7 @@ public class RealmManager { for (String r : AdminRoles.ALL_REALM_ROLES) { addAndSetAdminRole(r, realmAdminClient, adminRole); } + addQueryCompositeRoles(realmAdminClient); } private void addAndSetAdminRole(String roleName, ClientModel parentClient, RoleModel parentRole) { @@ -385,6 +400,7 @@ public class RealmManager { addAndSetAdminRole(r, realmAdminClient, adminRole); } } + addQueryCompositeRoles(realmAdminClient); } @@ -502,7 +518,8 @@ public class RealmManager { // I need to postpone impersonation because it needs "realm-management" client and its roles set if (postponeImpersonationSetup) { setupImpersonationService(realm); - } + String realmAdminClientId = getRealmAdminClientId(realm); + } if (postponeAdminCliSetup) { setupAdminCli(realm); 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 36362cb67b..32a86be828 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 @@ -195,6 +195,7 @@ public class ClientResource { if (Profile.isFeatureEnabled(Profile.Feature.AUTHORIZATION)) { representation.setAuthorizationServicesEnabled(authorization().isEnabled()); } + representation.setAccess(auth.clients().getAccess(client)); return representation; } 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 41b91befdf..88b5fdddf1 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 @@ -114,6 +114,7 @@ public class ClientsResource { } rep.add(representation); + representation.setAccess(auth.clients().getAccess(clientModel)); } else if (!viewableOnly) { ClientRepresentation client = new ClientRepresentation(); client.setId(clientModel.getId()); @@ -126,7 +127,9 @@ public class ClientsResource { ClientModel clientModel = realm.getClientByClientId(clientId); if (clientModel != null) { if (auth.clients().canView(clientModel)) { - rep.add(ModelToRepresentation.toRepresentation(clientModel)); + ClientRepresentation representation = ModelToRepresentation.toRepresentation(clientModel); + representation.setAccess(auth.clients().getAccess(clientModel)); + rep.add(representation); } else if (!viewableOnly && auth.clients().canList()){ ClientRepresentation client = new ClientRepresentation(); client.setId(clientModel.getId()); 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 a5b7a6c77b..3de46b0e29 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 @@ -77,7 +77,7 @@ public class GroupResource { @Context private UriInfo uriInfo; - /** + /** * * * @return @@ -88,7 +88,11 @@ public class GroupResource { public GroupRepresentation getGroup() { this.auth.groups().requireView(group); - return ModelToRepresentation.toGroupHierarchy(group, true); + GroupRepresentation rep = ModelToRepresentation.toGroupHierarchy(group, true); + + rep.setAccess(auth.groups().getAccess(group)); + + return rep; } /** diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java index 44e4892143..ca9d53640f 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java @@ -249,6 +249,7 @@ public class UserResource { if (session.getProvider(BruteForceProtector.class).isTemporarilyDisabled(session, realm, user)) { rep.setEnabled(false); } + rep.setAccess(auth.users().getAccess(user)); return rep; } @@ -761,6 +762,7 @@ public class UserResource { if (group == null) { throw new NotFoundException("Group not found"); } + auth.groups().requireManageMembers(group); try { if (user.isMemberOf(group)){ @@ -783,6 +785,7 @@ public class UserResource { if (group == null) { throw new NotFoundException("Group not found"); } + //auth.groups().requireManageMembers(group); if (!user.isMemberOf(group)){ user.joinGroup(group); adminEvent.operation(OperationType.CREATE).resource(ResourceType.GROUP_MEMBERSHIP).representation(ModelToRepresentation.toRepresentation(group, true)).resourcePath(uriInfo).success(); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java index fc4bfee770..6ed677f92b 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java @@ -259,7 +259,9 @@ public class UsersResource { boolean canViewGlobal = auth.users().canView(); for (UserModel user : userModels) { if (!canViewGlobal && !auth.users().canView(user)) continue; - results.add(ModelToRepresentation.toRepresentation(session, realm, user)); + UserRepresentation userRep = ModelToRepresentation.toRepresentation(session, realm, user); + userRep.setAccess(auth.users().getAccess(user)); + results.add(userRep); } return results; } 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 51e26c05da..25da468837 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 @@ -19,6 +19,8 @@ package org.keycloak.services.resources.admin.permissions; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientTemplateModel; +import java.util.Map; + /** * @author Bill Burke * @version $Revision: 1 $ @@ -73,4 +75,6 @@ public interface ClientPermissionEvaluator { boolean canMapCompositeRoles(ClientModel client); boolean canMapClientScopeRoles(ClientModel client); + + Map getAccess(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 06fa5d822e..39f7b4d6c6 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 @@ -492,4 +492,13 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa return root.evaluatePermission(resource, scope, server); } + @Override + public Map getAccess(ClientModel client) { + Map map = new HashMap<>(); + map.put("view", canView(client)); + map.put("manage", canManage(client)); + return map; + } + + } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionEvaluator.java index 91ca5c5e9d..450b0746d8 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionEvaluator.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionEvaluator.java @@ -18,6 +18,8 @@ package org.keycloak.services.resources.admin.permissions; import org.keycloak.models.GroupModel; +import java.util.Map; + /** * @author Bill Burke * @version $Revision: 1 $ @@ -50,4 +52,6 @@ public interface GroupPermissionEvaluator { boolean canManageMembers(GroupModel group); void requireManageMembers(GroupModel group); + + Map getAccess(GroupModel group); } 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 a3277ba316..5fcfbbbb16 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 @@ -33,8 +33,8 @@ public interface GroupPermissionManagement { Policy viewMembersPermission(GroupModel group); Policy manageMembersPermission(GroupModel group); - Policy viewPermissionGroup(GroupModel group); - Policy managePermissionGroup(GroupModel group); + Policy viewPermission(GroupModel group); + Policy managePermission(GroupModel group); Resource resource(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 acf80c13d1..222380229d 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,7 +23,6 @@ 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; @@ -125,7 +124,7 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag @Override public boolean canList() { - return root.hasOneAdminRole(AdminRoles.VIEW_USERS, AdminRoles.MANAGE_USERS); + return root.hasOneAdminRole(AdminRoles.VIEW_USERS, AdminRoles.MANAGE_USERS, AdminRoles.QUERY_GROUPS); } @Override @@ -164,11 +163,11 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag private void deletePermissions(GroupModel group) { ResourceServer server = root.realmResourceServer(); if (server == null) return; - Policy managePermission = managePermissionGroup(group); + Policy managePermission = managePermission(group); if (managePermission != null) { authz.getStoreFactory().getPolicyStore().delete(managePermission.getId()); } - Policy viewPermission = viewPermissionGroup(group); + Policy viewPermission = viewPermission(group); if (viewPermission != null) { authz.getStoreFactory().getPolicyStore().delete(viewPermission.getId()); } @@ -201,7 +200,7 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag } @Override - public Policy viewPermissionGroup(GroupModel group) { + public Policy viewPermission(GroupModel group) { ResourceServer server = root.realmResourceServer(); if (server == null) return null; String viewPermissionName = getViewPermissionGroup(group); @@ -209,7 +208,7 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag } @Override - public Policy managePermissionGroup(GroupModel group) { + public Policy managePermission(GroupModel group) { ResourceServer server = root.realmResourceServer(); if (server == null) return null; String managePermissionName = getManagePermissionGroup(group); @@ -228,8 +227,8 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag @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(AdminPermissionManagement.VIEW_SCOPE, viewPermission(group).getId()); + scopes.put(AdminPermissionManagement.MANAGE_SCOPE, managePermission(group).getId()); scopes.put(MANAGE_MEMBERS_SCOPE, manageMembersPermission(group).getId()); scopes.put(VIEW_MEMBERS_SCOPE, viewMembersPermission(group).getId()); return scopes; @@ -250,7 +249,7 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId()); if (resource == null) return canManage(); - Policy policy = managePermissionGroup(group); + Policy policy = managePermission(group); if (policy == null) { return canManage(); } @@ -283,7 +282,7 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId()); if (resource == null) return canView(); - Policy policy = viewPermissionGroup(group); + Policy policy = viewPermission(group); if (policy == null) { return canView(); } @@ -405,6 +404,15 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag } } + @Override + public Map getAccess(GroupModel group) { + Map map = new HashMap<>(); + map.put("view", canView(group)); + map.put("manage", canManage(group)); + return map; + } + + } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionEvaluator.java index e276ae29df..05ce719862 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionEvaluator.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionEvaluator.java @@ -18,6 +18,8 @@ package org.keycloak.services.resources.admin.permissions; import org.keycloak.models.UserModel; +import java.util.Map; + /** * @author Bill Burke * @version $Revision: 1 $ @@ -47,4 +49,6 @@ public interface UserPermissionEvaluator { boolean canImpersonate(UserModel user); void requireImpersonate(UserModel user); + + Map getAccess(UserModel user); } 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 b095592766..f8dd53b952 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 @@ -281,7 +281,7 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme @Override public boolean canQuery() { - return canView(); + return canView() || root.hasOneAdminRole(AdminRoles.QUERY_USERS); } @Override @@ -392,6 +392,14 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme } } + @Override + public Map getAccess(UserModel user) { + Map map = new HashMap<>(); + map.put("view", canView(user)); + map.put("manage", canManage(user)); + return map; + } + 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 72f3df93af..d32ad794e7 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 @@ -56,6 +56,8 @@ import static org.keycloak.testsuite.auth.page.AuthRealm.TEST; //@Ignore public class FineGrainAdminUnitTest extends AbstractKeycloakTest { + public static final String CLIENT_NAME = "application"; + @Override public void addTestRealms(List testRealms) { RealmRepresentation testRealmRep = new RealmRepresentation(); @@ -70,7 +72,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { AdminPermissionManagement permissions = AdminPermissions.management(session, realm); RoleModel realmRole = realm.addRole("realm-role"); RoleModel realmRole2 = realm.addRole("realm-role2"); - ClientModel client1 = realm.addClient("role-namespace"); + ClientModel client1 = realm.addClient(CLIENT_NAME); RoleModel client1Role = client1.addRole("client-role"); GroupModel group = realm.createGroup("top"); @@ -80,7 +82,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { compositeRole.addCompositeRole(mapperRole); compositeRole.addCompositeRole(managerRole); - // realm-role and role-namespace.client-role will have a role policy associated with their map-role permission + // realm-role and application.client-role will have a role policy associated with their map-role permission { permissions.roles().setPermissionsEnabled(client1Role, true); Policy mapRolePermission = permissions.roles().mapRolePermission(client1Role); @@ -122,7 +124,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { public static void setupUsers(KeycloakSession session) { RealmModel realm = session.realms().getRealmByName(TEST); - ClientModel client = realm.getClientByClientId("role-namespace"); + ClientModel client = realm.getClientByClientId(CLIENT_NAME); RoleModel realmRole = realm.getRole("realm-role"); RoleModel realmRole2 = realm.getRole("realm-role2"); RoleModel clientRole = client.getRole("client-role"); @@ -131,6 +133,9 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { RoleModel compositeRole = realm.getRole("composite-role"); ClientModel realmManagementClient = realm.getClientByClientId("realm-management"); RoleModel adminRole = realmManagementClient.getRole(AdminRoles.REALM_ADMIN); + RoleModel queryGroupsRole = realmManagementClient.getRole(AdminRoles.QUERY_GROUPS); + RoleModel queryUsersRole = realmManagementClient.getRole(AdminRoles.QUERY_USERS); + RoleModel queryClientsRole = realmManagementClient.getRole(AdminRoles.QUERY_CLIENTS); UserModel nomapAdmin = session.users().addUser(realm, "nomap-admin"); nomapAdmin.setEnabled(true); @@ -168,6 +173,8 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { groupMember.joinGroup(group); groupMember.setEnabled(true); UserModel groupManager = session.users().addUser(realm, "groupManager"); + groupManager.grantRole(queryGroupsRole); + groupManager.grantRole(queryUsersRole); groupManager.setEnabled(true); groupManager.grantRole(mapperRole); session.userCredentialManager().updateCredential(realm, groupManager, UserCredentialModel.password("password")); @@ -175,6 +182,8 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { UserModel groupManagerNoMapper = session.users().addUser(realm, "noMapperGroupManager"); groupManagerNoMapper.setEnabled(true); session.userCredentialManager().updateCredential(realm, groupManagerNoMapper, UserCredentialModel.password("password")); + groupManagerNoMapper.grantRole(queryGroupsRole); + groupManagerNoMapper.grantRole(queryUsersRole); UserPolicyRepresentation groupManagerRep = new UserPolicyRepresentation(); groupManagerRep.setName("groupManagers"); @@ -184,10 +193,12 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { Policy groupManagerPolicy = permissions.authz().getStoreFactory().getPolicyStore().create(groupManagerRep, server); Policy groupManagerPermission = permissions.groups().manageMembersPermission(group); groupManagerPermission.addAssociatedPolicy(groupManagerPolicy); + permissions.groups().viewPermission(group).addAssociatedPolicy(groupManagerPolicy); UserModel clientMapper = session.users().addUser(realm, "clientMapper"); clientMapper.setEnabled(true); clientMapper.grantRole(managerRole); + clientMapper.grantRole(queryUsersRole); session.userCredentialManager().updateCredential(realm, clientMapper, UserCredentialModel.password("password")); Policy clientMapperPolicy = permissions.clients().mapRolesPermission(client); UserPolicyRepresentation userRep = new UserPolicyRepresentation(); @@ -196,6 +207,19 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { Policy userPolicy = permissions.authz().getStoreFactory().getPolicyStore().create(userRep, permissions.clients().resourceServer(client)); clientMapperPolicy.addAssociatedPolicy(userPolicy); + UserModel clientManager = session.users().addUser(realm, "clientManager"); + clientManager.setEnabled(true); + clientManager.grantRole(queryClientsRole); + session.userCredentialManager().updateCredential(realm, clientManager, UserCredentialModel.password("password")); + + Policy clientManagerPolicy = permissions.clients().managePermission(client); + userRep = new UserPolicyRepresentation(); + userRep.setName("clientManager"); + userRep.addUser("clientManager"); + userPolicy = permissions.authz().getStoreFactory().getPolicyStore().create(userRep, permissions.clients().resourceServer(client)); + clientManagerPolicy.addAssociatedPolicy(userPolicy); + + @@ -206,7 +230,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { RealmModel realm = session.realms().getRealmByName(TEST); RoleModel realmRole = realm.getRole("realm-role"); RoleModel realmRole2 = realm.getRole("realm-role2"); - ClientModel client = realm.getClientByClientId("role-namespace"); + ClientModel client = realm.getClientByClientId(CLIENT_NAME); RoleModel clientRole = client.getRole("client-role"); // test authorized @@ -309,7 +333,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { RoleRepresentation realmRole2 = adminClient.realm(TEST).roles().get("realm-role2").toRepresentation(); List realmRole2Set = new LinkedList<>(); realmRole2Set.add(realmRole2); - ClientRepresentation client = adminClient.realm(TEST).clients().findByClientId("role-namespace").get(0); + ClientRepresentation client = adminClient.realm(TEST).clients().findByClientId(CLIENT_NAME).get(0); RoleRepresentation clientRole = adminClient.realm(TEST).clients().get(client.getId()).roles().get("client-role").toRepresentation(); List clientRoleSet = new LinkedList<>(); clientRoleSet.add(clientRole); @@ -463,7 +487,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest { RoleRepresentation realmRole2 = adminClient.realm(TEST).roles().get("realm-role2").toRepresentation(); List realmRole2Set = new LinkedList<>(); realmRole2Set.add(realmRole); - ClientRepresentation client = adminClient.realm(TEST).clients().findByClientId("role-namespace").get(0); + ClientRepresentation client = adminClient.realm(TEST).clients().findByClientId(CLIENT_NAME).get(0); RoleRepresentation clientRole = adminClient.realm(TEST).clients().get(client.getId()).roles().get("client-role").toRepresentation(); List clientRoleSet = new LinkedList<>(); clientRoleSet.add(clientRole); diff --git a/themes/src/main/resources/theme/base/admin/resources/js/app.js b/themes/src/main/resources/theme/base/admin/resources/js/app.js index 2864559796..c4e870fdac 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/app.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/app.js @@ -1398,9 +1398,6 @@ module.config([ '$routeProvider', function($routeProvider) { realm : function(RealmLoader) { return RealmLoader(); }, - clients : function(ClientListLoader) { - return ClientListLoader(); - }, serverInfo : function(ServerInfoLoader) { return ServerInfoLoader(); } diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js index 945fc5a699..515eb99720 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js @@ -728,9 +728,9 @@ module.controller('ClientImportCtrl', function($scope, $location, $upload, realm }); -module.controller('ClientListCtrl', function($scope, realm, clients, Client, serverInfo, $route, Dialog, Notifications, filterFilter) { +module.controller('ClientListCtrl', function($scope, realm, Client, serverInfo, $route, Dialog, Notifications, filterFilter) { $scope.realm = realm; - $scope.clients = clients; + $scope.clients = Client.query({realm: realm.realm, viewableOnly: true}); $scope.currentPage = 1; $scope.currentPageInput = 1; $scope.pageSize = 20; diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js index e3d3070cbb..ab4e72a878 100644 --- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js @@ -1,81 +1,95 @@ +function getAccess(Auth, Current, role) { + if (!Current.realm)return false; + var realmAccess = Auth.user && Auth.user['realm_access']; + if (realmAccess) { + realmAccess = realmAccess[Current.realm.realm]; + if (realmAccess) { + return realmAccess.indexOf(role) >= 0; + } + } + return false; +} + +function getAccessObject(Auth, Current) { + return { + get createRealm() { + return Auth.user && Auth.user.createRealm; + }, + + get queryUsers() { + return getAccess(Auth, Current, 'query-users') || this.viewUsers; + }, + + get queryGroups() { + return getAccess(Auth, Current, 'query-groups') || this.viewUsers; + }, + + get queryClients() { + return getAccess(Auth, Current, 'query-clients') || this.viewClients; + }, + + get viewRealm() { + return getAccess(Auth, Current, 'view-realm') || getAccess(Auth, Current, 'manage-realm') || this.manageRealm; + }, + + get viewClients() { + return getAccess(Auth, Current, 'view-clients') || getAccess(Auth, Current, 'manage-clients') || this.manageClients; + }, + + get viewUsers() { + return getAccess(Auth, Current, 'view-users') || getAccess(Auth, Current, 'manage-users') || this.manageClients; + }, + + get viewEvents() { + return getAccess(Auth, Current, 'view-events') || getAccess(Auth, Current, 'manage-events') || this.manageClients; + }, + + get viewIdentityProviders() { + return getAccess(Auth, Current, 'view-identity-providers') || getAccess(Auth, Current, 'manage-identity-providers') || this.manageIdentityProviders; + }, + + get viewAuthorization() { + return getAccess(Auth, Current, 'view-authorization') || this.manageAuthorization; + }, + + get manageRealm() { + return getAccess(Auth, Current, 'manage-realm'); + }, + + get manageClients() { + return getAccess(Auth, Current, 'manage-clients'); + }, + + get manageUsers() { + return getAccess(Auth, Current, 'manage-users'); + }, + + get manageEvents() { + return getAccess(Auth, Current, 'manage-events'); + }, + + get manageIdentityProviders() { + return getAccess(Auth, Current, 'manage-identity-providers'); + }, + + get manageAuthorization() { + return getAccess(Auth, Current, 'manage-authorization'); + }, + + get impersonation() { + return getAccess(Auth, Current, 'impersonation'); + } + }; +} + + module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location, Notifications, ServerInfo) { $scope.authUrl = authUrl; $scope.resourceUrl = resourceUrl; $scope.auth = Auth; $scope.serverInfo = ServerInfo.get(); - function getAccess(role) { - if (!Current.realm) { - return false; - } - - var realmAccess = Auth.user && Auth.user['realm_access']; - if (realmAccess) { - realmAccess = realmAccess[Current.realm.realm]; - if (realmAccess) { - return realmAccess.indexOf(role) >= 0; - } - } - return false; - } - - $scope.access = { - get createRealm() { - return Auth.user && Auth.user.createRealm; - }, - - get viewRealm() { - return getAccess('view-realm') || getAccess('manage-realm') || this.manageRealm; - }, - - get viewClients() { - return getAccess('view-clients') || getAccess('manage-clients') || this.manageClients; - }, - - get viewUsers() { - return getAccess('view-users') || getAccess('manage-users') || this.manageClients; - }, - - get viewEvents() { - return getAccess('view-events') || getAccess('manage-events') || this.manageClients; - }, - - get viewIdentityProviders() { - return getAccess('view-identity-providers') || getAccess('manage-identity-providers') || this.manageIdentityProviders; - }, - - get viewAuthorization() { - return getAccess('view-authorization') || this.manageAuthorization; - }, - - get manageRealm() { - return getAccess('manage-realm'); - }, - - get manageClients() { - return getAccess('manage-clients'); - }, - - get manageUsers() { - return getAccess('manage-users'); - }, - - get manageEvents() { - return getAccess('manage-events'); - }, - - get manageIdentityProviders() { - return getAccess('manage-identity-providers'); - }, - - get manageAuthorization() { - return getAccess('manage-authorization'); - }, - - get impersonation() { - return getAccess('impersonation'); - } - }; + $scope.access = getAccessObject(Auth, Current); $scope.$watch(function() { return $location.path(); @@ -85,20 +99,36 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location }); }); -module.controller('HomeCtrl', function(Realm, Auth, $location) { +module.controller('HomeCtrl', function(Realm, Auth, Current, $location) { + Realm.query(null, function(realms) { var realm; if (realms.length == 1) { - realm = realms[0].realm; + realm = realms[0]; } else if (realms.length == 2) { if (realms[0].realm == Auth.user.realm) { - realm = realms[1].realm; + realm = realms[1]; } else if (realms[1].realm == Auth.user.realm) { - realm = realms[0].realm; + realm = realms[0]; } } if (realm) { - $location.url('/realms/' + realm); + Current.realms = realms; + Current.realm = realm; + var access = getAccessObject(Auth, Current); + if (access.viewRealm || access.manageRealm) { + $location.url('/realms/' + realm.realm ); + } else if (access.queryClients) { + $location.url('/realms/' + realm.realm + "/clients"); + } else if (access.viewIdentityProviders) { + $location.url('/realms/' + realm.realm + "/identity-provider-settings"); + } else if (access.queryUsers) { + $location.url('/realms/' + realm.realm + "/users"); + } else if (access.queryGroups) { + $location.url('/realms/' + realm.realm + "/groups"); + } else if (access.viewEvents) { + $location.url('/realms/' + realm.realm + "/events"); + } } else { $location.url('/realms'); } 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 index 50241b2139..bb6d7b6d58 100644 --- 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 @@ -6,7 +6,7 @@ -
+
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 988c1fa384..5b423d06f9 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 @@ -7,7 +7,7 @@ - +
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 index 61770c91cf..897a0ed36d 100644 --- 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 @@ -6,7 +6,7 @@ - +
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html index 440994ff8b..98820eb75c 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html @@ -10,10 +10,10 @@

{{:: 'add-node' | translate}}

{{node.host|capitalize}} - +

- +
@@ -27,7 +27,7 @@
-
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-clustering.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-clustering.html index 4a6312b4d7..e729582dae 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-clustering.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-clustering.html @@ -7,7 +7,7 @@ - + {{:: 'basic-configuration' | translate}}
@@ -31,7 +31,7 @@
-
+
@@ -43,7 +43,7 @@ - - + diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers-add.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers-add.html index 04c2339238..653777924d 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers-add.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers-add.html @@ -45,7 +45,7 @@
+
{{:: 'register-node-manually' | translate}} diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html index 1d59078d1a..a2d59e1fd0 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html @@ -1,11 +1,11 @@
- +
-
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-export.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-export.html index 382f612f29..8fbd0792e0 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-export.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-export.html @@ -9,7 +9,7 @@

{{:: 'generate-private-key' | translate}}

- + @@ -48,7 +48,7 @@ {{:: 'store-password.tooltip' | translate}}
-
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html index 9968a316f8..f96c4f0c3f 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html @@ -9,7 +9,7 @@

{{:: 'import-client-certificate' | translate}}

- + diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html index 29f652455a..8df035ce57 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html @@ -1,4 +1,4 @@ -
+
@@ -63,7 +63,7 @@
-
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html index 744ea80dac..d4fb071683 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html @@ -1,5 +1,5 @@
- +
@@ -7,7 +7,7 @@
-
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html index b1b1062dfb..387177491d 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html @@ -7,7 +7,7 @@ - +
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html index 3eb084f44d..5f24b21d82 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html @@ -7,7 +7,7 @@ - +
@@ -391,7 +391,7 @@
-
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-keys.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-keys.html index e2bc22ef6d..e8d6259b40 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-keys.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-keys.html @@ -7,7 +7,7 @@ - + @@ -137,7 +137,7 @@ kc-select-action="click" readonly>{{keyInfo.certificate}}
-
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html index 51aaa2009b..03ebf5cc39 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html @@ -42,7 +42,7 @@
{{:: 'edit' | translate}} {{:: 'export' | translate}}{{:: 'delete' | translate}}{{:: 'delete' | translate}}
{{:: 'no-results' | translate}}
-
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html index ba9b9bf5cc..5648c19bf8 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html @@ -7,7 +7,7 @@ - +
@@ -35,7 +35,7 @@
-
+ @@ -55,7 +55,7 @@ {{mapperTypes[mapper.protocolMapper].category}} {{mapperTypes[mapper.protocolMapper].name}} {{:: 'edit' | translate}} - {{:: 'delete' | translate}} + {{:: 'delete' | translate}} {{:: 'no-mappers-available' | translate}} diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html index 38560e825f..761b3374eb 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html @@ -7,7 +7,7 @@ - +
@@ -18,7 +18,7 @@
-
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html index 9c3e215a1a..d21d7ef593 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html @@ -10,7 +10,7 @@ - +
@@ -47,13 +47,13 @@
-
+
-
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html index da4d49d482..458b412a5d 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html @@ -10,7 +10,7 @@ - - + diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-export.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-export.html index 21651656ad..753f564bd1 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-export.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-export.html @@ -9,7 +9,7 @@

{{:: 'export-saml-key' | translate}} {{client.clientId|capitalize}}

- + @@ -55,7 +55,7 @@ {{:: 'store-password.tooltip' | translate}}
-
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-import.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-import.html index dec13a8c4c..b479058d66 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-import.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-import.html @@ -9,7 +9,7 @@

{{:: 'import-saml-key' | translate}} {{client.clientId|capitalize}}

- + diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html index 52e54686b6..c5374bb654 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html @@ -7,7 +7,7 @@ - +
{{:: 'signing-key' | translate}} {{:: 'saml-signing-key' | translate}}
@@ -27,7 +27,7 @@
-
+
@@ -53,7 +53,7 @@
-
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html index fc10e6bf66..482ef5b77f 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html @@ -9,7 +9,7 @@

{{client.clientId}} {{:: 'scope-mappings' | translate}}

- +
@@ -41,7 +41,7 @@
-
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html index 27f967d408..24b2594841 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html @@ -10,7 +10,7 @@

{{client.clientId}} {{:: 'service-accounts' | translate}}

- +
@@ -102,7 +102,7 @@
-
+ diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-attributes.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-attributes.html index 26d30e1174..10e3d8fcfe 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/user-attributes.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-attributes.html @@ -6,7 +6,7 @@ -
+
+ @@ -29,7 +29,7 @@ {{role.description}} {{:: 'edit' | translate}}{{:: 'delete' | translate}}{{:: 'delete' | translate}}
{{:: 'no-client-roles-available' | translate}}
@@ -29,7 +29,7 @@
-
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html index f1998f8c3f..d7259fda8e 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html @@ -7,7 +7,7 @@ - +
@@ -62,7 +62,7 @@
- +
{{:: 'user-enabled.tooltip' | translate}}
@@ -93,7 +93,7 @@
- +
{{:: 'email-verified.tooltip' | translate}}
@@ -138,7 +138,7 @@
-
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-list.html index f1dd03b839..569e1fc8d4 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/user-list.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-list.html @@ -55,7 +55,7 @@ {{user.firstName}} {{:: 'edit' | translate}} {{:: 'impersonate' | translate}} - {{:: 'delete' | translate}} + {{:: 'delete' | translate}} {{:: 'users.instruction' | translate}} diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html index 1fe11e933c..02c195940c 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html @@ -8,9 +8,9 @@ - + @@ -20,7 +20,7 @@ - + @@ -34,7 +34,7 @@ - +
- {{:: 'started' | translate}} {{:: 'last-access' | translate}} {{:: 'clients' | translate}}{{:: 'action' | translate}}{{:: 'action' | translate}}
{{:: 'logout' | translate}}{{:: 'logout' | translate}}
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html index 25d22d7a44..53b0a3d5e7 100755 --- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html +++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html @@ -16,7 +16,7 @@
-