client permission tests

This commit is contained in:
Bill Burke 2017-06-02 15:49:20 -04:00
parent b9f7a43a72
commit a41d282e92
9 changed files with 278 additions and 30 deletions

View file

@ -203,11 +203,12 @@ public class ClientsResource {
*/
@Path("{id}")
public ClientResource getClient(final @PathParam("id") String id) {
auth.clients().requireList();
ClientModel clientModel = realm.getClientById(id);
if (clientModel == null) {
throw new NotFoundException("Could not find client");
// we do this to make sure somebody can't phish ids
if (!auth.clients().canList()) throw new NotFoundException("Could not find client");
else throw new ForbiddenException();
}
session.getContext().setClient(clientModel);

View file

@ -192,11 +192,11 @@ public class UsersResource {
*/
@Path("{id}")
public UserResource user(final @PathParam("id") String id) {
auth.users().requireQuery();
UserModel user = session.users().getUserById(id, realm);
if (user == null) {
throw new NotFoundException("User not found");
// we do this to make sure somebody can't phish ids
if (auth.users().canQuery()) throw new NotFoundException("User not found");
else throw new ForbiddenException();
}
UserResource resource = new UserResource(realm, user, auth, adminEvent);
ResteasyProviderFactory.getInstance().injectProperties(resource);

View file

@ -67,4 +67,10 @@ public interface ClientPermissionEvaluator {
boolean canView(ClientTemplateModel template);
void requireView(ClientTemplateModel template);
boolean canMapRoles(ClientModel client);
boolean canMapCompositeRoles(ClientModel client);
boolean canMapClientScopeRoles(ClientModel client);
}

View file

@ -16,6 +16,8 @@
*/
package org.keycloak.services.resources.admin.permissions;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.models.ClientModel;
/**
@ -23,7 +25,13 @@ import org.keycloak.models.ClientModel;
* @version $Revision: 1 $
*/
public interface ClientPermissionManagement {
public static final String MAP_ROLES_SCOPE = "map-roles";
boolean isPermissionsEnabled(ClientModel client);
void setPermissionsEnabled(ClientModel client, boolean enable);
Policy mapRolesPermission(ClientModel client);
ResourceServer resourceServer(ClientModel client);
}

View file

@ -55,7 +55,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
}
private String getResourceName(ClientModel client) {
return "group.resource." + client.getId();
return "client.resource." + client.getId();
}
private String getManagePermissionName(ClientModel client) {
@ -64,25 +64,50 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
private String getViewPermissionName(ClientModel client) {
return "view.permission.client." + client.getId();
}
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 getMapRoleCompositePermissionName(ClientModel client) {
return RolePermissionManagement.MAP_ROLE_COMPOSITE_SCOPE + ".permission.client." + client.getId();
}
private void initialize(ClientModel client) {
ResourceServer server = root.findOrCreateResourceServer(client);
Scope manageScope = manageScope(server);
if (manageScope == null) {
authz.getStoreFactory().getScopeStore().create(AdminPermissionManagement.MANAGE_SCOPE, server);
manageScope = authz.getStoreFactory().getScopeStore().create(AdminPermissionManagement.MANAGE_SCOPE, server);
}
Scope viewScope = viewScope(server);
if (manageScope == null) {
authz.getStoreFactory().getScopeStore().create(AdminPermissionManagement.VIEW_SCOPE, server);
if (viewScope == null) {
viewScope = authz.getStoreFactory().getScopeStore().create(AdminPermissionManagement.VIEW_SCOPE, server);
}
Scope mapRoleScope = mapRolesScope(server);
if (mapRoleScope == null) {
mapRoleScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLES_SCOPE, server);
}
Scope mapRoleClientScope = mapRoleClientScope(server);
if (mapRoleClientScope == null) {
mapRoleClientScope = authz.getStoreFactory().getScopeStore().create(RolePermissionManagement.MAP_ROLE_CLIENT_SCOPE_SCOPE, server);
}
Scope mapRoleCompositeScope = mapRoleCompositeScope(server);
if (mapRoleCompositeScope == null) {
mapRoleCompositeScope = authz.getStoreFactory().getScopeStore().create(RolePermissionManagement.MAP_ROLE_COMPOSITE_SCOPE, server);
}
String resourceName = getResourceName(client);
Resource resource = authz.getStoreFactory().getResourceStore().findByName(resourceName, server.getId());
if (resource == null) {
resource = authz.getStoreFactory().getResourceStore().create(resourceName, server, server.getClientId());
resource.setType("Client");
Set<Scope> scopeset = new HashSet<>();
scopeset.add(manageScope);
scopeset.add(viewScope);
scopeset.add(mapRoleScope);
scopeset.add(mapRoleClientScope);
scopeset.add(mapRoleCompositeScope);
resource.updateScopes(scopeset);
}
String managePermissionName = getManagePermissionName(client);
@ -99,26 +124,46 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
Policy viewClientsPolicy = root.roles().rolePolicy(server, role);
Helper.addScopePermission(authz, server, viewPermissionName, resource, viewScope, viewClientsPolicy);
}
String mapRolePermissionName = getMapRolesPermissionName(client);
Policy mapRolePermission = authz.getStoreFactory().getPolicyStore().findByName(mapRolePermissionName, server.getId());
if (mapRolePermission == null) {
Helper.addEmptyScopePermission(authz, server, mapRolePermissionName, resource, mapRoleScope);
}
String mapRoleClientScopePermissionName = getMapRoleClientScopePermissionName(client);
Policy mapRoleClientScopePermission = authz.getStoreFactory().getPolicyStore().findByName(mapRoleClientScopePermissionName, server.getId());
if (mapRoleClientScopePermission == null) {
Helper.addEmptyScopePermission(authz, server, mapRoleClientScopePermissionName, resource, mapRoleClientScope);
}
String mapRoleCompositePermissionName = getMapRoleCompositePermissionName(client);
Policy mapRoleCompositePermission = authz.getStoreFactory().getPolicyStore().findByName(mapRoleCompositePermissionName, server.getId());
if (mapRoleCompositePermission == null) {
Helper.addEmptyScopePermission(authz, server, mapRoleCompositePermissionName, resource, mapRoleCompositeScope);
}
}
private void deletePolicy(String name, ClientModel client, ResourceServer server) {
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getViewPermissionName(client), server.getId());
if (policy != null) {
authz.getStoreFactory().getPolicyStore().delete(policy.getId());
}
}
private void deletePermissions(ClientModel client) {
ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId());
ResourceServer server = resourceServer(client);
if (server == null) return;
Policy managePermission = authz.getStoreFactory().getPolicyStore().findByName(getManagePermissionName(client), server.getId());
if (managePermission != null) {
authz.getStoreFactory().getPolicyStore().delete(managePermission.getId());
}
Policy viewPermission = authz.getStoreFactory().getPolicyStore().findByName(getViewPermissionName(client), server.getId());
if (viewPermission != null) {
authz.getStoreFactory().getPolicyStore().delete(viewPermission.getId());
}
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);
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());;
if (resource != null) authz.getStoreFactory().getResourceStore().delete(resource.getId());
}
@Override
public boolean isPermissionsEnabled(ClientModel client) {
ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId());
ResourceServer server = resourceServer(client);
if (server == null) return false;
return authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId()) != null;
@ -142,6 +187,15 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
private Scope viewScope(ResourceServer server) {
return authz.getStoreFactory().getScopeStore().findByName(AdminPermissionManagement.VIEW_SCOPE, server.getId());
}
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() {
@ -202,7 +256,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
return canManage();
}
ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId());
ResourceServer server = resourceServer(client);
if (server == null) return canManage();
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());
@ -236,7 +290,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
return canView();
}
ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId());
ResourceServer server = resourceServer(client);
if (server == null) return canView();
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());
@ -312,4 +366,85 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionMa
throw new ForbiddenException();
}
}
@Override
public boolean canMapRoles(ClientModel client) {
ResourceServer server = resourceServer(client);
if (server == null) return false;
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());
if (resource == null) return false;
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRolesPermissionName(client), server.getId());
if (policy == null) {
return false;
}
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
// if no policies attached to permission then just do default behavior
if (associatedPolicies == null || associatedPolicies.isEmpty()) {
return false;
}
Scope scope = mapRolesScope(server);
return root.evaluatePermission(resource, scope, server);
}
@Override
public Policy mapRolesPermission(ClientModel client) {
ResourceServer server = resourceServer(client);
if (server == null) return null;
return authz.getStoreFactory().getPolicyStore().findByName(getMapRolesPermissionName(client), server.getId());
}
@Override
public ResourceServer resourceServer(ClientModel client) {
return authz.getStoreFactory().getResourceServerStore().findByClient(client.getId());
}
@Override
public boolean canMapCompositeRoles(ClientModel client) {
ResourceServer server = resourceServer(client);
if (server == null) return false;
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());
if (policy == null) {
return false;
}
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
// if no policies attached to permission then just do default behavior
if (associatedPolicies == null || associatedPolicies.isEmpty()) {
return false;
}
Scope scope = mapRoleCompositeScope(server);
return root.evaluatePermission(resource, scope, server);
}
@Override
public boolean canMapClientScopeRoles(ClientModel client) {
ResourceServer server = resourceServer(client);
if (server == null) return false;
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());
if (policy == null) {
return false;
}
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
// if no policies attached to permission then just do default behavior
if (associatedPolicies == null || associatedPolicies.isEmpty()) {
return false;
}
Scope scope = mapRoleClientScope(server);
return root.evaluatePermission(resource, scope, server);
}
}

