ui for permission reference

This commit is contained in:
Bill Burke 2017-06-05 19:52:51 -04:00
parent a41d282e92
commit 536a57a514
22 changed files with 456 additions and 72 deletions

View file

@ -31,6 +31,7 @@ import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
@ -41,6 +42,7 @@ import org.keycloak.protocol.ClientInstallationProvider;
import org.keycloak.representations.adapters.action.GlobalRequestResult;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.ManagementPermissionReference;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
import org.keycloak.services.ErrorResponse;
@ -52,6 +54,8 @@ import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
import org.keycloak.services.validation.ClientValidator;
import org.keycloak.services.validation.PairwiseClientValidator;
import org.keycloak.services.validation.ValidationMessages;
@ -536,4 +540,55 @@ public class ClientResource {
return resource;
}
/**
* Return object stating whether client Authorization permissions have been initialized or not and a reference
*
* @return
*/
@Path("management/permissions")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public ManagementPermissionReference getManagementPermissions() {
auth.roles().requireView(client);
AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
if (!permissions.clients().isPermissionsEnabled(client)) {
return new ManagementPermissionReference();
}
return toMgmtRef(client, permissions);
}
public static ManagementPermissionReference toMgmtRef(ClientModel client, AdminPermissionManagement permissions) {
ManagementPermissionReference ref = new ManagementPermissionReference();
ref.setEnabled(true);
ref.setResource(permissions.clients().resource(client).getId());
ref.setScopePermissions(permissions.clients().getPermissions(client));
return ref;
}
/**
* Return object stating whether client Authorization permissions have been initialized or not and a reference
*
*
* @return initialized manage permissions reference
*/
@Path("management/permissions")
@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@NoCache
public ManagementPermissionReference setManagementPermissionsEnabled(ManagementPermissionReference ref) {
auth.clients().requireManage(client);
if (ref.isEnabled()) {
AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
permissions.clients().setPermissionsEnabled(client, ref.isEnabled());
return toMgmtRef(client, permissions);
} else {
return new ManagementPermissionReference();
}
}
}

View file

@ -207,7 +207,7 @@ public class ClientsResource {
ClientModel clientModel = realm.getClientById(id);
if (clientModel == null) {
// we do this to make sure somebody can't phish ids
if (!auth.clients().canList()) throw new NotFoundException("Could not find client");
if (auth.clients().canList()) throw new NotFoundException("Could not find client");
else throw new ForbiddenException();
}

View file

@ -21,6 +21,7 @@ import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
@ -28,6 +29,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.ManagementPermissionReference;
import org.keycloak.representations.idm.UserRepresentation;
import javax.ws.rs.Consumes;
@ -50,6 +52,8 @@ import java.util.Map;
import java.util.Set;
import org.keycloak.services.ErrorResponse;
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
/**
* @resource Groups
@ -214,4 +218,55 @@ public class GroupResource {
return results;
}
/**
* Return object stating whether client Authorization permissions have been initialized or not and a reference
*
* @return
*/
@Path("management/permissions")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public ManagementPermissionReference getManagementPermissions() {
auth.groups().requireView(group);
AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
if (!permissions.groups().isPermissionsEnabled(group)) {
return new ManagementPermissionReference();
}
return toMgmtRef(group, permissions);
}
public static ManagementPermissionReference toMgmtRef(GroupModel group, AdminPermissionManagement permissions) {
ManagementPermissionReference ref = new ManagementPermissionReference();
ref.setEnabled(true);
ref.setResource(permissions.groups().resource(group).getId());
ref.setScopePermissions(permissions.groups().getPermissions(group));
return ref;
}
/**
* Return object stating whether client Authorization permissions have been initialized or not and a reference
*
*
* @return initialized manage permissions reference
*/
@Path("management/permissions")
@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@NoCache
public ManagementPermissionReference setManagementPermissionsEnabled(ManagementPermissionReference ref) {
auth.groups().requireManage(group);
if (ref.isEnabled()) {
AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
permissions.groups().setPermissionsEnabled(group, ref.isEnabled());
return toMgmtRef(group, permissions);
} else {
return new ManagementPermissionReference();
}
}
}

