Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Bill Burke 2016-02-10 14:09:43 -05:00
commit fd212a2a2a
76 changed files with 1328 additions and 512 deletions

View file

@ -41,7 +41,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -73,7 +73,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -64,25 +64,25 @@
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-naming</artifactId>
<version>${jboss.version}</version>
<version>${jboss.as.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-server</artifactId>
<version>${jboss.version}</version>
<version>${jboss.as.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-ee</artifactId>
<version>${jboss.version}</version>
<version>${jboss.as.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-web</artifactId>
<version>${jboss.version}</version>
<version>${jboss.as.version}</version>
</dependency>
<dependency>

View file

@ -72,7 +72,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -236,7 +236,7 @@
kc.createLogoutUrl = function(options) {
var url = getRealmUrl()
+ '/protocol/openid-connect/logout'
+ '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options));
+ '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options, false));
return url;
}
@ -842,14 +842,18 @@
return createPromise().promise;
},
redirectUri: function(options) {
redirectUri: function(options, encodeHash) {
if (arguments.length == 1) {
encodeHash = true;
}
if (options && options.redirectUri) {
return options.redirectUri;
} else if (kc.redirectUri) {
return kc.redirectUri;
} else {
var redirectUri = location.href;
if (location.hash) {
if (location.hash && encodeHash) {
redirectUri = redirectUri.substring(0, location.href.indexOf('#'));
redirectUri += (redirectUri.indexOf('?') == -1 ? '?' : '&') + 'redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
}

View file

@ -75,7 +75,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -53,7 +53,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -41,7 +41,6 @@
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
@ -50,11 +49,10 @@
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
@ -82,7 +80,6 @@
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>${bouncycastle.crypto.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>

View file

@ -75,7 +75,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>

View file

@ -79,21 +79,15 @@
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-annotations</artifactId>
<version>${jboss-logging-tools.version}</version>
<!-- This is a compile-time dependency of this project, but is not needed at compile or runtime by other
projects that depend on this project.-->
<version>${jboss.logging.tools.wf8.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-processor</artifactId>
<version>${jboss-logging-tools.version}</version>
<!-- This is a compile-time dependency of this project, but is not needed at compile or runtime by other
projects that depend on this project.-->
<version>${jboss.logging.tools.wf8.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>

View file

@ -89,7 +89,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>

View file

@ -53,7 +53,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -64,25 +64,25 @@
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-naming</artifactId>
<version>${jboss.version}</version>
<version>${jboss.as.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-server</artifactId>
<version>${jboss.version}</version>
<version>${jboss.as.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-ee</artifactId>
<version>${jboss.version}</version>
<version>${jboss.as.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-web</artifactId>
<version>${jboss.version}</version>
<version>${jboss.as.version}</version>
</dependency>
<dependency>
@ -104,7 +104,7 @@ projects that depend on this project.-->
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-controller</artifactId>
<version>${jboss.version}</version>
<version>${jboss.as.version}</version>
</dependency>
<dependency>

View file

@ -61,7 +61,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -69,7 +69,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -69,7 +69,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>

View file

@ -67,7 +67,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -45,7 +45,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -23,6 +23,7 @@ import org.keycloak.adapters.spi.KeycloakAccount;
import org.keycloak.common.util.Encode;
import org.keycloak.common.util.MultivaluedHashMap;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
@ -173,11 +174,54 @@ public class FilterSessionStore implements AdapterSessionStore {
public ServletInputStream getInputStream() throws IOException {
if (needRequestRestore && body != null) {
final ByteArrayInputStream is = new ByteArrayInputStream(body);
return new ServletInputStream() {
private int lastIndex = 0;
private ReadListener readListener = null;
@Override
public boolean isFinished() {
return lastIndex == body.length;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
this.readListener = readListener;
if (!isFinished()) {
try {
readListener.onDataAvailable();
} catch (IOException e) {
readListener.onError(e);
}
} else {
try {
readListener.onAllDataRead();
} catch (IOException e) {
readListener.onError(e);
}
}
}
@Override
public int read() throws IOException {
return is.read();
int i = -1;
if (!isFinished()) {
i = body[lastIndex];
lastIndex++;
if (isFinished() && readListener != null) {
try {
readListener.onAllDataRead();
} catch (IOException e) {
readListener.onError(e);
throw e;
}
}
}
return i;
}
};
}

View file

@ -47,7 +47,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -107,6 +107,12 @@
(via the subsystem, or auth-method in web.xml
</para>
</simplesect>
<simplesect>
<title>Client Registration service endpoints moved</title>
<para>
The Client Registration service endpoints have been moved from <literal>{realm}/clients</literal> to <literal>{realm}/clients-registrations</literal>.
</para>
</simplesect>
<simplesect>
<title>Deprecated OpenID Connect endpoints</title>
<para>

View file

@ -27,7 +27,7 @@
<para>
The Client Registration Service provides built-in support for Keycloak Client Representations, OpenID Connect
Client Meta Data and SAML Entity Descriptors. It's also possible to plugin custom client registration providers
if required. The Client Registration Service endpoint is <literal>&lt;KEYCLOAK URL&gt;/realms/&lt;realm&gt;/clients/&lt;provider&gt;</literal>.
if required. The Client Registration Service endpoint is <literal>&lt;KEYCLOAK URL&gt;/realms/&lt;realm&gt;/clients-registrations/&lt;provider&gt;</literal>.
</para>
<para>
The built-in supported <literal>providers</literal> are:
@ -123,23 +123,23 @@ Authorization: bearer eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOiJmMjJmNzQyYy04ZjNlLTQ2M....
</para>
<para>
To create a client create a Client Representation (JSON) then do a HTTP POST to:
<literal>&lt;KEYCLOAK URL&gt;/realms/&lt;realm&gt;/clients/default</literal>. It will return a Client Representation
<literal>&lt;KEYCLOAK URL&gt;/realms/&lt;realm&gt;/clients-registrations/default</literal>. It will return a Client Representation
that also includes the registration access token. You should save the registration access token somewhere
if you want to retrieve the config, update or delete the client later.
</para>
<para>
To retrieve the Client Representation then do a HTTP GET to:
<literal>&lt;KEYCLOAK URL&gt;/realms/&lt;realm&gt;/clients/default/&lt;client id&gt;</literal>. It will also
<literal>&lt;KEYCLOAK URL&gt;/realms/&lt;realm&gt;/clients-registrations/default/&lt;client id&gt;</literal>. It will also
return a new registration access token.
</para>
<para>
To update the Client Representation then do a HTTP PUT to with the updated Client Representation to:
<literal>&lt;KEYCLOAK URL&gt;/realms/&lt;realm&gt;/clients/default/&lt;client id&gt;</literal>. It will also
<literal>&lt;KEYCLOAK URL&gt;/realms/&lt;realm&gt;/clients-registrations/default/&lt;client id&gt;</literal>. It will also
return a new registration access token.
</para>
<para>
To delete the Client Representation then do a HTTP DELETE to:
<literal>&lt;KEYCLOAK URL&gt;/realms/&lt;realm&gt;/clients/default/&lt;client id&gt;</literal>
<literal>&lt;KEYCLOAK URL&gt;/realms/&lt;realm&gt;/clients-registrations/default/&lt;client id&gt;</literal>
</para>
</section>
@ -155,7 +155,7 @@ Authorization: basic BASE64(client-id + ':' + client-secret)
</para>
<para>
To retrieve the Adapter Configuration then do a HTTP GET to:
<literal>&lt;KEYCLOAK URL&gt;//realms/&lt;realm&gt;/clients/installation/&lt;client id&gt;</literal>
<literal>&lt;KEYCLOAK URL&gt;//realms/&lt;realm&gt;/clients-registrations/installation/&lt;client id&gt;</literal>
</para>
<para>
No authentication is required for public clients. This means that for the JavaScript adapter you can
@ -172,7 +172,7 @@ Authorization: basic BASE64(client-id + ':' + client-secret)
</para>
<para>
The endpoint to use these specifications to register clients in Keycloak is:
<literal>&lt;KEYCLOAK URL&gt;/realms/&lt;realm&gt;/clients/oidc[/&lt;client id&gt;]</literal>.
<literal>&lt;KEYCLOAK URL&gt;/realms/&lt;realm&gt;/clients-registrations/oidc[/&lt;client id&gt;]</literal>.
</para>
<para>
This endpoints can also be found in the OpenID Connect Discovery endpoint for the realm:
@ -190,7 +190,7 @@ Authorization: basic BASE64(client-id + ':' + client-secret)
</para>
<para>
To create a client do a HTTP POST with the SAML Entity Descriptor to:
<literal>&lt;KEYCLOAK URL&gt;/realms/&lt;realm&gt;/clients/saml2-entity-descriptor</literal>.
<literal>&lt;KEYCLOAK URL&gt;/realms/&lt;realm&gt;/clients-registrations/saml2-entity-descriptor</literal>.
</para>
</section>

View file

@ -50,7 +50,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -58,7 +58,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -48,7 +48,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -42,7 +42,7 @@
<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -42,7 +42,7 @@
<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -42,7 +42,7 @@
<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -48,7 +48,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -42,7 +42,7 @@
<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -42,7 +42,7 @@
<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -34,7 +34,7 @@
<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -34,8 +34,7 @@
<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<version>1.0.1.Final</version>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -34,8 +34,7 @@
<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<version>1.0.1.Final</version>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -52,7 +52,7 @@
<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -53,7 +53,7 @@
<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>

View file

@ -44,7 +44,7 @@
<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -42,7 +42,7 @@
<dependencies>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

View file

@ -177,7 +177,7 @@ public class ClientRegistration {
}
public ClientRegistrationBuilder url(String authUrl, String realm) {
url = HttpUtil.getUrl(authUrl, "realms", realm, "clients");
url = HttpUtil.getUrl(authUrl, "realms", realm, "clients-registrations");
return this;
}

View file

@ -63,7 +63,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>

115
pom.xml
View file

@ -40,71 +40,74 @@
<product.version>${project.version}</product.version>
<product.build-time>${timestamp}</product.build-time>
<apacheds.version>2.0.0-M17</apacheds.version>
<apacheds.codec.version>1.0.0-M23</apacheds.codec.version>
<org.apache.james.apache-mime4j.version>0.6</org.apache.james.apache-mime4j.version>
<bouncycastle.crypto.version>1.52</bouncycastle.crypto.version>
<!-- WildFly -->
<eap.version>7.0.0.Beta</eap.version>
<jboss.as.version>7.2.0.Final</jboss.as.version>
<wildfly.version>10.0.0.Final</wildfly.version>
<aesh.version>0.66.4</aesh.version>
<apache.httpcomponents.version>4.5</apache.httpcomponents.version>
<apache.httpcomponents.httpcore.version>4.4.1</apache.httpcomponents.httpcore.version>
<apache.mime4j.version>0.6</apache.mime4j.version>
<bouncycastle.version>1.52</bouncycastle.version>
<dom4j.version>1.6.1</dom4j.version>
<github.relaxng.version>2011.1</github.relaxng.version>
<h2.version>1.3.173</h2.version>
<hibernate.entitymanager.version>5.0.7.Final</hibernate.entitymanager.version>
<hibernate.javax.persistence.version>1.0.0.Final</hibernate.javax.persistence.version>
<infinispan.version>8.1.0.Final</infinispan.version>
<jackson.version>2.5.4</jackson.version>
<apache.httpcomponents.version>4.3.6</apache.httpcomponents.version>
<apache.httpcomponents.httpcore.version>4.3.3</apache.httpcomponents.httpcore.version>
<resteasy.version>3.0.14.Final</resteasy.version>
<keycloak.apache.httpcomponents.version>4.2.1</keycloak.apache.httpcomponents.version>
<undertow.version>1.3.10.Final</undertow.version>
<picketlink.version>2.7.0.Final</picketlink.version>
<mongo.driver.version>3.2.0</mongo.driver.version>
<javax.mail.version>1.5.5</javax.mail.version>
<jboss.logging.version>3.3.0.Final</jboss.logging.version>
<jboss.logging.tools.version>2.0.1.Final</jboss.logging.tools.version>
<jboss-logging-tools.version>1.2.0.Beta1</jboss-logging-tools.version>
<jboss.spec.javax.xml.bind.jboss-jaxb-api_2.2_spec.version>1.0.4.Final</jboss.spec.javax.xml.bind.jboss-jaxb-api_2.2_spec.version>
<jboss.logging.tools.wf8.version>1.2.0.Final</jboss.logging.tools.wf8.version>
<jboss-jaxrs-api_2.0_spec>1.0.0.Final</jboss-jaxrs-api_2.0_spec>
<hibernate.javax.persistence.version>1.0.0.Final</hibernate.javax.persistence.version>
<hibernate.javax.persistence.artifactId>hibernate-jpa-2.1-api</hibernate.javax.persistence.artifactId>
<hibernate.entitymanager.version>4.3.10.Final</hibernate.entitymanager.version>
<h2.version>1.3.173</h2.version>
<mysql.version>5.1.29</mysql.version>
<postgresql.version>9.3-1100-jdbc41</postgresql.version>
<dom4j.version>1.6.1</dom4j.version>
<xml-apis.version>1.4.01</xml-apis.version>
<jboss.spec.javax.xml.bind.jboss-jaxb-api_2.2_spec.version>1.0.4.Final</jboss.spec.javax.xml.bind.jboss-jaxb-api_2.2_spec.version>
<log4j.version>1.2.16</log4j.version>
<resteasy.version>3.0.14.Final</resteasy.version>
<servlet.api.31.version>1.0.0.Final</servlet.api.31.version>
<slf4j.version>1.7.7</slf4j.version>
<wildfly.version>10.0.0.Final</wildfly.version>
<wildfly.core.version>2.0.10.Final</wildfly.core.version>
<wildfly.build-tools.version>1.1.0.Final</wildfly.build-tools.version>
<eap.version>7.0.0.Beta</eap.version>
<jboss.version>7.2.0.Final</jboss.version>
<servlet.api.30.version>1.0.2.Final</servlet.api.30.version>
<google.zxing.version>3.2.1</google.zxing.version>
<github.relaxng.version>2011.1</github.relaxng.version>
<freemarker.version>2.3.23</freemarker.version>
<twitter4j.version>4.0.4</twitter4j.version>
<selenium.version>2.35.0</selenium.version>
<sun.istack.version>2.21</sun.istack.version>
<sun.jaxb.version>2.2.11</sun.jaxb.version>
<sun.xsom.version>20140925</sun.xsom.version>
<javax.mail.version>1.4.5</javax.mail.version>
<infinispan.version>8.1.0.Final</infinispan.version>
<liquibase.version>3.4.1</liquibase.version>
<undertow.version>1.3.15.Final</undertow.version>
<wildfly.core.version>2.0.10.Final</wildfly.core.version>
<wildfly.build-tools.version>1.1.0.Final</wildfly.build-tools.version>
<!--<xmlsec.version>2.0.5</xmlsec.version>--> <!-- WILDFLY VERSION -->
<xmlsec.version>1.5.1</xmlsec.version>
<!-- Others -->
<apacheds.version>2.0.0-M17</apacheds.version>
<apacheds.codec.version>1.0.0-M23</apacheds.codec.version>
<google.zxing.version>3.2.1</google.zxing.version>
<freemarker.version>2.3.23</freemarker.version>
<jetty9.version>9.1.0.v20131115</jetty9.version>
<liquibase.version>3.4.1</liquibase.version>
<mongo.driver.version>3.2.0</mongo.driver.version>
<mysql.version>5.1.29</mysql.version>
<osgi.version>4.2.0</osgi.version>
<pax.web.version>3.1.2</pax.web.version>
<postgresql.version>9.3-1100-jdbc41</postgresql.version>
<twitter4j.version>4.0.4</twitter4j.version>
<!-- Test -->
<greenmail.version>1.3.1b</greenmail.version>
<hamcrest.version>1.3</hamcrest.version>
<jmeter.version>2.10</jmeter.version>
<junit.version>4.12</junit.version>
<hamcrest.version>1.3</hamcrest.version>
<log4j.version>1.2.17</log4j.version>
<greenmail.version>1.3.1b</greenmail.version>
<xmlsec.version>1.5.1</xmlsec.version>
<aesh.version>0.66.4</aesh.version>
<picketlink.version>2.7.0.Final</picketlink.version>
<selenium.version>2.35.0</selenium.version>
<xml-apis.version>1.4.01</xml-apis.version>
<!-- Maven Plugins -->
<embedmongo.plugin.version>0.1.12</embedmongo.plugin.version>
<enforcer.plugin.version>1.4</enforcer.plugin.version>
<jboss.as.plugin.version>7.5.Final</jboss.as.plugin.version>
<wildfly.plugin.version>1.0.1.Final</wildfly.plugin.version>
<minify.plugin.version>1.7.2</minify.plugin.version>
<embedmongo.plugin.version>0.1.12</embedmongo.plugin.version>
<jmeter.plugin.version>1.9.0</jmeter.plugin.version>
<jmeter.analysis.plugin.version>1.0.4</jmeter.analysis.plugin.version>
<minify.plugin.version>1.7.2</minify.plugin.version>
<osgi.bundle.plugin.version>2.3.7</osgi.bundle.plugin.version>
<wildfly.plugin.version>1.0.1.Final</wildfly.plugin.version>
</properties>
<url>http://keycloak.org</url>
@ -201,16 +204,16 @@
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>${bouncycastle.crypto.version}</version>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>${bouncycastle.crypto.version}</version>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<artifactId>javax.mail-api</artifactId>
<version>${javax.mail.version}</version>
</dependency>
<dependency>
@ -300,8 +303,8 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<version>${servlet.api.30.version}</version>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
<version>${servlet.api.31.version}</version>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
@ -343,7 +346,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>${hibernate.javax.persistence.version}</version>
</dependency>
<dependency>
@ -508,7 +511,7 @@
<dependency>
<groupId>org.apache.james</groupId>
<artifactId>apache-mime4j</artifactId>
<version>${org.apache.james.apache-mime4j.version}</version>
<version>${apache.mime4j.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
@ -584,12 +587,6 @@
<artifactId>wildfly-web-common</artifactId>
<version>${wildfly.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-processor</artifactId>
<version>${jboss-logging-tools.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-core</artifactId>

View file

@ -54,7 +54,7 @@
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<artifactId>javax.mail-api</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
@ -63,7 +63,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
</dependency>
<dependency>
<groupId>org.twitter4j</groupId>

View file

@ -17,11 +17,11 @@
package org.keycloak.services.clientregistration;
import org.jboss.resteasy.spi.NotFoundException;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.KeycloakSession;
import org.keycloak.services.ErrorResponseException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;

View file

@ -21,6 +21,7 @@ import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.AbstractOAuthClient;
import org.keycloak.common.ClientConnection;
import org.keycloak.OAuth2Constants;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@ -113,13 +114,15 @@ public abstract class AbstractSecuredLocalService {
throw new BadRequestException("state not specified");
}
URI uri = getBaseRedirectUri();
URI redirectUri = path != null ? uri.resolve(path) : uri;
KeycloakUriBuilder redirect = KeycloakUriBuilder.fromUri(getBaseRedirectUri());
if (path != null) {
redirect.path(path);
}
if (referrer != null) {
redirectUri = redirectUri.resolve("?referrer=" + referrer);
redirect.queryParam("referrer", referrer);
}
return Response.status(302).location(redirectUri).build();
return Response.status(302).location(redirect.build()).build();
} finally {
}
}

View file

@ -84,23 +84,6 @@ public class RealmsResource {
return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getBrokerService");
}
@Path("{realm}/login-status-iframe.html")
@Deprecated
public Object getLoginStatusIframe(final @PathParam("realm") String name,
@QueryParam("client_id") String client_id,
@QueryParam("origin") String origin) {
RealmModel realm = init(name);
EventBuilder event = new EventBuilder(realm, session, clientConnection);
LoginProtocolFactory factory = (LoginProtocolFactory)session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, OIDCLoginProtocol.LOGIN_PROTOCOL);
OIDCLoginProtocolService endpoint = (OIDCLoginProtocolService)factory.createProtocolEndpoint(realm, event);
ResteasyProviderFactory.getInstance().injectProperties(endpoint);
return endpoint.getLoginStatusIframe();
}
@Path("{realm}/protocol/{protocol}")
public Object getProtocol(final @PathParam("realm") String name,
final @PathParam("protocol") String protocol) {
@ -115,13 +98,6 @@ public class RealmsResource {
return endpoint;
}
@Path("{realm}/tokens")
@Deprecated
public Object getTokenService(final @PathParam("realm") String name) {
// for backward compatibility.
return getProtocol(name, "openid-connect");
}
@Path("{realm}/login-actions")
public LoginActionsService getLoginActionsService(final @PathParam("realm") String name) {
RealmModel realm = init(name);
@ -131,7 +107,7 @@ public class RealmsResource {
return service;
}
@Path("{realm}/clients")
@Path("{realm}/clients-registrations")
public ClientRegistrationService getClientsService(final @PathParam("realm") String name) {
RealmModel realm = init(name);
EventBuilder event = new EventBuilder(realm, session, clientConnection);

View file

@ -13,17 +13,28 @@ other options are: `auth-server-wildfly` and `auth-server-eap7`. The values corr
**Note 1:** For the non-default options it's necessary to build a corresponding server module prior to running any of the test modules.
This can be done by building the server module directly (from `servers/wildfly`/`servers/eap7`),
or by activating `auth-server-wildfly`/`auth-server-eap7` profile when building from the top level module.
(The profiles will also set the proper value of the `auth.server.container` property.)
**Note 2:** Most server-side configurations are done during the build of the server module
and included in the output artifact - which is then consumed by the test modules( if a corresponding profile is activated).
To reflect a change in server config in the test (e.g. a datasource) it's necessary to rebuild the server module after each change.
### Migration
#### Migration
Migration tests can be enabled by setting `-Dmigrated.auth.server.container` property or activating a corresponding profile.
When enabled, the `AuthServerTestEnricher` class will start/stop the selected *migrated* instance
even **before** the *current* auth server instance is started.
Migration tests can be enabled by setting `-Dmigrated.auth.server.version` property. Supported versions can be found at the bottom of `tests/pom.xml`.
When enabled, the `AuthServerTestEnricher` class will start and stop the selected migrated instance
*before* the current auth server instance is started.
#### Cluster Setup
Cluster setup can be enabled with profile `auth-server-wildfly-cluster`.
(It is also necessary to build the server modules with this profile before running the test. See *Notes 1 and 2* above.)
Clustering tests require MULTICAST to be enabled on machine's `loopback` network interface.
This can be done by running the following commands under root privileges:
```
route add -net 224.0.0.0 netmask 240.0.0.0 dev lo
ifconfig lo multicast
```
### App Servers

View file

@ -3,12 +3,15 @@ package org.keycloak.testsuite.cluster;
import java.util.ArrayList;
import java.util.List;
import org.jboss.arquillian.container.test.api.ContainerController;
import org.jboss.arquillian.graphene.page.Page;
import org.jboss.arquillian.test.api.ArquillianResource;
import static org.junit.Assert.assertTrue;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.models.Constants;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.arquillian.ContainerInfo;
import org.keycloak.testsuite.auth.page.AuthRealm;
import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN;
import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
@ -35,25 +38,38 @@ public abstract class AbstractClusterTest extends AbstractKeycloakTest {
controller.start(backendNode.getQualifier());
assertTrue(controller.isStarted(backendNode.getQualifier()));
log.info("Initializing admin client for: '" + backendNode.getContextRoot() + "/auth'");
backendAdminClients.add(Keycloak.getInstance(backendNode.getContextRoot() + "/auth",
MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID));
backendAdminClients.add(createAdminClientFor(backendNode));
}
}
protected ContainerInfo backendInfo(int i) {
protected Keycloak createAdminClientFor(ContainerInfo backendNode) {
log.info("Initializing admin client for " + backendNode.getContextRoot() + "/auth");
return Keycloak.getInstance(backendNode.getContextRoot() + "/auth",
MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID);
}
protected ContainerInfo backendNode(int i) {
return suiteContext.getAuthServerBackendsInfo().get(i);
}
protected void startBackendNode(int i) {
String container = backendInfo(i).getQualifier();
String container = backendNode(i).getQualifier();
if (!controller.isStarted(container)) {
controller.start(container);
backendAdminClients.set(i, createAdminClientFor(backendNode(i)));
}
}
protected void stopBackendNode(int i) {
controller.kill(backendInfo(i).getQualifier());
protected void killBackendNode(int i) {
backendAdminClients.get(i).close();
controller.kill(backendNode(i).getQualifier());
}
protected void listRealms(int i) {
log.info(String.format("Node %s: AccessTokenString: %s", i + 1, backendAdminClients.get(i).tokenManager().getAccessTokenString()));
for (RealmRepresentation r : backendAdminClients.get(i).realms().findAll()) {
log.info(String.format("Node %s: Realm: %s, Id: %s", i + 1, r.getRealm(), r.getId()));
}
}
}

View file

@ -0,0 +1,57 @@
package org.keycloak.testsuite.cluster;
import org.junit.Before;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.testsuite.arquillian.ContainerInfo;
import static org.keycloak.testsuite.util.WaitUtils.pause;
/**
*
* @author tkyjovsk
*/
public abstract class AbstractTwoNodeClusterTest extends AbstractClusterTest {
@Before
public void beforeTwoNodeClusterTest() {
startBackendNodes(2);
pause(3000);
}
protected ContainerInfo backend1Info() {
return backendNode(0);
}
protected ContainerInfo backend2Info() {
return backendNode(1);
}
protected Keycloak backend1AdminClient() {
return backendAdminClients.get(0);
}
protected Keycloak backend2AdminClient() {
return backendAdminClients.get(1);
}
protected void startBackend1() {
startBackendNode(0);
}
protected void startBackend2() {
startBackendNode(1);
}
protected void failback() {
startBackend1();
startBackend2();
}
protected void killBackend1() {
killBackendNode(0);
}
protected void killBackend2() {
killBackendNode(1);
}
}

View file

@ -3,55 +3,43 @@ package org.keycloak.testsuite.cluster;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.arquillian.ContainerInfo;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
import static org.keycloak.testsuite.util.WaitUtils.pause;
/**
*
* @author tkyjovsk
*/
public class TwoNodeClusterTest extends AbstractClusterTest {
public class EntityInvalidationClusterTest extends AbstractTwoNodeClusterTest {
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
}
@Before
public void beforeTwoNodeClusterTest() {
startBackendNodes(2);
pause(3000);
@Test
public void realmCRUDWithoutFailover() {
realmCRUD(TEST + "_wofo", false);
}
@Test
public void testRealm() {
testRealm(TEST, false);
public void realmCRUDWithFailover() {
realmCRUD(TEST + "_wfo", true);
}
@Test
public void testRealmWithFailover() {
testRealm(TEST + "_fo", true);
}
public void testRealm(String realm, boolean containerFailover) {
public void realmCRUD(String realm, boolean containerFailover) {
RealmRepresentation testRealm = new RealmRepresentation();
testRealm.setRealm(realm);
testRealm.setEnabled(true);
// CREATE on node1
log.info("Creating test realm via node1.");
backend1AdminClient().realms().create(testRealm);
log.info("Test realm created.");
// check if created on node1
RealmRepresentation testRealmOnBackend1 = backend1AdminClient().realms().realm(realm).toRepresentation();
assertEquals(testRealmOnBackend1.getRealm(), testRealm.getRealm());
if (containerFailover) {
stopBackend1();
killBackend1();
}
// check if created on node2
@ -62,27 +50,28 @@ public class TwoNodeClusterTest extends AbstractClusterTest {
failback();
// UPDATE on node2
testRealmOnBackend2.setRealm(realm + "_updated");
String realmUpdated = realm + "_updated";
testRealmOnBackend2.setRealm(realmUpdated);
backend2AdminClient().realms().realm(realm).update(testRealmOnBackend2);
if (containerFailover) {
stopBackend2();
killBackend2();
}
// check if updated on node1
testRealmOnBackend1 = backend1AdminClient().realms().realm(realm).toRepresentation();
testRealmOnBackend1 = backend1AdminClient().realms().realm(realmUpdated).toRepresentation();
assertEquals(testRealmOnBackend1.getId(), testRealmOnBackend2.getId());
assertEquals(testRealmOnBackend1.getRealm(), testRealmOnBackend2.getRealm());
failback();
// DELETE on node1
backend1AdminClient().realms().realm(realm).remove();
backend1AdminClient().realms().realm(realmUpdated).remove();
if (containerFailover) {
stopBackend1();
killBackend1();
}
// check if deleted on node2
boolean testRealmOnBackend2Exists = false;
for (RealmRepresentation realmOnBackend2 : backend2AdminClient().realms().findAll()) {
if (realm.equals(realmOnBackend2.getRealm())
if (realmUpdated.equals(realmOnBackend2.getRealm())
|| testRealmOnBackend1.getId().equals(realmOnBackend2.getId())) {
testRealmOnBackend2Exists = true;
break;
@ -91,41 +80,30 @@ public class TwoNodeClusterTest extends AbstractClusterTest {
assertFalse(testRealmOnBackend2Exists);
}
protected ContainerInfo backend1Info() {
return backendInfo(0);
}
@Test
public void createRealmViaFrontend() {
String realm = TEST + "_fe";
protected ContainerInfo backend2Info() {
return backendInfo(1);
}
RealmRepresentation testRealm = new RealmRepresentation();
testRealm.setRealm(realm);
testRealm.setEnabled(true);
protected Keycloak backend1AdminClient() {
return backendAdminClients.get(0);
}
// CREATE on frontend
adminClient.realms().create(testRealm);
protected Keycloak backend2AdminClient() {
return backendAdminClients.get(1);
}
// check if created on frontend
RealmRepresentation testRealmOnFrontend = adminClient.realms().realm(realm).toRepresentation();
assertEquals(testRealmOnFrontend.getRealm(), testRealm.getRealm());
protected void startBackend1() {
startBackendNode(0);
}
// check if created on node1
RealmRepresentation testRealmOnBackend1 = backend1AdminClient().realms().realm(realm).toRepresentation();
assertEquals(testRealmOnBackend1.getId(), testRealmOnFrontend.getId());
assertEquals(testRealmOnBackend1.getRealm(), testRealmOnFrontend.getRealm());
protected void startBackend2() {
startBackendNode(1);
// check if created on node2
RealmRepresentation testRealmOnBackend2 = backend2AdminClient().realms().realm(realm).toRepresentation();
assertEquals(testRealmOnBackend2.getId(), testRealmOnFrontend.getId());
assertEquals(testRealmOnBackend2.getRealm(), testRealmOnFrontend.getRealm());
}
protected void failback() {
startBackend1();
startBackend2();
}
protected void stopBackend1() {
stopBackendNode(0);
}
protected void stopBackend2() {
stopBackendNode(1);
}
}

View file

@ -0,0 +1,95 @@
package org.keycloak.testsuite.cluster;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import org.junit.Ignore;
import org.junit.Test;
import org.keycloak.representations.idm.RealmRepresentation;
import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
import static org.keycloak.testsuite.util.WaitUtils.pause;
import org.openqa.selenium.Cookie;
/**
*
* @author tkyjovsk
*/
public class SessionFailoverClusterTest extends AbstractTwoNodeClusterTest {
public static final String KEYCLOAK_SESSION_COOKIE = "KEYCLOAK_SESSION";
public static final String KEYCLOAK_IDENTITY_COOKIE = "KEYCLOAK_IDENTITY";
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
}
@Test
@Ignore("work in progress") // only works with owners="2" at the moment
public void sessionFailover() {
// LOGOUT
accountPage.navigateTo();
driver.navigate().refresh();
pause(3000);
loginPage.form().login(ADMIN, ADMIN);
assertCurrentUrlStartsWith(accountPage);
Cookie sessionCookie = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
assertNotNull(sessionCookie);
killBackend1();
// check if session survived backend failure
driver.navigate().refresh();
pause(3000);
assertCurrentUrlStartsWith(accountPage);
Cookie sessionCookieAfterFailover = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
assertNotNull(sessionCookieAfterFailover);
assertEquals(sessionCookieAfterFailover.getValue(), sessionCookie.getValue());
failback();
// check if session survived backend failback
driver.navigate().refresh();
pause(3000);
assertCurrentUrlStartsWith(accountPage);
Cookie sessionCookieAfterFailback = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
assertNotNull(sessionCookieAfterFailback);
assertEquals(sessionCookieAfterFailover.getValue(), sessionCookie.getValue());
// LOGOUT
accountPage.navigateTo();
accountPage.signOut();
assertCurrentUrlDoesntStartWith(accountPage);
masterRealmPage.navigateTo();
sessionCookie = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
assertNull(sessionCookie);
killBackend1();
// check if session survived backend failure
driver.navigate().refresh();
pause(3000);
assertCurrentUrlDoesntStartWith(accountPage);
masterRealmPage.navigateTo();
sessionCookieAfterFailover = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
assertNull(sessionCookieAfterFailover);
failback();
// check if session survived backend failback
driver.navigate().refresh();
pause(3000);
assertCurrentUrlDoesntStartWith(accountPage);
masterRealmPage.navigateTo();
sessionCookieAfterFailback = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
assertNull(sessionCookieAfterFailback);
}
}

View file

@ -72,10 +72,12 @@
<property name="enabled">${auth.server.wildfly.cluster}</property>
<property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
<property name="jbossHome">${keycloak.balancer.home}</property>
<property name="javaVmArguments">
<property name="jbossArguments">
-Djboss.socket.binding.port-offset=${auth.server.port.offset}
-Xms64m -Xmx512m -XX:MaxPermSize=256m
${adapter.test.props}
</property>
<property name="javaVmArguments">
-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m
-Djava.net.preferIPv4Stack=true
</property>
<property name="managementPort">${auth.server.management.port}</property>
<property name="startupTimeoutInSeconds">${startup.timeout.sec}</property>
@ -87,11 +89,14 @@
<property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
<property name="jbossHome">${keycloak.backend1.home}</property>
<property name="serverConfig">standalone-ha.xml</property>
<property name="javaVmArguments">
<property name="jbossArguments">
-Djboss.socket.binding.port-offset=${auth.server.backend1.port.offset}
-Xms64m -Xmx512m -XX:MaxPermSize=256m
${adapter.test.props}
-Djboss.node.name=node1
${adapter.test.props}
</property>
<property name="javaVmArguments">
-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m
-Djava.net.preferIPv4Stack=true
</property>
<!--<property name="outputToConsole">false</property>-->
<property name="managementPort">${auth.server.backend1.management.port}</property>
@ -104,11 +109,14 @@
<property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
<property name="jbossHome">${keycloak.backend2.home}</property>
<property name="serverConfig">standalone-ha.xml</property>
<property name="javaVmArguments">
<property name="jbossArguments">
-Djboss.socket.binding.port-offset=${auth.server.backend2.port.offset}
-Xms64m -Xmx512m -XX:MaxPermSize=256m
${adapter.test.props}
-Djboss.node.name=node2
${adapter.test.props}
</property>
<property name="javaVmArguments">
-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m
-Djava.net.preferIPv4Stack=true
</property>
<!--<property name="outputToConsole">false</property>-->
<property name="managementPort">${auth.server.backend2.management.port}</property>

View file

@ -69,7 +69,7 @@
<artifactItem>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-dist</artifactId>
<version>${jboss.version}</version>
<version>${jboss.as.version}</version>
<type>zip</type>
<outputDirectory>${containers.home}</outputDirectory>
</artifactItem>

View file

@ -363,7 +363,7 @@
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>

View file

@ -57,7 +57,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
@ -183,7 +183,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>

View file

@ -1,243 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.List;
import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.context.Flag;
import org.jboss.logging.Logger;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.KeycloakSessionTask;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.UserSessionProviderFactory;
import org.keycloak.models.session.UserSessionPersisterProvider;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.common.util.Time;
/**
* HOWTO USE THIS:
*
* 1) Run KeycloakServer with system properties (assuming mongo up and running on localhost):
* -Dkeycloak.realm.provider=mongo -Dkeycloak.user.provider=mongo -Dkeycloak.userSessionPersister.provider=mongo -Dkeycloak.connectionsMongo.db=keycloak -Dkeycloak.connectionsInfinispan.clustered=true -Dresources -DstartInfinispanCLI
*
* 2) Write command on STDIN to persist 50000 userSessions to mongo: persistSessions 50000
*
* 3) Run command "clear" to ensure infinispan cache is cleared. Doublecheck with command "size" is 0
*
* 4) Write command to load sessions from persistent storage - 100 sessions per worker transaction: loadPersistentSessions 100
*
* See the progress in log. Finally run command "size" to ensure size is 100001 (50000 userSessions + 50000 clientSessions + 1 initializationState item)
*
* 5) Alternative to step 3+4 - Kill the server after step 2 and start two KeycloakServer in parallel on ports 8081 and 8082 . See the progress in logs of loading persistent sessions to infinispan.
* Kill the coordinator (usually 8081 node) during startup and see the node 8082 became coordinator and took ownership of loading persistent sessions. After node 8082 fully started, the size of infinispan is again 100001
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class InfinispanCLI {
private static final Logger log = Logger.getLogger(InfinispanCLI.class);
private final KeycloakSessionFactory sessionFactory;
public InfinispanCLI(KeycloakServer server) {
this.sessionFactory = server.getSessionFactory();
}
// WARNING: Stdin blocking operation
public void start() throws IOException {
log.info("Starting infinispan CLI. Exit with 'exit'");
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String line;
try {
while ((line = reader.readLine()) != null) {
log.info("Command: " + line);
if (line.equals("exit")) {
return;
}
final String finalLine = line;
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
@Override
public void run(KeycloakSession session) {
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
Cache<String, SessionEntity> ispnCache = provider.getCache(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME);
runTask(finalLine, ispnCache);
}
});
}
} finally {
log.info("Exit infinispan CLI");
reader.close();
}
}
private void runTask(String line, Cache<String, SessionEntity> cache) {
try {
String[] splits = line.split(" ");
if (splits[0].equals("put")) {
UserSessionEntity userSession = new UserSessionEntity();
String id = splits[1];
userSession.setId(id);
userSession.setRealm(splits[2]);
userSession.setLastSessionRefresh(Time.currentTime());
cache.put(id, userSession);
} else if (splits[0].equals("get")) {
String id = splits[1];
UserSessionEntity userSession = (UserSessionEntity) cache.get(id);
printSession(id, userSession);
} else if (splits[0].equals("remove")) {
String id = splits[1];
cache.remove(id);
} else if (splits[0].equals("clear")) {
cache.clear();
log.info("Cache cleared");
} else if (splits[0].equals("size")) {
log.info("Size: " + cache.size());
} else if (splits[0].equals("list")) {
for (String id : cache.keySet()) {
SessionEntity entity = cache.get(id);
if (!(entity instanceof UserSessionEntity)) {
continue;
}
UserSessionEntity userSession = (UserSessionEntity) cache.get(id);
log.info("list: key=" + id + ", value=" + toString(userSession));
}
} else if (splits[0].equals("getLocal")) {
String id = splits[1];
cache = ((AdvancedCache) cache).withFlags(Flag.CACHE_MODE_LOCAL);
UserSessionEntity userSession = (UserSessionEntity) cache.get(id);
printSession(id, userSession);
} else if (splits[0].equals("persistSessions")) {
final int count = Integer.parseInt(splits[1]);
final List<String> userSessionIds = new LinkedList<>();
final List<String> clientSessionIds = new LinkedList<>();
// Create sessions in separate transaction first
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
@Override
public void run(KeycloakSession session) {
RealmModel realm = session.realms().getRealmByName("master");
UserModel john = session.users().getUserByUsername("admin", realm);
ClientModel testApp = realm.getClientByClientId("security-admin-console");
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
for (int i=0 ; i<count ; i++) {
UserSessionModel userSession = session.sessions().createUserSession(realm, john, "john-doh@localhost", "127.0.0.2", "form", true, null, null);
ClientSessionModel clientSession = session.sessions().createClientSession(realm, testApp);
clientSession.setUserSession(userSession);
clientSession.setRedirectUri("http://redirect");
clientSession.setNote("foo", "bar-" + i);
userSessionIds.add(userSession.getId());
clientSessionIds.add(clientSession.getId());
}
}
});
log.info("Sessions created in infinispan storage");
// Persist them now
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
@Override
public void run(KeycloakSession session) {
RealmModel realm = session.realms().getRealmByName("master");
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
for (String userSessionId : userSessionIds) {
UserSessionModel userSession = session.sessions().getUserSession(realm, userSessionId);
persister.createUserSession(userSession, true);
}
log.info("userSessions persisted");
for (String clientSessionId : clientSessionIds) {
ClientSessionModel clientSession = session.sessions().getClientSession(realm, clientSessionId);
persister.createClientSession(clientSession, true);
}
log.info("clientSessions persisted");
}
});
// Persist them now
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
@Override
public void run(KeycloakSession session) {
RealmModel realm = session.realms().getRealmByName("master");
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
log.info(count + " sessions persisted. Total number of sessions: " + persister.getUserSessionsCount(true));
}
});
} else if (splits[0].equals("loadPersistentSessions")) {
int sessionsPerSegment = Integer.parseInt(splits[1]);
UserSessionProviderFactory sessionProviderFactory = (UserSessionProviderFactory) sessionFactory.getProviderFactory(UserSessionProvider.class);
sessionProviderFactory.loadPersistentSessions(sessionFactory, 10, sessionsPerSegment);
log.info("All persistent sessions loaded successfully");
}
} catch (RuntimeException e) {
log.error("Error occured during command. ", e);
}
}
private void printSession(String id, UserSessionEntity userSession) {
if (userSession == null) {
log.info("Not found session with Id: " + id);
} else {
log.info("Found session. ID: " + toString(userSession));
}
}
private String toString(UserSessionEntity userSession) {
return "ID: " + userSession.getId() + ", realm: " + userSession.getRealm() + ", lastAccessTime: " + Time.toDate(userSession.getLastSessionRefresh()) +
", clientSessions: " + userSession.getClientSessions().size();
}
}

View file

@ -33,6 +33,7 @@ import org.keycloak.services.filters.KeycloakSessionServletFilter;
import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.testsuite.util.cli.InfinispanCLI;
import org.keycloak.util.JsonSerialization;
import javax.servlet.DispatcherType;

View file

@ -774,4 +774,27 @@ public class AccountTest {
Assert.assertEquals(0, thirdPartyEntry.getProtocolMappersGranted().size());
}
@Test
public void loginToSpecificPage() {
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
Assert.assertTrue(changePasswordPage.isCurrent());
events.clear();
}
@Test
public void loginToSpecificPageWithReferrer() {
driver.navigate().to(changePasswordPage.getPath() + "?referrer=account");
System.out.println(driver.getCurrentUrl());
loginPage.login("test-user@localhost", "password");
System.out.println(driver.getCurrentUrl());
Assert.assertTrue(changePasswordPage.isCurrent());
events.clear();
}
}

View file

@ -284,7 +284,7 @@ public abstract class AbstractKerberosTest {
SpnegoAuthenticator.bypassChallengeJavascript = true;
driver.navigate().to(KERBEROS_APP_URL);
String kcLoginPageLocation = driver.getCurrentUrl();
String location = "http://localhost:8081/auth/realms/test/protocol/openid-connect/auth?response_type=code&client_id=kerberos-app&redirect_uri=http%3A%2F%2Flocalhost%3A8081%2Fkerberos-portal&state=0%2F88a96ddd-84fe-4e77-8a46-02394d7b3a7d&login=true";
// Request for SPNEGO login sent with Resteasy client
spnegoSchemeFactory.setCredentials(username, password);
Response response = client.target(kcLoginPageLocation).request().get();

View file

@ -22,6 +22,7 @@ import java.security.PrivilegedExceptionAction;
import javax.security.auth.Subject;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.Credentials;
import org.apache.http.impl.auth.SPNegoScheme;
import org.apache.http.impl.auth.SPNegoSchemeFactory;
import org.apache.http.params.HttpParams;
@ -72,7 +73,7 @@ public class KeycloakSPNegoSchemeFactory extends SPNegoSchemeFactory {
@Override
protected byte[] generateGSSToken(byte[] input, Oid oid, String authServer) throws GSSException {
protected byte[] generateGSSToken(byte[] input, Oid oid, String authServer, Credentials credentials) throws GSSException {
KerberosUsernamePasswordAuthenticator authenticator = new KerberosUsernamePasswordAuthenticator(kerberosConfig);
try {
Subject clientSubject = authenticator.authenticateSubject(username, password);

View file

@ -58,7 +58,7 @@ public class AccountPasswordPage extends AbstractAccountPage {
}
public boolean isCurrent() {
return driver.getTitle().contains("Account Management") && driver.getCurrentUrl().endsWith("/account/password");
return driver.getTitle().contains("Account Management") && driver.getCurrentUrl().split("\\?")[0].endsWith("/account/password");
}
public void open() {

View file

@ -0,0 +1,89 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.util.cli;
import java.util.List;
import org.jboss.logging.Logger;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.KeycloakSessionTask;
import org.keycloak.models.utils.KeycloakModelUtils;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public abstract class AbstractCommand {
protected final Logger log = Logger.getLogger(this.getClass().getName());
protected List<String> args;
protected KeycloakSessionFactory sessionFactory;
public void injectProperties(List<String> args, InfinispanCLI cli, KeycloakSessionFactory sessionFactory) {
this.args = args;
this.sessionFactory = sessionFactory;
}
public void runCommand() {
try {
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
@Override
public void run(KeycloakSession session) {
doRunCommand(session);
}
});
} catch (HandledException handled) {
// Fine to ignore. Was handled already
} catch (RuntimeException e) {
log.error("Error occured during command. ", e);
}
}
public abstract String getName();
protected abstract void doRunCommand(KeycloakSession session);
protected String getArg(int index) {
try {
return args.get(index);
} catch (IndexOutOfBoundsException ex) {
log.errorf("Usage: %s", printUsage());
throw new HandledException();
}
}
protected Integer getIntArg(int index) {
String str = getArg(index);
try {
return Integer.parseInt(str);
} catch (NumberFormatException nex) {
log.errorf("Usage: %s", printUsage());
throw new HandledException();
}
}
public String printUsage() {
return getName();
}
public static class HandledException extends RuntimeException {
}
}

View file

@ -0,0 +1,198 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.util.cli;
import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.context.Flag;
import org.keycloak.common.util.Time;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public abstract class AbstractOfflineCacheCommand extends AbstractCommand {
@Override
protected void doRunCommand(KeycloakSession session) {
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
Cache<String, SessionEntity> ispnCache = provider.getCache(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME);
doRunCacheCommand(session, ispnCache);
}
protected void printSession(String id, UserSessionEntity userSession) {
if (userSession == null) {
log.info("Not found session with Id: " + id);
} else {
log.info("Found session. ID: " + toString(userSession));
}
}
protected String toString(UserSessionEntity userSession) {
int clientSessionsSize = userSession.getClientSessions()==null ? 0 : userSession.getClientSessions().size();
return "ID: " + userSession.getId() + ", realm: " + userSession.getRealm() + ", lastAccessTime: " + Time.toDate(userSession.getLastSessionRefresh()) +
", clientSessions: " + clientSessionsSize;
}
protected abstract void doRunCacheCommand(KeycloakSession session, Cache<String, SessionEntity> cache);
// IMPLS
public static class PutCommand extends AbstractOfflineCacheCommand {
@Override
public String getName() {
return "put";
}
@Override
protected void doRunCacheCommand(KeycloakSession session, Cache<String, SessionEntity> cache) {
UserSessionEntity userSession = new UserSessionEntity();
String id = getArg(0);
userSession.setId(id);
userSession.setRealm(getArg(1));
userSession.setLastSessionRefresh(Time.currentTime());
cache.put(id, userSession);
}
@Override
public String printUsage() {
return getName() + " <user-session-id> <realm-name>";
}
}
public static class GetCommand extends AbstractOfflineCacheCommand {
@Override
public String getName() {
return "get";
}
@Override
protected void doRunCacheCommand(KeycloakSession session, Cache<String, SessionEntity> cache) {
String id = getArg(0);
UserSessionEntity userSession = (UserSessionEntity) cache.get(id);
printSession(id, userSession);
}
@Override
public String printUsage() {
return getName() + " <user-session-id>";
}
}
public static class RemoveCommand extends AbstractOfflineCacheCommand {
@Override
public String getName() {
return "remove";
}
@Override
protected void doRunCacheCommand(KeycloakSession session, Cache<String, SessionEntity> cache) {
String id = getArg(0);
cache.remove(id);
}
@Override
public String printUsage() {
return getName() + " <user-session-id>";
}
}
public static class ClearCommand extends AbstractOfflineCacheCommand {
@Override
public String getName() {
return "clear";
}
@Override
protected void doRunCacheCommand(KeycloakSession session, Cache<String, SessionEntity> cache) {
cache.clear();
}
}
public static class SizeCommand extends AbstractOfflineCacheCommand {
@Override
public String getName() {
return "size";
}
@Override
protected void doRunCacheCommand(KeycloakSession session, Cache<String, SessionEntity> cache) {
log.info("Size: " + cache.size());
}
}
public static class ListCommand extends AbstractOfflineCacheCommand {
@Override
public String getName() {
return "list";
}
@Override
protected void doRunCacheCommand(KeycloakSession session, Cache<String, SessionEntity> cache) {
for (String id : cache.keySet()) {
SessionEntity entity = cache.get(id);
if (!(entity instanceof UserSessionEntity)) {
continue;
}
UserSessionEntity userSession = (UserSessionEntity) cache.get(id);
log.info("list: key=" + id + ", value=" + toString(userSession));
}
}
}
public static class GetLocalCommand extends AbstractOfflineCacheCommand {
@Override
public String getName() {
return "getLocal";
}
@Override
protected void doRunCacheCommand(KeycloakSession session, Cache<String, SessionEntity> cache) {
String id = getArg(0);
cache = ((AdvancedCache) cache).withFlags(Flag.CACHE_MODE_LOCAL);
UserSessionEntity userSession = (UserSessionEntity) cache.get(id);
printSession(id, userSession);
}
@Override
public String printUsage() {
return getName() + " <user-session-id>";
}
}
}

View file

@ -0,0 +1,187 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.util.cli;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.jboss.logging.Logger;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.testsuite.KeycloakServer;
/**
* HOWTO USE THIS:
*
* 1) Run KeycloakServer with system properties (assuming mongo up and running on localhost):
* -Dkeycloak.realm.provider=mongo -Dkeycloak.user.provider=mongo -Dkeycloak.userSessionPersister.provider=mongo -Dkeycloak.connectionsMongo.db=keycloak -Dkeycloak.connectionsInfinispan.clustered=true -Dresources -DstartInfinispanCLI
*
* 2) Write command on STDIN to persist 50000 userSessions to mongo: persistSessions 50000
*
* 3) Run command "clear" to ensure infinispan cache is cleared. Doublecheck with command "size" is 0
*
* 4) Write command to load sessions from persistent storage - 100 sessions per worker transaction: loadPersistentSessions 100
*
* See the progress in log. Finally run command "size" to ensure size is 100001 (50000 userSessions + 50000 clientSessions + 1 initializationState item)
*
* 5) Alternative to step 3+4 - Kill the server after step 2 and start two KeycloakServer in parallel on ports 8081 and 8082 . See the progress in logs of loading persistent sessions to infinispan.
* Kill the coordinator (usually 8081 node) during startup and see the node 8082 became coordinator and took ownership of loading persistent sessions. After node 8082 fully started, the size of infinispan is again 100001
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class InfinispanCLI {
private static final Logger log = Logger.getLogger(InfinispanCLI.class);
private static final Class<?>[] BUILTIN_COMMANDS = {
ExitCommand.class,
HelpCommand.class,
AbstractOfflineCacheCommand.PutCommand.class,
AbstractOfflineCacheCommand.GetCommand.class,
AbstractOfflineCacheCommand.GetLocalCommand.class,
AbstractOfflineCacheCommand.RemoveCommand.class,
AbstractOfflineCacheCommand.SizeCommand.class,
AbstractOfflineCacheCommand.ListCommand.class,
AbstractOfflineCacheCommand.ClearCommand.class,
PersistSessionsCommand.class,
LoadPersistentSessionsCommand.class,
UserCommands.Create.class,
UserCommands.Remove.class,
UserCommands.Count.class,
UserCommands.GetUser.class
};
private final KeycloakSessionFactory sessionFactory;
private final Map<String, Class<? extends AbstractCommand>> commands = new LinkedHashMap<>();
public InfinispanCLI(KeycloakServer server) {
this.sessionFactory = server.getSessionFactory();
// register builtin commands
for (Class<?> clazz : BUILTIN_COMMANDS) {
Class<? extends AbstractCommand> commandClazz = (Class<? extends AbstractCommand>) clazz;
try {
AbstractCommand command = commandClazz.newInstance();
commands.put(command.getName(), commandClazz);
} catch (Exception ex) {
log.error("Error registering command of class: " + commandClazz.getName(), ex);
}
}
}
public void registerCommand(String name, Class<? extends AbstractCommand> command) {
commands.put(name, command);
}
// WARNING: Stdin blocking operation
public void start() throws IOException {
log.info("Starting infinispan CLI. Exit with 'exit' . Available commands with 'help' ");
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String line;
try {
while ((line = reader.readLine()) != null) {
log.info("Command: " + line);
String[] splits = line.split(" ");
String commandName = splits[0];
Class<? extends AbstractCommand> commandClass = commands.get(commandName);
if (commandClass == null) {
log.errorf("Unknown command: %s", commandName);
} else {
try {
AbstractCommand command = commandClass.newInstance();
List<String> args = new ArrayList<>(Arrays.asList(splits));
args.remove(0);
command.injectProperties(args, this, sessionFactory);
command.runCommand();
// Just special handling of ExitCommand
if (command instanceof ExitCommand) {
return;
}
} catch (InstantiationException ex) {
log.error(ex);
} catch (IllegalAccessException ex) {
log.error(ex);
}
}
}
} finally {
log.info("Exit infinispan CLI");
reader.close();
}
}
public static class ExitCommand extends AbstractCommand {
@Override
public String getName() {
return "exit";
}
@Override
public void runCommand() {
// no need to implement. Exit handled in parent
}
@Override
protected void doRunCommand(KeycloakSession session) {
// no need to implement
}
@Override
public String printUsage() {
return getName();
}
}
public static class HelpCommand extends AbstractCommand {
private List<String> commandNames = new ArrayList<>();
@Override
public void injectProperties(List<String> args, InfinispanCLI cli, KeycloakSessionFactory sessionFactory) {
for (String commandName : cli.commands.keySet()) {
commandNames.add(commandName);
}
}
@Override
public String getName() {
return "help";
}
@Override
public void runCommand() {
log.info("Available commands: " + commandNames.toString());
}
@Override
protected void doRunCommand(KeycloakSession session) {
// no need to implement
}
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.util.cli;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.UserSessionProviderFactory;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class LoadPersistentSessionsCommand extends AbstractCommand {
@Override
public String getName() {
return "loadPersistentSessions";
}
@Override
protected void doRunCommand(KeycloakSession session) {
int sessionsPerSegment = getIntArg(0);
UserSessionProviderFactory sessionProviderFactory = (UserSessionProviderFactory) sessionFactory.getProviderFactory(UserSessionProvider.class);
sessionProviderFactory.loadPersistentSessions(sessionFactory, 10, sessionsPerSegment);
log.info("All persistent sessions loaded successfully");
}
@Override
public String printUsage() {
return super.printUsage() + " <sessions-count-per-segment>";
}
}

View file

@ -0,0 +1,125 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.util.cli;
import java.util.LinkedList;
import java.util.List;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionTask;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.session.UserSessionPersisterProvider;
import org.keycloak.models.utils.KeycloakModelUtils;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class PersistSessionsCommand extends AbstractCommand {
@Override
public String getName() {
return "persistSessions";
}
@Override
public void doRunCommand(KeycloakSession sess) {
final int count = getIntArg(0);
final List<String> userSessionIds = new LinkedList<>();
final List<String> clientSessionIds = new LinkedList<>();
// Create sessions in separate transaction first
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
@Override
public void run(KeycloakSession session) {
RealmModel realm = session.realms().getRealmByName("master");
UserModel john = session.users().getUserByUsername("admin", realm);
ClientModel testApp = realm.getClientByClientId("security-admin-console");
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
for (int i = 0; i < count; i++) {
UserSessionModel userSession = session.sessions().createUserSession(realm, john, "john-doh@localhost", "127.0.0.2", "form", true, null, null);
ClientSessionModel clientSession = session.sessions().createClientSession(realm, testApp);
clientSession.setUserSession(userSession);
clientSession.setRedirectUri("http://redirect");
clientSession.setNote("foo", "bar-" + i);
userSessionIds.add(userSession.getId());
clientSessionIds.add(clientSession.getId());
}
}
});
log.info("Sessions created in infinispan storage");
// Persist them now
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
@Override
public void run(KeycloakSession session) {
RealmModel realm = session.realms().getRealmByName("master");
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
int counter = 0;
for (String userSessionId : userSessionIds) {
counter++;
UserSessionModel userSession = session.sessions().getUserSession(realm, userSessionId);
persister.createUserSession(userSession, true);
if (counter%1000 == 0) {
log.infof("%d user sessions persisted. Continue", counter);
}
}
log.infof("All %d user sessions persisted", counter);
counter = 0;
for (String clientSessionId : clientSessionIds) {
counter++;
ClientSessionModel clientSession = session.sessions().getClientSession(realm, clientSessionId);
persister.createClientSession(clientSession, true);
if (counter%1000 == 0) {
log.infof("%d client sessions persisted. Continue", counter);
}
}
log.infof("All %d client sessions persisted", counter);
}
});
// Persist them now
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
@Override
public void run(KeycloakSession session) {
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
log.info("Total number of sessions in persister: " + persister.getUserSessionsCount(true));
}
});
}
@Override
public String printUsage() {
return super.printUsage() + " <sessions-count>";
}
}

View file

@ -0,0 +1,212 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.util.cli;
import java.util.HashSet;
import java.util.Set;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class UserCommands {
public static class Create extends AbstractCommand {
@Override
public String getName() {
return "createUsers";
}
@Override
protected void doRunCommand(KeycloakSession session) {
String usernamePrefix = getArg(0);
String password = getArg(1);
String realmName = getArg(2);
int first = getIntArg(3);
int count = getIntArg(4);
String roleNames = getArg(5);
RealmModel realm = session.realms().getRealmByName(realmName);
if (realm == null) {
log.errorf("Unknown realm: %s", realmName);
return;
}
Set<RoleModel> roles = findRoles(realm, roleNames);
int last = first + count;
for (int counter = first; counter < last; counter++) {
String username = usernamePrefix + counter;
UserModel user = session.users().addUser(realm, username);
user.setEnabled(true);
user.setEmail(username + "@keycloak.org");
UserCredentialModel passwordCred = UserCredentialModel.password(password);
user.updateCredential(passwordCred);
for (RoleModel role : roles) {
user.grantRole(role);
}
}
log.infof("Users from %s to %s created", usernamePrefix + first, usernamePrefix + (last - 1));
}
@Override
public String printUsage() {
return super.printUsage() + " <username-prefix> <password> <realm-name> <starting-user-offset> <count> <realm-roles-list>. \nRoles list is divided by comma (client roles not yet supported)>\n" +
"Example usage: " + super.printUsage() + " test test demo 0 20 user,admin";
}
private Set<RoleModel> findRoles(RealmModel realm, String rolesList) {
Set<RoleModel> result = new HashSet<>();
String[] roles = rolesList.split(",");
for (String roleName : roles) {
roleName = roleName.trim();
RoleModel role;
if (roleName.contains("/")) {
String[] spl = roleName.split("/");
ClientModel client = realm.getClientByClientId(spl[0]);
if (client == null) {
log.errorf("Client not found: %s", spl[0]);
throw new HandledException();
}
role = client.getRole(spl[1]);
} else {
role = realm.getRole(roleName);
}
if (role == null) {
log.errorf("Role not found: %s", roleName);
throw new HandledException();
}
result.add(role);
}
return result;
}
}
public static class Remove extends AbstractCommand {
@Override
public String getName() {
return "removeUsers";
}
@Override
protected void doRunCommand(KeycloakSession session) {
String usernamePrefix = getArg(0);
String realmName = getArg(1);
int first = getIntArg(2);
int count = getIntArg(3);
RealmModel realm = session.realms().getRealmByName(realmName);
if (realm == null) {
log.errorf("Unknown realm: %s", realmName);
return;
}
int last = first + count;
for (int counter = first; counter < last; counter++) {
String username = usernamePrefix + counter;
UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
log.errorf("User '%s' not found", username);
} else {
session.users().removeUser(realm, user);
}
}
log.infof("Users from %s to %s removed", usernamePrefix + first, usernamePrefix + (last - 1));
}
@Override
public String printUsage() {
return super.printUsage() + " <username-prefix> <realm-name> <starting-user-offset> <count> \n" +
"Example usage: " + super.printUsage() + " test demo 0 20";
}
}
public static class Count extends AbstractCommand {
@Override
public String getName() {
return "getUsersCount";
}
@Override
protected void doRunCommand(KeycloakSession session) {
String realmName = getArg(0);
RealmModel realm = session.realms().getRealmByName(realmName);
if (realm == null) {
log.errorf("Unknown realm: %s", realmName);
return;
}
int usersCount = session.users().getUsersCount(realm);
log.infof("Users count in realm %s: %d", realmName, usersCount);
}
@Override
public String printUsage() {
return super.printUsage() + " <realm-name>";
}
}
public static class GetUser extends AbstractCommand {
@Override
public String getName() {
return "getUser";
}
@Override
protected void doRunCommand(KeycloakSession session) {
String realmName = getArg(0);
String username = getArg(1);
RealmModel realm = session.realms().getRealmByName(realmName);
if (realm == null) {
log.errorf("Unknown realm: %s", realmName);
return;
}
UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
log.infof("User '%s' doesn't exist in realm '%s'", username, realmName);
} else {
log.infof("User: ID: '%s', username: '%s', mail: '%s'", user.getId(), user.getUsername(), user.getEmail());
}
}
@Override
public String printUsage() {
return super.printUsage() + " <realm-name> <username>";
}
}
}

View file

@ -59,7 +59,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
@ -165,7 +165,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>

View file

@ -59,7 +59,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
@ -165,7 +165,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>

View file

@ -63,7 +63,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
@ -165,7 +165,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>

View file

@ -203,8 +203,7 @@
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<version>${hibernate.javax.persistence.version}</version>
<artifactId>hibernate-jpa-2.1-api</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>

View file

@ -54,7 +54,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
@ -156,7 +156,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>

View file

@ -53,7 +53,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
@ -165,7 +165,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>

View file

@ -77,7 +77,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
@ -197,7 +197,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>

View file

@ -49,7 +49,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
@ -169,7 +169,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>

View file

@ -56,7 +56,7 @@
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
@ -158,7 +158,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>

View file

@ -70,20 +70,13 @@
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-annotations</artifactId>
<version>${jboss-logging-tools.version}</version>
<!-- This is a compile-time dependency of this project, but is not needed at compile or runtime by other
projects that depend on this project.-->
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-processor</artifactId>
<!-- This is a compile-time dependency of this project, but is not needed at compile or runtime by other
projects that depend on this project.-->
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>