keycloak-uncached

Details

diff --git a/core/src/main/java/org/keycloak/representations/idm/PartialImportRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/PartialImportRepresentation.java
index a960bee..fab1f56 100644
--- a/core/src/main/java/org/keycloak/representations/idm/PartialImportRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/PartialImportRepresentation.java
@@ -21,7 +21,7 @@ import java.util.List;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 
 /**
- * Used for partial import of users, clients, roles, and identity providers.
+ * Used for partial import of users, groups, clients, roles, and identity providers.
  *
  * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
  */
@@ -32,6 +32,7 @@ public class PartialImportRepresentation {
     protected Policy policy = Policy.FAIL;
     protected String ifResourceExists = "";
     protected List<UserRepresentation> users;
+    protected List<GroupRepresentation> groups;
     protected List<ClientRepresentation> clients;
     protected List<IdentityProviderRepresentation> identityProviders;
     protected RolesRepresentation roles;
@@ -40,6 +41,10 @@ public class PartialImportRepresentation {
         return (users != null) && !users.isEmpty();
     }
 
+    public boolean hasGroups() {
+        return (groups != null) && !groups.isEmpty();
+    }
+
     public boolean hasClients() {
         return (clients != null) && !clients.isEmpty();
     }
@@ -81,6 +86,14 @@ public class PartialImportRepresentation {
         return clients;
     }
 
+    public List<GroupRepresentation> getGroups() {
+        return groups;
+    }
+
+    public void setGroups(List<GroupRepresentation> groups) {
+        this.groups = groups;
+    }
+
     public void setClients(List<ClientRepresentation> clients) {
         this.clients = clients;
     }
diff --git a/services/src/main/java/org/keycloak/partialimport/GroupsPartialImport.java b/services/src/main/java/org/keycloak/partialimport/GroupsPartialImport.java
new file mode 100644
index 0000000..e9632d6
--- /dev/null
+++ b/services/src/main/java/org/keycloak/partialimport/GroupsPartialImport.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.partialimport;
+
+import java.util.List;
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.representations.idm.GroupRepresentation;
+import org.keycloak.representations.idm.PartialImportRepresentation;
+
+/**
+ * Partial import handler for Groups.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class GroupsPartialImport extends AbstractPartialImport<GroupRepresentation> {
+
+    @Override
+    public List<GroupRepresentation> getRepList(PartialImportRepresentation partialImportRep) {
+        return partialImportRep.getGroups();
+    }
+
+    @Override
+    public String getName(GroupRepresentation group) {
+        return group.getName();
+    }
+
+    private GroupModel findGroupModel(RealmModel realm, GroupRepresentation groupRep) {
+        return KeycloakModelUtils.findGroupByPath(realm, groupRep.getPath());
+    }
+
+    @Override
+    public String getModelId(RealmModel realm, KeycloakSession session, GroupRepresentation groupRep) {
+        return findGroupModel(realm, groupRep).getId();
+    }
+
+    @Override
+    public boolean exists(RealmModel realm, KeycloakSession session, GroupRepresentation groupRep) {
+        return findGroupModel(realm, groupRep) != null;
+    }
+
+    @Override
+    public String existsMessage(GroupRepresentation groupRep) {
+        return "Group '" + groupRep.getPath() + "' already exists";
+    }
+
+    @Override
+    public ResourceType getResourceType() {
+        return ResourceType.GROUP;
+    }
+
+    @Override
+    public void remove(RealmModel realm, KeycloakSession session, GroupRepresentation groupRep) {
+        GroupModel group = realm.getGroupById(getModelId(realm, session, groupRep));
+        realm.removeGroup(group);
+    }
+
+    @Override
+    public void create(RealmModel realm, KeycloakSession session, GroupRepresentation groupRep) {
+        RepresentationToModel.importGroup(realm, null, groupRep);
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/partialimport/PartialImportManager.java b/services/src/main/java/org/keycloak/partialimport/PartialImportManager.java
index d01d8dd..59d6217 100644
--- a/services/src/main/java/org/keycloak/partialimport/PartialImportManager.java
+++ b/services/src/main/java/org/keycloak/partialimport/PartialImportManager.java
@@ -50,6 +50,7 @@ public class PartialImportManager {
         partialImports.add(new ClientsPartialImport());
         partialImports.add(new RolesPartialImport());
         partialImports.add(new IdentityProvidersPartialImport());
+        partialImports.add(new GroupsPartialImport());
         partialImports.add(new UsersPartialImport());
     }
 
@@ -78,8 +79,8 @@ public class PartialImportManager {
 
         for (PartialImportResult result : results.getResults()) {
             switch (result.getAction()) {
-                case ADDED : addedEvent(result); break;
-                case OVERWRITTEN: overwrittenEvent(result); break;
+                case ADDED : fireCreatedEvent(result); break;
+                case OVERWRITTEN: fireUpdateEvent(result); break;
             }
         }
 
@@ -90,14 +91,14 @@ public class PartialImportManager {
         return Response.ok(results).build();
     }
 
-    private void addedEvent(PartialImportResult result) {
+    private void fireCreatedEvent(PartialImportResult result) {
         adminEvent.operation(OperationType.CREATE)
                   .resourcePath(result.getResourceType().getPath(), result.getId())
                   .representation(result.getRepresentation())
                   .success();
     };
 
-    private void overwrittenEvent(PartialImportResult result) {
+    private void fireUpdateEvent(PartialImportResult result) {
         adminEvent.operation(OperationType.UPDATE)
                   .resourcePath(result.getResourceType().getPath(), result.getId())
                   .representation(result.getRepresentation())
diff --git a/services/src/main/java/org/keycloak/partialimport/ResourceType.java b/services/src/main/java/org/keycloak/partialimport/ResourceType.java
index 3cc158f..9e97ecd 100644
--- a/services/src/main/java/org/keycloak/partialimport/ResourceType.java
+++ b/services/src/main/java/org/keycloak/partialimport/ResourceType.java
@@ -23,7 +23,7 @@ package org.keycloak.partialimport;
  * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
  */
 public enum ResourceType {
-    USER, CLIENT, IDP, REALM_ROLE, CLIENT_ROLE;
+    USER, GROUP, CLIENT, IDP, REALM_ROLE, CLIENT_ROLE;
 
     /**
      * Used to create the admin path in events.
@@ -33,6 +33,7 @@ public enum ResourceType {
     public String getPath() {
         switch(this) {
             case USER: return "users";
+            case GROUP: return "groups";
             case CLIENT: return "clients";
             case IDP: return "identity-provider-settings";
             case REALM_ROLE: return "realms";
@@ -45,6 +46,7 @@ public enum ResourceType {
     public String toString() {
         switch(this) {
             case USER: return "User";
+            case GROUP: return "Group";
             case CLIENT: return "Client";
             case IDP: return "Identity Provider";
             case REALM_ROLE: return "Realm Role";
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialimport/PartialImportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialimport/PartialImportTest.java
index 0440f7b..1d746c3 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialimport/PartialImportTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialimport/PartialImportTest.java
@@ -32,6 +32,7 @@ import org.keycloak.admin.client.resource.UserResource;
 import org.keycloak.partialimport.PartialImportResult;
 import org.keycloak.partialimport.PartialImportResults;
 import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.GroupRepresentation;
 import org.keycloak.representations.idm.IdentityProviderRepresentation;
 import org.keycloak.representations.idm.PartialImportRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
@@ -56,9 +57,10 @@ import org.keycloak.testsuite.admin.ApiUtil;
  */
 public class PartialImportTest extends AbstractAuthTest {
 
-    private static final int NUM_RESOURCE_TYPES = 5;
+    private static final int NUM_RESOURCE_TYPES = 6;
     private static final String CLIENT_ROLES_CLIENT = "clientRolesClient";
     private static final String USER_PREFIX = "user";
+    private static final String GROUP_PREFIX = "group";
     private static final String CLIENT_PREFIX = "client";
     private static final String REALM_ROLE_PREFIX = "realmRole";
     private static final String CLIENT_ROLE_PREFIX = "clientRole";
@@ -95,6 +97,14 @@ public class PartialImportTest extends AbstractAuthTest {
     }
 
     @Before
+    public void removeGroups() {
+        List<GroupRepresentation> toRemove = testRealmResource().groups().groups();
+        for (GroupRepresentation group: toRemove) {
+            testRealmResource().groups().group(group.getId()).remove();
+        }
+    }
+
+    @Before
     public void removeClients() {
         List<ClientRepresentation> toRemove = testRealmResource().clients().findAll();
         for (ClientRepresentation client : toRemove) {
@@ -164,6 +174,19 @@ public class PartialImportTest extends AbstractAuthTest {
         piRep.setUsers(users);
     }
 
+    private void addGroups() {
+        List<GroupRepresentation> groups = new ArrayList<>();
+
+        for (int i=0; i < NUM_ENTITIES; i++) {
+            GroupRepresentation group = new GroupRepresentation();
+            group.setName(GROUP_PREFIX + i);
+            group.setPath("/" + GROUP_PREFIX + i);
+            groups.add(group);
+        }
+
+        piRep.setGroups(groups);
+    }
+
     private void addClients() {
         List<ClientRepresentation> clients = new ArrayList<>();
 
@@ -323,6 +346,12 @@ public class PartialImportTest extends AbstractAuthTest {
     }
 
     @Test
+    public void testAddGroupsFail() {
+        addGroups();
+        testFail();
+    }
+
+    @Test
     public void testAddClientsFail() {
         addClients();
         testFail();
@@ -362,6 +391,12 @@ public class PartialImportTest extends AbstractAuthTest {
     }
 
     @Test
+    public void testAddGroupsSkip() {
+        addGroups();
+        testSkip();
+    }
+
+    @Test
     public void testAddClientsSkip() {
         addClients();
         testSkip();
@@ -401,6 +436,12 @@ public class PartialImportTest extends AbstractAuthTest {
     }
 
     @Test
+    public void testAddGroupsOverwrite() {
+        addGroups();
+        testOverwrite();
+    }
+
+    @Test
     public void testAddClientsOverwrite() {
         addClients();
         testOverwrite();
@@ -427,6 +468,7 @@ public class PartialImportTest extends AbstractAuthTest {
 
     private void importEverything() {
         addUsers();
+        addGroups();
         addClients();
         addProviders();
         addRealmRoles();
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index a86792a..6658b51 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -548,6 +548,7 @@ file=File
 exported-json-file=Exported json file
 import-from-realm=Import from realm
 import-users=Import users
+import-groups=Import groups
 import-clients=Import clients
 import-identity-providers=Import identity providers
 import-realm-roles=Import realm roles
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
index 94b550d..26b7c4e 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
@@ -2117,6 +2117,7 @@ module.controller('RealmImportCtrl', function($scope, realm, $route,
     $scope.overwrite = false;
     $scope.skip = false;
     $scope.importUsers = false;
+    $scope.importGroups = false;
     $scope.importClients = false;
     $scope.importIdentityProviders = false;
     $scope.importRealmRoles = false;
@@ -2147,11 +2148,7 @@ module.controller('RealmImportCtrl', function($scope, realm, $route,
         }
         
         $scope.importing = true;
-        $scope.importUsers = $scope.hasArray('users');
-        $scope.importClients = $scope.hasArray('clients');
-        $scope.importIdentityProviders = $scope.hasArray('identityProviders');
-        $scope.importRealmRoles = $scope.hasRealmRoles();
-        $scope.importClientRoles = $scope.hasClientRoles();
+        setOnOffSwitchDefaults();
         $scope.results = {};
         if (!$scope.hasResources()) {
             $scope.nothingToImport();
@@ -2180,6 +2177,15 @@ module.controller('RealmImportCtrl', function($scope, realm, $route,
         return endIndex;
     }
     
+    function setOnOffSwitchDefaults() {
+        $scope.importUsers = $scope.hasArray('users');
+        $scope.importGroups = $scope.hasArray('groups');
+        $scope.importClients = $scope.hasArray('clients');
+        $scope.importIdentityProviders = $scope.hasArray('identityProviders');
+        $scope.importRealmRoles = $scope.hasRealmRoles();
+        $scope.importClientRoles = $scope.hasClientRoles();
+    }
+    
     $scope.setFirstPage = function() {
         $scope.currentPage = 0;
     }
@@ -2256,6 +2262,7 @@ module.controller('RealmImportCtrl', function($scope, realm, $route,
     
     $scope.hasResources = function() {
         return ($scope.importUsers && $scope.hasArray('users')) ||
+               ($scope.importGroups && $scope.hasArray('groups')) ||
                ($scope.importClients && $scope.hasArray('clients')) ||
                ($scope.importIdentityProviders && $scope.hasArray('identityProviders')) ||
                ($scope.importRealmRoles && $scope.hasRealmRoles()) ||
@@ -2270,6 +2277,7 @@ module.controller('RealmImportCtrl', function($scope, realm, $route,
         if (!angular.equals($scope.fileContent, oldCopy)) {
             $scope.changed = true;
         }
+        setOnOffSwitchDefaults();
     }, true);
     
     $scope.successMessage = function() {
@@ -2287,6 +2295,7 @@ module.controller('RealmImportCtrl', function($scope, realm, $route,
         var json = angular.copy($scope.fileContent);
         json.ifResourceExists = $scope.ifResourceExists;
         if (!$scope.importUsers) delete json.users;
+        if (!$scope.importGroups) delete json.groups;
         if (!$scope.importIdentityProviders) delete json.identityProviders;
         if (!$scope.importClients) delete json.clients;
         
@@ -2311,4 +2320,4 @@ module.controller('RealmImportCtrl', function($scope, realm, $route,
         $route.reload();
     }
 
-});
\ No newline at end of file
+});
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/partial-import.html b/themes/src/main/resources/theme/base/admin/resources/partials/partial-import.html
index f4ab51d..4df1207 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/partial-import.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/partial-import.html
@@ -39,6 +39,13 @@
                 </div>
             </div>
 
+            <div class="form-group" data-ng-show="importing && hasArray('groups') && !hasResults()">
+                <label class="col-md-2 control-label" for="importGroups">{{:: 'import-groups' | translate}} ({{itemCount('groups')}})</label>
+                <div class="col-sm-6">
+                    <input ng-model="importGroups" name="importGroups" id="importGroups" onoffswitch on-text="{{:: 'onText'| translate}}" off-text="{{:: 'offText'| translate}}"/>
+                </div>
+            </div>
+            
             <div class="form-group" data-ng-show="importing && hasArray('clients') && !hasResults()">
                 <label class="col-md-2 control-label" for="importClients">{{:: 'import-clients' | translate}} ({{itemCount('clients')}})</label>
                 <div class="col-sm-6">