Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Bill Burke 2015-11-12 11:31:55 -05:00
commit 0d20e3c7ff
35 changed files with 1141 additions and 253 deletions

View file

@ -1,18 +1,5 @@
package org.keycloak.connections.jpa;
import org.hibernate.ejb.AvailableSettings;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
import org.keycloak.connections.jpa.util.JpaUtils;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import javax.naming.InitialContext;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
@ -22,10 +9,25 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.naming.InitialContext;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.sql.DataSource;
import org.hibernate.ejb.AvailableSettings;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
import org.keycloak.connections.jpa.util.JpaUtils;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ServerInfoAwareProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class DefaultJpaConnectionProviderFactory implements JpaConnectionProviderFactory {
public class DefaultJpaConnectionProviderFactory implements JpaConnectionProviderFactory, ServerInfoAwareProviderFactory {
private static final Logger logger = Logger.getLogger(DefaultJpaConnectionProviderFactory.class);

View file

@ -1,10 +1,10 @@
package org.keycloak.connections.jpa;
import org.keycloak.provider.ServerInfoAwareProviderFactory;
import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface JpaConnectionProviderFactory extends ServerInfoAwareProviderFactory<JpaConnectionProvider> {
public interface JpaConnectionProviderFactory extends ProviderFactory<JpaConnectionProvider> {
}

View file

@ -1,11 +1,12 @@
package org.keycloak.connections.mongo;
import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoClientURI;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import java.lang.reflect.Method;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.net.ssl.SSLSocketFactory;
import org.jboss.logging.Logger;
import org.keycloak.Config;
@ -15,18 +16,19 @@ import org.keycloak.connections.mongo.impl.context.TransactionMongoStoreInvocati
import org.keycloak.connections.mongo.updater.MongoUpdaterProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ServerInfoAwareProviderFactory;
import javax.net.ssl.SSLSocketFactory;
import java.lang.reflect.Method;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoClientURI;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class DefaultMongoConnectionFactoryProvider implements MongoConnectionProviderFactory {
public class DefaultMongoConnectionFactoryProvider implements MongoConnectionProviderFactory, ServerInfoAwareProviderFactory {
// TODO Make it dynamic
private String[] entities = new String[]{

View file

@ -1,9 +1,9 @@
package org.keycloak.connections.mongo;
import org.keycloak.provider.ServerInfoAwareProviderFactory;
import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface MongoConnectionProviderFactory extends ServerInfoAwareProviderFactory<MongoConnectionProvider> {
public interface MongoConnectionProviderFactory extends ProviderFactory<MongoConnectionProvider> {
}

View file

@ -114,6 +114,10 @@
<name>picketlink.version</name>
<value>${picketlink.version}</value>
</injection>
<injection>
<name>wildfly.version</name>
<value>${wildfly.version}</value>
</injection>
</injections>
<options>
<xmlTransformerType>saxon</xmlTransformerType>

View file

@ -16,7 +16,7 @@
</para>
<para>
For example to implement the Event Listener SPI you start by implementing EventListenerProviderFactory:
<programlisting><![CDATA[{
<programlisting><![CDATA[
package org.acme.provider;
import ...
@ -43,7 +43,7 @@ public class MyEventListenerProviderFactory implements EventListenerProviderFact
}
}
}]]></programlisting>
]]></programlisting>
The example uses an imagined MaxList which has a maximum size and is concurrency safe. When the maximum size is reached
and new entries are added the oldest entry is removed. Keycloak creates a single instance of
EventListenerProviderFactory which makes it possible to store state for multiple requests. EventListenerProvider
@ -51,7 +51,7 @@ public class MyEventListenerProviderFactory implements EventListenerProviderFact
</para>
<para>
Next you would implement EventListenerProvider:
<programlisting><![CDATA[{
<programlisting><![CDATA[
package org.acme.provider;
import ...
@ -75,13 +75,61 @@ public class MyEventListenerProvider implements EventListenerProvider {
}
}
}]]></programlisting>
]]></programlisting>
</para>
<para>
The file <literal>META-INF/services/org.keycloak.events.EventListenerProviderFactory</literal> should
contain the full name of your ProviderFactory implementation:
<programlisting><![CDATA[org.acme.provider.MyEventListenerProviderFactory]]></programlisting>
</para>
<section>
<title>Show info from you SPI implementation in Keycloak admin console</title>
<para>
Sometimes it is useful to show additional info about your Provider to a Keycloak administrator.
You can show provider build time informations (eg. version of custom provider currently installed),
current configuration of the provider (eg. url of remote system your provider talks to) or some operational
info (average time of response from remote system your provider talks to).
Keycloak admin console provides Server Info page to show this kind of information.
</para>
<para>
To show info from your provider it is enough to implement
<literal>org.keycloak.provider.ServerInfoAwareProviderFactory</literal> interface in your ProviderFactory.
Example implementation for MyEventListenerProviderFactory from previous example:
<programlisting><![CDATA[
package org.acme.provider;
import ...
public class MyEventListenerProviderFactory implements EventListenerProviderFactory, ServerInfoAwareProviderFactory {
private List<Event> events;
private int max;
...
@Override
public void init(Config.Scope config) {
max = config.getInt("max");
events = new MaxList(max);
}
...
@Override
public Map<String, String> getOperationalInfo() {
Map<String, String> ret = new LinkedHashMap<>();
ret.put("version", "1.0");
ret.put("listSizeMax", max + "");
ret.put("listSizeCurrent", events.size() + "");
return ret;
}
}
]]></programlisting>
</para>
</section>
</section>
<section>
@ -98,13 +146,13 @@ public class MyEventListenerProvider implements EventListenerProvider {
To register a provider using Modules first create a module. To do this you can either use the jboss-cli
script or manually create a folder inside KEYCLOAK_HOME/modules and add your jar and a <literal>module.xml</literal>.
For example to add the event listener sysout example provider using the jboss-cli script execute:
<programlisting><![CDATA[{
<programlisting><![CDATA[
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-sysout --resources=target/event-listener-sysout-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-model-api,org.keycloak.keycloak-events-api"
}]]></programlisting>
]]></programlisting>
Or to manually create it start by creating the folder <literal>KEYCLOAK_HOME/modules/org/keycloak/examples/event-sysout/main</literal>.
Then copy <literal>event-listener-sysout-example.jar</literal> to this folder and create <literal>module.xml</literal>
with the following content:
<programlisting><![CDATA[{
<programlisting><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.examples.event-sysout">
<resources>
@ -116,7 +164,7 @@ public class MyEventListenerProvider implements EventListenerProvider {
<module name="org.keycloak.keycloak-events-api"/>
</dependencies>
</module>
}]]></programlisting>
]]></programlisting>
</para>
<para>
Once you've created the module you need to register this module with Keycloak. This is done by editing

View file

@ -43,9 +43,9 @@
<section id="overlay_install">
<title>Install on existing WildFly 9.0.1.Final</title>
<title>Install on existing WildFly &wildfly.version;</title>
<para>
Keycloak can be installed into an existing WildFly 9.0.0.Final server. To do this download
Keycloak can be installed into an existing WildFly &wildfly.version; server. To do this download
<literal>keycloak-overlay-&project.version;.zip</literal> or <literal>keycloak-overlay-&project.version;.tar.gz</literal>.
Once downloaded extract into the root directory of your WildFly installation. To start WildFly with Keycloak
run:
@ -62,11 +62,15 @@
<para>
To add Keycloak to other sever configurations (standalone.xml, standalone-ha.xml, etc.) start the server with
the desired server-config. If you are running the server in standalone mode run:
<programlisting>cd &lt;WILDFLY_HOME&gt;/bin
./jboss-cli.sh -c --file=keycloak-install.cli</programlisting>
<programlisting>
cd &lt;WILDFLY_HOME&gt;/bin
./jboss-cli.sh -c --file=keycloak-install.cli
</programlisting>
Or if you are running in clustering (HA) mode (by having used -c standalone-ha.xml) then run:
<programlisting>cd &lt;WILDFLY_HOME&gt;/bin
./jboss-cli.sh -c --file=keycloak-install-ha.cli</programlisting>
<programlisting>
cd &lt;WILDFLY_HOME&gt;/bin
./jboss-cli.sh -c --file=keycloak-install-ha.cli
</programlisting>
You may see exceptions in the server log, but after restarting the server they should be gone.
You can restart the server with:
<programlisting>&lt;WILDFLY_HOME&gt;/bin/jboss-cli.sh -c :reload</programlisting>
@ -75,7 +79,7 @@
<section>
<title>Install on existing JBoss EAP 6.4.0.GA</title>
<para>
Same procedure as WildFly 9.0.1.Final, but download <literal>keycloak-overlay-eap6-&project.version;.zip</literal> or <literal>keycloak-overlay-eap6-&project.version;.tar.gz</literal>.
Same procedure as WildFly &wildfly.version;, but download <literal>keycloak-overlay-eap6-&project.version;.zip</literal> or <literal>keycloak-overlay-eap6-&project.version;.tar.gz</literal>.
</para>
</section>
<section>
@ -85,7 +89,7 @@
To install it first download <literal>keycloak-demo-&project.version;.zip</literal> or
<literal>keycloak-demo-&project.version;.tar.gz</literal>. Once downloaded extract it inside
<literal>keycloak-demo-&project.version;</literal> you'll find <literal>keycloak</literal> which contains
a full WildFly 9.0.0.Final server with Keycloak Server and Adapters included. You'll also find <literal>docs</literal>
a full WildFly &wildfly.version; server with Keycloak Server and Adapters included. You'll also find <literal>docs</literal>
and <literal>examples</literal> which contains everything you need to get started developing applications that use Keycloak.
</para>
<para>
@ -437,12 +441,12 @@ All configuration options are optional. Default value for directory is <literal>
settings you can specify before boot time. This is configured in the
<literal>standalone/configuration/keycloak-server.json</literal>.
By default the setting is like this:
<programlisting><![CDATA[
"connectionsHttpClient": {
"default": {
"disable-trust-manager": true
}
},
<programlisting><![CDATA[
"connectionsHttpClient": {
"default": {
"disable-trust-manager": true
}
},
]]></programlisting>
Possible configuration options are:
<variablelist>
@ -659,25 +663,25 @@ All configuration options are optional. Default value for directory is <literal>
to do with the <literal>keytool</literal> utility that comes with the Java jdk.
</para>
<para>
<programlisting>
$ keytool -genkey -alias localhost -keyalg RSA -keystore keycloak.jks -validity 10950
Enter keystore password: secret
Re-enter new password: secret
What is your first and last name?
[Unknown]: localhost
What is the name of your organizational unit?
[Unknown]: Keycloak
What is the name of your organization?
[Unknown]: Red Hat
What is the name of your City or Locality?
[Unknown]: Westford
What is the name of your State or Province?
[Unknown]: MA
What is the two-letter country code for this unit?
[Unknown]: US
Is CN=localhost, OU=Keycloak, O=Test, L=Westford, ST=MA, C=US correct?
[no]: yes
</programlisting>
<programlisting>
$ keytool -genkey -alias localhost -keyalg RSA -keystore keycloak.jks -validity 10950
Enter keystore password: secret
Re-enter new password: secret
What is your first and last name?
[Unknown]: localhost
What is the name of your organizational unit?
[Unknown]: Keycloak
What is the name of your organization?
[Unknown]: Red Hat
What is the name of your City or Locality?
[Unknown]: Westford
What is the name of your State or Province?
[Unknown]: MA
What is the two-letter country code for this unit?
[Unknown]: US
Is CN=localhost, OU=Keycloak, O=Test, L=Westford, ST=MA, C=US correct?
[no]: yes
</programlisting>
</para>
<para>
You should answer <literal>What is your first and last name ?</literal> question with
@ -693,44 +697,44 @@ All configuration options are optional. Default value for directory is <literal>
</para>
<para>
The first thing to do is generate a Certificate Request:
<programlisting>
$ keytool -certreq -alias yourdomain -keystore keycloak.jks > keycloak.careq
</programlisting>
<programlisting>
$ keytool -certreq -alias yourdomain -keystore keycloak.jks > keycloak.careq
</programlisting>
</para>
<para>
Where <literal>yourdomain</literal> is a DNS name for which this certificate is generated for.
Keytool generates the request:
<programlisting>
-----BEGIN NEW CERTIFICATE REQUEST-----
MIIC2jCCAcICAQAwZTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk1BMREwDwYDVQQHEwhXZXN0Zm9y
ZDEQMA4GA1UEChMHUmVkIEhhdDEQMA4GA1UECxMHUmVkIEhhdDESMBAGA1UEAxMJbG9jYWxob3N0
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr7kck2TaavlEOGbcpi9c0rncY4HhdzmY
Ax2nZfq1eZEaIPqI5aTxwQZzzLDK9qbeAd8Ji79HzSqnRDxNYaZu7mAYhFKHgixsolE3o5Yfzbw1
29Rvy+eUVe+WZxv5oo9wolVVpdSINIMEL2LaFhtX/c1dqiqYVpfnvFshZQaIg2nL8juzZcBjj4as
H98gIS7khql/dkZKsw9NLvyxgJvp7PaXurX29fNf3ihG+oFrL22oFyV54BWWxXCKU/GPn61EGZGw
Ft2qSIGLdctpMD1aJR2bcnlhEjZKDksjQZoQ5YMXaAGkcYkG6QkgrocDE2YXDbi7GIdf9MegVJ35
2DQMpwIDAQABoDAwLgYJKoZIhvcNAQkOMSEwHzAdBgNVHQ4EFgQUQwlZJBA+fjiDdiVzaO9vrE/i
n2swDQYJKoZIhvcNAQELBQADggEBAC5FRvMkhal3q86tHPBYWBuTtmcSjs4qUm6V6f63frhveWHf
PzRrI1xH272XUIeBk0gtzWo0nNZnf0mMCtUBbHhhDcG82xolikfqibZijoQZCiGiedVjHJFtniDQ
9bMDUOXEMQ7gHZg5q6mJfNG9MbMpQaUVEEFvfGEQQxbiFK7hRWU8S23/d80e8nExgQxdJWJ6vd0X
MzzFK6j4Dj55bJVuM7GFmfdNC52pNOD5vYe47Aqh8oajHX9XTycVtPXl45rrWAH33ftbrS8SrZ2S
vqIFQeuLL3BaHwpl3t7j2lMWcK1p80laAxEASib/fAwrRHpLHBXRcq6uALUOZl4Alt8=
-----END NEW CERTIFICATE REQUEST-----
</programlisting>
<programlisting>
-----BEGIN NEW CERTIFICATE REQUEST-----
MIIC2jCCAcICAQAwZTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk1BMREwDwYDVQQHEwhXZXN0Zm9y
ZDEQMA4GA1UEChMHUmVkIEhhdDEQMA4GA1UECxMHUmVkIEhhdDESMBAGA1UEAxMJbG9jYWxob3N0
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr7kck2TaavlEOGbcpi9c0rncY4HhdzmY
Ax2nZfq1eZEaIPqI5aTxwQZzzLDK9qbeAd8Ji79HzSqnRDxNYaZu7mAYhFKHgixsolE3o5Yfzbw1
29Rvy+eUVe+WZxv5oo9wolVVpdSINIMEL2LaFhtX/c1dqiqYVpfnvFshZQaIg2nL8juzZcBjj4as
H98gIS7khql/dkZKsw9NLvyxgJvp7PaXurX29fNf3ihG+oFrL22oFyV54BWWxXCKU/GPn61EGZGw
Ft2qSIGLdctpMD1aJR2bcnlhEjZKDksjQZoQ5YMXaAGkcYkG6QkgrocDE2YXDbi7GIdf9MegVJ35
2DQMpwIDAQABoDAwLgYJKoZIhvcNAQkOMSEwHzAdBgNVHQ4EFgQUQwlZJBA+fjiDdiVzaO9vrE/i
n2swDQYJKoZIhvcNAQELBQADggEBAC5FRvMkhal3q86tHPBYWBuTtmcSjs4qUm6V6f63frhveWHf
PzRrI1xH272XUIeBk0gtzWo0nNZnf0mMCtUBbHhhDcG82xolikfqibZijoQZCiGiedVjHJFtniDQ
9bMDUOXEMQ7gHZg5q6mJfNG9MbMpQaUVEEFvfGEQQxbiFK7hRWU8S23/d80e8nExgQxdJWJ6vd0X
MzzFK6j4Dj55bJVuM7GFmfdNC52pNOD5vYe47Aqh8oajHX9XTycVtPXl45rrWAH33ftbrS8SrZ2S
vqIFQeuLL3BaHwpl3t7j2lMWcK1p80laAxEASib/fAwrRHpLHBXRcq6uALUOZl4Alt8=
-----END NEW CERTIFICATE REQUEST-----
</programlisting>
</para>
<para>
Send this ca request to your CA. The CA will issue you a signed certificate and send it to you.
Before you import your new cert, you must obtain and import the root certificate of the CA.
You can download the cert from CA (ie.: root.crt) and import as follows:
<programlisting>
$ keytool -import -keystore keycloak.jks -file root.crt -alias root
</programlisting>
<programlisting>
$ keytool -import -keystore keycloak.jks -file root.crt -alias root
</programlisting>
</para>
<para>
Last step is import your new CA generated certificate to your keystore:
<programlisting>
$ keytool -import -alias yourdomain -keystore keycloak.jks -file your-certificate.cer
</programlisting>
<programlisting>
$ keytool -import -alias yourdomain -keystore keycloak.jks -file your-certificate.cer
</programlisting>
</para>
</section>
</section>
@ -744,18 +748,19 @@ All configuration options are optional. Default value for directory is <literal>
</para>
<para>
To the <literal>security-realms</literal> element add:
<programlisting><![CDATA[<security-realm name="UndertowRealm">
<server-identities>
<ssl>
<keystore path="keycloak.jks" relative-to="jboss.server.config.dir" keystore-password="secret" />
</ssl>
</server-identities>
</security-realm>]]></programlisting>
<programlisting><![CDATA[
<security-realm name="UndertowRealm">
<server-identities>
<ssl>
<keystore path="keycloak.jks" relative-to="jboss.server.config.dir" keystore-password="secret" />
</ssl>
</server-identities>
</security-realm>
]]></programlisting>
</para>
<para>
Find the element <literal>&lt;server name="default-server"&gt;</literal> (it's a child element of <literal>&lt;subsystem xmlns="urn:jboss:domain:undertow:1.0"&gt;</literal>) and add:
<programlisting><![CDATA[<https-listener name="https" socket-binding="https" security-realm="UndertowRealm"/>
]]></programlisting>
<programlisting><![CDATA[<https-listener name="https" socket-binding="https" security-realm="UndertowRealm"/>]]></programlisting>
</para>
<para>
Check the <ulink url="https://docs.jboss.org/author/display/WFLY8/Undertow+(web)+subsystem+configuration">Wildfly Undertow</ulink> documentation for more information on fine tuning the socket connections.
@ -865,12 +870,12 @@ All configuration options are optional. Default value for directory is <literal>
</para>
<para>
To do this, add the <literal>default-web-module</literal> attribute in the Undertow subystem in standalone.xml.
<programlisting><![CDATA[
<programlisting><![CDATA[
<subsystem xmlns="urn:jboss:domain:undertow:2.0">
<server name="default-server">
<host name="default-host" alias="localhost" default-web-module="keycloak-server.war">
<location name="/" handler="welcome-content"/>
</host>
<server name="default-server">
<host name="default-host" alias="localhost" default-web-module="keycloak-server.war">
<location name="/" handler="welcome-content"/>
</host>
]]></programlisting>
</para>
<para>

View file

@ -877,7 +877,7 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, $route, se
$scope.viewImportDetails = function() {
$modal.open({
templateUrl: resourceUrl + '/partials/modal/view-object.html',
controller: 'JsonModalCtrl',
controller: 'ObjectModalCtrl',
resolve: {
object: function () {
return $scope.client;

View file

@ -37,7 +37,7 @@
<td>{{mapper.name}}</td>
<td>{{mapperTypes[mapper.protocolMapper].category}}</td>
<td>{{mapperTypes[mapper.protocolMapper].name}}</td>
<td><input type="checkbox" ng-model="mapper.isChecked"></td>
<td><input type="checkbox" ng-model="mapper.isChecked" id="{{mapper.protocolMapper}}"></td>
</tr>
<tr data-ng-show="mappers.length == 0">
<td>{{:: 'no-mappers-available' | translate}}</td>

View file

@ -54,7 +54,7 @@
<div class="form-group" data-ng-show="realm.otpPolicyType == 'hotp'">
<label class="col-md-2 control-label" for="counter">Initial Counter</label>
<div class="col-md-6">
<input class="form-control" type="text" id="counter" name="counter" data-ng-model="realm.otpPolicyInitialCounter" autofocus>
<input class="form-control" type="number" required min="1" max="120" id="counter" name="counter" data-ng-model="realm.otpPolicyInitialCounter" autofocus>
</div>
<kc-tooltip>What should the initial counter value be?</kc-tooltip>
</div>

View file

@ -20,8 +20,8 @@
<tbody>
<tr ng-repeat="requiredAction in requiredActions" data-ng-show="requiredActions.length > 0">
<td>{{requiredAction.name}}</td>
<td><input type="checkbox" ng-model="requiredAction.enabled" ng-change="updateRequiredAction(requiredAction)"></td>
<td><input type="checkbox" ng-model="requiredAction.defaultAction" ng-change="updateRequiredAction(requiredAction)"></td>
<td><input type="checkbox" ng-model="requiredAction.enabled" ng-change="updateRequiredAction(requiredAction)" id="{{requiredAction.alias}}.enabled"></td>
<td><input type="checkbox" ng-model="requiredAction.defaultAction" ng-change="updateRequiredAction(requiredAction)" id="{{requiredAction.alias}}.defaultAction"></td>
</tr>
<tr data-ng-show="requiredActions.length == 0">
<td>No required actions configured</td>

View file

@ -43,21 +43,21 @@
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty9.version}</version>
<scope>compile</scope>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${jetty9.version}</version>
<scope>compile</scope>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
<version>${jetty9.version}</version>
<scope>compile</scope>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -153,23 +153,23 @@
+ '&response_type=code';
if (options && options.prompt) {
url += '&prompt=' + options.prompt;
url += '&prompt=' + encodeURIComponent(options.prompt);
}
if (options && options.loginHint) {
url += '&login_hint=' + options.loginHint;
url += '&login_hint=' + encodeURIComponent(options.loginHint);
}
if (options && options.idpHint) {
url += '&kc_idp_hint=' + options.idpHint;
url += '&kc_idp_hint=' + encodeURIComponent(options.idpHint);
}
if (options && options.scope) {
url += '&scope=' + options.scope;
url += '&scope=' + encodeURIComponent(options.scope);
}
if (options && options.locale) {
url += '&ui_locales=' + options.locale;
url += '&ui_locales=' + encodeURIComponent(options.locale);
}
return url;

View file

@ -1,9 +1,9 @@
package org.keycloak.adapters.osgi;
import java.net.URL;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.util.security.Constraint;
@ -133,7 +133,8 @@ public class PaxWebIntegrationService {
Constraint constraint = constraintMapping.getConstraint();
String[] roles = constraint.getRoles();
// name property is unavailable on constraint object :/
String name = "Constraint-" + new Random().nextInt();
String name = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
int dataConstraint = constraint.getDataConstraint();
String dataConstraintStr;

View file

@ -3,15 +3,15 @@ package org.keycloak.provider;
import java.util.Map;
/**
* Marker interface for ProviderFactory of Provider which wants to show some info on "Server Info" page in Admin console.
* Marker interface for {@link ProviderFactory} of Provider which wants to show some info on "Server Info" page in Admin console.
*
* @author Vlastimil Elias (velias at redhat dot com)
*/
public interface ServerInfoAwareProviderFactory<T extends Provider> extends ProviderFactory<T> {
public interface ServerInfoAwareProviderFactory {
/**
* Get operational info about given provider. This info contains informations about providers configuration and operational conditions (eg. errors in connection to remote systems etc) which is
* shown on "Server Info" page.
* Return actual info about the provider. This info contains informations about providers configuration and operational conditions (eg. errors in connection to remote systems etc) which is
* shown on "Server Info" page then.
*
* @return Map with keys describing value and relevant values itself
*/

View file

@ -48,8 +48,8 @@
<dom4j.version>1.6.1</dom4j.version>
<xml-apis.version>1.4.01</xml-apis.version>
<slf4j.version>1.7.7</slf4j.version>
<wildfly.version>9.0.1.Final</wildfly.version>
<wildfly.core.version>1.0.1.Final</wildfly.core.version>
<wildfly.version>9.0.2.Final</wildfly.version>
<wildfly.core.version>1.0.2.Final</wildfly.core.version>
<wildfly.build-tools.version>1.0.0.Final</wildfly.build-tools.version>
<!-- this is EAP 6.4 alpha, publicly available -->

View file

@ -1,5 +1,17 @@
package org.keycloak.services.resources.admin.info;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import javax.ws.rs.GET;
import javax.ws.rs.core.Context;
import org.keycloak.broker.provider.IdentityProvider;
import org.keycloak.broker.provider.IdentityProviderFactory;
import org.keycloak.events.EventType;
@ -21,10 +33,6 @@ import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.ProtocolMapperTypeRepresentation;
import org.keycloak.social.SocialIdentityProvider;
import javax.ws.rs.GET;
import javax.ws.rs.core.Context;
import java.util.*;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@ -73,7 +81,6 @@ public class ServerInfoAdminResource {
for (Spi spi : spis) {
SpiInfoRepresentation spiRep = new SpiInfoRepresentation();
spiRep.setInternal(spi.isInternal());
spiRep.setSystemInfo(ServerInfoAwareProviderFactory.class.isAssignableFrom(spi.getProviderFactoryClass()));
List<String> providerIds = new LinkedList<>(session.listProviderIds(spi.getProviderClass()));
Collections.sort(providerIds);
@ -83,8 +90,9 @@ public class ServerInfoAdminResource {
if (providerIds != null) {
for (String name : providerIds) {
ProviderRepresentation provider = new ProviderRepresentation();
if (spiRep.isSystemInfo()) {
provider.setOperationalInfo(((ServerInfoAwareProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(spi.getProviderClass(), name)).getOperationalInfo());
ProviderFactory<?> pi = session.getKeycloakSessionFactory().getProviderFactory(spi.getProviderClass(), name);
if (ServerInfoAwareProviderFactory.class.isAssignableFrom(pi.getClass())) {
provider.setOperationalInfo(((ServerInfoAwareProviderFactory) pi).getOperationalInfo());
}
providers.put(name, provider);
}

View file

@ -8,7 +8,6 @@ import java.util.Map;
public class SpiInfoRepresentation {
private boolean internal;
private boolean systemInfo;
private Map<String, ProviderRepresentation> providers;
@ -20,14 +19,6 @@ public class SpiInfoRepresentation {
this.internal = internal;
}
public boolean isSystemInfo() {
return systemInfo;
}
public void setSystemInfo(boolean systemInfo) {
this.systemInfo = systemInfo;
}
public Map<String, ProviderRepresentation> getProviders() {
return providers;
}

View file

@ -15,7 +15,18 @@
<exclude.console>-</exclude.console>
<exclude.account>-</exclude.account>
</properties>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-util-embedded-ldap</artifactId>
<exclusions>
<exclusion>
<groupId>bouncycastle</groupId>
<artifactId>bcprov-jdk15</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>

View file

@ -57,7 +57,7 @@ public class AdminConsoleRealm extends AdminConsoleRealmsRoot {
private WebElement rolesLink;
@FindBy(partialLinkText = "Identity Providers")
private WebElement identityProvidersLink;
@FindBy(partialLinkText = "User Feferation")
@FindBy(partialLinkText = "User Federation")
private WebElement userFederationLink;
@FindBy(partialLinkText = "Authentication")
private WebElement authenticationLink;

View file

@ -3,7 +3,7 @@ package org.keycloak.testsuite.console.page.authentication;
import org.keycloak.testsuite.console.page.AdminConsoleRealm;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
import static org.keycloak.testsuite.util.WaitUtils.*;
/**
* @author tkyjovsk
@ -20,6 +20,9 @@ public class Authentication extends AdminConsoleRealm {
@FindBy(xpath = "//div[contains(@class, 'alert-success')]")
private WebElement success;
@FindBy(xpath = "//button[@class='close']/span")
private WebElement close;
public String getSuccessMessage() {
waitAjaxForElement(success);
return success.getText();
@ -30,6 +33,13 @@ public class Authentication extends AdminConsoleRealm {
return error.getText();
}
public void closeNotification() {
if (close.isDisplayed()) {
close.click();
}
waitAjaxForElementNotVisible(close);
}
public AuthenticationTabs tabs() {
return authenticationTabs;
}

View file

@ -7,11 +7,17 @@ import org.openqa.selenium.support.FindBy;
/**
* @author tkyjovsk
* @author mhajas
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class RequiredActions extends Authentication {
public final static String ENABLED = "enabled";
public final static String DEFAULT_ACTION = "defaultAction";
public final static String ENABLED = ".enabled";
public final static String DEFAULT = ".defaultAction";
public final static String CONFIGURE_TOTP = "CONFIGURE_TOTP";
public final static String UPDATE_PROFILE = "UPDATE_PROFILE";
public final static String TERMS_AND_CONDITIONS = "terms_and_conditions";
public final static String UPDATE_PASSWORD = "UPDATE_PASSWORD";
public final static String VERIFY_EMAIL = "VERIFY_EMAIL";
@FindBy(tagName = "table")
private WebElement requiredActionTable;
@ -21,51 +27,59 @@ public class RequiredActions extends Authentication {
return super.getUriFragment() + "/required-actions";
}
private void setRequiredActionValue(String row, String column, boolean value) {
WebElement checkbox = requiredActionTable.findElement(By.xpath("//td[text()='" + row + "']/..//input[@ng-model='requiredAction." + column + "']"));
private void setRequiredActionValue(String id, boolean value) {
WebElement checkbox = requiredActionTable.findElement(By.id(id));
if (checkbox.isSelected() != value) {
checkbox.click();
}
}
private void setRequiredActionEnabledValue(String id, boolean value) {
setRequiredActionValue(id + ENABLED, value);
}
private void setRequiredActionDefaultValue(String id, boolean value) {
setRequiredActionValue(id + DEFAULT, value);
}
public void setTermsAndConditionEnabled(boolean value) {
setRequiredActionValue("Terms and Conditions", ENABLED, value);
setRequiredActionEnabledValue(TERMS_AND_CONDITIONS, value);
}
public void setTermsAndConditionDefaultAction(boolean value) {
setRequiredActionValue("Terms and Conditions", DEFAULT_ACTION, value);
setRequiredActionDefaultValue(TERMS_AND_CONDITIONS, value);
}
public void setVerifyEmailEnabled(boolean value) {
setRequiredActionValue("Verify Email", ENABLED, value);
setRequiredActionEnabledValue(VERIFY_EMAIL, value);
}
public void setVerifyEmailDefaultAction(boolean value) {
setRequiredActionValue("Verify Email", DEFAULT_ACTION, value);
setRequiredActionDefaultValue(VERIFY_EMAIL, value);
}
public void setUpdatePasswordEnabled(boolean value) {
setRequiredActionValue("Update Password", ENABLED, value);
setRequiredActionEnabledValue(UPDATE_PASSWORD, value);
}
public void setUpdatePasswordDefaultAction(boolean value) {
setRequiredActionValue("Update Password", DEFAULT_ACTION, value);
setRequiredActionDefaultValue(UPDATE_PASSWORD, value);
}
public void setConfigureTotpEnabled(boolean value) {
setRequiredActionValue("Configure Totp", ENABLED, value);
setRequiredActionEnabledValue(CONFIGURE_TOTP, value);
}
public void setConfigureTotpDefaultAction(boolean value) {
setRequiredActionValue("Configure Totp", DEFAULT_ACTION, value);
setRequiredActionDefaultValue(CONFIGURE_TOTP, value);
}
public void setUpdateProfileEnabled(boolean value) {
setRequiredActionValue("Update Profile", ENABLED, value);
setRequiredActionEnabledValue(UPDATE_PROFILE, value);
}
public void setUpdateProfileDefaultAction(boolean value) {
setRequiredActionValue("Update Profile", DEFAULT_ACTION, value);
setRequiredActionDefaultValue(UPDATE_PROFILE, value);
}
}

View file

@ -1,14 +1,112 @@
package org.keycloak.testsuite.console.page.clients;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.testsuite.console.page.fragment.DataTable;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author tkyjovsk
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class ClientMappers extends Client {
public static final String ADD_BUILTIN = "Add Builtin";
@FindBy(tagName = "table")
private ClientMapperTable table;
@Override
public String getUriFragment() {
return super.getUriFragment() + "/mappers";
}
public ClientMapperTable mapperTable() {
return table;
}
public class ClientMapperTable extends DataTable {
public List<ProtocolMapperRepresentation> searchMappings(String searchPattern) {
search(searchPattern);
return getMappingsFromRows();
}
public void createMapper() {
waitAjaxForBody();
clickHeaderLink(CREATE);
}
public void addBuiltin() {
waitAjaxForBody();
clickHeaderLink(ADD_BUILTIN);
}
public void clickMapper(String mapperName) {
waitAjaxForBody();
body().findElement(By.linkText(mapperName)).click();
}
public void clickMapper(ProtocolMapperRepresentation mapper) {
clickMapper(mapper.getName());
}
private void clickMapperActionButton(String mapperName, String buttonText) {
waitAjaxForBody();
clickRowActionButton(getRowByLinkText(mapperName), buttonText);
}
private void clickMapperActionButton(ProtocolMapperRepresentation mapper, String buttonName) {
clickMapperActionButton(mapper.getName(), buttonName);
}
public void editMapper(String mapperName) {
clickMapperActionButton(mapperName, EDIT);
}
public void editMapper(ProtocolMapperRepresentation mapper) {
clickMapperActionButton(mapper, EDIT);
}
public void deleteMapper(String mapperName) {
clickMapperActionButton(mapperName, DELETE);
}
public void deleteMapper(ProtocolMapperRepresentation mapper) {
clickMapperActionButton(mapper, DELETE);
}
public ProtocolMapperRepresentation getMappingFromRow(WebElement row) {
if (!row.isDisplayed()) {return null;} // Is that necessary?
ProtocolMapperRepresentation mappingsRepresentation = new ProtocolMapperRepresentation();
List<WebElement> cols = row.findElements(By.tagName("td"));
mappingsRepresentation.setName(cols.get(0).getText());
//mappingsRepresentation.setProtocol(cols.get(1).getText());
mappingsRepresentation.setProtocolMapper(cols.get(2).getText());
return mappingsRepresentation;
}
public List<ProtocolMapperRepresentation> getMappingsFromRows() {
List<ProtocolMapperRepresentation> mappings = new ArrayList<ProtocolMapperRepresentation>();
for (WebElement row : rows()) {
ProtocolMapperRepresentation mapperRepresentation = getMappingFromRow(row);
if (mapperRepresentation != null) {
mappings.add(mapperRepresentation);
}
}
return mappings;
}
}
}

View file

@ -0,0 +1,21 @@
package org.keycloak.testsuite.console.page.clients;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.console.page.AdminConsoleCreate;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class CreateClientMappers extends AdminConsoleCreate {
@Page
private CreateClientMappersForm form;
public CreateClientMappers() {
setEntity("mappers");
}
public CreateClientMappersForm form() {
return form;
}
}

View file

@ -0,0 +1,187 @@
package org.keycloak.testsuite.console.page.clients;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
import org.keycloak.testsuite.page.Form;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
import java.util.List;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*
* TODO: SAML
*/
public class CreateClientMappersForm extends Form {
// Mappers types
public static final String HARDCODED_ROLE = "Hardcoded Role";
public static final String HARDCODED_CLAIM = "Hardcoded claim";
public static final String USER_SESSION_NOTE = "User Session Note";
public static final String ROLE_NAME_MAPPER = "Role Name Mapper";
public static final String USER_ADDRESS = "User Address";
public static final String USERS_FULL_NAME = "User's full name";
public static final String USER_ATTRIBUTE = "User Attribute";
public static final String USER_PROPERTY = "User Property";
@FindBy(id = "name")
private WebElement nameElement;
@FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='consentRequired']]")
private OnOffSwitch consentRequiredSwitch;
@FindBy(id = "consentText")
private WebElement consentTextElement;
@FindBy(id = "mapperTypeCreate")
private Select mapperTypeSelect;
@FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Property']//following-sibling::node()//input[@type='text']")
private WebElement propertyInput;
@FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='User Attribute']//following-sibling::node()//input[@type='text']")
private WebElement userAttributeInput;
@FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='User Session Note']//following-sibling::node()//input[@type='text']")
private WebElement userSessionNoteInput;
@FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Multivalued']//following-sibling::node()//div[@class='onoffswitch']")
private OnOffSwitch multivaluedInput;
@FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Role']//following-sibling::node()//input[@type='text']")
private WebElement roleInput;
@FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='New Role Name']//following-sibling::node()//input[@type='text']")
private WebElement newRoleInput;
@FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Token Claim Name']//following-sibling::node()//input[@type='text']")
private WebElement tokenClaimNameInput;
@FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Claim value']//following-sibling::node()//input[@type='text']")
private WebElement tokenClaimValueInput;
@FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Claim JSON Type']//following-sibling::node()//select")
private Select claimJSONTypeInput;
@FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Add to ID token']//following-sibling::node()//div[@class='onoffswitch']")
private OnOffSwitch addToIDTokenInput;
@FindBy(xpath = ".//div[@properties='mapperType.properties']//label[text()='Add to access token']//following-sibling::node()//div[@class='onoffswitch']")
private OnOffSwitch addToAccessTokenInput;
public boolean isConsentRequired() {
return consentRequiredSwitch.isOn();
}
public void setConsentRequired(boolean consentRequired) {
consentRequiredSwitch.setOn(consentRequired);
}
public String getConsentText() {
return getInputValue(consentTextElement);
}
public void setConsentText(String consentText) {
setInputValue(consentTextElement, consentText);
}
public String getMapperType() {
return mapperTypeSelect.getFirstSelectedOption().getText();
}
public void setMapperType(String type) {
mapperTypeSelect.selectByVisibleText(type);
}
public String getProperty() {
return getInputValue(propertyInput);
}
public void setProperty(String value) {
setInputValue(propertyInput, value);
}
public String getUserAttribute() {
return getInputValue(userAttributeInput);
}
public void setUserAttribute(String value) {
setInputValue(userAttributeInput, value);
}
public String getUserSessionNote() {
return getInputValue(userSessionNoteInput);
}
public void setUserSessionNote(String value) {
setInputValue(userSessionNoteInput, value);
}
public boolean isMultivalued() {
return multivaluedInput.isOn();
}
public void setMultivalued(boolean value) {
multivaluedInput.setOn(value);
}
public String getRole() {
return getInputValue(roleInput);
}
public void setRole(String value) {
setInputValue(roleInput, value);
}
public String getNewRole() {
return getInputValue(newRoleInput);
}
public void setNewRole(String value) {
setInputValue(newRoleInput, value);
}
public String getTokenClaimName() {
return getInputValue(tokenClaimNameInput);
}
public void setTokenClaimName(String value) {
setInputValue(tokenClaimNameInput, value);
}
public String getTokenClaimValue() {
return getInputValue(tokenClaimValueInput);
}
public void setTokenClaimValue(String value) {
setInputValue(tokenClaimValueInput, value);
}
public String getClaimJSONType() {
return claimJSONTypeInput.getFirstSelectedOption().getText();
}
public void setClaimJSONType(String value) {
claimJSONTypeInput.selectByVisibleText(value);
}
public boolean isAddToIDToken() {
return addToIDTokenInput.isOn();
}
public void setAddToIDToken(boolean value) {
addToIDTokenInput.setOn(value);
}
public boolean isAddToAccessToken() {
return addToAccessTokenInput.isOn();
}
public void setAddToAccessToken(boolean value) {
addToAccessTokenInput.setOn(value);
}
}

View file

@ -0,0 +1,28 @@
package org.keycloak.testsuite.console.page.federation;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.console.page.AdminConsoleCreate;
/**
*
* @author pdrozd
*/
public class CreateKerberosUserProvider extends AdminConsoleCreate {
@Page
private KerberosUserProviderForm form;
public CreateKerberosUserProvider() {
setEntity("user-federation");
}
@Override
public String getUriFragment() {
return super.getUriFragment() + "/providers/kerberos";
}
public KerberosUserProviderForm form() {
return form;
}
}

View file

@ -1,5 +1,6 @@
package org.keycloak.testsuite.console.page.federation;
import org.jboss.arquillian.graphene.page.Page;
import org.keycloak.testsuite.console.page.AdminConsoleCreate;
/**
@ -8,6 +9,9 @@ import org.keycloak.testsuite.console.page.AdminConsoleCreate;
*/
public class CreateLdapUserProvider extends AdminConsoleCreate {
@Page
private LdapUserProviderForm form;
public CreateLdapUserProvider() {
setEntity("user-federation");
}
@ -17,4 +21,7 @@ public class CreateLdapUserProvider extends AdminConsoleCreate {
return super.getUriFragment() + "/providers/ldap";
}
public LdapUserProviderForm form() {
return form;
}
}

View file

@ -0,0 +1,81 @@
package org.keycloak.testsuite.console.page.federation;
import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
import org.keycloak.testsuite.page.Form;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
/**
* @author pdrozd
*/
public class KerberosUserProviderForm extends Form {
@FindBy(id = "consoleDisplayName")
private WebElement consoleDisplayNameInput;
@FindBy(id = "priority")
private WebElement priorityInput;
@FindBy(id = "kerberosRealm")
private WebElement kerberosRealmInput;
@FindBy(id = "serverPrincipal")
private WebElement serverPrincipalInput;
@FindBy(id = "keyTab")
private WebElement keyTabInput;
@FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='debug']]")
private OnOffSwitch debug;
@FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='allowPasswordAuthentication']]")
private OnOffSwitch allowPwdAuth;
@FindBy(id = "editMode")
private Select editModeSelect;
@FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='updateProfileFirstLogin']]")
private OnOffSwitch updateProfileFirstLogin;
public void setConsoleDisplayNameInput(String name) {
setInputValue(consoleDisplayNameInput, name);
}
public void setPriorityInput(Integer priority) {
setInputValue(priorityInput, String.valueOf(priority));
}
public void setKerberosRealmInput(String kerberosRealm) {
waitGuiForElement(By.id("kerberosRealm"));
setInputValue(kerberosRealmInput, kerberosRealm);
}
public void setServerPrincipalInput(String serverPrincipal) {
setInputValue(serverPrincipalInput, serverPrincipal);
}
public void setKeyTabInput(String keyTab) {
setInputValue(keyTabInput, keyTab);
}
public void setDebugEnabled(boolean debugEnabled) {
this.debug.setOn(debugEnabled);
}
public void setAllowPasswordAuthentication(boolean enabled) {
allowPwdAuth.setOn(enabled);
}
public void selectEditMode(String mode) {
waitGuiForElement(By.id("editMode"));
editModeSelect.selectByVisibleText(mode);
}
public void setUpdateProfileFirstLogin(boolean enabled) {
updateProfileFirstLogin.setOn(enabled);
}
}

View file

@ -1,5 +1,8 @@
package org.keycloak.testsuite.console.page.federation;
import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
import org.jboss.arquillian.graphene.findby.FindByJQuery;
import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
import org.keycloak.testsuite.page.Form;
@ -8,10 +11,8 @@ import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
/**
* Created by fkiss.
* @author fkiss, pdrozd
*/
public class LdapUserProviderForm extends Form {
@ -24,24 +25,33 @@ public class LdapUserProviderForm extends Form {
@FindBy(id = "usernameLDAPAttribute")
private WebElement usernameLDAPAttributeInput;
@FindBy(id = "rdnLDAPAttribute")
private WebElement rdnLDAPAttributeInput;
@FindBy(id = "uuidLDAPAttribute")
private WebElement uuidLDAPAttributeInput;
@FindBy(id = "userObjectClasses")
private WebElement userObjectClassesInput;
@FindBy(id = "ldapConnectionUrl")
private WebElement ldapConnectionUrlInput;
@FindBy(id = "ldapBaseDn")
private WebElement ldapBaseDnInput;
@FindBy(id = "ldapUsersDn")
private WebElement ldapUserDnInput;
@FindBy(id = "authType")
private Select authTypeSelect;
@FindBy(id = "ldapBindDn")
private WebElement ldapBindDnInput;
@FindBy(id = "ldapBindCredential")
private WebElement ldapBindCredentialInput;
@FindBy(id = "searchScope")
private Select searchScopeSelect;
@FindBy(id = "kerberosRealm")
private WebElement kerberosRealmInput;
@ -72,59 +82,173 @@ public class LdapUserProviderForm extends Form {
@FindByJQuery("a:contains('Test authentication')")
private WebElement testAuthenticationButton;
@FindByJQuery("div[class='onoffswitch']:eq(0)")
@FindByJQuery("a:contains('Synchronize changed users')")
private WebElement synchronizeChangedUsersButton;
@FindByJQuery("button:contains('Synchronize all users')")
private WebElement synchronizeAllUsersButton;
@FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='syncRegistrations']]")
private OnOffSwitch syncRegistrations;
@FindByJQuery("div[class='onoffswitch']:eq(1)")
@FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='connectionPooling']]")
private OnOffSwitch connectionPooling;
@FindByJQuery("div[class='onoffswitch']:eq(2)")
@FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='pagination']]")
private OnOffSwitch pagination;
@FindByJQuery("div[class='onoffswitch']:eq(3)")
@FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='userAccountControlsAfterPasswordUpdate']]")
private OnOffSwitch enableAccountAfterPasswordUpdate;
@FindBy(xpath = "//div[contains(@class,'onoffswitch') and ./input[@id='allowKerberosAuthentication']]")
private OnOffSwitch allowKerberosAuth;
@FindByJQuery("div[class='onoffswitch']:eq(4)")
@FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='debug']]")
private OnOffSwitch debug;
@FindByJQuery("div[class='onoffswitch']:eq(5)")
@FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='useKerberosForPasswordAuthentication']]")
private OnOffSwitch useKerberosForPwdAuth;
@FindByJQuery("div[class='onoffswitch']:eq(6)")
@FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='compositeSwitch']]")
private OnOffSwitch periodicFullSync;
@FindByJQuery("div[class='onoffswitch']:eq(7)")
@FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='changedSyncEnabled']]")
private OnOffSwitch periodicChangedUsersSync;
@FindByJQuery("button:contains('Save')")
private WebElement saveButton;
public void setConsoleDisplayNameInput(String name) {
setInputValue(consoleDisplayNameInput, name);
}
public void selectEditMode(String mode){
public void setPriorityInput(Integer priority) {
setInputValue(priorityInput, String.valueOf(priority));
}
public void setUsernameLDAPAttributeInput(String usernameLDAPAttribute) {
setInputValue(usernameLDAPAttributeInput, usernameLDAPAttribute);
}
public void setRdnLDAPAttributeInput(String rdnLDAPAttribute) {
setInputValue(rdnLDAPAttributeInput, rdnLDAPAttribute);
}
public void setUuidLDAPAttributeInput(String uuidLDAPAttribute) {
setInputValue(uuidLDAPAttributeInput, uuidLDAPAttribute);
}
public void setUserObjectClassesInput(String userObjectClasses) {
setInputValue(userObjectClassesInput, userObjectClasses);
}
public void setLdapConnectionUrlInput(String ldapConnectionUrl) {
setInputValue(ldapConnectionUrlInput, ldapConnectionUrl);
}
public void setLdapUserDnInput(String ldapUserDn) {
setInputValue(ldapUserDnInput, ldapUserDn);
}
public void setLdapBindDnInput(String ldapBindDn) {
setInputValue(ldapBindDnInput, ldapBindDn);
}
public void setLdapBindCredentialInput(String ldapBindCredential) {
setInputValue(ldapBindCredentialInput, ldapBindCredential);
}
public void setKerberosRealmInput(String kerberosRealm) {
waitAjaxForElement(kerberosRealmInput);
setInputValue(kerberosRealmInput, kerberosRealm);
}
public void setServerPrincipalInput(String serverPrincipal) {
waitAjaxForElement(serverPrincipalInput);
setInputValue(serverPrincipalInput, serverPrincipal);
}
public void setKeyTabInput(String keyTab) {
waitAjaxForElement(keyTabInput);
setInputValue(keyTabInput, keyTab);
}
public void setBatchSizeForSyncInput(String batchSizeForSync) {
setInputValue(batchSizeForSyncInput, batchSizeForSync);
}
public void selectEditMode(String mode) {
waitGuiForElement(By.id("editMode"));
editModeSelect.selectByVisibleText(mode);
}
public void selectVendor(String vendor){
waitGuiForElement(By.id("editMode"));
public void selectVendor(String vendor) {
waitGuiForElement(By.id("vendor"));
vendorSelect.selectByVisibleText(vendor);
}
public void configureLdap(String displayName, String editMode, String vendor, String connectionUrl, String userDN, String ldapBindDn, String ldapBindCredential){
consoleDisplayNameInput.sendKeys(displayName);
editModeSelect.selectByVisibleText(editMode);
selectVendor(vendor);
ldapConnectionUrlInput.sendKeys(connectionUrl);
ldapUserDnInput.sendKeys(userDN);
ldapBindDnInput.sendKeys(ldapBindDn);
ldapBindCredentialInput.sendKeys(ldapBindCredential);
saveButton.click();
public void selectAuthenticationType(String authenticationType) {
waitGuiForElement(By.id("authType"));
authTypeSelect.selectByVisibleText(authenticationType);
}
public void testConnection(){
public void selectSearchScope(String searchScope) {
waitGuiForElement(By.id("searchScope"));
searchScopeSelect.selectByVisibleText(searchScope);
}
public void setSyncRegistrationsEnabled(boolean syncRegistrationsEnabled) {
this.syncRegistrations.setOn(syncRegistrationsEnabled);
}
public void setConnectionPoolingEnabled(boolean connectionPoolingEnabled) {
this.connectionPooling.setOn(connectionPoolingEnabled);
}
public void setPaginationEnabled(boolean paginationEnabled) {
this.pagination.setOn(paginationEnabled);
}
public void setAccountAfterPasswordUpdateEnabled(boolean enabled) {
if ((!enableAccountAfterPasswordUpdate.isOn() && enabled)
|| !enabled && enableAccountAfterPasswordUpdate.isOn()) {
driver.findElement(By
.xpath("//div[contains(@class,'onoffswitch') and ./input[@id='userAccountControlsAfterPasswordUpdate']]"))
.findElements(By.tagName("span")).get(0).click();
}
}
public void setAllowKerberosAuthEnabled(boolean enabled) {
if ((!allowKerberosAuth.isOn() && enabled) || !enabled && allowKerberosAuth.isOn()) {
driver.findElement(
By.xpath("//div[contains(@class,'onoffswitch') and ./input[@id='allowKerberosAuthentication']]"))
.findElements(By.tagName("span")).get(0).click();
}
}
public void setDebugEnabled(boolean debugEnabled) {
this.debug.setOn(debugEnabled);
}
public void setUseKerberosForPwdAuthEnabled(boolean useKerberosForPwdAuthEnabled) {
this.useKerberosForPwdAuth.setOn(useKerberosForPwdAuthEnabled);
}
public void setPeriodicFullSyncEnabled(boolean periodicFullSyncEnabled) {
this.periodicFullSync.setOn(periodicFullSyncEnabled);
}
public void setPeriodicChangedUsersSyncEnabled(boolean periodicChangedUsersSyncEnabled) {
this.periodicChangedUsersSync.setOn(periodicChangedUsersSyncEnabled);
}
public void testConnection() {
testConnectionButton.click();
}
public void testAuthentication(){
public void testAuthentication() {
testAuthenticationButton.click();
}
public void synchronizeAllUsers() {
waitAjaxForElement(synchronizeAllUsersButton);
synchronizeAllUsersButton.click();
}
}

View file

@ -36,6 +36,14 @@ public class OnOffSwitch {
@ArquillianResource
private Actions actions;
public OnOffSwitch() {
}
public OnOffSwitch(WebElement root, Actions actions) {
this.root = root;
this.actions = actions;
}
public boolean isOn() {
waitAjaxForElement(root);
return root.findElement(By.tagName("input")).isSelected();

View file

@ -41,6 +41,11 @@ public final class WaitUtils {
.element(element).is().not().present();
}
public static void waitAjaxForElementNotVisible(WebElement element) {
waitAjax().until()
.element(element).is().not().visible();
}
public static void waitGuiForElement(By element, String message) {
waitGui().until(message)
.element(element).is().present();

View file

@ -24,6 +24,7 @@ package org.keycloak.testsuite.console.authentication;
import org.jboss.arquillian.graphene.page.Page;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.console.AbstractConsoleTest;
@ -66,42 +67,58 @@ public class OTPPolicyTest extends AbstractConsoleTest {
}
@Test
@Ignore //KEYCLOAK-2051 when you close notification, it is not displayed again
public void invalidValuesTest() {
otpPolicyPage.form().setValues(OTPType.TIME_BASED, OTPHashAlg.SHA1, Digits.EMPTY, "", "30");
assertEquals("Error! Missing or invalid field(s). Please verify the fields in red.", otpPolicyPage.getErrorMessage());
otpPolicyPage.closeNotification();
otpPolicyPage.navigateTo();// workaround: input.clear() doesn't work when <input type="number" ...
otpPolicyPage.form().setValues(OTPType.TIME_BASED, OTPHashAlg.SHA1, Digits.EMPTY, " ", "30");
assertEquals("Error! Missing or invalid field(s). Please verify the fields in red.", otpPolicyPage.getErrorMessage());
otpPolicyPage.closeNotification();
otpPolicyPage.navigateTo();
otpPolicyPage.form().setValues(OTPType.TIME_BASED, OTPHashAlg.SHA1, Digits.EMPTY, "no number", "30");
assertEquals("Error! Missing or invalid field(s). Please verify the fields in red.", otpPolicyPage.getErrorMessage());
otpPolicyPage.closeNotification();
otpPolicyPage.navigateTo();
RealmRepresentation realm = testRealmResource().toRepresentation();
assertEquals(Integer.valueOf(1), realm.getOtpPolicyLookAheadWindow());
otpPolicyPage.form().setValues(OTPType.TIME_BASED, OTPHashAlg.SHA1, Digits.EMPTY, "1", "");
assertEquals("Error! Missing or invalid field(s). Please verify the fields in red.", otpPolicyPage.getErrorMessage());
otpPolicyPage.closeNotification();
otpPolicyPage.navigateTo();
otpPolicyPage.form().setValues(OTPType.TIME_BASED, OTPHashAlg.SHA1, Digits.EMPTY, "1", " ");
assertEquals("Error! Missing or invalid field(s). Please verify the fields in red.", otpPolicyPage.getErrorMessage());
otpPolicyPage.closeNotification();
otpPolicyPage.navigateTo();
otpPolicyPage.form().setValues(OTPType.TIME_BASED, OTPHashAlg.SHA1, Digits.EMPTY, "1", "no number");
assertEquals("Error! Missing or invalid field(s). Please verify the fields in red.", otpPolicyPage.getErrorMessage());
otpPolicyPage.closeNotification();
otpPolicyPage.navigateTo();
realm = testRealmResource().toRepresentation();
assertEquals(Integer.valueOf(30), realm.getOtpPolicyPeriod());
otpPolicyPage.form().setValues(OTPType.COUNTER_BASED, OTPHashAlg.SHA1, Digits.EMPTY, "1", "");
assertEquals("Error! Missing or invalid field(s). Please verify the fields in red.", otpPolicyPage.getErrorMessage());
otpPolicyPage.closeNotification();
otpPolicyPage.navigateTo();
otpPolicyPage.form().setValues(OTPType.COUNTER_BASED, OTPHashAlg.SHA1, Digits.EMPTY, "1", " ");
assertEquals("Error! Missing or invalid field(s). Please verify the fields in red.", otpPolicyPage.getErrorMessage());
otpPolicyPage.closeNotification();
otpPolicyPage.navigateTo();
otpPolicyPage.form().setValues(OTPType.COUNTER_BASED, OTPHashAlg.SHA1, Digits.EMPTY, "1", "no number");
assertEquals("Error! Missing or invalid field(s). Please verify the fields in red.", otpPolicyPage.getErrorMessage());
otpPolicyPage.form().setValues(OTPType.COUNTER_BASED, OTPHashAlg.SHA1, Digits.EMPTY, "1", "1 2");
assertEquals("Error! Missing or invalid field(s). Please verify the fields in red.", otpPolicyPage.getErrorMessage());
otpPolicyPage.closeNotification();
otpPolicyPage.navigateTo();
realm = testRealmResource().toRepresentation();
assertEquals(Integer.valueOf(0), realm.getOtpPolicyInitialCounter());

View file

@ -0,0 +1,75 @@
package org.keycloak.testsuite.console.federation;
import static org.junit.Assert.assertEquals;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Test;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
import org.keycloak.testsuite.console.AbstractConsoleTest;
import org.keycloak.testsuite.console.page.federation.CreateKerberosUserProvider;
/**
* @author pdrozd
*/
public class KerberosUserFederationTest extends AbstractConsoleTest {
private static final String UNSYNCED = "UNSYNCED";
private static final String READ_ONLY = "READ_ONLY";
@Page
private CreateKerberosUserProvider createKerberosUserProvider;
@Test
public void configureKerberosProvider() {
createKerberosUserProvider.navigateTo();
createKerberosUserProvider.form().setConsoleDisplayNameInput("kerberos");
createKerberosUserProvider.form().setKerberosRealmInput("KEYCLOAK.ORG");
createKerberosUserProvider.form().setServerPrincipalInput("HTTP/localhost@KEYCLOAK.ORG");
createKerberosUserProvider.form().setKeyTabInput("http.keytab");
createKerberosUserProvider.form().setDebugEnabled(true);
createKerberosUserProvider.form().setAllowPasswordAuthentication(true);
createKerberosUserProvider.form().selectEditMode(READ_ONLY);
createKerberosUserProvider.form().setUpdateProfileFirstLogin(true);
createKerberosUserProvider.form().save();
assertFlashMessageSuccess();
RealmRepresentation realm = testRealmResource().toRepresentation();
UserFederationProviderRepresentation ufpr = realm.getUserFederationProviders().get(0);
assertKerberosSetings(ufpr, "KEYCLOAK.ORG", "HTTP/localhost@KEYCLOAK.ORG", "http.keytab", "true", "true", "true");
}
@Test
public void invalidSettingsTest() {
createKerberosUserProvider.navigateTo();
createKerberosUserProvider.form().setConsoleDisplayNameInput("kerberos");
createKerberosUserProvider.form().setServerPrincipalInput("HTTP/localhost@KEYCLOAK.ORG");
createKerberosUserProvider.form().setKeyTabInput("http.keytab");
createKerberosUserProvider.form().setDebugEnabled(true);
createKerberosUserProvider.form().setAllowPasswordAuthentication(true);
createKerberosUserProvider.form().selectEditMode(UNSYNCED);
createKerberosUserProvider.form().setUpdateProfileFirstLogin(true);
createKerberosUserProvider.form().save();
assertFlashMessageDanger();
createKerberosUserProvider.form().setServerPrincipalInput("");
createKerberosUserProvider.form().setKerberosRealmInput("KEYCLOAK.ORG");;
createKerberosUserProvider.form().save();
assertFlashMessageDanger();
createKerberosUserProvider.form().setServerPrincipalInput("HTTP/localhost@KEYCLOAK.ORG");;
createKerberosUserProvider.form().setKeyTabInput("");
createKerberosUserProvider.form().save();
assertFlashMessageDanger();
createKerberosUserProvider.form().setKeyTabInput("http.keytab");;
createKerberosUserProvider.form().save();
assertFlashMessageSuccess();
}
private void assertKerberosSetings(UserFederationProviderRepresentation ufpr, String kerberosRealm, String serverPrincipal, String keyTab, String debug, String useKerberosForPasswordAuthentication, String updateProfileFirstLogin) {
assertEquals(kerberosRealm, ufpr.getConfig().get("kerberosRealm"));
assertEquals(serverPrincipal, ufpr.getConfig().get("serverPrincipal"));
assertEquals(keyTab, ufpr.getConfig().get("keyTab"));
assertEquals(debug, ufpr.getConfig().get("debug"));
assertEquals(useKerberosForPasswordAuthentication, ufpr.getConfig().get("allowKerberosAuthentication"));
assertEquals(updateProfileFirstLogin, ufpr.getConfig().get("updateProfileFirstLogin"));
}
}

View file

@ -1,71 +1,192 @@
package org.keycloak.testsuite.console.federation;
import static org.junit.Assert.assertEquals;
import java.util.Properties;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.*;
import org.keycloak.models.LDAPConstants;
import org.keycloak.representations.idm.UserRepresentation;
import org.junit.Test;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
import org.keycloak.testsuite.console.AbstractConsoleTest;
import org.keycloak.testsuite.console.page.federation.LdapUserProviderForm;
import org.keycloak.testsuite.console.page.federation.UserFederation;
import org.keycloak.testsuite.console.page.users.Users;
import org.keycloak.testsuite.util.LDAPTestConfiguration;
import java.util.Map;
import static org.junit.Assert.assertTrue;
import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
import static org.keycloak.testsuite.admin.Users.setPasswordFor;
import org.keycloak.testsuite.console.page.federation.CreateLdapUserProvider;
import org.keycloak.util.ldap.LDAPEmbeddedServer;
/**
* Created by fkiss.
* @author fkiss, pdrozd
*/
public class LdapUserFederationTest extends AbstractConsoleTest {
@Page
private LdapUserProviderForm ldapUserProviderForm;
private static final String UNSYNCED = "UNSYNCED";
private static final String READ_ONLY = "READ_ONLY";
private static final String RED_HAT_DIRECTORY_SERVER = "Red Hat Directory Server";
private static final String WRITABLE = "WRITABLE";
private static final String ACTIVE_DIRECTORY = "Active Directory";
@Page
private UserFederation userFederationPage;
private CreateLdapUserProvider createLdapUserProvider;
@Page
private Users usersPage;
@Before
public void beforeTestLdapUserFederation() {
//configure().userFederation();
}
@Ignore
@Test
public void addAndConfigureProvider() {
adminConsolePage.navigateTo();
testRealmLoginPage.form().login(testUser);
public void configureAdProvider() {
createLdapUserProvider.navigateTo();
createLdapUserProvider.form().selectVendor(ACTIVE_DIRECTORY);
createLdapUserProvider.form().setConsoleDisplayNameInput("ldap");
createLdapUserProvider.form().selectEditMode(WRITABLE);
createLdapUserProvider.form().setLdapConnectionUrlInput("ldap://localhost:389");
createLdapUserProvider.form().setLdapBindDnInput("KEYCLOAK/Administrator");
createLdapUserProvider.form().setLdapUserDnInput("ou=People,dc=keycloak,dc=org");
createLdapUserProvider.form().setLdapBindCredentialInput("secret");
createLdapUserProvider.form().setAccountAfterPasswordUpdateEnabled(false);
// enable kerberos
createLdapUserProvider.form().setAllowKerberosAuthEnabled(true);
createLdapUserProvider.form().setKerberosRealmInput("KEYCLOAK.ORG");
createLdapUserProvider.form().setServerPrincipalInput("HTTP/localhost@KEYCLOAK.ORG");
createLdapUserProvider.form().setKeyTabInput("http.keytab");
createLdapUserProvider.form().setDebugEnabled(true);
createLdapUserProvider.form().save();
assertFlashMessageSuccess();
String name = "ldapname";
String LDAP_CONNECTION_PROPERTIES_LOCATION = "ldap/ldap-connection.properties";
LDAPTestConfiguration ldapTestConfiguration = LDAPTestConfiguration.readConfiguration(LDAP_CONNECTION_PROPERTIES_LOCATION);
UserRepresentation newUser = new UserRepresentation();
String testUsername = "defaultrole tester";
newUser.setUsername(testUsername);
setPasswordFor(newUser, PASSWORD);
Map<String,String> ldapConfig = ldapTestConfiguration.getLDAPConfig();
//addLdapProviderTest
configure().userFederation();
userFederationPage.addProvider("ldap");
ldapUserProviderForm.configureLdap(ldapConfig.get(LDAPConstants.LDAP_PROVIDER), ldapConfig.get(LDAPConstants.EDIT_MODE), ldapConfig.get(LDAPConstants.VENDOR), ldapConfig.get(LDAPConstants.CONNECTION_URL), ldapConfig.get(LDAPConstants.USERS_DN), ldapConfig.get(LDAPConstants.BIND_DN), ldapConfig.get(LDAPConstants.BIND_CREDENTIAL));
RealmRepresentation realm = testRealmResource().toRepresentation();
UserFederationProviderRepresentation ufpr = realm.getUserFederationProviders().get(0);
assertLdapProviderSetting(ufpr, "ldap", 0, WRITABLE, "false", "ad", "1", "true", "true", "false");
assertLdapBasicMapping(ufpr, "cn", "cn", "objectGUID", "person, organizationalPerson, user",
"ou=People,dc=keycloak,dc=org");
assertLdapSyncSetings(ufpr, "1000", 0, 0);
assertLdapKerberosSetings(ufpr, "KEYCLOAK.ORG", "HTTP/localhost@KEYCLOAK.ORG", "http.keytab", "true", "false");
}
@Ignore
@Test
public void caseSensitiveSearch() {
// This should fail for now due to case-sensitivity
adminConsolePage.navigateTo();
testRealmLoginPage.form().login("johnKeycloak", "Password1");
assertTrue(flashMessage.getText(), flashMessage.isDanger());
public void configureRhdsProvider() {
createLdapUserProvider.navigateTo();
createLdapUserProvider.form().selectVendor(RED_HAT_DIRECTORY_SERVER);
createLdapUserProvider.form().setConsoleDisplayNameInput("ldap");
createLdapUserProvider.form().selectEditMode(READ_ONLY);
createLdapUserProvider.form().setLdapConnectionUrlInput("ldap://localhost:389");
createLdapUserProvider.form().setLdapBindDnInput("uid=admin,ou=system");
createLdapUserProvider.form().setLdapUserDnInput("ou=People,dc=keycloak,dc=org");
createLdapUserProvider.form().setLdapBindCredentialInput("secret");
createLdapUserProvider.form().save();
assertFlashMessageSuccess();
RealmRepresentation realm = testRealmResource().toRepresentation();
UserFederationProviderRepresentation ufpr = realm.getUserFederationProviders().get(0);
assertLdapProviderSetting(ufpr, "ldap", 0, READ_ONLY, "false", "rhds", "1", "true", "true", "true");
assertLdapBasicMapping(ufpr, "uid", "uid", "nsuniqueid", "inetOrgPerson, organizationalPerson",
"ou=People,dc=keycloak,dc=org");
assertLdapSyncSetings(ufpr, "1000", 0, 0);
}
}
@Test
public void invalidSettingsTest() {
createLdapUserProvider.navigateTo();
createLdapUserProvider.form().selectVendor(ACTIVE_DIRECTORY);
createLdapUserProvider.form().setConsoleDisplayNameInput("ldap");
createLdapUserProvider.form().selectEditMode(UNSYNCED);
createLdapUserProvider.form().setLdapBindDnInput("uid=admin,ou=system");
createLdapUserProvider.form().setLdapUserDnInput("ou=People,dc=keycloak,dc=org");
createLdapUserProvider.form().setLdapBindCredentialInput("secret");
createLdapUserProvider.form().save();
assertFlashMessageDanger();
createLdapUserProvider.form().setLdapUserDnInput("");
createLdapUserProvider.form().setLdapConnectionUrlInput("ldap://localhost:389");
createLdapUserProvider.form().save();
assertFlashMessageDanger();
createLdapUserProvider.form().setLdapUserDnInput("ou=People,dc=keycloak,dc=org");
createLdapUserProvider.form().setLdapBindDnInput("");
createLdapUserProvider.form().save();
assertFlashMessageDanger();
createLdapUserProvider.form().setLdapBindDnInput("uid=admin,ou=system");
createLdapUserProvider.form().setLdapBindCredentialInput("");
createLdapUserProvider.form().save();
assertFlashMessageDanger();
createLdapUserProvider.form().setLdapBindCredentialInput("secret");
createLdapUserProvider.form().save();
assertFlashMessageSuccess();
}
@Test
public void testConnection() throws Exception {
createLdapUserProvider.navigateTo();
createLdapUserProvider.form().selectVendor("Other");
createLdapUserProvider.form().setConsoleDisplayNameInput("ldap");
createLdapUserProvider.form().selectEditMode(WRITABLE);
createLdapUserProvider.form().setLdapConnectionUrlInput("ldap://localhost:10389");
createLdapUserProvider.form().setLdapBindDnInput("uid=admin,ou=system");
createLdapUserProvider.form().setLdapUserDnInput("ou=People,dc=keycloak,dc=org");
createLdapUserProvider.form().setLdapBindCredentialInput("secret");
createLdapUserProvider.form().setAccountAfterPasswordUpdateEnabled(true);
createLdapUserProvider.form().save();
assertFlashMessageSuccess();
LDAPEmbeddedServer ldapServer = null;
try {
ldapServer = startEmbeddedLdapServer();
createLdapUserProvider.form().testConnection();
assertFlashMessageSuccess();
createLdapUserProvider.form().testAuthentication();
assertFlashMessageSuccess();
createLdapUserProvider.form().synchronizeAllUsers();
assertFlashMessageSuccess();
createLdapUserProvider.form().setLdapBindCredentialInput("secret1");
createLdapUserProvider.form().testAuthentication();
assertFlashMessageDanger();
} finally {
if (ldapServer != null) {
ldapServer.stop();
}
}
}
private void assertLdapProviderSetting(UserFederationProviderRepresentation ufpr, String name, int priority,
String editMode, String syncRegistrations, String vendor, String searchScope, String connectionPooling,
String pagination, String enableAccountAfterPasswordUpdate) {
assertEquals(name, ufpr.getDisplayName());
assertEquals(priority, ufpr.getPriority());
assertEquals(editMode, ufpr.getConfig().get("editMode"));
assertEquals(syncRegistrations, ufpr.getConfig().get("syncRegistrations"));
assertEquals(vendor, ufpr.getConfig().get("vendor"));
assertEquals(searchScope, ufpr.getConfig().get("searchScope"));
assertEquals(connectionPooling, ufpr.getConfig().get("connectionPooling"));
assertEquals(pagination, ufpr.getConfig().get("pagination"));
assertEquals(enableAccountAfterPasswordUpdate, ufpr.getConfig().get("userAccountControlsAfterPasswordUpdate"));
}
private void assertLdapBasicMapping(UserFederationProviderRepresentation ufpr, String usernameLdapAttribute,
String rdnLdapAttr, String uuidLdapAttr, String userObjectClasses, String userDN) {
assertEquals(usernameLdapAttribute, ufpr.getConfig().get("usernameLDAPAttribute"));
assertEquals(rdnLdapAttr, ufpr.getConfig().get("rdnLDAPAttribute"));
assertEquals(uuidLdapAttr, ufpr.getConfig().get("uuidLDAPAttribute"));
assertEquals(userObjectClasses, ufpr.getConfig().get("userObjectClasses"));
assertEquals(userDN, ufpr.getConfig().get("usersDn"));
}
private void assertLdapKerberosSetings(UserFederationProviderRepresentation ufpr, String kerberosRealm,
String serverPrincipal, String keyTab, String debug, String useKerberosForPasswordAuthentication) {
assertEquals(kerberosRealm, ufpr.getConfig().get("kerberosRealm"));
assertEquals(serverPrincipal, ufpr.getConfig().get("serverPrincipal"));
assertEquals(keyTab, ufpr.getConfig().get("keyTab"));
assertEquals(debug, ufpr.getConfig().get("debug"));
assertEquals(useKerberosForPasswordAuthentication,
ufpr.getConfig().get("useKerberosForPasswordAuthentication"));
}
private void assertLdapSyncSetings(UserFederationProviderRepresentation ufpr, String batchSize,
int periodicFullSync, int periodicChangedUsersSync) {
assertEquals(batchSize, ufpr.getConfig().get("batchSizeForSync"));
assertEquals(periodicFullSync, ufpr.getFullSyncPeriod());
assertEquals(periodicChangedUsersSync, ufpr.getChangedSyncPeriod());
}
private LDAPEmbeddedServer startEmbeddedLdapServer() throws Exception {
Properties defaultProperties = new Properties();
defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_DSF, LDAPEmbeddedServer.DSF_INMEMORY);
defaultProperties.setProperty(LDAPEmbeddedServer.PROPERTY_LDIF_FILE, "classpath:ldap/users.ldif");
LDAPEmbeddedServer ldapEmbeddedServer = new LDAPEmbeddedServer(defaultProperties);
ldapEmbeddedServer.init();
ldapEmbeddedServer.start();
return ldapEmbeddedServer;
}
}

View file

@ -0,0 +1,20 @@
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