View file

@ -143,6 +143,9 @@ class RolePermissions implements RolePermissionEvaluator, RolePermissionManageme
if (!root.isAdminSameRealm()) {
return root.users().canManage();
}
if (role.getContainer() instanceof ClientModel) {
if (root.clients().canMapRoles((ClientModel)role.getContainer())) return true;
}
if (!isPermissionsEnabled(role)){
return root.users().canManage();
}
@ -216,6 +219,9 @@ class RolePermissions implements RolePermissionEvaluator, RolePermissionManageme
if (!root.isAdminSameRealm()) {
return canManage(role);
}
if (role.getContainer() instanceof ClientModel) {
if (root.clients().canMapCompositeRoles((ClientModel)role.getContainer())) return true;
}
if (!isPermissionsEnabled(role)){
return canManage(role);
}
@ -245,6 +251,9 @@ class RolePermissions implements RolePermissionEvaluator, RolePermissionManageme
if (!root.isAdminSameRealm()) {
return root.clients().canManage();
}
if (role.getContainer() instanceof ClientModel) {
if (root.clients().canMapClientScopeRoles((ClientModel)role.getContainer())) return true;
}
if (!isPermissionsEnabled(role)){
return root.clients().canManage();
}

View file

@ -34,6 +34,10 @@ public interface UserPermissionEvaluator {
void requireQuery();
boolean canQuery(UserModel user);
void requireQuery(UserModel user);
boolean canView();
boolean canView(UserModel user);
void requireView(UserModel user);

View file

@ -291,6 +291,19 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
}
}
@Override
public boolean canQuery(UserModel user) {
return canView(user);
}
@Override
public void requireQuery(UserModel user) {
if (!canQuery(user)) {
throw new ForbiddenException();
}
}
/**

View file

@ -114,6 +114,9 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
{
permissions.groups().setPermissionsEnabled(group, true);
}
{
permissions.clients().setPermissionsEnabled(client1, true);
}
}
@ -156,12 +159,6 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
UserModel user1 = session.users().addUser(realm, "user1");
user1.setEnabled(true);
UserModel user2 = session.users().addUser(realm, "user2");
user2.setEnabled(true);
UserModel user3 = session.users().addUser(realm, "user3");
user3.setEnabled(true);
UserModel user4 = session.users().addUser(realm, "user4");
user4.setEnabled(true);
// group management
AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
@ -188,6 +185,17 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
Policy groupManagerPermission = permissions.groups().manageMembersPermission(group);
groupManagerPermission.addAssociatedPolicy(groupManagerPolicy);
UserModel clientMapper = session.users().addUser(realm, "clientMapper");
clientMapper.setEnabled(true);
clientMapper.grantRole(managerRole);
session.userCredentialManager().updateCredential(realm, clientMapper, UserCredentialModel.password("password"));
Policy clientMapperPolicy = permissions.clients().mapRolesPermission(client);
UserPolicyRepresentation userRep = new UserPolicyRepresentation();
userRep.setName("userClientMapper");
userRep.addUser("clientMapper");
Policy userPolicy = permissions.authz().getStoreFactory().getPolicyStore().create(userRep, permissions.clients().resourceServer(client));
clientMapperPolicy.addAssociatedPolicy(userPolicy);
@ -254,6 +262,17 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
Assert.assertFalse(permissionsForAdmin.roles().canMapRole(realmRole2));
}
// test client.mapRoles
{
UserModel admin = session.users().getUserByUsername("clientMapper", realm);
AdminPermissionEvaluator permissionsForAdmin = AdminPermissions.evaluator(session, realm, realm, admin);
UserModel user = session.users().getUserByUsername("authorized", realm);
Assert.assertTrue(permissionsForAdmin.users().canManage(user));
Assert.assertFalse(permissionsForAdmin.roles().canMapRole(realmRole));
Assert.assertTrue(permissionsForAdmin.roles().canMapRole(clientRole));
Assert.assertFalse(permissionsForAdmin.roles().canMapRole(realmRole2));
}
}
@ -283,9 +302,7 @@ 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);
UserRepresentation groupMember = adminClient.realm(TEST).users().search("groupMember").get(0);
RoleRepresentation realmRole = adminClient.realm(TEST).roles().get("realm-role").toRepresentation();
List<RoleRepresentation> realmRoleSet = new LinkedList<>();
realmRoleSet.add(realmRole);
@ -373,6 +390,61 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
}
}
{
Keycloak realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
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();
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);
roles = realmClient.realm(TEST).users().get(user1.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);
try {
realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().add(realmRole2Set);
Assert.fail("should fail with forbidden exception");
} catch (ClientErrorException e) {
Assert.assertEquals(e.getResponse().getStatus(), 403);
}
try {
realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().add(realmRoleSet);
Assert.fail("should fail with forbidden exception");
} catch (ClientErrorException e) {
Assert.assertEquals(e.getResponse().getStatus(), 403);
}
}
// test client.mapRoles
{
Keycloak realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
TEST, "clientMapper", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
List<RoleRepresentation> roles = null;
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) -> {
return r.getName().equals("client-role");
}));
roles = realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().listAvailable();
Assert.assertTrue(roles.isEmpty());
try {
realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().add(realmRoleSet);
Assert.fail("should fail with forbidden exception");
} catch (ClientErrorException e) {
Assert.assertEquals(e.getResponse().getStatus(), 403);
}
}
}
@Test