KEYCLOAK-1531 Added embedded LDAP for examples. Added module keycloak-util-embedded-ldap with common code for testsuite and examples

This commit is contained in:
mposolda 2015-06-30 14:57:18 +02:00
parent 351a17f68d
commit c6c73e5e59
34 changed files with 1262 additions and 428 deletions

View file

@ -89,13 +89,6 @@ public class BasicDBObjectMapper<S> implements Mapper<BasicDBObject, S> {
Type[] genericTypeArguments = parameterized.getActualTypeArguments(); Type[] genericTypeArguments = parameterized.getActualTypeArguments();
List<Type> genericTypes = Arrays.asList(genericTypeArguments); List<Type> genericTypes = Arrays.asList(genericTypeArguments);
/*for (Type genericType : genericTypeArguments) {
if (genericType instanceof Class) {
genericTypes.add((Class<?>) genericType);
} else {
System.out.println("foo");
}
}*/
Class<?> expectedReturnType = (Class<?>)parameterized.getRawType(); Class<?> expectedReturnType = (Class<?>)parameterized.getRawType();
context = new MapperContext<Object, Object>(valueFromDB, expectedReturnType, genericTypes); context = new MapperContext<Object, Object>(valueFromDB, expectedReturnType, genericTypes);

View file

@ -19,7 +19,7 @@ cp http.keytab /tmp/http.keytab
``` ```
Alternative is to configure different location for `keyTab` property in `kerberosrealm.json` configuration file (On Windows this will be needed). Alternative is to configure different location for `keyTab` property in `kerberosrealm.json` configuration file (On Windows this will be needed).
Note that in production, keytab file should be in secured location accessible just to the user under which is Keycloak server running. WARNING: In production, keytab file should be in secured location accessible just to the user under which is Keycloak server running.
**3)** Run Keycloak server and import `kerberosrealm.json` into it through admin console. This will import realm with sample application **3)** Run Keycloak server and import `kerberosrealm.json` into it through admin console. This will import realm with sample application
@ -37,12 +37,13 @@ Also if you are on Linux, make sure that record like:
``` ```
is in your `/etc/hosts` before other records for the 127.0.0.1 host to avoid issues related to incompatible reverse lookup (Ensure the similar for other OS as well) is in your `/etc/hosts` before other records for the 127.0.0.1 host to avoid issues related to incompatible reverse lookup (Ensure the similar for other OS as well)
**4)** Install kerberos client. This is platform dependent. If you are on Fedora, Ubuntu or RHEL, you can install package `freeipa-client`, which contains Kerberos client and bunch of other stuff.
**4)** Configure Kerberos client (On linux it's in file `/etc/krb5.conf` ). You need to configure `KEYCLOAK.ORG` realm and enable `forwardable` flag, which is needed **5)** Configure Kerberos client (On linux it's in file `/etc/krb5.conf` ). You need to configure `KEYCLOAK.ORG` realm for host `localhost` and enable `forwardable` flag, which is needed
for credential delegation example, as application needs to forward Kerberos ticket and authenticate with it against LDAP server. for credential delegation example, as application needs to forward Kerberos ticket and authenticate with it against LDAP server.
See [this file](https://github.com/keycloak/keycloak/blob/master/testsuite/integration/src/test/resources/kerberos/test-krb5.conf) for inspiration. See [this file](https://github.com/keycloak/keycloak/blob/master/testsuite/integration/src/test/resources/kerberos/test-krb5.conf) for inspiration.
**5)** Run ApacheDS based Kerberos server embedded in Keycloak. Easiest is to checkout keycloak sources, build and then run KerberosEmbeddedServer **6)** Run ApacheDS based Kerberos server embedded in Keycloak. Easiest is to checkout keycloak sources, build and then run KerberosEmbeddedServer
as shown here: as shown here:
``` ```
@ -55,12 +56,12 @@ mvn exec:java -Pkerberos
More details about embedded Kerberos server in [testsuite README](https://github.com/keycloak/keycloak/blob/master/misc/Testsuite.md#kerberos-server). More details about embedded Kerberos server in [testsuite README](https://github.com/keycloak/keycloak/blob/master/misc/Testsuite.md#kerberos-server).
**6)** Configure browser (Firefox, Chrome or other) and enable SPNEGO authentication and credential delegation for `localhost` . **7)** Configure browser (Firefox, Chrome or other) and enable SPNEGO authentication and credential delegation for `localhost` .
In Firefox it can be done by adding `localhost` to both `network.negotiate-auth.trusted-uris` and `network.negotiate-auth.delegation-uris` . In Firefox it can be done by adding `localhost` to both `network.negotiate-auth.trusted-uris` and `network.negotiate-auth.delegation-uris` .
More info in [testsuite README](https://github.com/keycloak/keycloak/blob/master/misc/Testsuite.md#kerberos-server). More info in [testsuite README](https://github.com/keycloak/keycloak/blob/master/misc/Testsuite.md#kerberos-server).
**7)** Test the example. Obtain kerberos ticket by running command from CMD (on linux): **8)** Test the example. Obtain kerberos ticket by running command from CMD (on linux):
``` ```
kinit hnelson@KEYCLOAK.ORG kinit hnelson@KEYCLOAK.ORG
``` ```

View file

View file

@ -0,0 +1,21 @@
<assembly>
<id>embedded-ldap</id>
<formats>
<format>dir</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
<dependencySet>
<unpack>false</unpack>
<useTransitiveDependencies>true</useTransitiveDependencies>
<useTransitiveFiltering>true</useTransitiveFiltering>
<includes>
<include>org.keycloak:keycloak-util-embedded-ldap</include>
<include>org.slf4j:slf4j-log4j12</include>
</includes>
</dependencySet>
</dependencySets>
</assembly>