View file

@ -17,21 +17,38 @@
package org.keycloak.services.resources.admin.permissions;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.models.ClientModel;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface ClientPermissionManagement {
public static final String MAP_ROLES_SCOPE = "map-roles";
public static final String MAP_ROLES_CLIENT_SCOPE = "map-roles-client-scope";
public static final String MAP_ROLES_COMPOSITE_SCOPE = "map-roles-composite";
boolean isPermissionsEnabled(ClientModel client);
void setPermissionsEnabled(ClientModel client, boolean enable);
Resource resource(ClientModel client);
Map<String, String> getPermissions(ClientModel client);
Policy mapRolesPermission(ClientModel client);
Policy mapRolesClientScopePermission(ClientModel client);
Policy mapRolesCompositePermission(ClientModel client);
Policy managePermission(ClientModel client);
Policy viewPermission(ClientModel client);
ResourceServer resourceServer(ClientModel client);
}

View file

@ -30,7 +30,9 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.services.ForbiddenException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
@ -67,11 +69,11 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
private String getMapRolesPermissionName(ClientModel client) {
return MAP_ROLES_SCOPE + ".permission.client." + client.getId();
}
private String getMapRoleClientScopePermissionName(ClientModel client) {
return RolePermissionManagement.MAP_ROLE_CLIENT_SCOPE_SCOPE + ".permission.client." + client.getId();
private String getMapRolesClientScopePermissionName(ClientModel client) {
return MAP_ROLES_CLIENT_SCOPE + ".permission.client." + client.getId();
}
private String getMapRoleCompositePermissionName(ClientModel client) {
return RolePermissionManagement.MAP_ROLE_COMPOSITE_SCOPE + ".permission.client." + client.getId();
private String getMapRolesCompositePermissionName(ClientModel client) {
return MAP_ROLES_COMPOSITE_SCOPE + ".permission.client." + client.getId();
}
private void initialize(ClientModel client) {
@ -88,13 +90,13 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
if (mapRoleScope == null) {
mapRoleScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLES_SCOPE, server);
}
Scope mapRoleClientScope = mapRoleClientScope(server);
Scope mapRoleClientScope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_CLIENT_SCOPE, server.getId());
if (mapRoleClientScope == null) {
mapRoleClientScope = authz.getStoreFactory().getScopeStore().create(RolePermissionManagement.MAP_ROLE_CLIENT_SCOPE_SCOPE, server);
mapRoleClientScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLES_CLIENT_SCOPE, server);
}
Scope mapRoleCompositeScope = mapRoleCompositeScope(server);
Scope mapRoleCompositeScope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_COMPOSITE_SCOPE, server.getId());
if (mapRoleCompositeScope == null) {
mapRoleCompositeScope = authz.getStoreFactory().getScopeStore().create(RolePermissionManagement.MAP_ROLE_COMPOSITE_SCOPE, server);
mapRoleCompositeScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLES_COMPOSITE_SCOPE, server);
}
String resourceName = getResourceName(client);
@ -129,12 +131,12 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
if (mapRolePermission == null) {
Helper.addEmptyScopePermission(authz, server, mapRolePermissionName, resource, mapRoleScope);
}
String mapRoleClientScopePermissionName = getMapRoleClientScopePermissionName(client);
String mapRoleClientScopePermissionName = getMapRolesClientScopePermissionName(client);
Policy mapRoleClientScopePermission = authz.getStoreFactory().getPolicyStore().findByName(mapRoleClientScopePermissionName, server.getId());
if (mapRoleClientScopePermission == null) {
Helper.addEmptyScopePermission(authz, server, mapRoleClientScopePermissionName, resource, mapRoleClientScope);
}
String mapRoleCompositePermissionName = getMapRoleCompositePermissionName(client);
String mapRoleCompositePermissionName = getMapRolesCompositePermissionName(client);
Policy mapRoleCompositePermission = authz.getStoreFactory().getPolicyStore().findByName(mapRoleCompositePermissionName, server.getId());
if (mapRoleCompositePermission == null) {
Helper.addEmptyScopePermission(authz, server, mapRoleCompositePermissionName, resource, mapRoleCompositeScope);
@ -155,8 +157,8 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
deletePolicy(getManagePermissionName(client), client, server);
deletePolicy(getViewPermissionName(client), client, server);
deletePolicy(getMapRolesPermissionName(client), client, server);
deletePolicy(getMapRoleClientScopePermissionName(client), client, server);
deletePolicy(getMapRoleCompositePermissionName(client), client, server);
deletePolicy(getMapRolesClientScopePermissionName(client), client, server);
deletePolicy(getMapRolesCompositePermissionName(client), client, server);
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());;
if (resource != null) authz.getStoreFactory().getResourceStore().delete(resource.getId());
}
@ -190,12 +192,6 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
private Scope mapRolesScope(ResourceServer server) {
return authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_SCOPE, server.getId());
}
private Scope mapRoleClientScope(ResourceServer server) {
return authz.getStoreFactory().getScopeStore().findByName(RolePermissionManagement.MAP_ROLE_CLIENT_SCOPE_SCOPE, server.getId());
}
private Scope mapRoleCompositeScope(ResourceServer server) {
return authz.getStoreFactory().getScopeStore().findByName(RolePermissionManagement.MAP_ROLE_COMPOSITE_SCOPE, server.getId());
}
@Override
public boolean canList() {
@ -250,6 +246,27 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
}
}
@Override
public Resource resource(ClientModel client) {
ResourceServer server = resourceServer(client);
if (server == null) return null;
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());
if (resource == null) return null;
return resource;
}
@Override
public Map<String, String> getPermissions(ClientModel client) {
Map<String, String> scopes = new HashMap<>();
scopes.put(MAP_ROLES_SCOPE, mapRolesPermission(client).getId());
scopes.put(MAP_ROLES_CLIENT_SCOPE, mapRolesClientScopePermission(client).getId());
scopes.put(MAP_ROLES_COMPOSITE_SCOPE, mapRolesCompositePermission(client).getId());
scopes.put(AdminPermissionManagement.VIEW_SCOPE, viewPermission(client).getId());
scopes.put(AdminPermissionManagement.MANAGE_SCOPE, managePermission(client).getId());
return scopes;
}
@Override
public boolean canManage(ClientModel client) {
if (!root.isAdminSameRealm()) {
@ -397,6 +414,34 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
return authz.getStoreFactory().getPolicyStore().findByName(getMapRolesPermissionName(client), server.getId());
}
@Override
public Policy mapRolesClientScopePermission(ClientModel client) {
ResourceServer server = resourceServer(client);
if (server == null) return null;
return authz.getStoreFactory().getPolicyStore().findByName(getMapRolesClientScopePermissionName(client), server.getId());
}
@Override
public Policy mapRolesCompositePermission(ClientModel client) {
ResourceServer server = resourceServer(client);
if (server == null) return null;
return authz.getStoreFactory().getPolicyStore().findByName(getMapRolesCompositePermissionName(client), server.getId());
}
@Override
public Policy managePermission(ClientModel client) {
ResourceServer server = resourceServer(client);
if (server == null) return null;
return authz.getStoreFactory().getPolicyStore().findByName(getManagePermissionName(client), server.getId());
}
@Override
public Policy viewPermission(ClientModel client) {
ResourceServer server = resourceServer(client);
if (server == null) return null;
return authz.getStoreFactory().getPolicyStore().findByName(getViewPermissionName(client), server.getId());
}
@Override
public ResourceServer resourceServer(ClientModel client) {
return authz.getStoreFactory().getResourceServerStore().findByClient(client.getId());
@ -410,7 +455,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());
if (resource == null) return false;
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRoleCompositePermissionName(client), server.getId());
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRolesCompositePermissionName(client), server.getId());
if (policy == null) {
return false;
}
@ -421,7 +466,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
return false;
}
Scope scope = mapRoleCompositeScope(server);
Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_COMPOSITE_SCOPE, server.getId());
return root.evaluatePermission(resource, scope, server);
}
@Override
@ -432,7 +477,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());
if (resource == null) return false;
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRoleClientScopePermissionName(client), server.getId());
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRolesClientScopePermissionName(client), server.getId());
if (policy == null) {
return false;
}
@ -443,7 +488,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
return false;
}
Scope scope = mapRoleClientScope(server);
Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_CLIENT_SCOPE, server.getId());
return root.evaluatePermission(resource, scope, server);
}

