keycloak-uncached

[KEYCLOAK-8281] - Deletion of client with token exchange policy

9/12/2018 2:04:32 PM

Details

diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
index 2dadfc5..73c7c7d 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
@@ -137,7 +137,9 @@ public class JPAPolicyStore implements PolicyStore {
         List<Predicate> predicates = new ArrayList();
         querybuilder.select(root.get("id"));
 
-        predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId));
+        if (resourceServerId != null) {
+            predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId));
+        }
 
         attributes.forEach((name, value) -> {
             if ("permission".equals(name)) {
@@ -156,6 +158,9 @@ public class JPAPolicyStore implements PolicyStore {
                 predicates.add(root.join("resources").get("id").in(value));
             } else if ("scope".equals(name)) {
                 predicates.add(root.join("scopes").get("id").in(value));
+            } else if (name.startsWith("config:")) {
+                predicates.add(root.joinMap("config").key().in(name.substring("config:".length())));
+                predicates.add(builder.like(root.joinMap("config").value().as(String.class), "%" + value[0] + "%"));
             } else {
                 predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
             }
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/ClientApplicationSynchronizer.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/ClientApplicationSynchronizer.java
index d8af293..dfc069e 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/ClientApplicationSynchronizer.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/ClientApplicationSynchronizer.java
@@ -18,13 +18,21 @@
 
 package org.keycloak.authorization.store.syncronization;
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
 import org.keycloak.authorization.store.ResourceServerStore;
 import org.keycloak.authorization.store.StoreFactory;
 import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.RealmModel.ClientRemovedEvent;
 import org.keycloak.provider.ProviderFactory;
+import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -35,16 +43,39 @@ public class ClientApplicationSynchronizer implements Synchronizer<ClientRemoved
     public void synchronize(ClientRemovedEvent event, KeycloakSessionFactory factory) {
         ProviderFactory<AuthorizationProvider> providerFactory = factory.getProviderFactory(AuthorizationProvider.class);
         AuthorizationProvider authorizationProvider = providerFactory.create(event.getKeycloakSession());
+
+        removeFromClientPolicies(event, authorizationProvider);
+    }
+
+    private void removeFromClientPolicies(ClientRemovedEvent event, AuthorizationProvider authorizationProvider) {
         StoreFactory storeFactory = authorizationProvider.getStoreFactory();
         ResourceServerStore store = storeFactory.getResourceServerStore();
         ResourceServer resourceServer = store.findById(event.getClient().getId());
 
         if (resourceServer != null) {
-            String id = resourceServer.getId();
-            //storeFactory.getResourceStore().findByResourceServer(id).forEach(resource -> storeFactory.getResourceStore().delete(resource.getId()));
-            //storeFactory.getScopeStore().findByResourceServer(id).forEach(scope -> storeFactory.getScopeStore().delete(scope.getId()));
-            //storeFactory.getPolicyStore().findByResourceServer(id).forEach(scope -> storeFactory.getPolicyStore().delete(scope.getId()));
-            storeFactory.getResourceServerStore().delete(id);
+            storeFactory.getResourceServerStore().delete(resourceServer.getId());
+        }
+
+        Map<String, String[]> attributes = new HashMap<>();
+
+        attributes.put("type", new String[] {"client"});
+        attributes.put("config:clients", new String[] {event.getClient().getId()});
+
+        List<Policy> search = storeFactory.getPolicyStore().findByResourceServer(attributes, null, -1, -1);
+
+        for (Policy policy : search) {
+            PolicyProviderFactory policyFactory = authorizationProvider.getProviderFactory(policy.getType());
+            ClientPolicyRepresentation representation = ClientPolicyRepresentation.class.cast(policyFactory.toRepresentation(policy, authorizationProvider));
+            Set<String> clients = representation.getClients();
+
+            clients.remove(event.getClient().getId());
+
+            if (clients.isEmpty()) {
+                policyFactory.onRemove(policy, authorizationProvider);
+                authorizationProvider.getStoreFactory().getPolicyStore().delete(policy.getId());
+            } else {
+                policyFactory.onUpdate(policy, representation, authorizationProvider);
+            }
         }
     }
 }
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java
index b760e8d..57b712f 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java
@@ -17,8 +17,15 @@
 
 package org.keycloak.authorization.store.syncronization;
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
 import org.keycloak.authorization.store.PolicyStore;
 import org.keycloak.authorization.store.ResourceServerStore;
 import org.keycloak.authorization.store.ResourceStore;
@@ -28,6 +35,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserModel.UserRemovedEvent;
 import org.keycloak.provider.ProviderFactory;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -38,12 +46,45 @@ public class UserSynchronizer implements Synchronizer<UserRemovedEvent> {
     public void synchronize(UserRemovedEvent event, KeycloakSessionFactory factory) {
         ProviderFactory<AuthorizationProvider> providerFactory = factory.getProviderFactory(AuthorizationProvider.class);
         AuthorizationProvider authorizationProvider = providerFactory.create(event.getKeycloakSession());
+
+        removeUserResources(event, authorizationProvider);
+        removeFromUserPolicies(event, authorizationProvider);
+    }
+
+    private void removeFromUserPolicies(UserRemovedEvent event, AuthorizationProvider authorizationProvider) {
         StoreFactory storeFactory = authorizationProvider.getStoreFactory();
+        PolicyStore policyStore = storeFactory.getPolicyStore();
         UserModel userModel = event.getUser();
-        ResourceStore resourceStore = storeFactory.getResourceStore();
+        Map<String, String[]> attributes = new HashMap<>();
+
+        attributes.put("type", new String[] {"user"});
+        attributes.put("config:users", new String[] {userModel.getId()});
+
+        List<Policy> search = policyStore.findByResourceServer(attributes, null, -1, -1);
+
+        for (Policy policy : search) {
+            PolicyProviderFactory policyFactory = authorizationProvider.getProviderFactory(policy.getType());
+            UserPolicyRepresentation representation = UserPolicyRepresentation.class.cast(policyFactory.toRepresentation(policy, authorizationProvider));
+            Set<String> users = representation.getUsers();
+
+            users.remove(userModel.getId());
+
+            if (users.isEmpty()) {
+                policyFactory.onRemove(policy, authorizationProvider);
+                policyStore.delete(policy.getId());
+            } else {
+                policyFactory.onUpdate(policy, representation, authorizationProvider);
+            }
+        }
+    }
+
+    private void removeUserResources(UserRemovedEvent event, AuthorizationProvider authorizationProvider) {
+        StoreFactory storeFactory = authorizationProvider.getStoreFactory();
         PolicyStore policyStore = storeFactory.getPolicyStore();
+        ResourceStore resourceStore = storeFactory.getResourceStore();
         ResourceServerStore resourceServerStore = storeFactory.getResourceServerStore();
         RealmModel realm = event.getRealm();
+        UserModel userModel = event.getUser();
 
         realm.getClients().forEach(clientModel -> {
             ResourceServer resourceServer = resourceServerStore.findById(clientModel.getId());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ClientPolicyManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ClientPolicyManagementTest.java
index d3613da..0671bfb 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ClientPolicyManagementTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ClientPolicyManagementTest.java
@@ -27,10 +27,12 @@ import java.util.stream.Collectors;
 import javax.ws.rs.NotFoundException;
 import javax.ws.rs.core.Response;
 
+import org.junit.Assert;
 import org.junit.Test;
 import org.keycloak.admin.client.resource.AuthorizationResource;
 import org.keycloak.admin.client.resource.ClientPoliciesResource;
 import org.keycloak.admin.client.resource.ClientPolicyResource;
+import org.keycloak.admin.client.resource.ClientsResource;
 import org.keycloak.admin.client.resource.PolicyResource;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
@@ -50,7 +52,10 @@ public class ClientPolicyManagementTest extends AbstractPolicyManagementTest {
         return super.createTestRealm()
                 .client(ClientBuilder.create().clientId("Client A"))
                 .client(ClientBuilder.create().clientId("Client B"))
-                .client(ClientBuilder.create().clientId("Client C"));
+                .client(ClientBuilder.create().clientId("Client C"))
+                .client(ClientBuilder.create().clientId("Client D"))
+                .client(ClientBuilder.create().clientId("Client E"))
+                .client(ClientBuilder.create().clientId("Client F"));
     }
 
     @Test
@@ -126,6 +131,51 @@ public class ClientPolicyManagementTest extends AbstractPolicyManagementTest {
         }
     }
 
+
+    @Test
+    public void testDeleteClient() {
+        AuthorizationResource authorization = getClient().authorization();
+        ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
+
+        representation.setName("Update Test Client Policy");
+        representation.setDescription("description");
+        representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+        representation.setLogic(Logic.NEGATIVE);
+        representation.addClient("Client D");
+        representation.addClient("Client E");
+        representation.addClient("Client F");
+
+        assertCreated(authorization, representation);
+
+        ClientsResource clients = getRealm().clients();
+        ClientRepresentation client = clients.findByClientId("Client D").get(0);
+
+        clients.get(client.getId()).remove();
+
+        representation = authorization.policies().client().findById(representation.getId()).toRepresentation();
+
+        Assert.assertEquals(2, representation.getClients().size());
+        Assert.assertFalse(representation.getClients().contains(client.getId()));
+
+        client = clients.findByClientId("Client E").get(0);
+        clients.get(client.getId()).remove();
+
+        representation = authorization.policies().client().findById(representation.getId()).toRepresentation();
+
+        Assert.assertEquals(1, representation.getClients().size());
+        Assert.assertFalse(representation.getClients().contains(client.getId()));
+
+        client = clients.findByClientId("Client F").get(0);
+        clients.get(client.getId()).remove();
+
+        try {
+            authorization.policies().client().findById(representation.getId()).toRepresentation();
+            fail("Client policy should be removed");
+        } catch (NotFoundException nfe) {
+            // ignore
+        }
+    }
+
     @Test
     public void testGenericConfig() {
         AuthorizationResource authorization = getClient().authorization();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/UserPolicyManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/UserPolicyManagementTest.java
index ff96a0f..5724978 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/UserPolicyManagementTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/UserPolicyManagementTest.java
@@ -29,11 +29,13 @@ import java.util.stream.Collectors;
 import javax.ws.rs.NotFoundException;
 import javax.ws.rs.core.Response;
 
+import org.junit.Assert;
 import org.junit.Test;
 import org.keycloak.admin.client.resource.AuthorizationResource;
 import org.keycloak.admin.client.resource.PolicyResource;
 import org.keycloak.admin.client.resource.UserPoliciesResource;
 import org.keycloak.admin.client.resource.UserPolicyResource;
+import org.keycloak.admin.client.resource.UsersResource;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.representations.idm.authorization.DecisionStrategy;
 import org.keycloak.representations.idm.authorization.Logic;
@@ -52,7 +54,10 @@ public class UserPolicyManagementTest extends AbstractPolicyManagementTest {
         return super.createTestRealm()
                 .user(UserBuilder.create().username("User A"))
                 .user(UserBuilder.create().username("User B"))
-                .user(UserBuilder.create().username("User C"));
+                .user(UserBuilder.create().username("User C"))
+                .user(UserBuilder.create().username("User D"))
+                .user(UserBuilder.create().username("User E"))
+                .user(UserBuilder.create().username("User F"));
     }
 
     @Test
@@ -128,6 +133,50 @@ public class UserPolicyManagementTest extends AbstractPolicyManagementTest {
     }
 
     @Test
+    public void testDeleteUser() {
+        AuthorizationResource authorization = getClient().authorization();
+        UserPolicyRepresentation representation = new UserPolicyRepresentation();
+
+        representation.setName("Realm User Policy");
+        representation.setDescription("description");
+        representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+        representation.setLogic(Logic.NEGATIVE);
+        representation.addUser("User D");
+        representation.addUser("User E");
+        representation.addUser("User F");
+
+        assertCreated(authorization, representation);
+
+        UsersResource users = getRealm().users();
+        UserRepresentation user = users.search("User D").get(0);
+
+        users.get(user.getId()).remove();
+
+        representation = authorization.policies().user().findById(representation.getId()).toRepresentation();
+
+        Assert.assertEquals(2, representation.getUsers().size());
+        Assert.assertFalse(representation.getUsers().contains(user.getId()));
+
+        user = users.search("User E").get(0);
+        users.get(user.getId()).remove();
+
+        representation = authorization.policies().user().findById(representation.getId()).toRepresentation();
+
+        Assert.assertEquals(1, representation.getUsers().size());
+        Assert.assertFalse(representation.getUsers().contains(user.getId()));
+
+        user = users.search("User F").get(0);
+        users.get(user.getId()).remove();
+
+        try {
+            authorization.policies().user().findById(representation.getId()).toRepresentation();
+            fail("User policy should be removed");
+        } catch (NotFoundException nfe) {
+            // ignore
+        }
+    }
+
+    @Test
     public void testGenericConfig() {
         AuthorizationResource authorization = getClient().authorization();
         UserPolicyRepresentation representation = new UserPolicyRepresentation();