View file

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>keycloak-examples-ldap-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.keycloak.example.demo</groupId>
<artifactId>keycloak-examples-embedded-ldap</artifactId>
<packaging>jar</packaging>
<name>LDAP Demo Application</name>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-util-embedded-ldap</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>embedded-ldap</finalName>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>assemble</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>assembly.xml</descriptor>
</descriptors>
<outputDirectory>
target
</outputDirectory>
<workDirectory>
target/assembly/work
</workDirectory>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>org.keycloak.example.ldap.embedded.EmbeddedLDAPLauncher</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,127 @@
package org.keycloak.example.ldap.embedded;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This is supposed to be executed from JAR file (java -jar target/embedded-ldap.jar ). For executing from IDE or Maven use directly
* the proper class (LDAPEmbeddedServer, KerberosEmbeddedServer or KerberosKeytabCreator)
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class EmbeddedLDAPLauncher {
public static void main(String[] args) throws Exception {
String arg = args.length == 0 ? null : args[0];
if (arg == null) {
System.err.println("Missing argument: either 'kerberos', 'ldap' or 'keytabCreator' must be passed as argument");
System.exit(1);
}
String clazz = null;
File home = getHome();
Properties defaultProperties = new Properties();
if (arg.equalsIgnoreCase("ldap")) {
clazz = "org.keycloak.util.ldap.LDAPEmbeddedServer";
File ldapLdif = file(home, "..", "ldap-app", "users.ldif");
defaultProperties.put("ldap.ldif", ldapLdif.getAbsolutePath());
} else if (arg.equalsIgnoreCase("kerberos")) {
clazz = "org.keycloak.util.ldap.KerberosEmbeddedServer";
File kerberosLdif = file(home, "..", "..", "kerberos", "users.ldif");
defaultProperties.put("ldap.ldif", kerberosLdif.getAbsolutePath());
} else if (arg.equalsIgnoreCase("keytabCreator")) {
clazz = "org.keycloak.util.ldap.KerberosKeytabCreator";
} else {
System.err.println("Invalid argument: '" + arg + "' . Either 'kerberos', 'ldap' or 'keytabCreator' must be passed as argument");
System.exit(1);
}
// Remove first argument
String[] newArgs = new String[args.length - 1];
for (int i=0 ; i<(args.length - 1) ; i++) {
newArgs[i] = args[i + 1];
}
System.out.println("Executing " + clazz);
runClass(clazz, newArgs, defaultProperties);
}
private static void runClass(String className, String[] args, Properties defaultProperties) throws Exception {
File home = getHome();
File lib = file(home, "target", "embedded-ldap");
if (!lib.exists()) {
System.err.println("Could not find lib directory: " + lib.toString());
System.exit(1);
} else {
System.out.println("Found directory to load jars: " + lib.getAbsolutePath());
}
List<URL> jars = new ArrayList<URL>();
for (File file : lib.listFiles()) {
jars.add(file.toURI().toURL());
}
URL[] urls = jars.toArray(new URL[jars.size()]);
URLClassLoader loader = new URLClassLoader(urls, EmbeddedLDAPLauncher.class.getClassLoader());
Class mainClass = loader.loadClass(className);
Method executeMethod = null;
for (Method m : mainClass.getMethods()) if (m.getName().equals("execute")) { executeMethod = m; break; }
Object obj = args;
executeMethod.invoke(null, obj, defaultProperties);
}
private static File getHome() {
String launcherPath = EmbeddedLDAPLauncher.class.getName().replace('.', '/') + ".class";
URL jarfile = EmbeddedLDAPLauncher.class.getClassLoader().getResource(launcherPath);
if (jarfile != null) {
Matcher m = Pattern.compile("jar:(file:.*)!/" + launcherPath).matcher(jarfile.toString());
if (m.matches()) {
try {
File jarPath = new File(new URI(m.group(1)));
File libPath = jarPath.getParentFile().getParentFile();
System.out.println("Home directory: " + libPath.toString());
if (!libPath.exists()) {
System.exit(1);
}
return libPath;
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
} else {
System.err.println("jar file null: " + launcherPath);
}
return null;
}
private static File file(File home, String... pathItems) {
File current = home;
for (String item : pathItems) {
if (item.equals("..")) {
current = current.getParentFile();
} else {
current = new File(current, item);
}
}
return current;
}
}

View file

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>keycloak-examples-ldap-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.keycloak.example.demo</groupId>
<artifactId>keycloak-examples-ldap-app</artifactId>
<packaging>war</packaging>
<name>LDAP Demo Application</name>
<repositories>
<repository>
<id>jboss</id>
<name>jboss repo</name>
<url>http://repository.jboss.org/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>ldap-portal</finalName>
<plugins>
<plugin>
<groupId>org.jboss.as.plugins</groupId>
<artifactId>jboss-as-maven-plugin</artifactId>
<configuration>
<skip>false</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
<configuration>
<skip>false</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

20
examples/ldap/pom.xml Normal file
View file

@ -0,0 +1,20 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-examples-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final-SNAPSHOT</version>
</parent>
<name>Keycloak LDAP Examples - Parent</name>
<description/>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-examples-ldap-parent</artifactId>
<packaging>pom</packaging>
<modules>
<module>embedded-ldap</module>
<module>ldap-app</module>
</modules>
</project>

View file

@ -44,5 +44,6 @@
<module>kerberos</module> <module>kerberos</module>
<module>themes</module> <module>themes</module>
<module>saml</module> <module>saml</module>
<module>ldap</module>
</modules> </modules>
</project> </project>

11
pom.xml
View file

@ -155,6 +155,7 @@
<module>testsuite</module> <module>testsuite</module>
<module>timer</module> <module>timer</module>
<module>export-import</module> <module>export-import</module>
<module>util</module>
</modules> </modules>
<dependencyManagement> <dependencyManagement>
@ -431,25 +432,21 @@
<groupId>org.apache.directory.server</groupId> <groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-core-annotations</artifactId> <artifactId>apacheds-core-annotations</artifactId>
<version>${apacheds.version}</version> <version>${apacheds.version}</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.directory.server</groupId> <groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-interceptor-kerberos</artifactId> <artifactId>apacheds-interceptor-kerberos</artifactId>
<version>${apacheds.version}</version> <version>${apacheds.version}</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.directory.server</groupId> <groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-server-annotations</artifactId> <artifactId>apacheds-server-annotations</artifactId>
<version>${apacheds.version}</version> <version>${apacheds.version}</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.directory.api</groupId> <groupId>org.apache.directory.api</groupId>
<artifactId>api-ldap-codec-standalone</artifactId> <artifactId>api-ldap-codec-standalone</artifactId>
<version>${apacheds.codec.version}</version> <version>${apacheds.codec.version}</version>
<scope>test</scope>
</dependency> </dependency>
<!-- Selenium --> <!-- Selenium -->
@ -1120,6 +1117,11 @@
<version>${project.version}</version> <version>${project.version}</version>
<type>zip</type> <type>zip</type>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-util-embedded-ldap</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-docs-dist</artifactId> <artifactId>keycloak-docs-dist</artifactId>
@ -1184,6 +1186,7 @@
<version>${project.version}</version> <version>${project.version}</version>
<classifier>classes</classifier> <classifier>classes</classifier>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>federation-properties-example</artifactId> <artifactId>federation-properties-example</artifactId>

View file

@ -190,40 +190,8 @@
<!-- Apache DS --> <!-- Apache DS -->
<dependency> <dependency>
<groupId>org.apache.directory.server</groupId> <groupId>org.keycloak</groupId>
<artifactId>apacheds-core-annotations</artifactId> <artifactId>keycloak-util-embedded-ldap</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.directory.jdbm</groupId>
<artifactId>apacheds-jdbm1</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-interceptor-kerberos</artifactId>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-server-annotations</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.directory.jdbm</groupId>
<artifactId>apacheds-jdbm1</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.directory.api</groupId>
<artifactId>api-ldap-codec-standalone</artifactId>
</dependency> </dependency>
<dependency> <dependency>
@ -344,7 +312,7 @@
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId> <artifactId>exec-maven-plugin</artifactId>
<configuration> <configuration>
<mainClass>org.keycloak.testsuite.ldap.LDAPEmbeddedServer</mainClass> <mainClass>org.keycloak.util.ldap.LDAPEmbeddedServer</mainClass>
<classpathScope>test</classpathScope> <classpathScope>test</classpathScope>
</configuration> </configuration>
</plugin> </plugin>
@ -359,7 +327,7 @@
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId> <artifactId>exec-maven-plugin</artifactId>
<configuration> <configuration>
<mainClass>org.keycloak.testsuite.ldap.KerberosEmbeddedServer</mainClass> <mainClass>org.keycloak.util.ldap.KerberosEmbeddedServer</mainClass>
<classpathScope>test</classpathScope> <classpathScope>test</classpathScope>
</configuration> </configuration>
</plugin> </plugin>

View file

@ -68,10 +68,10 @@ public class FederationProvidersIntegrationTest {
LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel); LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
FederationTestUtils.removeAllLDAPUsers(ldapFedProvider, appRealm); FederationTestUtils.removeAllLDAPUsers(ldapFedProvider, appRealm);
LDAPObject john = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johnkeycloak", "John", "Doe", "john@email.org", "1234"); LDAPObject john = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johnkeycloak", "John", "Doe", "john@email.org", null, "1234");
ldapFedProvider.getLdapIdentityStore().updatePassword(john, "Password1"); ldapFedProvider.getLdapIdentityStore().updatePassword(john, "Password1");
LDAPObject existing = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "existing", "Existing", "Foo", "existing@email.org", "5678"); LDAPObject existing = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "existing", "Existing", "Foo", "existing@email.org", null, "5678");
} }
}); });
@ -270,7 +270,7 @@ public class FederationProvidersIntegrationTest {
RealmModel appRealm = new RealmManager(session).getRealmByName("test"); RealmModel appRealm = new RealmManager(session).getRealmByName("test");
LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel); LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
LDAPObject johnZip = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johnzip", "John", "Zip", "johnzip@email.org", "12398"); LDAPObject johnZip = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johnzip", "John", "Zip", "johnzip@email.org", null, "12398");
// Remove default zipcode mapper and add the mapper for "POstalCode" to test case sensitivity // Remove default zipcode mapper and add the mapper for "POstalCode" to test case sensitivity
UserFederationMapperModel currentZipMapper = appRealm.getUserFederationMapperByName(ldapModel.getId(), "zipCodeMapper"); UserFederationMapperModel currentZipMapper = appRealm.getUserFederationMapperByName(ldapModel.getId(), "zipCodeMapper");
@ -295,7 +295,7 @@ public class FederationProvidersIntegrationTest {
RealmModel appRealm = new RealmManager(session).getRealmByName("test"); RealmModel appRealm = new RealmManager(session).getRealmByName("test");
LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel); LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
LDAPObject johnDirect = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johndirect", "John", "Direct", "johndirect@email.org", "12399"); LDAPObject johnDirect = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johndirect", "John", "Direct", "johndirect@email.org", null, "12399");
// Fetch user from LDAP and check that postalCode is filled // Fetch user from LDAP and check that postalCode is filled
UserModel user = session.users().getUserByUsername("johndirect", appRealm); UserModel user = session.users().getUserByUsername("johndirect", appRealm);
@ -370,7 +370,7 @@ public class FederationProvidersIntegrationTest {
// Add the user with some fullName into LDAP directly. Ensure that fullName is saved into "cn" attribute in LDAP (currently mapped to model firstName) // Add the user with some fullName into LDAP directly. Ensure that fullName is saved into "cn" attribute in LDAP (currently mapped to model firstName)
LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel); LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "fullname", "James Dee", "Dee", "fullname@email.org", "4578"); FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "fullname", "James Dee", "Dee", "fullname@email.org", null, "4578");
// add fullname mapper to the provider and remove "firstNameMapper". For this test, we will simply map full name to the LDAP attribute, which was before firstName ( "givenName" on active directory, "cn" on other LDAP servers) // add fullname mapper to the provider and remove "firstNameMapper". For this test, we will simply map full name to the LDAP attribute, which was before firstName ( "givenName" on active directory, "cn" on other LDAP servers)
firstNameMapper = appRealm.getUserFederationMapperByName(ldapModel.getId(), "first name"); firstNameMapper = appRealm.getUserFederationMapperByName(ldapModel.getId(), "first name");
@ -485,10 +485,10 @@ public class FederationProvidersIntegrationTest {
RealmModel appRealm = session.realms().getRealmByName("test"); RealmModel appRealm = session.realms().getRealmByName("test");
LDAPFederationProvider ldapProvider = FederationTestUtils.getLdapProvider(session, ldapModel); LDAPFederationProvider ldapProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
FederationTestUtils.addLDAPUser(ldapProvider, appRealm, "username1", "John1", "Doel1", "user1@email.org", "121"); FederationTestUtils.addLDAPUser(ldapProvider, appRealm, "username1", "John1", "Doel1", "user1@email.org", null, "121");
FederationTestUtils.addLDAPUser(ldapProvider, appRealm, "username2", "John2", "Doel2", "user2@email.org", "122"); FederationTestUtils.addLDAPUser(ldapProvider, appRealm, "username2", "John2", "Doel2", "user2@email.org", null, "122");
FederationTestUtils.addLDAPUser(ldapProvider, appRealm, "username3", "John3", "Doel3", "user3@email.org", "123"); FederationTestUtils.addLDAPUser(ldapProvider, appRealm, "username3", "John3", "Doel3", "user3@email.org", null, "123");
FederationTestUtils.addLDAPUser(ldapProvider, appRealm, "username4", "John4", "Doel4", "user4@email.org", "124"); FederationTestUtils.addLDAPUser(ldapProvider, appRealm, "username4", "John4", "Doel4", "user4@email.org", null, "124");
// Users are not at local store at this moment // Users are not at local store at this moment
Assert.assertNull(session.userStorage().getUserByUsername("username1", appRealm)); Assert.assertNull(session.userStorage().getUserByUsername("username1", appRealm));

View file

@ -47,7 +47,7 @@ class FederationTestUtils {
} }
public static LDAPObject addLDAPUser(LDAPFederationProvider ldapProvider, RealmModel realm, final String username, public static LDAPObject addLDAPUser(LDAPFederationProvider ldapProvider, RealmModel realm, final String username,
final String firstName, final String lastName, final String email, final String postalCode) { final String firstName, final String lastName, final String email, final String street, final String... postalCode) {
UserModel helperUser = new UserModelDelegate(null) { UserModel helperUser = new UserModelDelegate(null) {
@Override @Override
@ -74,6 +74,8 @@ class FederationTestUtils {
public List<String> getAttribute(String name) { public List<String> getAttribute(String name) {
if ("postal_code".equals(name)) { if ("postal_code".equals(name)) {
return Arrays.asList(postalCode); return Arrays.asList(postalCode);
} else if ("street".equals(name)) {
return Arrays.asList(street);
} else { } else {
return Collections.emptyList(); return Collections.emptyList();
} }

View file

@ -1,6 +1,9 @@
package org.keycloak.testsuite.federation; package org.keycloak.testsuite.federation;
import java.net.URL; import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -17,6 +20,7 @@ import org.junit.runners.MethodSorters;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.federation.ldap.LDAPFederationProvider; import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.LDAPFederationProviderFactory; import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
import org.keycloak.federation.ldap.idm.model.LDAPObject;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.LDAPConstants; import org.keycloak.models.LDAPConstants;
@ -60,6 +64,19 @@ public class LDAPMultipleAttributesTest {
FederationTestUtils.addZipCodeLDAPMapper(appRealm, ldapModel); FederationTestUtils.addZipCodeLDAPMapper(appRealm, ldapModel);
FederationTestUtils.addUserAttributeMapper(appRealm, ldapModel, "streetMapper", "street", LDAPConstants.STREET); FederationTestUtils.addUserAttributeMapper(appRealm, ldapModel, "streetMapper", "street", LDAPConstants.STREET);
// Remove current users and add default users
LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
FederationTestUtils.removeAllLDAPUsers(ldapFedProvider, appRealm);
LDAPObject james = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "jbrown", "James", "Brown", "jbrown@keycloak.org", "", "88441");
ldapFedProvider.getLdapIdentityStore().updatePassword(james, "password");
// User for testing duplicating surname and postalCode
LDAPObject bruce = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "bwilson", "Bruce", "Wilson", "bwilson@keycloak.org", "Elm 5", "88441", "77332");
bruce.setAttribute("sn", new LinkedHashSet<>(Arrays.asList("Wilson", "Schneider")));
ldapFedProvider.getLdapIdentityStore().update(bruce);
ldapFedProvider.getLdapIdentityStore().updatePassword(bruce, "password");
// Create ldap-portal client // Create ldap-portal client
ClientModel ldapClient = appRealm.addClient("ldap-portal"); ClientModel ldapClient = appRealm.addClient("ldap-portal");
ldapClient.addRedirectUri("/ldap-portal"); ldapClient.addRedirectUri("/ldap-portal");

View file

@ -76,13 +76,13 @@ public class LDAPRoleMappingsTest {
FederationTestUtils.removeAllLDAPRoles(manager.getSession(), appRealm, ldapModel, "financeRolesMapper"); FederationTestUtils.removeAllLDAPRoles(manager.getSession(), appRealm, ldapModel, "financeRolesMapper");
// Add some users for testing // Add some users for testing
LDAPObject john = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johnkeycloak", "John", "Doe", "john@email.org", "1234"); LDAPObject john = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johnkeycloak", "John", "Doe", "john@email.org", null, "1234");
ldapFedProvider.getLdapIdentityStore().updatePassword(john, "Password1"); ldapFedProvider.getLdapIdentityStore().updatePassword(john, "Password1");
LDAPObject mary = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "marykeycloak", "Mary", "Kelly", "mary@email.org", "5678"); LDAPObject mary = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "marykeycloak", "Mary", "Kelly", "mary@email.org", null, "5678");
ldapFedProvider.getLdapIdentityStore().updatePassword(mary, "Password1"); ldapFedProvider.getLdapIdentityStore().updatePassword(mary, "Password1");
LDAPObject rob = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "robkeycloak", "Rob", "Brown", "rob@email.org", "8910"); LDAPObject rob = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "robkeycloak", "Rob", "Brown", "rob@email.org", null, "8910");
ldapFedProvider.getLdapIdentityStore().updatePassword(rob, "Password1"); ldapFedProvider.getLdapIdentityStore().updatePassword(rob, "Password1");
// Add some roles for testing // Add some roles for testing

View file

@ -1,4 +1,4 @@
package org.keycloak.testsuite.ldap; package org.keycloak.testsuite.federation;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;

View file

@ -61,7 +61,7 @@ public class SyncProvidersTest {
FederationTestUtils.removeAllLDAPUsers(ldapFedProvider, appRealm); FederationTestUtils.removeAllLDAPUsers(ldapFedProvider, appRealm);
for (int i=1 ; i<=5 ; i++) { for (int i=1 ; i<=5 ; i++) {
LDAPObject ldapUser = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "user" + i, "User" + i + "FN", "User" + i + "LN", "user" + i + "@email.org", "12" + i); LDAPObject ldapUser = FederationTestUtils.addLDAPUser(ldapFedProvider, appRealm, "user" + i, "User" + i + "FN", "User" + i + "LN", "user" + i + "@email.org", null, "12" + i);
ldapFedProvider.getLdapIdentityStore().updatePassword(ldapUser, "Password1"); ldapFedProvider.getLdapIdentityStore().updatePassword(ldapUser, "Password1");
} }
@ -123,7 +123,7 @@ public class SyncProvidersTest {
// Add user to LDAP and update 'user5' in LDAP // Add user to LDAP and update 'user5' in LDAP
LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel); LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
FederationTestUtils.addLDAPUser(ldapFedProvider, testRealm, "user6", "User6FN", "User6LN", "user6@email.org", "126"); FederationTestUtils.addLDAPUser(ldapFedProvider, testRealm, "user6", "User6FN", "User6LN", "user6@email.org", null, "126");
LDAPObject ldapUser5 = ldapFedProvider.loadLDAPUserByUsername(testRealm, "user5"); LDAPObject ldapUser5 = ldapFedProvider.loadLDAPUserByUsername(testRealm, "user5");
// NOTE: Changing LDAP attributes directly here // NOTE: Changing LDAP attributes directly here
ldapUser5.setSingleAttribute(LDAPConstants.EMAIL, "user5Updated@email.org"); ldapUser5.setSingleAttribute(LDAPConstants.EMAIL, "user5Updated@email.org");

View file

@ -1,110 +0,0 @@
package org.keycloak.testsuite.ldap;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName;
import org.keycloak.util.KerberosSerializationUtils;
import sun.security.jgss.GSSNameImpl;
import sun.security.jgss.krb5.Krb5NameElement;
/**
* Factory for ApacheDS based LDAP and Kerberos servers
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class EmbeddedServersFactory {
private static final String DEFAULT_BASE_DN = "dc=keycloak,dc=org";
private static final String DEFAULT_BIND_HOST = "localhost";
private static final int DEFAULT_BIND_PORT = 10389;
private static final String DEFAULT_LDIF_FILE = "ldap/users.ldif";
private static final String DEFAULT_KERBEROS_LDIF_FILE = "kerberos/users-kerberos.ldif";
private static final String DEFAULT_KERBEROS_REALM = "KEYCLOAK.ORG";
private static final int DEFAULT_KDC_PORT = 6088;
private static final String DEFAULT_KDC_ENCRYPTION_TYPES = "aes128-cts-hmac-sha1-96, des-cbc-md5, des3-cbc-sha1-kd";
private String baseDN;
private String bindHost;
private int bindPort;
private String ldapSaslPrincipal;
private String ldifFile;
private String kerberosRealm;
private int kdcPort;
private String kdcEncryptionTypes;
public static EmbeddedServersFactory readConfiguration() {
EmbeddedServersFactory factory = new EmbeddedServersFactory();
factory.readProperties();
return factory;
}
protected void readProperties() {
this.baseDN = System.getProperty("ldap.baseDN");
this.bindHost = System.getProperty("ldap.host");
String bindPort = System.getProperty("ldap.port");
this.ldifFile = System.getProperty("ldap.ldif");
this.ldapSaslPrincipal = System.getProperty("ldap.saslPrincipal");
this.kerberosRealm = System.getProperty("kerberos.realm");
String kdcPort = System.getProperty("kerberos.port");
this.kdcEncryptionTypes = System.getProperty("kerberos.encTypes");
if (baseDN == null || baseDN.isEmpty()) {
baseDN = DEFAULT_BASE_DN;
}
if (bindHost == null || bindHost.isEmpty()) {
bindHost = DEFAULT_BIND_HOST;
}
this.bindPort = (bindPort == null || bindPort.isEmpty()) ? DEFAULT_BIND_PORT : Integer.parseInt(bindPort);
if (ldifFile == null || ldifFile.isEmpty()) {
ldifFile = DEFAULT_LDIF_FILE;
}
if (kerberosRealm == null || kerberosRealm.isEmpty()) {
kerberosRealm = DEFAULT_KERBEROS_REALM;
}
this.kdcPort = (kdcPort == null || kdcPort.isEmpty()) ? DEFAULT_KDC_PORT : Integer.parseInt(kdcPort);
if (kdcEncryptionTypes == null || kdcEncryptionTypes.isEmpty()) {
kdcEncryptionTypes = DEFAULT_KDC_ENCRYPTION_TYPES;
}
}
public LDAPEmbeddedServer createLdapServer() {
// Override LDIF file with default for embedded LDAP
if (ldifFile.equals(DEFAULT_KERBEROS_LDIF_FILE)) {
ldifFile = DEFAULT_LDIF_FILE;
}
return new LDAPEmbeddedServer(baseDN, bindHost, bindPort, ldifFile, ldapSaslPrincipal);
}
public KerberosEmbeddedServer createKerberosServer() {
// Override LDIF file with default for embedded Kerberos
if (ldifFile.equals(DEFAULT_LDIF_FILE)) {
ldifFile = DEFAULT_KERBEROS_LDIF_FILE;
}
// Init ldap sasl principal just when creating kerberos server
if (ldapSaslPrincipal == null || ldapSaslPrincipal.isEmpty()) {
try {
// Same algorithm like sun.security.krb5.PrincipalName constructor
GSSName gssName = GSSManager.getInstance().createName("ldap@" + bindHost, GSSName.NT_HOSTBASED_SERVICE);
GSSNameImpl gssName1 = (GSSNameImpl) gssName;
Krb5NameElement krb5NameElement = (Krb5NameElement) gssName1.getElement(KerberosSerializationUtils.KRB5_OID);
this.ldapSaslPrincipal = krb5NameElement.getKrb5PrincipalName().toString();
} catch (GSSException uhe) {
throw new RuntimeException(uhe);
}
}
return new KerberosEmbeddedServer(baseDN, bindHost, bindPort, ldifFile, ldapSaslPrincipal, kerberosRealm, kdcPort, kdcEncryptionTypes);
}
}

View file

@ -1,196 +0,0 @@
package org.keycloak.testsuite.ldap;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.text.StrSubstitutor;
import org.apache.directory.api.ldap.model.entry.DefaultEntry;
import org.apache.directory.api.ldap.model.exception.LdapEntryAlreadyExistsException;
import org.apache.directory.api.ldap.model.ldif.LdifEntry;
import org.apache.directory.api.ldap.model.ldif.LdifReader;
import org.apache.directory.api.ldap.model.schema.SchemaManager;
import org.apache.directory.server.core.api.DirectoryService;
import org.apache.directory.server.core.api.partition.Partition;
import org.apache.directory.server.core.factory.DSAnnotationProcessor;
import org.apache.directory.server.core.factory.PartitionFactory;
import org.apache.directory.server.ldap.LdapServer;
import org.apache.directory.server.protocol.shared.transport.TcpTransport;
import org.apache.directory.server.protocol.shared.transport.Transport;
import org.jboss.logging.Logger;
import org.keycloak.util.StreamUtil;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class LDAPEmbeddedServer {
private static final Logger log = Logger.getLogger(LDAPEmbeddedServer.class);
protected final String baseDN;
protected final String bindHost;
protected final int bindPort;
protected final String ldifFile;
protected final String ldapSaslPrincipal;
protected DirectoryService directoryService;
protected LdapServer ldapServer;
public static void main(String[] args) throws Exception {
EmbeddedServersFactory factory = EmbeddedServersFactory.readConfiguration();
LDAPEmbeddedServer ldapEmbeddedServer = factory.createLdapServer();
ldapEmbeddedServer.init();
ldapEmbeddedServer.start();
}
public LDAPEmbeddedServer(String baseDN, String bindHost, int bindPort, String ldifFile, String ldapSaslPrincipal) {
this.baseDN = baseDN;
this.bindHost = bindHost;
this.bindPort = bindPort;
this.ldifFile = ldifFile;
this.ldapSaslPrincipal = ldapSaslPrincipal;
}
public void init() throws Exception {
log.info("Creating LDAP Directory Service. Config: baseDN=" + baseDN + ", bindHost=" + bindHost + ", bindPort=" + bindPort +
", ldapSaslPrincipal=" + ldapSaslPrincipal);
this.directoryService = createDirectoryService();
log.info("Importing LDIF: " + ldifFile);
importLdif();
log.info("Creating LDAP Server");
this.ldapServer = createLdapServer();
}
public void start() throws Exception {
log.info("Starting LDAP Server");
ldapServer.start();
log.info("LDAP Server started");
}
protected DirectoryService createDirectoryService() throws Exception {
// Parse "keycloak" from "dc=keycloak,dc=org"
String dcName = baseDN.split(",")[0].substring(3);
InMemoryDirectoryServiceFactory dsf = new InMemoryDirectoryServiceFactory();
DirectoryService service = dsf.getDirectoryService();
service.setAccessControlEnabled(false);
service.setAllowAnonymousAccess(false);
service.getChangeLog().setEnabled(false);
dsf.init(dcName + "DS");
SchemaManager schemaManager = service.getSchemaManager();
PartitionFactory partitionFactory = dsf.getPartitionFactory();
Partition partition = partitionFactory.createPartition(
schemaManager,
service.getDnFactory(),
dcName,
this.baseDN,
1000,
new File(service.getInstanceLayout().getPartitionsDirectory(), dcName));
partition.setCacheService( service.getCacheService() );
partition.initialize();
partition.setSchemaManager( schemaManager );
// Inject the partition into the DirectoryService
service.addPartition( partition );
// Last, process the context entry
String entryLdif =
"dn: " + baseDN + "\n" +
"dc: " + dcName + "\n" +
"objectClass: top\n" +
"objectClass: domain\n\n";
DSAnnotationProcessor.injectEntries(service, entryLdif);
return service;
}
protected LdapServer createLdapServer() {
LdapServer ldapServer = new LdapServer();
ldapServer.setServiceName("DefaultLdapServer");
ldapServer.setSearchBaseDn(this.baseDN);
// Read the transports
Transport ldap = new TcpTransport(this.bindHost, this.bindPort, 3, 50);
ldapServer.addTransports( ldap );
// Associate the DS to this LdapServer
ldapServer.setDirectoryService( directoryService );
// Propagate the anonymous flag to the DS
directoryService.setAllowAnonymousAccess(false);
return ldapServer;
}
private void importLdif() throws Exception {
Map<String, String> map = new HashMap<String, String>();
map.put("hostname", this.bindHost);
if (this.ldapSaslPrincipal != null) {
map.put("ldapSaslPrincipal", this.ldapSaslPrincipal);
}
// For now, assume that LDIF file is on classpath
InputStream is;
if (ldifFile.startsWith("file:")) {
is = new URL(ldifFile).openStream();
} else {
is = getClass().getClassLoader().getResourceAsStream(ldifFile);
}
if (is == null) {
throw new IllegalStateException("LDIF file not found on classpath. Location was: " + ldifFile);
}
final String ldifContent = StrSubstitutor.replace(StreamUtil.readString(is), map);
log.info("Content of LDIF: " + ldifContent);
final SchemaManager schemaManager = directoryService.getSchemaManager();
for (LdifEntry ldifEntry : new LdifReader(IOUtils.toInputStream(ldifContent))) {
try {
directoryService.getAdminSession().add(new DefaultEntry(schemaManager, ldifEntry.getEntry()));
} catch (LdapEntryAlreadyExistsException ignore) {
log.debug("Entry " + ldifEntry.getNewRdn() + " already exists. Ignoring");
}
}
}
public void stop() throws Exception {
stopLdapServer();
shutdownDirectoryService();
}
protected void stopLdapServer() {
log.info("Stopping LDAP server.");
ldapServer.stop();
}
protected void shutdownDirectoryService() throws Exception {
log.info("Stopping Directory service.");
directoryService.shutdown();
log.info("Removing Directory service workfiles.");
FileUtils.deleteDirectory(directoryService.getInstanceLayout().getInstanceDirectory());
}
}

View file

@ -2,11 +2,12 @@ package org.keycloak.testsuite.rule;
import java.io.File; import java.io.File;
import java.net.URL; import java.net.URL;
import java.util.Properties;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.testsuite.ldap.EmbeddedServersFactory; import org.keycloak.testsuite.federation.LDAPTestConfiguration;
import org.keycloak.testsuite.ldap.LDAPTestConfiguration; import org.keycloak.util.ldap.KerberosEmbeddedServer;
import org.keycloak.testsuite.ldap.LDAPEmbeddedServer; import org.keycloak.util.ldap.LDAPEmbeddedServer;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -33,7 +34,11 @@ public class KerberosRule extends LDAPRule {
} }
@Override @Override
protected LDAPEmbeddedServer createServer(EmbeddedServersFactory factory) { protected LDAPEmbeddedServer createServer() {
return factory.createKerberosServer(); Properties defaultProperties = new Properties();
defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_DSF, LDAPEmbeddedServer.DSF_INMEMORY);
defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_LDIF_FILE, "classpath:kerberos/users-kerberos.ldif");
return new KerberosEmbeddedServer(defaultProperties);
} }
} }

View file

@ -1,11 +1,11 @@
package org.keycloak.testsuite.rule; package org.keycloak.testsuite.rule;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import org.junit.rules.ExternalResource; import org.junit.rules.ExternalResource;
import org.keycloak.testsuite.ldap.EmbeddedServersFactory; import org.keycloak.testsuite.federation.LDAPTestConfiguration;
import org.keycloak.testsuite.ldap.LDAPTestConfiguration; import org.keycloak.util.ldap.LDAPEmbeddedServer;
import org.keycloak.testsuite.ldap.LDAPEmbeddedServer;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -23,8 +23,7 @@ public class LDAPRule extends ExternalResource {
ldapTestConfiguration = LDAPTestConfiguration.readConfiguration(connectionPropsLocation); ldapTestConfiguration = LDAPTestConfiguration.readConfiguration(connectionPropsLocation);
if (ldapTestConfiguration.isStartEmbeddedLdapLerver()) { if (ldapTestConfiguration.isStartEmbeddedLdapLerver()) {
EmbeddedServersFactory factory = EmbeddedServersFactory.readConfiguration(); ldapEmbeddedServer = createServer();
ldapEmbeddedServer = createServer(factory);
ldapEmbeddedServer.init(); ldapEmbeddedServer.init();
ldapEmbeddedServer.start(); ldapEmbeddedServer.start();
} }
@ -47,8 +46,12 @@ public class LDAPRule extends ExternalResource {
return LDAP_CONNECTION_PROPERTIES_LOCATION; return LDAP_CONNECTION_PROPERTIES_LOCATION;
} }
protected LDAPEmbeddedServer createServer(EmbeddedServersFactory factory) { protected LDAPEmbeddedServer createServer() {
return factory.createLdapServer(); 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() { public Map<String, String> getConfig() {

View file

@ -18,30 +18,3 @@ dn: ou=FinanceRoles,dc=keycloak,dc=org
objectclass: top objectclass: top
objectclass: organizationalUnit objectclass: organizationalUnit
ou: FinanceRoles ou: FinanceRoles
dn: uid=jbrown,ou=People,dc=keycloak,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
uid: jbrown
cn: James
sn: Brown
mail: jbrown@keycloak.org
postalCode: 88441
userPassword: password
dn: uid=bwilson,ou=People,dc=keycloak,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
uid: bwilson
cn: Bruce
sn: Wilson
sn: Schneider
mail: bwilson@keycloak.org
postalCode: 88441
postalCode: 77332
street: Elm 5
userPassword: password

View file

@ -0,0 +1,91 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-util-embedded-ldap</artifactId>
<name>Keycloak Util Embedded LDAP</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-core-annotations</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.directory.jdbm</groupId>
<artifactId>apacheds-jdbm1</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-interceptor-kerberos</artifactId>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-server-annotations</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.directory.jdbm</groupId>
<artifactId>apacheds-jdbm1</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.directory.api</groupId>
<artifactId>api-ldap-codec-standalone</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,267 @@
package org.keycloak.util.ldap;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.apache.directory.api.ldap.model.constants.SchemaConstants;
import org.apache.directory.api.ldap.model.exception.LdapEntryAlreadyExistsException;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.schema.LdapComparator;
import org.apache.directory.api.ldap.model.schema.SchemaManager;
import org.apache.directory.api.ldap.model.schema.comparators.NormalizingComparator;
import org.apache.directory.api.ldap.model.schema.registries.ComparatorRegistry;
import org.apache.directory.api.ldap.model.schema.registries.SchemaLoader;
import org.apache.directory.api.ldap.schemaextractor.SchemaLdifExtractor;
import org.apache.directory.api.ldap.schemaextractor.impl.DefaultSchemaLdifExtractor;
import org.apache.directory.api.ldap.schemaloader.LdifSchemaLoader;
import org.apache.directory.api.ldap.schemamanager.impl.DefaultSchemaManager;
import org.apache.directory.api.util.exception.Exceptions;
import org.apache.directory.server.constants.ServerDNConstants;
import org.apache.directory.server.core.DefaultDirectoryService;
import org.apache.directory.server.core.api.CacheService;
import org.apache.directory.server.core.api.DirectoryService;
import org.apache.directory.server.core.api.InstanceLayout;
import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
import org.apache.directory.server.core.api.partition.Partition;
import org.apache.directory.server.core.api.schema.SchemaPartition;
import org.apache.directory.server.core.factory.DefaultDirectoryServiceFactory;
import org.apache.directory.server.core.factory.DirectoryServiceFactory;
import org.apache.directory.server.core.factory.JdbmPartitionFactory;
import org.apache.directory.server.core.factory.LdifPartitionFactory;
import org.apache.directory.server.core.factory.PartitionFactory;
import org.apache.directory.server.core.partition.ldif.LdifPartition;
import org.apache.directory.server.i18n.I18n;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Slightly modified version of {@link DefaultDirectoryServiceFactory} which allows persistence among restarts and uses LDIF partitions by default
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
class FileDirectoryServiceFactory implements DirectoryServiceFactory {
/** A logger for this class */
private static final Logger LOG = LoggerFactory.getLogger(FileDirectoryServiceFactory.class);
/** The directory service. */
private DirectoryService directoryService;
/** The partition factory. */
private PartitionFactory partitionFactory;
public FileDirectoryServiceFactory()
{
try
{
// creating the instance here so that
// we we can set some properties like accesscontrol, anon access
// before starting up the service
directoryService = new DefaultDirectoryService();
// no need to register a shutdown hook during tests because this
// starts a lot of threads and slows down test execution
directoryService.setShutdownHookEnabled( false );
}
catch ( Exception e )
{
throw new RuntimeException( e );
}
try
{
String typeName = System.getProperty( "apacheds.partition.factory" );
if ( typeName != null )
{
Class<? extends PartitionFactory> type = ( Class<? extends PartitionFactory> ) Class.forName( typeName );
partitionFactory = type.newInstance();
}
else
{
// partitionFactory = new JdbmPartitionFactory();
partitionFactory = new LdifPartitionFactory();
}
}
catch ( Exception e )
{
LOG.error( "Error instantiating custom partiton factory", e );
throw new RuntimeException( e );
}
}
public FileDirectoryServiceFactory( DirectoryService directoryService, PartitionFactory partitionFactory )
{
this.directoryService = directoryService;
this.partitionFactory = partitionFactory;
}
/**
* {@inheritDoc}
*/
public void init( String name ) throws Exception
{
if ( ( directoryService != null ) && directoryService.isStarted() ) {
return;
}
build(name);
}
/**
* Build the working directory
*/
private void buildInstanceDirectory( String name ) throws IOException
{
String instanceDirectory = System.getProperty( "workingDirectory" );
if ( instanceDirectory == null )
{
instanceDirectory = System.getProperty( "java.io.tmpdir" ) + "/server-work-" + name;
}
InstanceLayout instanceLayout = new InstanceLayout( instanceDirectory );
/*if ( instanceLayout.getInstanceDirectory().exists() )
{
try
{
FileUtils.deleteDirectory(instanceLayout.getInstanceDirectory());
}
catch ( IOException e )
{
LOG.warn( "couldn't delete the instance directory before initializing the DirectoryService", e );
}
}*/
directoryService.setInstanceLayout( instanceLayout );
}
/**
* Inits the schema and schema partition.
*/
private void initSchema() throws Exception
{
File workingDirectory = directoryService.getInstanceLayout().getPartitionsDirectory();
// Extract the schema on disk (a brand new one) and load the registries
File schemaRepository = new File( workingDirectory, "schema" );
SchemaLdifExtractor extractor = new DefaultSchemaLdifExtractor( workingDirectory );
try
{
extractor.extractOrCopy();
}
catch ( IOException ioe )
{
// The schema has already been extracted, bypass
}
SchemaLoader loader = new LdifSchemaLoader( schemaRepository );
SchemaManager schemaManager = new DefaultSchemaManager( loader );
// We have to load the schema now, otherwise we won't be able
// to initialize the Partitions, as we won't be able to parse
// and normalize their suffix Dn
schemaManager.loadAllEnabled();
// Tell all the normalizer comparators that they should not normalize anything
ComparatorRegistry comparatorRegistry = schemaManager.getComparatorRegistry();
for ( LdapComparator<?> comparator : comparatorRegistry )
{
if ( comparator instanceof NormalizingComparator)
{
( ( NormalizingComparator ) comparator ).setOnServer();
}
}
directoryService.setSchemaManager( schemaManager );
// Init the LdifPartition
LdifPartition ldifPartition = new LdifPartition( schemaManager, directoryService.getDnFactory() );
ldifPartition.setPartitionPath( new File( workingDirectory, "schema" ).toURI() );
SchemaPartition schemaPartition = new SchemaPartition( schemaManager );
schemaPartition.setWrappedPartition( ldifPartition );
directoryService.setSchemaPartition( schemaPartition );
List<Throwable> errors = schemaManager.getErrors();
if ( errors.size() != 0 )
{
throw new Exception( I18n.err(I18n.ERR_317, Exceptions.printErrors(errors)) );
}
}
/**
* Inits the system partition.
*
* @throws Exception the exception
*/
private void initSystemPartition() throws Exception
{
// change the working directory to something that is unique
// on the system and somewhere either under target directory
// or somewhere in a temp area of the machine.
// Inject the System Partition
Partition systemPartition = partitionFactory.createPartition( directoryService.getSchemaManager(),
directoryService.getDnFactory(),
"system", ServerDNConstants.SYSTEM_DN, 500,
new File( directoryService.getInstanceLayout().getPartitionsDirectory(), "system" ) );
systemPartition.setSchemaManager(directoryService.getSchemaManager());
partitionFactory.addIndex(systemPartition, SchemaConstants.OBJECT_CLASS_AT, 100 );
directoryService.setSystemPartition( systemPartition );
}
/**
* Builds the directory server instance.
*
* @param name the instance name
*/
private void build( String name ) throws Exception
{
directoryService.setInstanceId( name );
buildInstanceDirectory( name );
CacheService cacheService = new CacheService();
cacheService.initialize( directoryService.getInstanceLayout() );
directoryService.setCacheService( cacheService );
// Init the service now
initSchema();
initSystemPartition();
directoryService.startup();
}
/**
* {@inheritDoc}
*/
public DirectoryService getDirectoryService() throws Exception
{
return directoryService;
}
/**
* {@inheritDoc}
*/
public PartitionFactory getPartitionFactory() throws Exception
{
return partitionFactory;
}
}

View file

@ -1,4 +1,4 @@
package org.keycloak.testsuite.ldap; package org.keycloak.util.ldap;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -76,7 +76,7 @@ class InMemoryDirectoryServiceFactory implements DirectoryServiceFactory {
directoryService.setInstanceId(name); directoryService.setInstanceId(name);
// instance layout // instance layout
InstanceLayout instanceLayout = new InstanceLayout(System.getProperty("java.io.tmpdir") + "/server-work-" + name); InstanceLayout instanceLayout = new InstanceLayout(System.getProperty("java.io.tmpdir") + "/server-work-inmemory-" + name);
if (instanceLayout.getInstanceDirectory().exists()) { if (instanceLayout.getInstanceDirectory().exists()) {
try { try {
FileUtils.deleteDirectory(instanceLayout.getInstanceDirectory()); FileUtils.deleteDirectory(instanceLayout.getInstanceDirectory());

View file

@ -1,4 +1,4 @@
package org.keycloak.testsuite.ldap; package org.keycloak.util.ldap;
import java.net.URL; import java.net.URL;
import java.util.Map; import java.util.Map;
@ -61,7 +61,7 @@ class InMemorySchemaPartition extends AbstractLdifPartition {
// add mandatory attributes // add mandatory attributes
if (entry.get(SchemaConstants.ENTRY_CSN_AT) == null) { if (entry.get(SchemaConstants.ENTRY_CSN_AT) == null) {
entry.add(SchemaConstants.ENTRY_CSN_AT, defaultCSNFactory.newInstance().toString()); entry.add(SchemaConstants.ENTRY_CSN_AT, AbstractLdifPartition.defaultCSNFactory.newInstance().toString());
} }
if (entry.get(SchemaConstants.ENTRY_UUID_AT) == null) { if (entry.get(SchemaConstants.ENTRY_UUID_AT) == null) {
entry.add(SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString()); entry.add(SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString());

View file

@ -1,9 +1,10 @@
package org.keycloak.testsuite.ldap; package org.keycloak.util.ldap;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Properties;
import java.util.Set; import java.util.Set;
import javax.security.auth.kerberos.KerberosPrincipal; import javax.security.auth.kerberos.KerberosPrincipal;
@ -25,7 +26,13 @@ import org.apache.directory.server.protocol.shared.transport.UdpTransport;
import org.apache.directory.shared.kerberos.KerberosTime; import org.apache.directory.shared.kerberos.KerberosTime;
import org.apache.directory.shared.kerberos.KerberosUtils; import org.apache.directory.shared.kerberos.KerberosUtils;
import org.apache.directory.shared.kerberos.codec.types.EncryptionType; import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.util.KerberosSerializationUtils;
import sun.security.jgss.GSSNameImpl;
import sun.security.jgss.krb5.Krb5NameElement;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -34,6 +41,16 @@ public class KerberosEmbeddedServer extends LDAPEmbeddedServer {
private static final Logger log = Logger.getLogger(KerberosEmbeddedServer.class); private static final Logger log = Logger.getLogger(KerberosEmbeddedServer.class);
public static final String PROPERTY_KERBEROS_REALM = "kerberos.realm";
public static final String PROPERTY_KDC_PORT = "kerberos.port";
public static final String PROPERTY_KDC_ENCTYPES = "kerberos.encTypes";
private static final String DEFAULT_KERBEROS_LDIF_FILE = "classpath:kerberos/default-users.ldif";
private static final String DEFAULT_KERBEROS_REALM = "KEYCLOAK.ORG";
private static final String DEFAULT_KDC_PORT = "6088";
private static final String DEFAULT_KDC_ENCRYPTION_TYPES = "aes128-cts-hmac-sha1-96, des-cbc-md5, des3-cbc-sha1-kd";
private final String kerberosRealm; private final String kerberosRealm;
private final int kdcPort; private final int kdcPort;
private final String kdcEncryptionTypes; private final String kdcEncryptionTypes;
@ -42,18 +59,53 @@ public class KerberosEmbeddedServer extends LDAPEmbeddedServer {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
EmbeddedServersFactory factory = EmbeddedServersFactory.readConfiguration(); Properties defaultProperties = new Properties();
KerberosEmbeddedServer kerberosEmbeddedServer = factory.createKerberosServer(); defaultProperties.put(PROPERTY_DSF, DSF_FILE);
execute(args, defaultProperties);
}
public static void execute(String[] args, Properties defaultProperties) throws Exception {
final KerberosEmbeddedServer kerberosEmbeddedServer = new KerberosEmbeddedServer(defaultProperties);
kerberosEmbeddedServer.init(); kerberosEmbeddedServer.init();
kerberosEmbeddedServer.start(); kerberosEmbeddedServer.start();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
kerberosEmbeddedServer.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
});
} }
protected KerberosEmbeddedServer(String baseDN, String bindHost, int bindPort, String ldifFile, String ldapSaslPrincipal, String kerberosRealm, int kdcPort, String kdcEncryptionTypes) { public KerberosEmbeddedServer(Properties defaultProperties) {
super(baseDN, bindHost, bindPort, ldifFile, ldapSaslPrincipal); super(defaultProperties);
this.kdcEncryptionTypes = kdcEncryptionTypes;
this.kerberosRealm = kerberosRealm; this.ldifFile = readProperty(PROPERTY_LDIF_FILE, DEFAULT_KERBEROS_LDIF_FILE);
this.kdcPort = kdcPort;
this.kerberosRealm = readProperty(PROPERTY_KERBEROS_REALM, DEFAULT_KERBEROS_REALM);
String kdcPort = readProperty(PROPERTY_KDC_PORT, DEFAULT_KDC_PORT);
this.kdcPort = Integer.parseInt(kdcPort);
this.kdcEncryptionTypes = readProperty(PROPERTY_KDC_ENCTYPES, DEFAULT_KDC_ENCRYPTION_TYPES);
if (ldapSaslPrincipal == null || ldapSaslPrincipal.isEmpty()) {
try {
// Same algorithm like sun.security.krb5.PrincipalName constructor
GSSName gssName = GSSManager.getInstance().createName("ldap@" + bindHost, GSSName.NT_HOSTBASED_SERVICE);
GSSNameImpl gssName1 = (GSSNameImpl) gssName;
Krb5NameElement krb5NameElement = (Krb5NameElement) gssName1.getElement(KerberosSerializationUtils.KRB5_OID);
this.ldapSaslPrincipal = krb5NameElement.getKrb5PrincipalName().toString();
} catch (GSSException uhe) {
throw new RuntimeException(uhe);
}
}
} }
@ -79,7 +131,7 @@ public class KerberosEmbeddedServer extends LDAPEmbeddedServer {
protected LdapServer createLdapServer() { protected LdapServer createLdapServer() {
LdapServer ldapServer = super.createLdapServer(); LdapServer ldapServer = super.createLdapServer();
ldapServer.setSaslHost( this.bindHost ); ldapServer.setSaslHost(this.bindHost);
ldapServer.setSaslPrincipal( this.ldapSaslPrincipal); ldapServer.setSaslPrincipal( this.ldapSaslPrincipal);
ldapServer.setSaslRealms(new ArrayList<String>()); ldapServer.setSaslRealms(new ArrayList<String>());

View file

@ -1,10 +1,11 @@
package org.keycloak.testsuite.ldap; package org.keycloak.util.ldap;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory; import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
import org.apache.directory.server.kerberos.shared.keytab.Keytab; import org.apache.directory.server.kerberos.shared.keytab.Keytab;
@ -34,7 +35,7 @@ public class KerberosKeytabCreator {
System.out.println("-------------------------"); System.out.println("-------------------------");
System.out.println("Arguments missing or invalid. Required arguments are: <principalName> <passPhrase> <outputKeytabFile>"); System.out.println("Arguments missing or invalid. Required arguments are: <principalName> <passPhrase> <outputKeytabFile>");
System.out.println("Example of usage:"); System.out.println("Example of usage:");
System.out.println("mvn exec:java -Dexec.mainClass=\"org.keycloak.testsuite.ldap.KerberosKeytabCreator\" -Dexec.args=\"HTTP/localhost@KEYCLOAK.ORG httppwd src/main/resources/kerberos/http.keytab\""); System.out.println("java -jar embedded-ldap/target/embedded-ldap.jar keytabCreator HTTP/localhost@KEYCLOAK.ORG httppassword /tmp/http.keytab");
} else { } else {
final File keytabFile = new File(args[2]); final File keytabFile = new File(args[2]);
createKeytab(args[0], args[1], keytabFile); createKeytab(args[0], args[1], keytabFile);
@ -42,6 +43,11 @@ public class KerberosKeytabCreator {
} }
} }
// Just for the reflection purposes
public static void execute(String[] args, Properties defaultProperties) throws Exception {
main(args);
}
/** /**
* Creates a keytab file for given principal. * Creates a keytab file for given principal.
* *

View file

@ -0,0 +1,272 @@
package org.keycloak.util.ldap;
import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.text.StrSubstitutor;
import org.apache.directory.api.ldap.model.entry.DefaultEntry;
import org.apache.directory.api.ldap.model.exception.LdapEntryAlreadyExistsException;
import org.apache.directory.api.ldap.model.ldif.LdifEntry;
import org.apache.directory.api.ldap.model.ldif.LdifReader;
import org.apache.directory.api.ldap.model.schema.SchemaManager;
import org.apache.directory.server.core.api.DirectoryService;
import org.apache.directory.server.core.api.partition.Partition;
import org.apache.directory.server.core.factory.DirectoryServiceFactory;
import org.apache.directory.server.core.factory.PartitionFactory;
import org.apache.directory.server.ldap.LdapServer;
import org.apache.directory.server.protocol.shared.transport.TcpTransport;
import org.apache.directory.server.protocol.shared.transport.Transport;
import org.jboss.logging.Logger;
import org.keycloak.util.FindFile;
import org.keycloak.util.StreamUtil;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class LDAPEmbeddedServer {
private static final Logger log = Logger.getLogger(LDAPEmbeddedServer.class);
public static final String PROPERTY_BASE_DN = "ldap.baseDN";
public static final String PROPERTY_BIND_HOST = "ldap.host";
public static final String PROPERTY_BIND_PORT = "ldap.port";
public static final String PROPERTY_LDIF_FILE = "ldap.ldif";
public static final String PROPERTY_SASL_PRINCIPAL = "ldap.saslPrincipal";
public static final String PROPERTY_DSF = "ldap.dsf";
private static final String DEFAULT_BASE_DN = "dc=keycloak,dc=org";
private static final String DEFAULT_BIND_HOST = "localhost";
private static final String DEFAULT_BIND_PORT = "10389";
private static final String DEFAULT_LDIF_FILE = "classpath:ldap/default-users.ldif";
public static final String DSF_INMEMORY = "mem";
public static final String DSF_FILE = "file";
public static final String DEFAULT_DSF = DSF_FILE;
protected Properties defaultProperties;
protected String baseDN;
protected String bindHost;
protected int bindPort;
protected String ldifFile;
protected String ldapSaslPrincipal;
protected String directoryServiceFactory;
protected DirectoryService directoryService;
protected LdapServer ldapServer;
public static void main(String[] args) throws Exception {
Properties defaultProperties = new Properties();
defaultProperties.put(PROPERTY_DSF, DSF_FILE);
execute(args, defaultProperties);
}
public static void execute(String[] args, Properties defaultProperties) throws Exception {
final LDAPEmbeddedServer ldapEmbeddedServer = new LDAPEmbeddedServer(defaultProperties);
ldapEmbeddedServer.init();
ldapEmbeddedServer.start();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
ldapEmbeddedServer.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public LDAPEmbeddedServer(Properties defaultProperties) {
this.defaultProperties = defaultProperties;
this.baseDN = readProperty(PROPERTY_BASE_DN, DEFAULT_BASE_DN);
this.bindHost = readProperty(PROPERTY_BIND_HOST, DEFAULT_BIND_HOST);
String bindPort = readProperty(PROPERTY_BIND_PORT, DEFAULT_BIND_PORT);
this.bindPort = Integer.parseInt(bindPort);
this.ldifFile = readProperty(PROPERTY_LDIF_FILE, DEFAULT_LDIF_FILE);
this.ldapSaslPrincipal = readProperty(PROPERTY_SASL_PRINCIPAL, null);
this.directoryServiceFactory = readProperty(PROPERTY_DSF, DEFAULT_DSF);
}
protected String readProperty(String propertyName, String defaultValue) {
String value = System.getProperty(propertyName);
if (value == null || value.isEmpty()) {
value = (String) this.defaultProperties.get(propertyName);
}
if (value == null || value.isEmpty()) {
value = defaultValue;
}
return value;
}
public void init() throws Exception {
log.info("Creating LDAP Directory Service. Config: baseDN=" + baseDN + ", bindHost=" + bindHost + ", bindPort=" + bindPort +
", ldapSaslPrincipal=" + ldapSaslPrincipal + ", directoryServiceFactory=" + directoryServiceFactory + ", ldif=" + ldifFile);
this.directoryService = createDirectoryService();
log.info("Importing LDIF: " + ldifFile);
importLdif();
log.info("Creating LDAP Server");
this.ldapServer = createLdapServer();
}
public void start() throws Exception {
log.info("Starting LDAP Server");
ldapServer.start();
log.info("LDAP Server started");
}
protected DirectoryService createDirectoryService() throws Exception {
// Parse "keycloak" from "dc=keycloak,dc=org"
String dcName = baseDN.split(",")[0];
dcName = dcName.substring(dcName.indexOf("=") + 1);
DirectoryServiceFactory dsf;
if (this.directoryServiceFactory.equals(DSF_INMEMORY)) {
dsf = new InMemoryDirectoryServiceFactory();
} else if (this.directoryServiceFactory.equals(DSF_FILE)) {
dsf = new FileDirectoryServiceFactory();
} else {
throw new IllegalStateException("Unknown value of directoryServiceFactory: " + this.directoryServiceFactory);
}
DirectoryService service = dsf.getDirectoryService();
service.setAccessControlEnabled(false);
service.setAllowAnonymousAccess(false);
service.getChangeLog().setEnabled(false);
dsf.init(dcName + "DS");
SchemaManager schemaManager = service.getSchemaManager();
PartitionFactory partitionFactory = dsf.getPartitionFactory();
Partition partition = partitionFactory.createPartition(
schemaManager,
service.getDnFactory(),
dcName,
this.baseDN,
1000,
new File(service.getInstanceLayout().getPartitionsDirectory(), dcName));
partition.setCacheService( service.getCacheService() );
partition.initialize();
partition.setSchemaManager( schemaManager );
// Inject the partition into the DirectoryService
service.addPartition( partition );
// Last, process the context entry
String entryLdif =
"dn: " + baseDN + "\n" +
"dc: " + dcName + "\n" +
"objectClass: top\n" +
"objectClass: domain\n\n";
importLdifContent(service, entryLdif);
return service;
}
protected LdapServer createLdapServer() {
LdapServer ldapServer = new LdapServer();
ldapServer.setServiceName("DefaultLdapServer");
ldapServer.setSearchBaseDn(this.baseDN);
// Read the transports
Transport ldap = new TcpTransport(this.bindHost, this.bindPort, 3, 50);
ldapServer.addTransports( ldap );
// Associate the DS to this LdapServer
ldapServer.setDirectoryService( directoryService );
// Propagate the anonymous flag to the DS
directoryService.setAllowAnonymousAccess(false);
return ldapServer;
}
private void importLdif() throws Exception {
Map<String, String> map = new HashMap<String, String>();
map.put("hostname", this.bindHost);
if (this.ldapSaslPrincipal != null) {
map.put("ldapSaslPrincipal", this.ldapSaslPrincipal);
}
// Find LDIF file on filesystem or classpath ( if it's like classpath:ldap/users.ldif )
InputStream is = FindFile.findFile(ldifFile);
if (is == null) {
throw new IllegalStateException("LDIF file not found on classpath or on file system. Location was: " + ldifFile);
}
final String ldifContent = StrSubstitutor.replace(StreamUtil.readString(is), map);
log.info("Content of LDIF: " + ldifContent);
final SchemaManager schemaManager = directoryService.getSchemaManager();
importLdifContent(directoryService, ldifContent);
}
private static void importLdifContent(DirectoryService directoryService, String ldifContent) throws Exception {
LdifReader ldifReader = new LdifReader(IOUtils.toInputStream(ldifContent));
try {
for (LdifEntry ldifEntry : ldifReader) {
try {
directoryService.getAdminSession().add(new DefaultEntry(directoryService.getSchemaManager(), ldifEntry.getEntry()));
} catch (LdapEntryAlreadyExistsException ignore) {
log.info("Entry " + ldifEntry.getDn() + " already exists. Ignoring");
}
}
} finally {
ldifReader.close();
}
}
public void stop() throws Exception {
stopLdapServer();
shutdownDirectoryService();
}
protected void stopLdapServer() {
log.info("Stopping LDAP server.");
ldapServer.stop();
}
protected void shutdownDirectoryService() throws Exception {
log.info("Stopping Directory service.");
directoryService.shutdown();
// Delete workfiles just for 'inmemory' implementation used in tests. Normally we want LDAP data to persist
File instanceDir = directoryService.getInstanceLayout().getInstanceDirectory();
if (this.directoryServiceFactory.equals(DSF_INMEMORY)) {
log.infof("Removing Directory service workfiles: %s", instanceDir.getAbsolutePath());
FileUtils.deleteDirectory(instanceDir);
} else {
log.info("Working LDAP directory not deleted. Delete it manually if you want to start with fresh LDAP data. Directory location: " + instanceDir.getAbsolutePath());
}
}
}

View file

@ -0,0 +1,90 @@
dn: dc=keycloak,dc=org
objectclass: dcObject
objectclass: organization
o: Keycloak
dc: Keycloak
dn: ou=People,dc=keycloak,dc=org
objectClass: organizationalUnit
objectClass: top
ou: People
dn: uid=krbtgt,ou=People,dc=keycloak,dc=org
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: krb5principal
objectClass: krb5kdcentry
cn: KDC Service
sn: Service
uid: krbtgt
userPassword: secret
krb5PrincipalName: krbtgt/KEYCLOAK.ORG@KEYCLOAK.ORG
krb5KeyVersionNumber: 0
dn: uid=ldap,ou=People,dc=keycloak,dc=org
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: krb5principal
objectClass: krb5kdcentry
cn: LDAP
sn: Service
uid: ldap
userPassword: randall
krb5PrincipalName: ${ldapSaslPrincipal}
krb5KeyVersionNumber: 0
dn: uid=HTTP,ou=People,dc=keycloak,dc=org
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: krb5principal
objectClass: krb5kdcentry
cn: HTTP
sn: Service
uid: HTTP
userPassword: httppwd
krb5PrincipalName: HTTP/${hostname}@KEYCLOAK.ORG
krb5KeyVersionNumber: 0
dn: uid=hnelson,ou=People,dc=keycloak,dc=org
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: krb5principal
objectClass: krb5kdcentry
cn: Horatio
sn: Nelson
mail: hnelson@keycloak.org
uid: hnelson
userPassword: secret
krb5PrincipalName: hnelson@KEYCLOAK.ORG
krb5KeyVersionNumber: 0
dn: uid=jduke,ou=People,dc=keycloak,dc=org
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: krb5principal
objectClass: krb5kdcentry
cn: Java
sn: Duke
mail: jduke@keycloak.org
uid: jduke
userPassword: theduke
krb5PrincipalName: jduke@KEYCLOAK.ORG
krb5KeyVersionNumber: 0
dn: uid=gsstestserver,ou=People,dc=keycloak,dc=org
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: krb5principal
objectClass: krb5kdcentry
cn: gsstestserver
sn: Service
uid: gsstestserver
userPassword: gsstestpwd
krb5PrincipalName: gsstestserver/xxx@KEYCLOAK.ORG
krb5KeyVersionNumber: 0

View file

@ -0,0 +1,47 @@
dn: dc=keycloak,dc=org
objectclass: dcObject
objectclass: organization
o: Keycloak
dc: Keycloak
dn: ou=People,dc=keycloak,dc=org
objectclass: top
objectclass: organizationalUnit
ou: People
dn: ou=RealmRoles,dc=keycloak,dc=org
objectclass: top
objectclass: organizationalUnit
ou: RealmRoles
dn: ou=FinanceRoles,dc=keycloak,dc=org
objectclass: top
objectclass: organizationalUnit
ou: FinanceRoles
dn: uid=jbrown,ou=People,dc=keycloak,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
uid: jbrown
cn: James
sn: Brown
mail: jbrown@keycloak.org
postalCode: 88441
userPassword: password
dn: uid=bwilson,ou=People,dc=keycloak,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
uid: bwilson
cn: Bruce
sn: Wilson
sn: Schneider
mail: bwilson@keycloak.org
postalCode: 88441
postalCode: 77332
street: Elm 5
userPassword: password

View file

@ -0,0 +1,9 @@
log4j.rootLogger=info, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n
log4j.logger.org.keycloak=info
log4j.logger.org.apache.directory.api=warn
log4j.logger.org.apache.directory.server.core=warn

22
util/pom.xml Normal file
View file

@ -0,0 +1,22 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.4.0.Final-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<name>Keycloak Util Parent</name>
<description/>
<modelVersion>4.0.0</modelVersion>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-util-parent</artifactId>
<packaging>pom</packaging>
<modules>
<module>embedded-ldap</module>
</modules>
</project>