keycloak-uncached

Details

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 5e1a99f..2e4543a 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<String, UserFederationMapperTypeRepresentation> 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 2a56aa0..312690c 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<String, UserFederationMapperTypeRepresentation> 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 0000000..214c488
--- /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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+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<String, String> getDefaultConfig(UserFederationProviderModel providerModel) {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public String getHelpText() {
+        return "Dummy";
+    }
+
+    @Override
+    public List<ProviderConfigProperty> 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<UserModel> 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 0000000..2bc7ca8
--- /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 0000000..5cebe8c
--- /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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+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<String, String> 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 f4a5d50..5540c39 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<String, String> 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 0000000..041d58c
--- /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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+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 0000000..72989a5
--- /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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+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<String, UserFederationMapperTypeRepresentation> 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<String> 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<String> getConfigPropertyNames(UserFederationMapperTypeRepresentation mapper) {
+        List<ConfigPropertyRepresentation> cfg = mapper.getProperties();
+        Set<String> 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<UserFederationMapperRepresentation> 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<String, String> cfg = new HashMap<>();
+        for (int i=0 ; i<config.length ; i+=2) {
+            cfg.put(config[i], config[i+1]);
+        }
+        rep.setConfig(cfg);
+        return rep;
+    }
+
+    private void assertMapper(UserFederationMapperRepresentation rep, String id, String name, String federationMapperType, String... config) {
+        Assert.assertEquals(id, rep.getId());
+        Assert.assertEquals(name, rep.getName());
+        Assert.assertEquals("ldap-1", rep.getFederationProviderDisplayName());
+        Assert.assertEquals(federationMapperType, rep.getFederationMapperType());
+
+        if (config == null) {
+            config = new String[] {};
+        }
+        Assert.assertEquals(rep.getConfig().size() * 2, config.length);
+        for (int i=0 ; i<config.length ; i+=2) {
+            String key = config[i];
+            String value = config[i+1];
+            Assert.assertEquals(value, rep.getConfig().get(key));
+        }
+    }
+
+    private UserFederationMapperRepresentation findMapperByName(List<UserFederationMapperRepresentation> 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 0cd3508..7e702a0 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 0000000..610312c
--- /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