View file

@ -17,6 +17,7 @@
package org.keycloak.services.resources.admin.permissions;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.models.GroupModel;
import org.keycloak.models.RoleModel;
@ -34,4 +35,8 @@ public interface GroupPermissionManagement {
Policy manageMembersPermission(GroupModel group);
Policy viewPermissionGroup(GroupModel group);
Policy managePermissionGroup(GroupModel group);
Resource resource(GroupModel group);
Map<String, String> getPermissions(GroupModel group);
}

View file

@ -23,13 +23,16 @@ import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.ClientModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.services.ForbiddenException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
@ -54,16 +57,16 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag
}
private static String getGroupResourceName(GroupModel group) {
return "group.resource." + getGroupSuffix(group);
return "group.resource." + group.getId();
}
public static String getManagePermissionGroup(GroupModel group) {
return "manage.permission.group." + getGroupSuffix(group);
return "manage.permission.group." + group.getId();
}
public static String getManageMembersPermissionGroup(GroupModel group) {
return "manage.members.permission.group." + getGroupSuffix(group);
return "manage.members.permission.group." + group.getId();
}
public static String getGroupSuffix(GroupModel group) {
@ -71,11 +74,11 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag
}
public static String getViewPermissionGroup(GroupModel group) {
return "view.permission.group." + getGroupSuffix(group);
return "view.permission.group." + group.getId();
}
public static String getViewMembersPermissionGroup(GroupModel group) {
return "view.members.permission.group." + getGroupSuffix(group);
return "view.members.permission.group." + group.getId();
}
private void initialize(GroupModel group) {
@ -102,7 +105,7 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag
Policy manageUsersPolicy = root.roles().manageUsersPolicy(server);
Helper.addScopePermission(authz, server, managePermissionName, groupResource, manageScope, manageUsersPolicy);
}
String viewPermissionName = getManagePermissionGroup(group);
String viewPermissionName = getViewPermissionGroup(group);
Policy viewPermission = authz.getStoreFactory().getPolicyStore().findByName(viewPermissionName, server.getId());
if (viewPermission == null) {
Policy viewUsersPolicy = root.roles().viewUsersPolicy(server);
@ -213,6 +216,27 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag
return authz.getStoreFactory().getPolicyStore().findByName(managePermissionName, server.getId());
}
@Override
public Resource resource(GroupModel group) {
ResourceServer server = root.realmResourceServer();
if (server == null) return null;
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId());
if (resource == null) return null;
return resource;
}
@Override
public Map<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(MANAGE_MEMBERS_SCOPE, manageMembersPermission(group).getId());
scopes.put(VIEW_MEMBERS_SCOPE, viewMembersPermission(group).getId());
return scopes;
}
@Override
public boolean canManage(GroupModel group) {

View file

@ -141,19 +141,19 @@ class RolePermissions implements RolePermissionEvaluator, RolePermissionManageme
@Override
public boolean canMapRole(RoleModel role) {
if (!root.isAdminSameRealm()) {
return root.users().canManage();
return root.users().canManageDefault();
}
if (role.getContainer() instanceof ClientModel) {
if (root.clients().canMapRoles((ClientModel)role.getContainer())) return true;
}
if (!isPermissionsEnabled(role)){
return root.users().canManage();
return root.users().canManageDefault();
}
ResourceServer resourceServer = getResourceServer(role);
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRolePermissionName(role), resourceServer.getId());
if (policy.getAssociatedPolicies().isEmpty()) {
return root.users().canManage(); // if no policies applied, just do default
return root.users().canManageDefault(); // if no policies applied, just do default
}
Resource roleResource = resource(role);

View file

@ -175,7 +175,11 @@ public abstract class AbstractKeycloakTest {
// Cleanup objects
for (TestCleanup cleanup : testContext.getCleanups().values()) {
if (cleanup != null) cleanup.executeCleanup();
try {
if (cleanup != null) cleanup.executeCleanup();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
testContext.getCleanups().clear();
}

View file

@ -282,7 +282,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
}
//@Test
@Test
public void testUI() throws Exception {
testingClient.server().run(FineGrainAdminUnitTest::setupPolices);
testingClient.server().run(FineGrainAdminUnitTest::setupUsers);
@ -308,7 +308,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
realmRoleSet.add(realmRole);
RoleRepresentation realmRole2 = adminClient.realm(TEST).roles().get("realm-role2").toRepresentation();
List<RoleRepresentation> realmRole2Set = new LinkedList<>();
realmRole2Set.add(realmRole);
realmRole2Set.add(realmRole2);
ClientRepresentation client = adminClient.realm(TEST).clients().findByClientId("role-namespace").get(0);
RoleRepresentation clientRole = adminClient.realm(TEST).clients().get(client.getId()).roles().get("client-role").toRepresentation();
List<RoleRepresentation> clientRoleSet = new LinkedList<>();
@ -395,18 +395,18 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
TEST, "groupManager", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
List<RoleRepresentation> roles = null;
realmClient.realm(TEST).users().get(groupMember.getId()).roles().clientLevel(client.getId()).add(clientRoleSet);
roles = realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).listAll();
roles = realmClient.realm(TEST).users().get(groupMember.getId()).roles().clientLevel(client.getId()).listAll();
Assert.assertTrue(roles.stream().anyMatch((r) -> {
return r.getName().equals("client-role");
}));
realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).remove(clientRoleSet);
realmClient.realm(TEST).users().get(groupMember.getId()).roles().clientLevel(client.getId()).remove(clientRoleSet);
roles = realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().listAvailable();
roles = realmClient.realm(TEST).users().get(groupMember.getId()).roles().realmLevel().listAvailable();
Assert.assertEquals(roles.size(), 1);
realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().add(realmRoleSet);
realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().remove(realmRoleSet);
realmClient.realm(TEST).users().get(groupMember.getId()).roles().realmLevel().add(realmRoleSet);
realmClient.realm(TEST).users().get(groupMember.getId()).roles().realmLevel().remove(realmRoleSet);
try {
realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().add(realmRole2Set);
realmClient.realm(TEST).users().get(groupMember.getId()).roles().realmLevel().add(realmRole2Set);
Assert.fail("should fail with forbidden exception");
} catch (ClientErrorException e) {
Assert.assertEquals(e.getResponse().getStatus(), 403);
@ -428,6 +428,8 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
Keycloak realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
TEST, "clientMapper", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
List<RoleRepresentation> roles = null;
roles = realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).listAll();
Assert.assertTrue(roles.isEmpty());
realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).add(clientRoleSet);
roles = realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).listAll();
Assert.assertTrue(roles.stream().anyMatch((r) -> {
@ -455,9 +457,6 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
testingClient.server().run(FineGrainAdminUnitTest::setupUsers);
UserRepresentation user1 = adminClient.realm(TEST).users().search("user1").get(0);
UserRepresentation user2 = adminClient.realm(TEST).users().search("user2").get(0);
UserRepresentation user3 = adminClient.realm(TEST).users().search("user3").get(0);
UserRepresentation user4 = adminClient.realm(TEST).users().search("user4").get(0);
RoleRepresentation realmRole = adminClient.realm(TEST).roles().get("realm-role").toRepresentation();
List<RoleRepresentation> realmRoleSet = new LinkedList<>();
realmRoleSet.add(realmRole);

View file

@ -1306,7 +1306,8 @@ credential-reset-actions-timeout.tooltip=Maximum time before the action permit e
ldap-mappers=LDAP Mappers
create-ldap-mapper=Create LDAP mapper
map-role-mgmt-scope-description=Policies that decide if an admin can map this role to a user or group
manage-mgmt-scope-description=Policies that decide if an admin can manage this resource or resources
manage-authz-users-scope-description=Policies that decide if an admin can manage all users in the realm
view-authz-users-scope-description=Policies that decide if an admin can view all users in realm
permissions-enabled-role=Permissions Enabled
permissions-enabled-role.tooltip=Whether or not to enable fine grain permissions for managing this role. Disabling will delete all current permissions that have been set up.
manage-permissions-role.tooltip=Fine grain permissions for managing roles. For example, you can define different policies for who is allowed to map a role.
@ -1314,6 +1315,20 @@ lookup=Lookup
manage-permissions-users.tooltip=Fine grain permssions for managing all users in realm. You can define different policies for who is allowed to manage users in the realm.
permissions-enabled-users=Permissions Enabled
permissions-enabled-users.tooltip=Whether or not to enable fine grain permissions for managing users. Disabling will delete all current permissions that have been set up.
manage-permissions-client.tooltip=Fine grain permssions for admins that want to manage this client or apply roles defined by this client.
manage-permissions-group.tooltip=Fine grain permssions for admins that want to manage this group or the members of this group.
manage-authz-group-scope-description=Policies that decide if an admin can manage this group
view-authz-group-scope-description=Policies that decide if an admin can view this group
view.members-authz-group-scope-description=Policies that decide if an admin can manage the members of this group
manage.members-authz-group-scope-description=Policies that decide if an admin can view the members of this group
manage-authz-client-scope-description=Policies that decide if an admin can manage this client
view-authz-client-scope-description=Policies that decide if an admin can view this client
map-roles-authz-client-scope-description=Policies that decide if an admin can map roles defined by this client
map-roles-client-scope-authz-client-scope-description=Policies that decide if an admin can apply roles defined by this client to the client scope of another client
map-roles-composite-authz-client-scope-description=Policies that decide if an admin can apply roles defined by this client as a composite to another role
map-role-authz-role-scope-description=Policies that decide if an admin can map role this role to a user or group
map-role-client-scope-authz-role-scope-description=Policies that decide if an admin can apply this role to the client scope of a client
map-role-composite-authz-role-scope-description=Policies that decide if an admin can apply this role as a composite to another role

View file

@ -424,6 +424,30 @@ module.config(['$routeProvider', function ($routeProvider) {
},
controller : 'UsersPermissionsCtrl'
})
.when('/realms/:realm/clients/:client/permissions', {
templateUrl : resourceUrl + '/partials/authz/mgmt/client-permissions.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
client : function(ClientLoader) {
return ClientLoader();
}
},
controller : 'ClientPermissionsCtrl'
})
.when('/realms/:realm/groups/:group/permissions', {
templateUrl : resourceUrl + '/partials/authz/mgmt/group-permissions.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
group : function(GroupLoader) {
return GroupLoader();
}
},
controller : 'GroupPermissionsCtrl'
})
;
}]);

