[KEYCLOAK-7885] Add user policy support to the policy API
This commit is contained in:
parent
1912a8acf4
commit
0b95cdacb8
3 changed files with 152 additions and 24 deletions
|
@ -45,6 +45,7 @@ import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
|
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation.RoleDefinition;
|
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation.RoleDefinition;
|
||||||
import org.keycloak.representations.idm.authorization.UmaPermissionRepresentation;
|
import org.keycloak.representations.idm.authorization.UmaPermissionRepresentation;
|
||||||
|
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
@ -106,6 +107,14 @@ public class UMAPolicyProviderFactory implements PolicyProviderFactory<UmaPermis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Set<String> users = representation.getUsers();
|
||||||
|
|
||||||
|
if (users != null) {
|
||||||
|
for (String user : users) {
|
||||||
|
createUserPolicy(policy, policyStore, user, representation.getOwner());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String condition = representation.getCondition();
|
String condition = representation.getCondition();
|
||||||
|
|
||||||
if (condition != null) {
|
if (condition != null) {
|
||||||
|
@ -184,6 +193,24 @@ public class UMAPolicyProviderFactory implements PolicyProviderFactory<UmaPermis
|
||||||
} else {
|
} else {
|
||||||
RepresentationToModel.toModel(rep, authorization, associatedPolicy);
|
RepresentationToModel.toModel(rep, authorization, associatedPolicy);
|
||||||
}
|
}
|
||||||
|
} else if ("user".equals(associatedRep.getType())) {
|
||||||
|
UserPolicyRepresentation rep = UserPolicyRepresentation.class.cast(associatedRep);
|
||||||
|
|
||||||
|
rep.setUsers(new HashSet<>());
|
||||||
|
|
||||||
|
Set<String> updatedUsers = representation.getUsers();
|
||||||
|
|
||||||
|
if (updatedUsers != null) {
|
||||||
|
for (String user : updatedUsers) {
|
||||||
|
rep.addUser(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rep.getUsers().isEmpty()) {
|
||||||
|
policyStore.delete(associatedPolicy.getId());
|
||||||
|
} else {
|
||||||
|
RepresentationToModel.toModel(rep, authorization, associatedPolicy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,6 +268,24 @@ public class UMAPolicyProviderFactory implements PolicyProviderFactory<UmaPermis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Set<String> updatedUsers = representation.getUsers();
|
||||||
|
|
||||||
|
if (updatedUsers != null) {
|
||||||
|
boolean createPolicy = true;
|
||||||
|
|
||||||
|
for (Policy associatedPolicy : associatedPolicies) {
|
||||||
|
if ("user".equals(associatedPolicy.getType())) {
|
||||||
|
createPolicy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (createPolicy) {
|
||||||
|
for (String user : updatedUsers) {
|
||||||
|
createUserPolicy(policy, policyStore, user, policy.getOwner());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String condition = representation.getCondition();
|
String condition = representation.getCondition();
|
||||||
|
|
||||||
if (condition != null) {
|
if (condition != null) {
|
||||||
|
@ -300,6 +345,12 @@ public class UMAPolicyProviderFactory implements PolicyProviderFactory<UmaPermis
|
||||||
for (String client : rep.getClients()) {
|
for (String client : rep.getClients()) {
|
||||||
representation.addClient(realm.getClientById(client).getClientId());
|
representation.addClient(realm.getClientById(client).getClientId());
|
||||||
}
|
}
|
||||||
|
} else if ("user".equals(associatedPolicy.getType())) {
|
||||||
|
UserPolicyRepresentation rep = UserPolicyRepresentation.class.cast(associatedRep);
|
||||||
|
|
||||||
|
for (String user : rep.getUsers()) {
|
||||||
|
representation.addUser(authorization.getKeycloakSession().users().getUserById(user, realm).getUsername());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,4 +442,17 @@ public class UMAPolicyProviderFactory implements PolicyProviderFactory<UmaPermis
|
||||||
|
|
||||||
policy.addAssociatedPolicy(associatedPolicy);
|
policy.addAssociatedPolicy(associatedPolicy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createUserPolicy(Policy policy, PolicyStore policyStore, String user, String owner) {
|
||||||
|
UserPolicyRepresentation rep = new UserPolicyRepresentation();
|
||||||
|
|
||||||
|
rep.setName(KeycloakModelUtils.generateId());
|
||||||
|
rep.addUser(user);
|
||||||
|
|
||||||
|
Policy associatedPolicy = policyStore.create(rep, policy.getResourceServer());
|
||||||
|
|
||||||
|
associatedPolicy.setOwner(owner);
|
||||||
|
|
||||||
|
policy.addAssociatedPolicy(associatedPolicy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,11 +26,10 @@ import java.util.Set;
|
||||||
*/
|
*/
|
||||||
public class UmaPermissionRepresentation extends AbstractPolicyRepresentation {
|
public class UmaPermissionRepresentation extends AbstractPolicyRepresentation {
|
||||||
|
|
||||||
private String id;
|
|
||||||
private String description;
|
|
||||||
private Set<String> roles;
|
private Set<String> roles;
|
||||||
private Set<String> groups;
|
private Set<String> groups;
|
||||||
private Set<String> clients;
|
private Set<String> clients;
|
||||||
|
private Set<String> users;
|
||||||
private String condition;
|
private String condition;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -38,22 +37,6 @@ public class UmaPermissionRepresentation extends AbstractPolicyRepresentation {
|
||||||
return "uma";
|
return "uma";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setId(String id){
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getId(){
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDescription() {
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDescription(String description) {
|
|
||||||
this.description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRoles(Set<String> roles) {
|
public void setRoles(Set<String> roles) {
|
||||||
this.roles = roles;
|
this.roles = roles;
|
||||||
}
|
}
|
||||||
|
@ -124,6 +107,27 @@ public class UmaPermissionRepresentation extends AbstractPolicyRepresentation {
|
||||||
return clients;
|
return clients;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUsers(Set<String> users) {
|
||||||
|
this.users = users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addUser(String... user) {
|
||||||
|
if (this.users == null) {
|
||||||
|
this.users = new HashSet<>();
|
||||||
|
}
|
||||||
|
this.users.addAll(Arrays.asList(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeUser(String user) {
|
||||||
|
if (this.users != null) {
|
||||||
|
this.users.remove(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getUsers() {
|
||||||
|
return this.users;
|
||||||
|
}
|
||||||
|
|
||||||
public void setCondition(String condition) {
|
public void setCondition(String condition) {
|
||||||
this.condition = condition;
|
this.condition = condition;
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,6 +111,7 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
|
||||||
newPermission.addGroup("/group_a", "/group_a/group_b", "/group_c");
|
newPermission.addGroup("/group_a", "/group_a/group_b", "/group_c");
|
||||||
newPermission.addClient("client-a", "resource-server-test");
|
newPermission.addClient("client-a", "resource-server-test");
|
||||||
newPermission.setCondition("$evaluation.grant()");
|
newPermission.setCondition("$evaluation.grant()");
|
||||||
|
newPermission.addUser("kolo");
|
||||||
|
|
||||||
ProtectionResource protection = getAuthzClient().protection("marta", "password");
|
ProtectionResource protection = getAuthzClient().protection("marta", "password");
|
||||||
|
|
||||||
|
@ -118,11 +119,17 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
|
||||||
|
|
||||||
assertEquals(newPermission.getName(), permission.getName());
|
assertEquals(newPermission.getName(), permission.getName());
|
||||||
assertEquals(newPermission.getDescription(), permission.getDescription());
|
assertEquals(newPermission.getDescription(), permission.getDescription());
|
||||||
|
assertNotNull(permission.getScopes());
|
||||||
assertTrue(permission.getScopes().containsAll(newPermission.getScopes()));
|
assertTrue(permission.getScopes().containsAll(newPermission.getScopes()));
|
||||||
|
assertNotNull(permission.getRoles());
|
||||||
assertTrue(permission.getRoles().containsAll(newPermission.getRoles()));
|
assertTrue(permission.getRoles().containsAll(newPermission.getRoles()));
|
||||||
|
assertNotNull(permission.getGroups());
|
||||||
assertTrue(permission.getGroups().containsAll(newPermission.getGroups()));
|
assertTrue(permission.getGroups().containsAll(newPermission.getGroups()));
|
||||||
|
assertNotNull(permission.getClients());
|
||||||
assertTrue(permission.getClients().containsAll(newPermission.getClients()));
|
assertTrue(permission.getClients().containsAll(newPermission.getClients()));
|
||||||
assertEquals(newPermission.getCondition(), permission.getCondition());
|
assertEquals(newPermission.getCondition(), permission.getCondition());
|
||||||
|
assertNotNull(permission.getUsers());
|
||||||
|
assertTrue(permission.getUsers().containsAll(newPermission.getUsers()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -233,6 +240,38 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
|
||||||
|
|
||||||
assertEquals(permission.getCondition(), updated.getCondition());
|
assertEquals(permission.getCondition(), updated.getCondition());
|
||||||
|
|
||||||
|
permission.addUser("alice");
|
||||||
|
|
||||||
|
protection.policy(resource.getId()).update(permission);
|
||||||
|
assertEquals(5, getAssociatedPolicies(permission).size());
|
||||||
|
updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||||
|
assertEquals(1, updated.getUsers().size());
|
||||||
|
assertEquals(permission.getUsers(), updated.getUsers());
|
||||||
|
|
||||||
|
permission.addUser("kolo");
|
||||||
|
|
||||||
|
protection.policy(resource.getId()).update(permission);
|
||||||
|
assertEquals(5, getAssociatedPolicies(permission).size());
|
||||||
|
updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||||
|
assertEquals(2, updated.getUsers().size());
|
||||||
|
assertEquals(permission.getUsers(), updated.getUsers());
|
||||||
|
|
||||||
|
permission.removeUser("alice");
|
||||||
|
|
||||||
|
protection.policy(resource.getId()).update(permission);
|
||||||
|
assertEquals(5, getAssociatedPolicies(permission).size());
|
||||||
|
updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||||
|
assertEquals(1, updated.getUsers().size());
|
||||||
|
assertEquals(permission.getUsers(), updated.getUsers());
|
||||||
|
|
||||||
|
permission.setUsers(null);
|
||||||
|
|
||||||
|
protection.policy(resource.getId()).update(permission);
|
||||||
|
assertEquals(4, getAssociatedPolicies(permission).size());
|
||||||
|
updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||||
|
|
||||||
|
assertEquals(permission.getUsers(), updated.getUsers());
|
||||||
|
|
||||||
permission.setCondition(null);
|
permission.setCondition(null);
|
||||||
|
|
||||||
protection.policy(resource.getId()).update(permission);
|
protection.policy(resource.getId()).update(permission);
|
||||||
|
@ -308,14 +347,14 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
|
||||||
protection.policy(resource.getId()).update(permission);
|
protection.policy(resource.getId()).update(permission);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
authzResponse = authorization.authorize(request);
|
authorization.authorize(request);
|
||||||
fail("User should not have permission");
|
fail("User should not have permission");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(AuthorizationDeniedException.class.isInstance(e));
|
assertTrue(AuthorizationDeniedException.class.isInstance(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
authzResponse = getAuthzClient().authorization("alice", "password").authorize(request);
|
getAuthzClient().authorization("alice", "password").authorize(request);
|
||||||
fail("User should not have permission");
|
fail("User should not have permission");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(AuthorizationDeniedException.class.isInstance(e));
|
assertTrue(AuthorizationDeniedException.class.isInstance(e));
|
||||||
|
@ -332,7 +371,7 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
|
||||||
protection.policy(resource.getId()).delete(permission.getId());
|
protection.policy(resource.getId()).delete(permission.getId());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
authzResponse = authorization.authorize(request);
|
authorization.authorize(request);
|
||||||
fail("User should not have permission");
|
fail("User should not have permission");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(AuthorizationDeniedException.class.isInstance(e));
|
assertTrue(AuthorizationDeniedException.class.isInstance(e));
|
||||||
|
@ -344,6 +383,27 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertEquals(404, HttpResponseException.class.cast(e.getCause()).getStatusCode());
|
assertEquals(404, HttpResponseException.class.cast(e.getCause()).getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create a user based permission, where only selected users are allowed access to the resource.
|
||||||
|
permission = new UmaPermissionRepresentation();
|
||||||
|
permission.setName("Custom User-Managed Permission");
|
||||||
|
permission.setDescription("Specific users are allowed access to the resource");
|
||||||
|
permission.addScope("Scope A");
|
||||||
|
permission.addUser("alice");
|
||||||
|
protection.policy(resource.getId()).create(permission);
|
||||||
|
|
||||||
|
// alice should be able to access the resource with the updated permission.
|
||||||
|
authzResponse = getAuthzClient().authorization("alice", "password").authorize(request);
|
||||||
|
assertNotNull(authzResponse);
|
||||||
|
|
||||||
|
// kolo shouldn't be able to access the resource with the updated permission.
|
||||||
|
try {
|
||||||
|
authorization.authorize(request);
|
||||||
|
fail("User should not have permission to access the protected resource");
|
||||||
|
} catch(Exception e) {
|
||||||
|
assertTrue(AuthorizationDeniedException.class.isInstance(e));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -395,13 +455,13 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
|
||||||
|
|
||||||
permission = protection.policy(resource.getId()).create(permission);
|
permission = protection.policy(resource.getId()).create(permission);
|
||||||
|
|
||||||
authzResponse = getAuthzClient().authorization("kolo", "password").authorize(request);
|
getAuthzClient().authorization("kolo", "password").authorize(request);
|
||||||
|
|
||||||
ticket.setGranted(false);
|
ticket.setGranted(false);
|
||||||
|
|
||||||
getAuthzClient().protection().permission().update(ticket);
|
getAuthzClient().protection().permission().update(ticket);
|
||||||
|
|
||||||
authzResponse = getAuthzClient().authorization("kolo", "password").authorize(request);
|
getAuthzClient().authorization("kolo", "password").authorize(request);
|
||||||
|
|
||||||
permission = getAuthzClient().protection("marta", "password").policy(resource.getId()).findById(permission.getId());
|
permission = getAuthzClient().protection("marta", "password").policy(resource.getId()).findById(permission.getId());
|
||||||
|
|
||||||
|
@ -495,7 +555,7 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
|
||||||
getAuthzClient().protection("alice", "password").policy(resource.getId()).create(new UmaPermissionRepresentation());
|
getAuthzClient().protection("alice", "password").policy(resource.getId()).create(new UmaPermissionRepresentation());
|
||||||
fail("Error expected");
|
fail("Error expected");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(HttpResponseException.class.cast(e.getCause()).toString().contains("Only resource onwer can access policies for resource"));
|
assertTrue(HttpResponseException.class.cast(e.getCause()).toString().contains("Only resource owner can access policies for resource"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue