From 530870f05efce76464ddc559a1b15eaa8ad94b32 Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Tue, 9 Aug 2016 15:06:29 -0400 Subject: [PATCH] realm components import/export --- .../client/resource/ComponentResource.java | 44 ++++++++++++++ .../client/resource/ComponentsResource.java | 57 ++++++++++++++++++ .../admin/client/resource/GroupsResource.java | 6 +- .../admin/client/resource/RealmResource.java | 3 + .../models/utils/ModelToRepresentation.java | 1 + .../resources/admin/ComponentResource.java | 12 +++- .../exportimport/ExportImportTest.java | 59 +++++++++++++++++++ 7 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ComponentResource.java create mode 100644 integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ComponentsResource.java diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ComponentResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ComponentResource.java new file mode 100644 index 0000000000..2839f862da --- /dev/null +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ComponentResource.java @@ -0,0 +1,44 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * 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.admin.client.resource; + +import org.jboss.resteasy.spi.NotFoundException; +import org.keycloak.representations.idm.ComponentRepresentation; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.MediaType; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface ComponentResource { + @GET + public ComponentRepresentation toRepresentation(); + + @PUT + @Consumes(MediaType.APPLICATION_JSON) + public void update(ComponentRepresentation rep); + + @DELETE + public void remove(); +} diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ComponentsResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ComponentsResource.java new file mode 100644 index 0000000000..d59c98394c --- /dev/null +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ComponentsResource.java @@ -0,0 +1,57 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * 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.admin.client.resource; + +import org.keycloak.representations.idm.ComponentRepresentation; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.util.List; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface ComponentsResource { + @GET + @Produces(MediaType.APPLICATION_JSON) + public List query(); + + @GET + @Produces(MediaType.APPLICATION_JSON) + public List query(@QueryParam("parent") String parent); + + @GET + @Produces(MediaType.APPLICATION_JSON) + public List query(@QueryParam("parent") String parent, @QueryParam("type") String type); + + @POST + @Consumes(MediaType.APPLICATION_JSON) + Response add(ComponentRepresentation rep); + + @Path("{id}") + ComponentResource component(@PathParam("id") String id); + + +} diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/GroupsResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/GroupsResource.java index 5bf0c4bbf3..78070941ad 100755 --- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/GroupsResource.java +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/GroupsResource.java @@ -38,7 +38,7 @@ public interface GroupsResource { @GET @NoCache @Produces(MediaType.APPLICATION_JSON) - public List groups(); + List groups(); /** * create or add a top level realm groupSet or create child. This will update the group and set the parent if it exists. Create it and set the parent @@ -48,9 +48,9 @@ public interface GroupsResource { */ @POST @Consumes(MediaType.APPLICATION_JSON) - public Response add(GroupRepresentation rep); + Response add(GroupRepresentation rep); @Path("{id}") - public GroupResource group(@PathParam("id") String id); + GroupResource group(@PathParam("id") String id); } diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java index b6db357cfc..eb25bfb43d 100644 --- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java @@ -193,4 +193,7 @@ public interface RealmResource { @DELETE void deleteSession(@PathParam("session") String sessionId); + @Path("components") + ComponentsResource components(); + } diff --git a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java index 5afeb65b9c..73c907bb85 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java @@ -763,6 +763,7 @@ public class ModelToRepresentation { rep.setName(component.getName()); rep.setProviderId(component.getProviderId()); rep.setProviderType(component.getProviderType()); + rep.setParentId(component.getParentId()); rep.setConfig(component.getConfig()); return rep; } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java index 0097b191ff..4ba8fa3121 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java @@ -42,6 +42,7 @@ import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; +import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -82,8 +83,15 @@ public class ComponentResource { @Produces(MediaType.APPLICATION_JSON) public List getComponents(@QueryParam("parent") String parent, @QueryParam("type") String type) { auth.requireManage(); - if (parent == null) parent = realm.getId(); - List components = realm.getComponents(parent, type); + List components = Collections.EMPTY_LIST; + if (parent == null) { + components = realm.getComponents(); + + } else if (type == null) { + components = realm.getComponents(parent); + } else { + components = realm.getComponents(parent, type); + } List reps = new LinkedList<>(); for (ComponentModel component : components) { ComponentRepresentation rep = ModelToRepresentation.toRepresentation(component); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java index 3a6a1bbe2b..82e4784afc 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java @@ -19,10 +19,12 @@ package org.keycloak.testsuite.exportimport; import org.junit.Assert; import org.junit.Test; +import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.exportimport.ExportImportConfig; import org.keycloak.exportimport.dir.DirExportProvider; import org.keycloak.exportimport.dir.DirExportProviderFactory; import org.keycloak.exportimport.singlefile.SingleFileExportProviderFactory; +import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import java.io.File; @@ -138,6 +140,63 @@ public class ExportImportTest extends AbstractExportImportTest { ExportImportUtil.assertDataImportedInRealm(adminClient, testingClient, testRealmRealm.toRepresentation()); } + @Test + public void testComponentExportImport() throws Throwable { + RealmRepresentation realmRep = new RealmRepresentation(); + realmRep.setRealm("component-realm"); + adminClient.realms().create(realmRep); + Assert.assertEquals(4, adminClient.realms().findAll().size()); + RealmResource realm = adminClient.realm("component-realm"); + realmRep = realm.toRepresentation(); + ComponentRepresentation component = new ComponentRepresentation(); + component.setProviderId("dummy"); + component.setProviderType("dummyType"); + component.setName("dummy-name"); + component.setParentId(realmRep.getId()); + component.setConfig(new MultivaluedHashMap<>()); + component.getConfig().add("name", "value"); + realm.components().add(component); + + + ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID); + String targetFilePath = getExportImportTestDirectory() + File.separator + "singleFile-realm.json"; + ExportImportConfig.setFile(targetFilePath); + + ExportImportConfig.setAction(ExportImportConfig.ACTION_EXPORT); + ExportImportConfig.setRealmName("component-realm"); + + testingClient.testing().runExport(); + + // Delete some realm (and some data in admin realm) + adminClient.realm("component-realm").remove(); + + Assert.assertEquals(3, adminClient.realms().findAll().size()); + + // Configure import + ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT); + + testingClient.testing().runImport(); + + realmRep = realm.toRepresentation(); + + List components = realm.components().query(); + + Assert.assertEquals(1, components.size()); + + component = components.get(0); + + Assert.assertEquals("dummy-name", component.getName()); + Assert.assertEquals("dummyType", component.getProviderType()); + Assert.assertEquals("dummy", component.getProviderId()); + Assert.assertEquals(realmRep.getId(), component.getParentId()); + Assert.assertEquals(1, component.getConfig().size()); + Assert.assertEquals("value", component.getConfig().getFirst("name")); + + adminClient.realm("component-realm").remove(); + } + + + private void removeRealm(String realmName) { adminClient.realm(realmName).remove();