View file

@ -2419,3 +2419,35 @@ module.controller('UsersPermissionsCtrl', function($scope, $http, $route, $locat
});
module.controller('ClientPermissionsCtrl', function($scope, $http, $route, $location, realm, client, ClientManagementPermissions, Notifications) {
$scope.client = client;
$scope.realm = realm;
ClientManagementPermissions.get({realm: realm.realm, client: client.id}, function(data) {
$scope.permissions = data;
});
$scope.setEnabled = function() {
var param = { enabled: $scope.permissions.enabled};
$scope.permissions = ClientManagementPermissions.update({realm: realm.realm, client: client.id}, param);
};
});
module.controller('GroupPermissionsCtrl', function($scope, $http, $route, $location, realm, group, GroupManagementPermissions, Client, Notifications) {
$scope.group = group;
$scope.realm = realm;
Client.query({realm: realm.realm, clientId: getManageClientId(realm)}, function(data) {
$scope.realmManagementClientId = data[0].id;
});
GroupManagementPermissions.get({realm: realm.realm, group: group.id}, function(data) {
$scope.permissions = data;
});
$scope.setEnabled = function() {
var param = { enabled: $scope.permissions.enabled};
$scope.permissions = GroupManagementPermissions.update({realm: realm.realm, group: group.id}, param);
};
});

