keycloak-aplcache

console work

6/7/2017 5:29:43 PM

Changes

Details

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 cc9e54b..a7ca83e 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<String, Boolean> access;
 
 
     public String getId() {
@@ -366,4 +367,12 @@ public class ClientRepresentation {
     public void setAuthorizationSettings(ResourceServerRepresentation authorizationSettings) {
         this.authorizationSettings = authorizationSettings;
     }
+
+    public Map<String, Boolean> getAccess() {
+        return access;
+    }
+
+    public void setAccess(Map<String, Boolean> 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 180db64..9c96970 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<String> realmRoles;
     protected Map<String, List<String>> clientRoles;
     protected List<GroupRepresentation> subGroups;
+    private Map<String, Boolean> access;
 
     public String getId() {
         return id;
@@ -97,4 +98,12 @@ public class GroupRepresentation {
     public void setSubGroups(List<GroupRepresentation> subGroups) {
         this.subGroups = subGroups;
     }
+
+    public Map<String, Boolean> getAccess() {
+        return access;
+    }
+
+    public void setAccess(Map<String, Boolean> 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 2a4ec62..4dcea95 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<SocialLinkRepresentation> socialLinks;
 
     protected List<String> groups;
+    private Map<String, Boolean> access;
 
     public String getSelf() {
         return self;
@@ -264,4 +265,12 @@ public class UserRepresentation {
     public void setDisableableCredentialTypes(Set<String> disableableCredentialTypes) {
         this.disableableCredentialTypes = disableableCredentialTypes;
     }
+
+    public Map<String, Boolean> getAccess() {
+        return access;
+    }
+
+    public void setAccess(Map<String, Boolean> 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 1a36d7a..4b620bc 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 0000000..0cb3eb6
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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 24455b8..cf605d1 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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
@@ -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 6637bd8..5ae5904 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 e94ff3c..21df21f 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 36362cb..32a86be 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 41b91be..88b5fdd 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 a5b7a6c..3de46b0 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/permissions/ClientPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java
index 51e26c0..25da468 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
@@ -73,4 +75,6 @@ public interface ClientPermissionEvaluator {
     boolean canMapCompositeRoles(ClientModel client);
 
     boolean canMapClientScopeRoles(ClientModel client);
+
+    Map<String, Boolean> 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 06fa5d8..39f7b4d 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<String, Boolean> getAccess(ClientModel client) {
+        Map<String, Boolean> 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 91ca5c5..450b074 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
@@ -50,4 +52,6 @@ public interface GroupPermissionEvaluator {
     boolean canManageMembers(GroupModel group);
 
     void requireManageMembers(GroupModel group);
+
+    Map<String, Boolean> 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 a3277ba..5fcfbbb 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 acf80c1..2223802 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<String, String> getPermissions(GroupModel group) {
         Map<String, String> 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<String, Boolean> getAccess(GroupModel group) {
+        Map<String, Boolean> 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 e276ae2..05ce719 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
@@ -47,4 +49,6 @@ public interface UserPermissionEvaluator {
     boolean canImpersonate(UserModel user);
 
     void requireImpersonate(UserModel user);
+
+    Map<String, Boolean> 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 b095592..f8dd53b 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<String, Boolean> getAccess(UserModel user) {
+        Map<String, Boolean> map = new HashMap<>();
+        map.put("view", canView(user));
+        map.put("manage", canManage(user));
+        return map;
+    }
+
 
 
 
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 44e4892..ca9d536 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 fc4bfee..6ed677f 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/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 72f3df9..d32ad79 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<RealmRepresentation> 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<RoleRepresentation> 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<RoleRepresentation> 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<RoleRepresentation> 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<RoleRepresentation> 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 2864559..c4e870f 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 945fc5a..515eb99 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 e3d3070..ab4e72a 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 @@
-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'];
+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) {
-            realmAccess = realmAccess[Current.realm.realm];
-            if (realmAccess) {
-                return realmAccess.indexOf(role) >= 0;
-            }
+            return realmAccess.indexOf(role) >= 0;
         }
-        return false;
     }
+    return false;
+}
 
-    $scope.access = {
+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('view-realm') || getAccess('manage-realm') || this.manageRealm;
+            return getAccess(Auth, Current, 'view-realm') || getAccess(Auth, Current, 'manage-realm') || this.manageRealm;
         },
 
         get viewClients() {
-            return getAccess('view-clients') || getAccess('manage-clients') || this.manageClients;
+            return getAccess(Auth, Current, 'view-clients') || getAccess(Auth, Current, 'manage-clients') || this.manageClients;
         },
 
         get viewUsers() {
-            return getAccess('view-users') || getAccess('manage-users') || this.manageClients;
+            return getAccess(Auth, Current, 'view-users') || getAccess(Auth, Current, 'manage-users') || this.manageClients;
         },
 
         get viewEvents() {
-            return getAccess('view-events') || getAccess('manage-events') || this.manageClients;
+            return getAccess(Auth, Current, 'view-events') || getAccess(Auth, Current, 'manage-events') || this.manageClients;
         },
 
         get viewIdentityProviders() {
-            return getAccess('view-identity-providers') || getAccess('manage-identity-providers') || this.manageIdentityProviders;
+            return getAccess(Auth, Current, 'view-identity-providers') || getAccess(Auth, Current, 'manage-identity-providers') || this.manageIdentityProviders;
         },
 
         get viewAuthorization() {
-            return getAccess('view-authorization') || this.manageAuthorization;
+            return getAccess(Auth, Current, 'view-authorization') || this.manageAuthorization;
         },
 
         get manageRealm() {
-            return getAccess('manage-realm');
+            return getAccess(Auth, Current, 'manage-realm');
         },
 
         get manageClients() {
-            return getAccess('manage-clients');
+            return getAccess(Auth, Current, 'manage-clients');
         },
 
         get manageUsers() {
-            return getAccess('manage-users');
+            return getAccess(Auth, Current, 'manage-users');
         },
 
         get manageEvents() {
-            return getAccess('manage-events');
+            return getAccess(Auth, Current, 'manage-events');
         },
 
         get manageIdentityProviders() {
-            return getAccess('manage-identity-providers');
+            return getAccess(Auth, Current, 'manage-identity-providers');
         },
 
         get manageAuthorization() {
-            return getAccess('manage-authorization');
+            return getAccess(Auth, Current, 'manage-authorization');
         },
 
         get impersonation() {
-            return getAccess('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();
+
+    $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 50241b2..bb6d7b6 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 @@
 
     <kc-tabs-client></kc-tabs-client>
 
-    <form class=form-horizontal" name="enableForm" novalidate kc-read-only="!access.manageAuthorization">
+    <form class=form-horizontal" name="enableForm" novalidate kc-read-only="!client.access.manage || !access.manageAuthorization">
         <fieldset class="border-top">
         <div class="form-group">
             <label class="col-md-2 control-label" for="permissionsEnabled">{{:: 'permissions-enabled-role' | translate}}</label>
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 988c1fa..5b423d0 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 @@
 
     <kc-tabs-client-role></kc-tabs-client-role>
 
-    <form class=form-horizontal" name="enableForm" novalidate kc-read-only="!access.manageAuthorization">
+    <form class=form-horizontal" name="enableForm" novalidate kc-read-only="!client.access.manage || !access.manageAuthorization">
         <fieldset class="border-top">
         <div class="form-group">
             <label class="col-md-2 control-label" for="permissionsEnabled">{{:: 'permissions-enabled-role' | translate}}</label>
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 61770c9..897a0ed 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 @@
 
     <kc-tabs-group></kc-tabs-group>
 
-    <form class=form-horizontal" name="enableForm" novalidate kc-read-only="!access.manageAuthorization">
+    <form class=form-horizontal" name="enableForm" novalidate kc-read-only="!group.access.manage || !access.manageAuthorization">
         <fieldset class="border-top">
         <div class="form-group">
             <label class="col-md-2 control-label" for="permissionsEnabled">{{:: 'permissions-enabled-role' | translate}}</label>
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 4a6312b..e729582 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 @@
 
     <kc-tabs-client></kc-tabs-client>
 
-    <form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!access.manageClients">
+    <form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!client.access.manage">
         <legend><span class="text">{{:: 'basic-configuration' | translate}}</span></legend>
         <fieldset >
             <div class="form-group clearfix">
@@ -31,7 +31,7 @@
             </div>
 
             <div class="form-group">
-                <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
+                <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.manage">
                     <button data-kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
                     <button data-kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
                 </div>
@@ -43,7 +43,7 @@
             <table class="table table-striped table-bordered">
                 <thead>
                     <tr>
-                        <th class="kc-table-actions" colspan="5" data-ng-show="access.manageClients">
+                        <th class="kc-table-actions" colspan="5" data-ng-show="client.access.manage">
                             <div class="pull-right">
                                 <a class="btn btn-default" tooltip="Manually register cluster node. This is usually not needed as cluster node should be registered automatically by adapter"
                                    tooltip-trigger="mouseover mouseout" tooltip-placement="bottom" href="#/register-node/realms/{{realm.realm}}/clients/{{client.id}}/clustering">{{:: 'register-node-manually' | translate}}</a>
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 440994f..98820eb 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 @@
     <h1 data-ng-show="create">{{:: 'add-node' | translate}}</h1>
     <h1 data-ng-hide="create">
         {{node.host|capitalize}}
-        <i id="removeClient" class="pficon pficon-delete clickable" data-ng-show="access.manageClients" data-ng-click="unregisterNode()"></i>
+        <i id="removeClient" class="pficon pficon-delete clickable" data-ng-show="client.access.manage" data-ng-click="unregisterNode()"></i>
     </h1>
 
-    <form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!access.manageClients" data-ng-show="create || registered">
+    <form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!client.access.manage" data-ng-show="create || registered">
         <div class="form-group">
             <label class="col-md-2 control-label" for="host">{{:: 'host' | translate}}</label>
             <div class="col-sm-6">
@@ -27,7 +27,7 @@
             </div>
         </div>
         <div class="form-group">
-            <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.manage">
                 <button data-kc-save data-ng-show="create">{{:: 'save' | translate}}</button>
             </div>
         </div>
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 b1b1062..3871774 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 @@
 
     <kc-tabs-client></kc-tabs-client>
 
-    <form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageClients">
+    <form class="form-horizontal" name="clientForm" novalidate kc-read-only="!client.access.manage">
         <fieldset class="border-top">
             <div class="form-group clearfix">
                 <label class="col-md-2 control-label" for="clientAuthenticatorType"> {{:: 'client-authenticator' | translate}}</label>
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 1d59078..a2d59e1 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 @@
 <div>
-    <form class="form-horizontal no-margin-top" name="credentialForm" novalidate kc-read-only="!access.manageClients" data-ng-show="currentAuthenticatorConfigProperties.length > 0" data-ng-controller="ClientGenericCredentialsCtrl">
+    <form class="form-horizontal no-margin-top" name="credentialForm" novalidate kc-read-only="!client.access.manage" data-ng-show="currentAuthenticatorConfigProperties.length > 0" data-ng-controller="ClientGenericCredentialsCtrl">
         <fieldset>
             <kc-provider-config realm="realm" config="client.attributes" properties="currentAuthenticatorConfigProperties"></kc-provider-config>
         </fieldset>
 
         <div class="form-group">
-            <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageClients">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.manage">
                 <button kc-save  data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
                 <button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
             </div>
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 29f6524..8df035c 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 @@
- <div class="form-horizontal no-margin-top" name="keyForm" novalidate kc-read-only="!access.manageClients" data-ng-controller="ClientSignedJWTCtrl">
+ <div class="form-horizontal no-margin-top" name="keyForm" novalidate kc-read-only="!client.access.manage" data-ng-controller="ClientSignedJWTCtrl">
 
      <div class="form-group">
          <label class="col-md-2 control-label" for="useJwksUrl">{{:: 'use-jwks-url' | translate}}</label>
@@ -63,7 +63,7 @@
      </div>
 
      <div class="form-group">
-         <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageClients">
+         <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.manage">
              <button class="btn btn-default" type="submit" data-ng-click="generateSigningKey()">{{:: 'gen-new-keys-and-cert' | translate}}</button>
              <button data-ng-disabled="useJwksUrl" class="btn btn-default" type="submit" data-ng-click="importCertificate()">{{:: 'import-certificate' | translate}}</button>
              <button kc-save  data-ng-disabled="!changed" data-ng-click="save()">{{:: 'save' | translate}}</button>
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 382f612..8fbd079 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 @@
 
     <h1>{{:: 'generate-private-key' | translate}}</h1>
 
-    <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
+    <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!client.access.manage">
         <input type="text" readonly value="this is not a login form" style="display: none;">
         <input type="password" readonly value="this is not a login form" style="display: none;">
 
@@ -48,7 +48,7 @@
                 <kc-tooltip>{{:: 'store-password.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
+                <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.manage">
                     <button class="btn btn-primary" type="submit" data-ng-click="download()">{{:: 'generate-and-download' | translate}}</button>
                     <button class="btn btn-default" type="submit" data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
                 </div>
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 9968a31..f96c4f0 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 @@
 
     <h1>{{:: 'import-client-certificate' | translate}}</h1>
 
-    <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
+    <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!client.access.manage">
         <input type="text" readonly value="this is not a login form" style="display: none;">
         <input type="password" readonly value="this is not a login form" style="display: none;">
 
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 744ea80..d4fb071 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 @@
 <div>
-    <form class="form-horizontal no-margin-top" name="credentialForm" novalidate kc-read-only="!access.manageClients" data-ng-controller="ClientSecretCtrl">
+    <form class="form-horizontal no-margin-top" name="credentialForm" novalidate kc-read-only="!client.access.manage" data-ng-controller="ClientSecretCtrl">
         <div class="form-group">
             <label class="col-md-2 control-label" for="secret">{{:: 'secret' | translate}}</label>
             <div class="col-sm-6">
@@ -7,7 +7,7 @@
                     <div class="col-sm-6">
                         <input readonly kc-select-action="click" class="form-control" type="text" id="secret" name="secret" data-ng-model="secret">
                     </div>
-                    <div class="col-sm-6" data-ng-show="access.manageClients">
+                    <div class="col-sm-6" data-ng-show="client.access.manage">
                         <button type="submit" data-ng-click="changePassword()" class="btn btn-default">{{:: 'regenerate-secret' | translate}}</button>
                     </div>
                 </div>
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 3eb084f..5f24b21 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 @@
 
     <kc-tabs-client></kc-tabs-client>
 
-    <form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageClients">
+    <form class="form-horizontal" name="clientForm" novalidate kc-read-only="!client.access.manage">
         <fieldset class="border-top">
             <div class="form-group">
                 <label class="col-md-2 control-label" for="clientId">{{:: 'client-id' | translate}}</label>
@@ -391,7 +391,7 @@
         </fieldset>
 
         <div class="form-group">
-            <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageClients">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.manage">
                 <button kc-save  data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
                 <button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
             </div>
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 e2bc22e..e8d6259 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 @@
 
     <kc-tabs-client></kc-tabs-client>
 
-    <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
+    <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!client.access.manage">
         <input type="text" readonly value="this is not a login form" style="display: none;">
         <input type="password" readonly value="this is not a login form" style="display: none;">
 
@@ -137,7 +137,7 @@
                               kc-select-action="click" readonly>{{keyInfo.certificate}}</textarea>
                 </div>
             </div>
-            <div class="form-group" data-ng-show="access.manageRealm">
+            <div class="form-group" data-ng-show="client.access.manage">
                 <div class="pull-right">
                     <button class="btn btn-primary" type="submit" data-ng-click="generate()">Generate new keys</button>
                 </div>
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 51aaa20..03ebf5c 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 @@
                 </td>
                 <td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}">{{:: 'edit' | translate}}</td>
                 <td class="kc-action-cell" data-ng-click="exportClient(client)">{{:: 'export' | translate}}</td>
-                <td class="kc-action-cell" data-ng-show="access.manageClients" data-ng-click="removeClient(client)">{{:: 'delete' | translate}}</td>
+                <td class="kc-action-cell" data-ng-show="client.access.manage" data-ng-click="removeClient(client)">{{:: 'delete' | translate}}</td>
             </tr>
             <tr data-ng-show="(clients | filter:search).length == 0">
                 <td class="text-muted" colspan="4" data-ng-show="search.clientId">{{:: 'no-results' | translate}}</td>
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 ba9b9bf..5648c19 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 @@
 
     <kc-tabs-client></kc-tabs-client>
 
-    <form class="form-horizontal" name="allowScope" novalidate kc-read-only="!access.manageClients">
+    <form class="form-horizontal" name="allowScope" novalidate kc-read-only="!client.access.manage">
         <fieldset class="border-top">
             <div class="form-group" ng-show="client.clientTemplate">
                 <label class="col-md-2 control-label" for="useTemplateScope">Inherit Template Mappers</label>
@@ -35,7 +35,7 @@
                         </div>
                     </div>
 
-                    <div class="pull-right" data-ng-show="access.manageClients">
+                    <div class="pull-right" data-ng-show="client.access.manage">
                         <a class="btn btn-default" href="#/create/client/{{realm.realm}}/{{client.id}}/mappers">{{:: 'create' | translate}}</a>
                         <a class="btn btn-default" href="#/realms/{{realm.realm}}/clients/{{client.id}}/add-mappers">{{:: 'add-builtin' | translate}}</a>
                     </div>
@@ -55,7 +55,7 @@
             <td>{{mapperTypes[mapper.protocolMapper].category}}</td>
             <td>{{mapperTypes[mapper.protocolMapper].name}}</td>
             <td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/mappers/{{mapper.id}}">{{:: 'edit' | translate}}</td>
-            <td class="kc-action-cell" data-ng-show="access.manageClients" data-ng-click="removeMapper(mapper)">{{:: 'delete' | translate}}</td>
+            <td class="kc-action-cell" data-ng-show="client.access.manage" data-ng-click="removeMapper(mapper)">{{:: 'delete' | translate}}</td>
         </tr>
         <tr data-ng-show="mappers.length == 0">
             <td>{{:: 'no-mappers-available' | translate}}</td>
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 04c2339..6537779 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 @@
         </tbody>
     </table>
 
-    <div data-ng-show="access.manageRealm">
+    <div data-ng-show="client.access.manage">
         <button class="btn btn-primary" data-ng-click="add()">{{:: 'add-selected' | translate}}</button>
     </div>
 </div>
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 38560e8..761b337 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 @@
 
     <kc-tabs-client></kc-tabs-client>
 
-    <form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!access.manageClients">
+    <form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!client.access.manage">
         <fieldset class="border-top">
             <div class="form-group">
                 <label class="col-md-2 control-label" for="notBefore">{{:: 'not-before' | translate}}</label>
@@ -18,7 +18,7 @@
             </div>
         </fieldset>
         <div class="form-group">
-            <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageClients">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.manage">
                 <button type="submit" data-ng-click="clear()" class="btn btn-default">{{:: 'clear' | translate}}</button>
                 <button type="submit" data-ng-click="setNotBeforeNow()" class="btn btn-default">{{:: 'set-to-now' | translate}}</button>
                 <button type="submit" data-ng-click="pushRevocation()" class="btn btn-primary" tooltip-trigger="mouseover mouseout" tooltip="{{:: 'client-revoke.push.tooltip' | translate}}" tooltip-placement="bottom">{{:: 'push' | translate}}</button>
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 9c3e215..d21d7ef 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 @@
 
     <kc-tabs-client-role></kc-tabs-client-role>
 
-    <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients">
+    <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!client.access.manage">
 
         <fieldset class="border-top">
             <div class="form-group">
@@ -47,13 +47,13 @@
         </fieldset>
 
         <div class="form-group">
-            <div class="col-md-10 col-md-offset-2" data-ng-show="create && access.manageClients">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="create && client.access.manages">
                 <button kc-save>{{:: 'save' | translate}}</button>
                 <button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
             </div>
         </div>
         <div class="form-group">
-            <div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="!create && client.access.manage">
                 <button kc-save  data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
                 <button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
             </div>
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 da4d49d..458b412 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 @@
     <table class="table table-striped table-bordered">
         <thead>
         <tr>
-            <th class="kc-table-actions" colspan="5" data-ng-show="access.manageClients">
+            <th class="kc-table-actions" colspan="5" data-ng-show="client.access.manage">
                 <div class="pull-right">
                     <a class="btn btn-default" href="#/create/role/{{realm.realm}}/clients/{{client.id}}">{{:: 'add-role' | translate}}</a>
                 </div>
@@ -29,7 +29,7 @@
             <td translate="{{role.composite}}"></td>
             <td>{{role.description}}</td>
             <td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}">{{:: 'edit' | translate}}</td>
-            <td class="kc-action-cell" data-ng-show="access.manageClients" data-ng-click="removeRole(role)">{{:: 'delete' | translate}}</td>
+            <td class="kc-action-cell" data-ng-show="client.access.manage" data-ng-click="removeRole(role)">{{:: 'delete' | translate}}</td>
         </tr>
         <tr data-ng-show="!roles || roles.length == 0">
             <td>{{:: 'no-client-roles-available' | translate}}</td>
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 2165165..753f564 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 @@
 
     <h1>{{:: 'export-saml-key' | translate}} {{client.clientId|capitalize}}</h1>
 
-    <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
+    <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!client.access.manage">
         <input type="text" readonly value="this is not a login form" style="display: none;">
         <input type="password" readonly value="this is not a login form" style="display: none;">
 
@@ -55,7 +55,7 @@
                 <kc-tooltip>{{:: 'store-password.tooltip' | translate}}</kc-tooltip>
            </div>
             <div class="form-group">
-                <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
+                <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.manage">
                     <button class="btn btn-primary" type="submit" data-ng-click="download()">{{:: 'download' | translate}}</button>
                 </div>
             </div>
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 dec13a8..b479058 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 @@
 
     <h1>{{:: 'import-saml-key' | translate}} {{client.clientId|capitalize}}</h1>
 
-    <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
+    <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!client.access.manage">
         <input type="text" readonly value="this is not a login form" style="display: none;">
         <input type="password" readonly value="this is not a login form" style="display: none;">
 
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 52e5468..c5374bb 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 @@
 
     <kc-tabs-client></kc-tabs-client>
 
-    <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
+    <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!client.access.manage">
         <fieldset class="form-group col-sm-10" data-ng-show="client.attributes['saml.client.signature'] == 'true'">
             <legend uncollapsed><span class="text">{{:: 'signing-key' | translate}}</span>  <kc-tooltip>{{:: 'saml-signing-key' | translate}}</kc-tooltip></legend>
             <div class="form-group" data-ng-hide="!signingKeyInfo.privateKey">
@@ -27,7 +27,7 @@
                 </div>
             </div>
             <div class="form-group">
-                <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
+                <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.manage">
                     <button class="btn btn-default" type="submit" data-ng-click="generateSigningKey()">{{:: 'generate-new-keys' | translate}}</button>
                     <button class="btn btn-default" type="submit" data-ng-click="importSigningKey()">{{:: 'import' | translate}}</button>
                     <button class="btn btn-default" type="submit" data-ng-hide="!signingKeyInfo.certificate" data-ng-click="exportSigningKey()">{{:: 'export' | translate}}</button>
@@ -53,7 +53,7 @@
                 </div>
             </div>
             <div class="form-group">
-                <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
+                <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.manage">
                     <button class="btn btn-default" type="submit" data-ng-click="generateEncryptionKey()">{{:: 'generate-new-keys' | translate}}</button>
                     <button class="btn btn-default" type="submit" data-ng-click="importEncryptionKey()">{{:: 'import' | translate}}</button>
                     <button class="btn btn-default" type="submit" data-ng-hide="!encryptionKeyInfo.certificate" data-ng-click="exportEncryptionKey()">{{:: 'export' | translate}}</button>
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 fc10e6b..482ef5b 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 @@
 
         <h2><span>{{client.clientId}}</span> {{:: 'scope-mappings' | translate}} </h2>
         <p class="subtitle"></p>
-        <form class="form-horizontal" name="allowScope" novalidate kc-read-only="!access.manageClients">
+        <form class="form-horizontal" name="allowScope" novalidate kc-read-only="!client.access.manage">
             <fieldset class="border-top">
                 <div class="form-group" ng-show="client.clientTemplate">
                     <label class="col-md-2 control-label" for="useTemplateScope">Inherit Template Scope</label>
@@ -41,7 +41,7 @@
             </fieldset>
         </form>
 
-        <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients" data-ng-hide="hideRoleSelector()">
+        <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!client.access.manage" data-ng-hide="hideRoleSelector()">
             <div class="form-group">
                 <label class="col-md-2 control-label" class="control-label">{{:: 'realm-roles' | translate}}</label>
                 <div class="col-md-10">
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 27f967d..24b2594 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 @@
     <h2><span>{{client.clientId}}</span> {{:: 'service-accounts' | translate}} </h2>
     <p class="subtitle"></p>
 
-    <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients" data-ng-show="client.serviceAccountsEnabled">
+    <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!client.access.manage" data-ng-show="client.serviceAccountsEnabled">
         <div class="form-group">
             <label class="col-md-2 control-label" class="control-label">{{:: 'realm-roles' | translate}}</label>
             <div class="col-md-10">
@@ -102,7 +102,7 @@
         </div>
     </form>
 
-    <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients" data-ng-show="!client.serviceAccountsEnabled">
+    <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!client.access.manage" data-ng-show="!client.serviceAccountsEnabled">
         <legend><span class="text" translate="service-account-is-not-enabled-for" translate-values="{client: client.clientId}"></span></legend>
     </form>
 
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 26d30e1..10e3d8f 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 @@
 
     <kc-tabs-user></kc-tabs-user>
 
-    <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageUsers">
+    <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!user.access.manage">
         <table class="table table-striped table-bordered">
             <thead>
             <tr>
@@ -29,7 +29,7 @@
             </tbody>
         </table>
 
-        <div class="form-group" data-ng-show="access.manageUsers">
+        <div class="form-group" data-ng-show="user.access.manage">
             <div class="col-md-12">
                 <button kc-save  data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
                 <button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
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 f1998f8..d7259fd 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 @@
 
     <kc-tabs-user></kc-tabs-user>
 
-    <form class="form-horizontal" name="userForm" novalidate kc-read-only="!access.manageUsers">
+    <form class="form-horizontal" name="userForm" novalidate kc-read-only="!create && !user.access.manage">
 
         <fieldset class="border-top">
             <div class="form-group">
@@ -62,7 +62,7 @@
             <div class="form-group clearfix block">
                 <label class="col-md-2 control-label" for="userEnabled">{{:: 'user-enabled' | translate}}</label>
                 <div class="col-md-6">
-                    <input ng-model="user.enabled" name="userEnabled" id="userEnabled" ng-disabled="!access.manageUsers" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
+                    <input ng-model="user.enabled" name="userEnabled" id="userEnabled" ng-disabled="!create && !user.access.manage" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
                 </div>
                 <kc-tooltip>{{:: 'user-enabled.tooltip' | translate}}</kc-tooltip>
             </div>
@@ -93,7 +93,7 @@
             <div class="form-group clearfix block">
                 <label class="col-md-2 control-label" for="emailVerified">{{:: 'email-verified' | translate}}</label>
                 <div class="col-md-6">
-                    <input ng-model="user.emailVerified" name="emailVerified" id="emailVerified" ng-disabled="!access.manageUsers" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
+                    <input ng-model="user.emailVerified" name="emailVerified" id="emailVerified" ng-disabled="!create && !user.access.manage" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
                 </div>
                 <kc-tooltip>{{:: 'email-verified.tooltip' | translate}}</kc-tooltip>
             </div>
@@ -138,7 +138,7 @@
                 <button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
             </div>
 
-            <div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageUsers">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="!create && !user.access.manage">
                 <button kc-save  data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
                 <button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
             </div>
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 f1dd03b..569e1fc 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 @@
             <td class="clip">{{user.firstName}}</td>
             <td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/users/{{user.id}}">{{:: 'edit' | translate}}</td>
             <td data-ng-show="serverInfo.profileInfo.disabledFeatures.indexOf('IMPERSONATION') == -1 && access.impersonation" class="kc-action-cell" data-ng-click="impersonate(user.id)">{{:: 'impersonate' | translate}}</td>
-            <td data-ng-show="access.manageUsers" class="kc-action-cell" data-ng-click="removeUser(user)">{{:: 'delete' | translate}}</td>
+            <td data-ng-show="user.access.manage" class="kc-action-cell" data-ng-click="removeUser(user)">{{:: 'delete' | translate}}</td>
         </tr>
         <tr data-ng-show="!users || users.length == 0">
             <td class="text-muted" data-ng-show="!users">{{:: 'users.instruction' | translate}}</td>
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 1fe11e9..02c1959 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 @@
 
     <table class="table table-striped table-bordered">
         <thead>
-        <tr data-ng-show="access.manageUsers">
+        <tr data-ng-show="user.access.manage">
             <th class="kc-table-actions" colspan="6">
-                <div class="pull-right" data-ng-show="access.manageUsers">
+                <div class="pull-right" data-ng-show="user.access.manage">
                     <a id="logoutAllSessions" class="btn btn-default" ng-click="logoutAll()">{{:: 'logout-all-sessions' | translate}}</a>
                 </div>
             </th>
@@ -20,7 +20,7 @@
             <th>{{:: 'started' | translate}}</th>
             <th>{{:: 'last-access' | translate}}</th>
             <th>{{:: 'clients' | translate}}</th>
-            <th data-ng-show="access.manageUsers">{{:: 'action' | translate}}</th>
+            <th data-ng-show="user.access.manage">{{:: 'action' | translate}}</th>
         </tr>
         </thead>
         <tbody>
@@ -34,7 +34,7 @@
                 </div>
             </ul>
             </td>
-            <td class="kc-action-cell" data-ng-show="access.manageUsers" ng-click="logoutSession(session.id)">{{:: 'logout' | translate}}</td>
+            <td class="kc-action-cell" data-ng-show="user.access.manage" ng-click="logoutSession(session.id)">{{:: 'logout' | translate}}</td>
         </tr>
         </tbody>
     </table>
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 25d22d7..53b0a3d 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 @@
       </div>
     </div>
 
-    <div class="nav-category" data-ng-show="current.realm">
+    <div class="nav-category" data-ng-show="current.realm && (access.viewRealm || access.queryClients || access.viewIdentityProviders)">
         <h2>{{:: 'configure' | translate}}</h2>
         <ul class="nav nav-pills nav-stacked">
             <li data-ng-show="access.viewRealm" data-ng-class="((!path[2]
@@ -32,7 +32,7 @@
     || path[2] == 'keys-settings' || path[2] == 'smtp-settings' || path[2] == 'ldap-settings' || path[2] == 'auth-settings') && path[3] != 'clients') && 'active'">
                 <a href="#/realms/{{realm.realm}}"><span class="pficon pficon-settings"></span> {{:: 'realm-settings' | translate}}</a>
             </li>
-            <li data-ng-show="access.viewClients" data-ng-class="(path[2] == 'clients' || path[3] == 'clients') && 'active'"><a href="#/realms/{{realm.realm}}/clients"><i class="fa fa-cube"></i> {{:: 'clients' | translate}}</a></li>
+            <li data-ng-show="access.queryClients" data-ng-class="(path[2] == 'clients' || path[3] == 'clients') && 'active'"><a href="#/realms/{{realm.realm}}/clients"><i class="fa fa-cube"></i> {{:: 'clients' | translate}}</a></li>
             <li data-ng-show="access.viewClients" data-ng-class="(path[2] == 'client-templates' || path[3] == 'client-templates') && 'active'"><a href="#/realms/{{realm.realm}}/client-templates"><i class="fa fa-cubes"></i> {{:: 'client-templates' | translate}}</a></li>
             <li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'roles' || path[2] == 'default-roles') && 'active'"><a href="#/realms/{{realm.realm}}/roles"><i class="fa fa-tasks"></i> {{:: 'roles' | translate}}</a></li>
             <li data-ng-show="access.viewIdentityProviders" data-ng-class="(path[2] == 'identity-provider-settings' || path[2] == 'identity-provider-mappers') && 'active'"><a href="#/realms/{{realm.realm}}/identity-provider-settings"><i class="fa fa-exchange"></i> {{:: 'identity-providers' | translate}}</a></li>
@@ -45,12 +45,12 @@
         </ul>
     </div>
 
-    <div class="nav-category" data-ng-show="current.realm">
+    <div class="nav-category" data-ng-show="current.realm && (access.viewRealm || access.queryGroups || access.queryUsers || access.viewEvents)">
         <h2>{{:: 'manage' | translate}}</h2>
         <ul class="nav nav-pills nav-stacked">
-            <li data-ng-show="access.viewUsers" data-ng-class="(path[2] == 'groups' 
+            <li data-ng-show="access.queryGroups" data-ng-class="(path[2] == 'groups'
                         || path[2] == 'default-groups') && 'active'"><a href="#/realms/{{realm.realm}}/groups"><span class="pficon pficon-users"></span> {{:: 'groups' | translate}}</a></li>
-            <li data-ng-show="access.viewUsers" data-ng-class="(path[2] == 'users') && 'active'"><a href="#/realms/{{realm.realm}}/users"><span class="pficon pficon-user"></span> {{:: 'users' | translate}}</a></li>
+            <li data-ng-show="access.queryUsers" data-ng-class="(path[2] == 'users') && 'active'"><a href="#/realms/{{realm.realm}}/users"><span class="pficon pficon-user"></span> {{:: 'users' | translate}}</a></li>
             <li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'sessions') && 'active'"><a href="#/realms/{{realm.realm}}/sessions/realm"><i class="fa fa-clock-o"></i> {{:: 'sessions' | translate}}</a></li>
             <li data-ng-show="access.viewEvents" data-ng-class="(path[2] == 'events' 
                         || path[2] == 'events-settings'
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 a18c31f..cf46c2e 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
@@ -3,7 +3,7 @@
     <h1 data-ng-show="create">{{:: 'add-client' | translate}}</h1>
     <h1 data-ng-hide="create">
         {{client.clientId|capitalize}}
-        <i id="removeClient" class="pficon pficon-delete clickable" data-ng-show="access.manageClients" data-ng-click="removeClient()"></i>
+        <i id="removeClient" class="pficon pficon-delete clickable" data-ng-show="client.access.manage" data-ng-click="removeClient()"></i>
     </h1>
 
     <ul class="nav nav-tabs"  data-ng-hide="create && !path[4]">
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html
index 66dad39..9c372e3 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html
@@ -8,7 +8,7 @@
     <ul class="nav nav-tabs" data-ng-show="!create">
         <li ng-class="{active: !path[4] && path[0] != 'create'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}">{{:: 'details' | translate}}</a></li>
         <li ng-class="{active: path[4] == 'user-attributes'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/user-attributes">{{:: 'attributes' | translate}}</a></li>
-        <li ng-class="{active: path[4] == 'user-credentials'}" data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/user-credentials">{{:: 'credentials' | translate}}</a></li>
+        <li ng-class="{active: path[4] == 'user-credentials'}" data-ng-show="user.access.manage"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/user-credentials">{{:: 'credentials' | translate}}</a></li>
         <li ng-class="{active: path[4] == 'role-mappings'}" ><a href="#/realms/{{realm.realm}}/users/{{user.id}}/role-mappings">{{:: 'role-mappings' | translate}}</a></li>
         <li ng-class="{active: path[4] == 'groups'}" ><a href="#/realms/{{realm.realm}}/users/{{user.id}}/groups">{{:: 'groups' | translate}}</a></li>
         <li ng-class="{active: path[4] == 'consents'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/consents">{{:: 'consents' | translate}}</a></li>
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-users.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-users.html
index 8cc17ea..1c9dc4a 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-users.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-users.html
@@ -3,7 +3,7 @@
 
     <ul class="nav nav-tabs">
         <li ng-class="{active: path[2] == 'users'}"><a href="#/realms/{{realm.realm}}/users">{{:: 'lookup' | translate}}</a></li>
-        <li ng-class="{active: path[2] == 'users-permissions'}">
+        <li ng-class="{active: path[2] == 'users-permissions'}"data-ng-show="access.viewRealm">
             <a href="#/realms/{{realm.realm}}/users-permissions">{{:: 'authz-permissions' | translate}}</a>
             <kc-tooltip>{{:: 'manage-permissions-users.tooltip' | translate}}</kc-tooltip>
         </li>