keycloak-uncached

Details

diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/UMAPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/UMAPolicyProviderFactory.java
index 9b0ddec..4fe6e89 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/UMAPolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/UMAPolicyProviderFactory.java
@@ -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.RoleDefinition;
 import org.keycloak.representations.idm.authorization.UmaPermissionRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
 
 /**
  * @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();
 
         if (condition != null) {
@@ -184,6 +193,24 @@ public class UMAPolicyProviderFactory implements PolicyProviderFactory<UmaPermis
                 } else {
                     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();
 
         if (condition != null) {
@@ -300,6 +345,12 @@ public class UMAPolicyProviderFactory implements PolicyProviderFactory<UmaPermis
                 for (String client : rep.getClients()) {
                     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);
     }
+
+    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);
+    }
 }
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/UmaPermissionRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/UmaPermissionRepresentation.java
index a7bccea..ee9a78c 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/UmaPermissionRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/UmaPermissionRepresentation.java
@@ -26,11 +26,10 @@ import java.util.Set;
  */
 public class UmaPermissionRepresentation extends AbstractPolicyRepresentation {
     
-    private String id;
-    private String description;
     private Set<String> roles;
     private Set<String> groups;
     private Set<String> clients;
+    private Set<String> users;
     private String condition;
 
     @Override
@@ -38,22 +37,6 @@ public class UmaPermissionRepresentation extends AbstractPolicyRepresentation {
         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) {
         this.roles = roles;
     }
@@ -124,6 +107,27 @@ public class UmaPermissionRepresentation extends AbstractPolicyRepresentation {
         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) {
         this.condition = condition;
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UserManagedPermissionServiceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UserManagedPermissionServiceTest.java
index 4abe759..1c886b0 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UserManagedPermissionServiceTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UserManagedPermissionServiceTest.java
@@ -111,6 +111,7 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
         newPermission.addGroup("/group_a", "/group_a/group_b", "/group_c");
         newPermission.addClient("client-a", "resource-server-test");
         newPermission.setCondition("$evaluation.grant()");
+        newPermission.addUser("kolo");
 
         ProtectionResource protection = getAuthzClient().protection("marta", "password");
 
@@ -118,11 +119,17 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
 
         assertEquals(newPermission.getName(), permission.getName());
         assertEquals(newPermission.getDescription(), permission.getDescription());
+        assertNotNull(permission.getScopes());
         assertTrue(permission.getScopes().containsAll(newPermission.getScopes()));
+        assertNotNull(permission.getRoles());
         assertTrue(permission.getRoles().containsAll(newPermission.getRoles()));
+        assertNotNull(permission.getGroups());
         assertTrue(permission.getGroups().containsAll(newPermission.getGroups()));
+        assertNotNull(permission.getClients());
         assertTrue(permission.getClients().containsAll(newPermission.getClients()));
         assertEquals(newPermission.getCondition(), permission.getCondition());
+        assertNotNull(permission.getUsers());
+        assertTrue(permission.getUsers().containsAll(newPermission.getUsers()));
     }
 
     @Test
@@ -233,6 +240,38 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
 
         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);
 
         protection.policy(resource.getId()).update(permission);
@@ -308,14 +347,14 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
         protection.policy(resource.getId()).update(permission);
 
         try {
-            authzResponse = authorization.authorize(request);
+            authorization.authorize(request);
             fail("User should not have permission");
         } catch (Exception e) {
             assertTrue(AuthorizationDeniedException.class.isInstance(e));
         }
 
         try {
-            authzResponse = getAuthzClient().authorization("alice", "password").authorize(request);
+            getAuthzClient().authorization("alice", "password").authorize(request);
             fail("User should not have permission");
         } catch (Exception e) {
             assertTrue(AuthorizationDeniedException.class.isInstance(e));
@@ -332,7 +371,7 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
         protection.policy(resource.getId()).delete(permission.getId());
 
         try {
-            authzResponse = authorization.authorize(request);
+            authorization.authorize(request);
             fail("User should not have permission");
         } catch (Exception e) {
             assertTrue(AuthorizationDeniedException.class.isInstance(e));
@@ -344,6 +383,27 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
         } catch (Exception e) {
             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
@@ -395,13 +455,13 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
 
         permission = protection.policy(resource.getId()).create(permission);
 
-        authzResponse = getAuthzClient().authorization("kolo", "password").authorize(request);
+        getAuthzClient().authorization("kolo", "password").authorize(request);
 
         ticket.setGranted(false);
 
         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());
 
@@ -495,7 +555,7 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
             getAuthzClient().protection("alice", "password").policy(resource.getId()).create(new UmaPermissionRepresentation());
             fail("Error expected");
         } 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"));
         }
     }