diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserFederationProviderResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserFederationProviderResource.java index 5e1a99fe41..2e4543ae61 100644 --- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserFederationProviderResource.java +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserFederationProviderResource.java @@ -64,6 +64,7 @@ public interface UserFederationProviderResource { @GET @Path("mapper-types") + @Produces(MediaType.APPLICATION_JSON) Map getMapperTypes(); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java index 2a56aa0b9c..312690c092 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java @@ -185,6 +185,7 @@ public class UserFederationProviderResource { */ @GET @Path("mapper-types") + @Produces(MediaType.APPLICATION_JSON) @NoCache public Map getMapperTypes() { auth.requireView(); diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/DummyUserFederationMapper.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/DummyUserFederationMapper.java new file mode 100644 index 0000000000..214c488b52 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/DummyUserFederationMapper.java @@ -0,0 +1,140 @@ +/* + * 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.testsuite.federation; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.keycloak.Config; +import org.keycloak.mappers.FederationConfigValidationException; +import org.keycloak.mappers.UserFederationMapper; +import org.keycloak.mappers.UserFederationMapperFactory; +import org.keycloak.models.GroupModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserFederationMapperModel; +import org.keycloak.models.UserFederationProvider; +import org.keycloak.models.UserFederationProviderModel; +import org.keycloak.models.UserFederationSyncResult; +import org.keycloak.models.UserModel; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.representations.idm.UserFederationMapperSyncConfigRepresentation; + +/** + * @author Marek Posolda + */ +public class DummyUserFederationMapper implements UserFederationMapperFactory, UserFederationMapper { + + public static final String PROVIDER_NAME = "dummy-mapper"; + + @Override + public String getFederationProviderType() { + return DummyUserFederationProviderFactory.PROVIDER_NAME; + } + + @Override + public String getDisplayCategory() { + return "Dummy"; + } + + @Override + public String getDisplayType() { + return "Dummy"; + } + + @Override + public UserFederationMapperSyncConfigRepresentation getSyncConfig() { + return new UserFederationMapperSyncConfigRepresentation(true, "dummyFedToKeycloak", true, "dummyKeycloakToFed"); + } + + @Override + public void validateConfig(RealmModel realm, UserFederationProviderModel fedProviderModel, UserFederationMapperModel mapperModel) throws FederationConfigValidationException { + + } + + @Override + public Map getDefaultConfig(UserFederationProviderModel providerModel) { + return Collections.emptyMap(); + } + + @Override + public String getHelpText() { + return "Dummy"; + } + + @Override + public List getConfigProperties() { + return Collections.emptyList(); + } + + @Override + public UserFederationMapper create(KeycloakSession session) { + return this; + } + + @Override + public void init(Config.Scope config) { + + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + + } + + @Override + public void close() { + + } + + @Override + public String getId() { + return PROVIDER_NAME; + } + + @Override + public UserFederationSyncResult syncDataFromFederationProviderToKeycloak(final UserFederationMapperModel mapperModel, UserFederationProvider federationProvider, KeycloakSession session, RealmModel realm) { + return new UserFederationSyncResult() { + + @Override + public String getStatus() { + return "dummyFedToKeycloakSuccess mapper=" + mapperModel.getName(); + } + + }; + } + + @Override + public UserFederationSyncResult syncDataFromKeycloakToFederationProvider(final UserFederationMapperModel mapperModel, UserFederationProvider federationProvider, KeycloakSession session, RealmModel realm) { + return new UserFederationSyncResult() { + + @Override + public String getStatus() { + return "dummyKeycloakToFedSuccess mapper=" + mapperModel.getName(); + } + + }; + } + + @Override + public List getGroupMembers(UserFederationMapperModel mapperModel, UserFederationProvider federationProvider, RealmModel realm, GroupModel group, int firstResult, int maxResults) { + return Collections.emptyList(); + } +} diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.mappers.UserFederationMapperFactory b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.mappers.UserFederationMapperFactory new file mode 100644 index 0000000000..2bc7ca8b28 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.mappers.UserFederationMapperFactory @@ -0,0 +1,52 @@ +# +# 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. +# + +# +# 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. +# + +# +# 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. +# + +org.keycloak.testsuite.federation.DummyUserFederationMapper \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPRule.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPRule.java new file mode 100644 index 0000000000..5cebe8cecd --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPRule.java @@ -0,0 +1,80 @@ +/* + * 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.testsuite.util; + +import java.util.Map; +import java.util.Properties; + +import org.junit.rules.ExternalResource; +import org.keycloak.util.ldap.LDAPEmbeddedServer; + +/** + * @author Marek Posolda + */ +public class LDAPRule extends ExternalResource { + + public static final String LDAP_CONNECTION_PROPERTIES_LOCATION = "classpath:ldap/ldap-connection.properties"; + + protected LDAPTestConfiguration ldapTestConfiguration; + protected LDAPEmbeddedServer ldapEmbeddedServer; + + @Override + protected void before() throws Throwable { + String connectionPropsLocation = getConnectionPropertiesLocation(); + ldapTestConfiguration = LDAPTestConfiguration.readConfiguration(connectionPropsLocation); + + if (ldapTestConfiguration.isStartEmbeddedLdapServer()) { + ldapEmbeddedServer = createServer(); + ldapEmbeddedServer.init(); + ldapEmbeddedServer.start(); + } + } + + @Override + protected void after() { + try { + if (ldapEmbeddedServer != null) { + ldapEmbeddedServer.stop(); + ldapEmbeddedServer = null; + ldapTestConfiguration = null; + } + } catch (Exception e) { + throw new RuntimeException("Error tearDown Embedded LDAP server.", e); + } + } + + protected String getConnectionPropertiesLocation() { + return LDAP_CONNECTION_PROPERTIES_LOCATION; + } + + protected LDAPEmbeddedServer createServer() { + Properties defaultProperties = new Properties(); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_DSF, LDAPEmbeddedServer.DSF_INMEMORY); + defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_LDIF_FILE, "classpath:ldap/users.ldif"); + + return new LDAPEmbeddedServer(defaultProperties); + } + + public Map getConfig() { + return ldapTestConfiguration.getLDAPConfig(); + } + + public int getSleepTime() { + return ldapTestConfiguration.getSleepTime(); + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java index f4a5d50dff..5540c3991b 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java @@ -20,13 +20,19 @@ package org.keycloak.testsuite.util; import static org.keycloak.testsuite.util.IOUtil.PROJECT_BUILD_DIRECTORY; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; import java.io.InputStream; +import java.net.URL; import java.util.HashMap; import java.util.Map; import org.apache.commons.configuration.PropertiesConfiguration; import org.jboss.logging.Logger; +import org.keycloak.common.constants.GenericConstants; import org.keycloak.common.constants.KerberosConstants; +import org.keycloak.common.util.FindFile; import org.keycloak.models.LDAPConstants; import org.keycloak.models.UserFederationProvider; @@ -37,7 +43,6 @@ public class LDAPTestConfiguration { private static final Logger log = Logger.getLogger(LDAPTestConfiguration.class); - private String connectionPropertiesLocation; private int sleepTime; private boolean startEmbeddedLdapServer = true; private Map config; @@ -95,8 +100,7 @@ public class LDAPTestConfiguration { public static LDAPTestConfiguration readConfiguration(String connectionPropertiesLocation) { LDAPTestConfiguration ldapTestConfiguration = new LDAPTestConfiguration(); - ldapTestConfiguration.setConnectionPropertiesLocation(getResource(connectionPropertiesLocation)); - ldapTestConfiguration.loadConnectionProperties(); + ldapTestConfiguration.loadConnectionProperties(connectionPropertiesLocation); return ldapTestConfiguration; } @@ -104,13 +108,28 @@ public class LDAPTestConfiguration { return new File(PROJECT_BUILD_DIRECTORY, "dependency/kerberos/" + resourceName).getAbsolutePath(); } - protected void loadConnectionProperties() { + protected void loadConnectionProperties(String connectionPropertiesLocation) { + // TODO: Improve and possibly use FindFile + InputStream is; + try { + if (connectionPropertiesLocation.startsWith(GenericConstants.PROTOCOL_CLASSPATH)) { + String classPathLocation = connectionPropertiesLocation.replace(GenericConstants.PROTOCOL_CLASSPATH, ""); + log.info("Reading LDAP configuration from classpath from: " + classPathLocation); + is = LDAPTestConfiguration.class.getClassLoader().getResourceAsStream(classPathLocation); + } else { + String file = getResource(connectionPropertiesLocation); + log.info("Reading LDAP configuration from: " + connectionPropertiesLocation); + is = new FileInputStream(file); + } + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + PropertiesConfiguration p; try { - log.info("Reading LDAP configuration from: " + connectionPropertiesLocation); p = new PropertiesConfiguration(); p.setDelimiterParsingDisabled(true); - p.load(connectionPropertiesLocation); + p.load(is); } catch (Exception e) { throw new RuntimeException(e); @@ -139,10 +158,6 @@ public class LDAPTestConfiguration { return config; } - public void setConnectionPropertiesLocation(String connectionPropertiesLocation) { - this.connectionPropertiesLocation = connectionPropertiesLocation; - } - public boolean isStartEmbeddedLdapServer() { return startEmbeddedLdapServer; } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationLdapConnectionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationLdapConnectionTest.java new file mode 100644 index 0000000000..041d58c97a --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationLdapConnectionTest.java @@ -0,0 +1,64 @@ +/* + * 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.testsuite.admin; + +import javax.ws.rs.core.Response; + +import org.junit.ClassRule; +import org.junit.Test; +import org.keycloak.services.managers.LDAPConnectionTestManager; +import org.keycloak.testsuite.Assert; +import org.keycloak.testsuite.util.LDAPRule; + +/** + * @author Marek Posolda + */ +public class UserFederationLdapConnectionTest extends AbstractAdminTest { + + @ClassRule + public static LDAPRule ldapRule = new LDAPRule(); + + @Test + public void testLdapConnections1() { + // Unknown action + Response response = realm.testLDAPConnection("unknown", "ldap://localhost:10389", "foo", "bar", "false"); + assertStatus(response, 400); + + // Bad host + response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_CONNECTION, "ldap://localhostt:10389", "foo", "bar", "false"); + assertStatus(response, 400); + + // Connection success + response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_CONNECTION, "ldap://localhost:10389", "foo", "bar", "false"); + assertStatus(response, 204); + + // Bad authentication + response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_AUTHENTICATION, "ldap://localhost:10389", "foo", "bar", "false"); + assertStatus(response, 400); + + // Authentication success + response = realm.testLDAPConnection(LDAPConnectionTestManager.TEST_AUTHENTICATION, "ldap://localhost:10389", "uid=admin,ou=system", "secret", "false"); + assertStatus(response, 204); + + } + + private void assertStatus(Response response, int status) { + Assert.assertEquals(status, response.getStatus()); + response.close(); + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationMapperTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationMapperTest.java new file mode 100644 index 0000000000..72989a5350 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationMapperTest.java @@ -0,0 +1,292 @@ +/* + * 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.testsuite.admin; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ws.rs.BadRequestException; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.core.Response; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.keycloak.admin.client.resource.UserFederationProviderResource; +import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapper; +import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapperFactory; +import org.keycloak.federation.ldap.mappers.membership.role.RoleLDAPFederationMapperFactory; +import org.keycloak.federation.ldap.mappers.membership.role.RoleMapperConfig; +import org.keycloak.representations.idm.ConfigPropertyRepresentation; +import org.keycloak.representations.idm.UserFederationMapperRepresentation; +import org.keycloak.representations.idm.UserFederationMapperTypeRepresentation; +import org.keycloak.representations.idm.UserFederationProviderRepresentation; +import org.keycloak.representations.idm.UserFederationSyncResultRepresentation; +import org.keycloak.testsuite.Assert; +import org.keycloak.testsuite.federation.DummyUserFederationMapper; +import org.keycloak.testsuite.util.UserFederationProviderBuilder; + +/** + * @author Marek Posolda + */ +public class UserFederationMapperTest extends AbstractAdminTest { + + private String ldapProviderId; + private String dummyProviderId; + + @Before + public void initFederationProviders() { + UserFederationProviderRepresentation ldapRep = UserFederationProviderBuilder.create() + .displayName("ldap-1") + .providerName("ldap") + .priority(1) + .build(); + Response resp = realm.userFederation().create(ldapRep); + this.ldapProviderId = ApiUtil.getCreatedId(resp); + resp.close(); + + UserFederationProviderRepresentation dummyRep = UserFederationProviderBuilder.create() + .displayName("dummy-1") + .providerName("dummy") + .priority(2) + .build(); + resp = realm.userFederation().create(dummyRep); + this.dummyProviderId = ApiUtil.getCreatedId(resp); + resp.close(); + } + + @After + public void cleanFederationProviders() { + realm.userFederation().get(ldapProviderId).remove(); + realm.userFederation().get(dummyProviderId).remove(); + } + + + @Test + public void testProviderFactories() { + // Test dummy mapper + Map mapperTypes = realm.userFederation().get(dummyProviderId).getMapperTypes(); + Assert.assertEquals(1, mapperTypes.size()); + Assert.assertEquals("Dummy", mapperTypes.get("dummy-mapper").getName()); + + + // Test LDAP mappers + mapperTypes = ldapProviderResource().getMapperTypes(); + Assert.assertTrue(mapperTypes.keySet().containsAll(Arrays.asList("user-attribute-ldap-mapper", "full-name-ldap-mapper", "role-ldap-mapper"))); + + UserFederationMapperTypeRepresentation attrMapper = mapperTypes.get("user-attribute-ldap-mapper"); + Assert.assertEquals("User Attribute", attrMapper.getName()); + Assert.assertFalse(attrMapper.getSyncConfig().isFedToKeycloakSyncSupported()); + Assert.assertFalse(attrMapper.getSyncConfig().isKeycloakToFedSyncSupported()); + Set propNames = getConfigPropertyNames(attrMapper); + Assert.assertTrue(propNames.containsAll(Arrays.asList("user.model.attribute", "ldap.attribute", "read.only"))); + Assert.assertEquals("false", attrMapper.getDefaultConfig().get("always.read.value.from.ldap")); + + UserFederationMapperTypeRepresentation roleMapper = mapperTypes.get("role-ldap-mapper"); + Assert.assertEquals("Role mappings", roleMapper.getName()); + Assert.assertTrue(roleMapper.getSyncConfig().isFedToKeycloakSyncSupported()); + Assert.assertTrue(roleMapper.getSyncConfig().isKeycloakToFedSyncSupported()); + Assert.assertEquals("sync-ldap-roles-to-keycloak", roleMapper.getSyncConfig().getFedToKeycloakSyncMessage()); + Assert.assertEquals("sync-keycloak-roles-to-ldap", roleMapper.getSyncConfig().getKeycloakToFedSyncMessage()); + propNames = getConfigPropertyNames(roleMapper); + Assert.assertTrue(propNames.containsAll(Arrays.asList("roles.dn", "role.name.ldap.attribute", "role.object.classes"))); + Assert.assertEquals("cn", roleMapper.getDefaultConfig().get("role.name.ldap.attribute")); + } + + private Set getConfigPropertyNames(UserFederationMapperTypeRepresentation mapper) { + List cfg = mapper.getProperties(); + Set result = new HashSet<>(); + for (ConfigPropertyRepresentation rep : cfg) { + result.add(rep.getName()); + } + return result; + + } + + + @Test + public void testUserAttributeMapperCRUD() { + // Test create fails with invalid config + UserFederationMapperRepresentation attrMapper = createMapperRep("email-mapper", UserAttributeLDAPFederationMapperFactory.PROVIDER_ID); + Response response = ldapProviderResource().addMapper(attrMapper); + Assert.assertEquals(400, response.getStatus()); + response.close(); + + attrMapper.getConfig().put(UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "email"); + response = ldapProviderResource().addMapper(attrMapper); + Assert.assertEquals(400, response.getStatus()); + response.close(); + + // Test create success when all mandatory attributes available + attrMapper.getConfig().put(UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "mail"); + String mapperId = createMapper(attrMapper); + + // Test get + UserFederationMapperRepresentation mapperRep = ldapProviderResource().getMapperById(mapperId); + assertMapper(mapperRep, mapperId, "email-mapper", UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "email", UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "mail"); + + // Test update fails with invalid config + mapperRep.getConfig().put(UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "mail-updated"); + mapperRep.getConfig().remove(UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE); + try { + ldapProviderResource().updateMapper(mapperId, mapperRep); + Assert.fail("Not expected update to success"); + } catch (BadRequestException bre) { + // Expected + } + + // Test not updated + mapperRep = ldapProviderResource().getMapperById(mapperId); + assertMapper(mapperRep, mapperId, "email-mapper", UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "email", UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "mail"); + + // Test update success + mapperRep.getConfig().put(UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "email-updated"); + mapperRep.getConfig().put(UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "mail-updated"); + ldapProviderResource().updateMapper(mapperId, mapperRep); + + mapperRep = ldapProviderResource().getMapperById(mapperId); + assertMapper(mapperRep, mapperId, "email-mapper", UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "email-updated", UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "mail-updated"); + + // Test removed successfully + ldapProviderResource().removeMapper(mapperId); + try { + ldapProviderResource().getMapperById(mapperId); + Assert.fail("Not expected find to success as mapper was removed"); + } catch (NotFoundException nfe) { + // Expected + } + } + + private String createMapper(UserFederationMapperRepresentation mapper) { + Response response = ldapProviderResource().addMapper(mapper); + Assert.assertEquals(201, response.getStatus()); + response.close(); + return ApiUtil.getCreatedId(response); + } + + + @Test + public void testRoleMapper() { + // Create role mapper will fail + UserFederationMapperRepresentation roleMapper = createMapperRep("role-mapper", RoleLDAPFederationMapperFactory.PROVIDER_ID, + RoleMapperConfig.ROLES_DN, "ou=roles,dc=keycloak,dc=org", + RoleMapperConfig.MODE, "READ_ONLY"); + Response response = ldapProviderResource().addMapper(roleMapper); + Assert.assertEquals(400, response.getStatus()); + response.close(); + + // Fix config and create successfully + roleMapper.getConfig().put(RoleMapperConfig.USE_REALM_ROLES_MAPPING, "true"); + String roleMapperId = createMapper(roleMapper); + + // Assert builtin mappers + List mappers = ldapProviderResource().getMappers(); + Assert.assertNotNull(findMapperByName(mappers, "email")); + Assert.assertNotNull(findMapperByName(mappers, "first name")); + Assert.assertNull(findMapperByName(mappers, "non-existent")); + + roleMapper = findMapperByName(mappers, "role-mapper"); + assertMapper(roleMapper, roleMapperId, "role-mapper", RoleLDAPFederationMapperFactory.PROVIDER_ID, + RoleMapperConfig.ROLES_DN, "ou=roles,dc=keycloak,dc=org", + RoleMapperConfig.MODE, "READ_ONLY", + RoleMapperConfig.USE_REALM_ROLES_MAPPING, "true"); + + + // Remove role mapper and assert not found anymore + ldapProviderResource().removeMapper(roleMapperId); + mappers = ldapProviderResource().getMappers(); + Assert.assertNull(findMapperByName(mappers, "role-mapper")); + } + + + @Test + public void testSyncMapper() { + // Create dummy mapper + UserFederationMapperRepresentation dummyMapperRep = new UserFederationMapperRepresentation(); + dummyMapperRep.setName("some-dummy"); + dummyMapperRep.setFederationMapperType(DummyUserFederationMapper.PROVIDER_NAME); + dummyMapperRep.setFederationProviderDisplayName("dummy-1"); + String mapperId = createMapper(dummyMapperRep); + + // Try to sync with unknown action - fail + try { + realm.userFederation().get(dummyProviderId).syncMapperData(mapperId, "unknown"); + Assert.fail("Not expected to pass"); + } catch (NotFoundException nfe) { + // Expected + } + + // Try fed To Keycloak sync + UserFederationSyncResultRepresentation result = realm.userFederation().get(dummyProviderId).syncMapperData(mapperId, "fedToKeycloak"); + Assert.assertEquals("dummyFedToKeycloakSuccess mapper=some-dummy", result.getStatus()); + + // Try keycloak to fed + result = realm.userFederation().get(dummyProviderId).syncMapperData(mapperId, "keycloakToFed"); + Assert.assertEquals("dummyKeycloakToFedSuccess mapper=some-dummy", result.getStatus()); + + } + + + private UserFederationProviderResource ldapProviderResource() { + return realm.userFederation().get(ldapProviderId); + } + + private UserFederationMapperRepresentation createMapperRep(String name, String type, String... config) { + UserFederationMapperRepresentation rep = new UserFederationMapperRepresentation(); + rep.setName(name); + rep.setFederationMapperType(type); + rep.setFederationProviderDisplayName("ldap-1"); + + Map cfg = new HashMap<>(); + for (int i=0 ; i mappers, String name) { + for (UserFederationMapperRepresentation rep : mappers) { + if (rep.getName().equals(name)) { + return rep; + } + } + return null; + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java index 0cd3508692..7e702a08da 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java @@ -22,7 +22,6 @@ import java.util.List; import javax.ws.rs.BadRequestException; import javax.ws.rs.NotFoundException; -import javax.ws.rs.core.GenericType; import javax.ws.rs.core.Response; import org.junit.Test; @@ -73,7 +72,7 @@ public class UserFederationTest extends AbstractAdminTest { userFederation().getProviderFactory("not-existent"); Assert.fail("Not expected to find not-existent provider"); } catch (NotFoundException nfe) { - nfe.getResponse().close(); + // Expected } } @@ -205,7 +204,7 @@ public class UserFederationTest extends AbstractAdminTest { userFederation().get(id).update(ldapRep); Assert.fail("Not expected to successfull update"); } catch (BadRequestException bre) { - bre.getResponse().close(); + // Expected } // Assert nothing was updated @@ -291,7 +290,7 @@ public class UserFederationTest extends AbstractAdminTest { userFederation().get(id1).syncUsers("unknown"); Assert.fail("Not expected to sync with unknown action"); } catch (NotFoundException nfe) { - nfe.getResponse().close(); + // Expected } // Assert sync didn't happen diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/ldap/ldap-connection.properties b/testsuite/integration-arquillian/tests/base/src/test/resources/ldap/ldap-connection.properties new file mode 100644 index 0000000000..610312c652 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/ldap/ldap-connection.properties @@ -0,0 +1,26 @@ +# +# 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. +# + +idm.test.ldap.connection.url=ldap\://localhost\:10389 +idm.test.ldap.base.dn=dc\=keycloak,dc\=org +idm.test.ldap.user.dn.suffix=ou\=People,dc\=keycloak,dc\=org +idm.test.ldap.start.embedded.ldap.server=true +idm.test.ldap.bind.dn=uid\=admin,ou\=system +idm.test.ldap.bind.credential=secret +idm.test.ldap.connection.pooling=true +idm.test.ldap.pagination=true +idm.test.ldap.batch.size.for.sync=3 \ No newline at end of file