View file

@ -144,4 +144,50 @@ module.service('AuthzDialog', function($modal) {
}
return dialog;
});
});
module.factory('RoleManagementPermissions', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/roles-by-id/:role/management/permissions', {
realm : '@realm',
role : '@role'
}, {
update: {
method: 'PUT'
}
});
});
module.factory('UsersManagementPermissions', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/users-management-permissions', {
realm : '@realm'
}, {
update: {
method: 'PUT'
}
});
});
module.factory('ClientManagementPermissions', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients/:client/management/permissions', {
realm : '@realm',
client : '@client'
}, {
update: {
method: 'PUT'
}
});
});
module.factory('GroupManagementPermissions', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/groups/:group/management/permissions', {
realm : '@realm',
group : '@group'
}, {
update: {
method: 'PUT'
}
});
});

View file

@ -842,28 +842,6 @@ module.factory('Role', function($resource) {
});
});
module.factory('RoleManagementPermissions', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/roles-by-id/:role/management/permissions', {
realm : '@realm',
role : '@role'
}, {
update: {
method: 'PUT'
}
});
});
module.factory('UsersManagementPermissions', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/users-management-permissions', {
realm : '@realm'
}, {
update: {
method: 'PUT'
}
});
});
module.factory('RoleById', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/roles-by-id/:role', {
realm : '@realm',

View file

@ -0,0 +1,39 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li>{{client.clientId}}</li>
</ol>
<kc-tabs-client></kc-tabs-client>
<form class=form-horizontal" name="enableForm" novalidate kc-read-only="!access.manageAuthorization">
<fieldset class="border-top">
<div class="form-group">
<label class="col-md-2 control-label" for="permissionsEnabled">{{:: 'permissions-enabled-role' | translate}}</label>
<div class="col-md-6">
<input ng-model="permissions.enabled" ng-click="setEnabled()" name="permissionsEnabled" id="permissionsEnabled" ng-disabled="!access.manageAuthorization" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
<kc-tooltip>{{:: 'permissions-enabled-role.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
</form>
<table class="datatable table table-striped table-bordered dataTable no-footer" data-ng-show="permissions.enabled">
<thead>
<tr>
<th>{{:: 'scope-name' | translate}}</th>
<th>{{:: 'description' | translate}}</th>
<th colspan="2">{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="(scopeName, scopeId) in permissions.scopePermissions">
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/scope/{{scopeId}}">{{scopeName}}</a></td>
<td translate="{{scopeName}}-authz-client-scope-description"></td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/scope/{{scopeId}}">{{:: 'edit' | translate}}</td>
</tr>
</tbody>
</table>
</div>
<kc-menu></kc-menu>

View file

@ -2,7 +2,6 @@
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles">{{:: 'roles' | translate}}</a></li>
<li>{{role.name}}</li>
</ol>
@ -30,7 +29,7 @@
<tbody>
<tr ng-repeat="(scopeName, scopeId) in permissions.scopePermissions">
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/scope/{{scopeId}}">{{scopeName}}</a></td>
<td translate="{{scopeName}}-mgmt-scope-description"></td>
<td translate="{{scopeName}}-authz-role-scope-description"></td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/scope/{{scopeId}}">{{:: 'edit' | translate}}</td>
</tr>
</tbody>

View file

@ -0,0 +1,39 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}/groups">{{:: 'groups' | translate}}</a></li>
<li>{{group.name}}</li>
</ol>
<kc-tabs-group></kc-tabs-group>
<form class=form-horizontal" name="enableForm" novalidate kc-read-only="!access.manageAuthorization">
<fieldset class="border-top">
<div class="form-group">
<label class="col-md-2 control-label" for="permissionsEnabled">{{:: 'permissions-enabled-role' | translate}}</label>
<div class="col-md-6">
<input ng-model="permissions.enabled" ng-click="setEnabled()" name="permissionsEnabled" id="permissionsEnabled" ng-disabled="!access.manageAuthorization" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
<kc-tooltip>{{:: 'permissions-enabled-role.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
</form>
<table class="datatable table table-striped table-bordered dataTable no-footer" data-ng-show="permissions.enabled">
<thead>
<tr>
<th>{{:: 'scope-name' | translate}}</th>
<th>{{:: 'description' | translate}}</th>
<th colspan="2">{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="(scopeName, scopeId) in permissions.scopePermissions">
<td><a href="#/realms/{{realm.realm}}/clients/{{realmManagementClientId}}/authz/resource-server/permission/scope/{{scopeId}}">{{scopeName}}</a></td>
<td translate="{{scopeName}}-authz-group-scope-description"></td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{realmManagementClientId}}/authz/resource-server/permission/scope/{{scopeId}}">{{:: 'edit' | translate}}</td>
</tr>
</tbody>
</table>
</div>
<kc-menu></kc-menu>

View file

@ -28,7 +28,7 @@
<tbody>
<tr ng-repeat="(scopeName, scopeId) in permissions.scopePermissions">
<td><a href="#/realms/{{realm.realm}}/clients/{{realmManagementClientId}}/authz/resource-server/permission/scope/{{scopeId}}">{{scopeName}}</a></td>
<td translate="{{scopeName}}-mgmt-scope-description"></td>
<td translate="{{scopeName}}-authz-role-scope-description"></td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{realmManagementClientId}}/authz/resource-server/permission/scope/{{scopeId}}">{{:: 'edit' | translate}}</td>
</tr>
</tbody>

View file

@ -24,7 +24,7 @@
<tbody>
<tr ng-repeat="(scopeName, scopeId) in permissions.scopePermissions">
<td><a href="#/realms/{{realm.realm}}/clients/{{realmManagementClientId}}/authz/resource-server/permission/scope/{{scopeId}}">{{scopeName}}</a></td>
<td translate="{{scopeName}}-mgmt-scope-description"></td>
<td translate="{{scopeName}}-authz-users-scope-description"></td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{realmManagementClientId}}/authz/resource-server/permission/scope/{{scopeId}}">{{:: 'edit' | translate}}</td>
</tr>
</tbody>

View file

@ -43,5 +43,9 @@
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/service-account-roles">{{:: 'service-account-roles' | translate}}</a>
<kc-tooltip>{{:: 'service-account-roles.tooltip' | translate}}</kc-tooltip>
</li>
<li ng-class="{active: path[4] == 'permissions'}">
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/permissions">{{:: 'authz-permissions' | translate}}</a>
<kc-tooltip>{{:: 'manage-permissions-client.tooltip' | translate}}</kc-tooltip>
</li>
</ul>
</div>

View file

@ -9,5 +9,9 @@
<li ng-class="{active: path[4] == 'attributes'}"><a href="#/realms/{{realm.realm}}/groups/{{group.id}}/attributes">{{:: 'attributes' | translate}}</a></li>
<li ng-class="{active: path[4] == 'role-mappings'}" ><a href="#/realms/{{realm.realm}}/groups/{{group.id}}/role-mappings">{{:: 'role-mappings' | translate}}</a></li>
<li ng-class="{active: path[4] == 'members'}"><a href="#/realms/{{realm.realm}}/groups/{{group.id}}/members">{{:: 'members' | translate}}</a></li>
<li ng-class="{active: path[4] == 'permissions'}">
<a href="#/realms/{{realm.realm}}/groups/{{group.id}}/permissions">{{:: 'authz-permissions' | translate}}</a>
<kc-tooltip>{{:: 'manage-permissions-group.tooltip' | translate}}</kc-tooltip>
</li>
</ul>
</div>