keycloak-aplcache
Changes
testsuite/integration-arquillian/README.md 114(+61 -53)
testsuite/integration-arquillian/servers/auth-server/jboss/wildfly/src/main/resources/xslt/module.xsl 0(+0 -0)
testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/AssertEventsServletFilter.java 66(+66 -0)
testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsListenerProvider.java 52(+52 -0)
testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsListenerProviderFactory.java 58(+58 -0)
testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsServer.java 95(+95 -0)
testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory 35(+35 -0)
testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/org/keycloak/testsuite/integration-arquillian-event-queue/main/module.xml 30(+30 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleExample.java 35(+34 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java 9(+8 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java 127(+108 -19)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AbstractEventTest.java 2(+1 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/AbstractGroupTest.java 86(+86 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupMappersTest.java 139(+139 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java 175(+51 -124)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java 365(+365 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/URLAssert.java 78(+75 -3)
testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json 3(+2 -1)
testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/EAPJSConsoleExampleAdapterTest.java 13(+13 -0)
testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/EAP6JSConsoleExampleAdapterTest.java 2(+1 -1)
Details
diff --git a/core/src/main/java/org/keycloak/representations/idm/EventRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/EventRepresentation.java
index 93be310..0621b9b 100644
--- a/core/src/main/java/org/keycloak/representations/idm/EventRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/EventRepresentation.java
@@ -105,4 +105,37 @@ public class EventRepresentation {
public void setDetails(Map<String, String> details) {
this.details = details;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ EventRepresentation that = (EventRepresentation) o;
+
+ if (time != that.time) return false;
+ if (type != null ? !type.equals(that.type) : that.type != null) return false;
+ if (realmId != null ? !realmId.equals(that.realmId) : that.realmId != null) return false;
+ if (clientId != null ? !clientId.equals(that.clientId) : that.clientId != null) return false;
+ if (userId != null ? !userId.equals(that.userId) : that.userId != null) return false;
+ if (sessionId != null ? !sessionId.equals(that.sessionId) : that.sessionId != null) return false;
+ if (ipAddress != null ? !ipAddress.equals(that.ipAddress) : that.ipAddress != null) return false;
+ if (error != null ? !error.equals(that.error) : that.error != null) return false;
+ return !(details != null ? !details.equals(that.details) : that.details != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) (time ^ (time >>> 32));
+ result = 31 * result + (type != null ? type.hashCode() : 0);
+ result = 31 * result + (realmId != null ? realmId.hashCode() : 0);
+ result = 31 * result + (clientId != null ? clientId.hashCode() : 0);
+ result = 31 * result + (userId != null ? userId.hashCode() : 0);
+ result = 31 * result + (sessionId != null ? sessionId.hashCode() : 0);
+ result = 31 * result + (ipAddress != null ? ipAddress.hashCode() : 0);
+ result = 31 * result + (error != null ? error.hashCode() : 0);
+ result = 31 * result + (details != null ? details.hashCode() : 0);
+ return result;
+ }
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java b/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
index e821985..0563112 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
@@ -129,7 +129,7 @@ public class GroupResource {
child = realm.createGroup(rep.getName());
updateGroup(rep, child);
URI uri = uriInfo.getBaseUriBuilder()
- .path(uriInfo.getMatchedURIs().get(1))
+ .path(uriInfo.getMatchedURIs().get(2))
.path(child.getId()).build();
builder.status(201).location(uri);
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml
index de8ba88..1d8ff36 100644
--- a/testsuite/integration-arquillian/pom.xml
+++ b/testsuite/integration-arquillian/pom.xml
@@ -35,11 +35,11 @@
<name>Keycloak Arquillian Integration TestSuite</name>
<properties>
-
+
<containers.home>${project.build.directory}/containers</containers.home>
<auth.server.java.home>${java.home}</auth.server.java.home>
<app.server.java.home>${java.home}</app.server.java.home>
-
+
<!--component versions-->
<arquillian-core.version>1.1.11.Final</arquillian-core.version>
<selenium.version>2.52.0</selenium.version>
@@ -77,7 +77,7 @@
<groupId>org.wildfly</groupId>
<artifactId>wildfly-arquillian-container-managed</artifactId>
<version>${arquillian-wildfly-container.version}</version>
- </dependency>
+ </dependency>
</dependencies>
</dependencyManagement>
@@ -102,6 +102,7 @@
<modules>
<module>servers</module>
<module>tests</module>
+ <module>test-apps</module>
</modules>
-
+
</project>
testsuite/integration-arquillian/README.md 114(+61 -53)
diff --git a/testsuite/integration-arquillian/README.md b/testsuite/integration-arquillian/README.md
index 1adc054..4a0cfa1 100644
--- a/testsuite/integration-arquillian/README.md
+++ b/testsuite/integration-arquillian/README.md
@@ -1,33 +1,36 @@
# Keycloak Arquillian Integration Testsuite
-## Container Lifecycles
+## Overview
+
+For overview see the **Modules Overview** section at the bottom of this README.
-### Keycloak Auth Server
-There is only one instance of Keycloak server running during a single test run.
-It is automatically started by Arquillian on the `BeforeSuite` event and stopped `AfterSuite`.
+## Container Lifecycles
+
+### Auth Server
-The type of container can be determined by property `-Dauth.server.container`. Default value is `auth-server-undertow`,
-other options are: `auth-server-wildfly` and `auth-server-eap7`. The values correspond to Arquillian *container qualifiers* in `arquillian.xml` config file.
+Keycloak server is automatically started by the testsuite on the `BeforeSuite` event and stopped on `AfterSuite` event.
-**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.
+By default the server runs in embedded Undertow.
-**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.
+#### Wildfly/EAP
-#### Migration
+Testsuite supports running server on Wildfly/EAP. For this it's necessary to:
+- build the project including the `distribution` module
+ (artifact `keycloak-server-dist`/`-overlay` needs to be available before running the testsuite),
+- activate profile `auth-server-wildfly` or `auth-server-eap7`.
-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.
+[More details...](servers/auth-server/README.md)
#### 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.)
+The cluster setup for server can be enabled by activating profile `auth-server-cluster`.
+
+The cluster setup is not supported for server on Undetow. Profile `auth-server-wildfly` or `auth-server-eap` needs to be activated.
+
+The setup includes:
+- a `mod_cluster` load balancer on Wildfly
+- two clustered nodes of Keycloak server on Wildfly/EAP
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:
@@ -36,20 +39,20 @@ route add -net 224.0.0.0 netmask 240.0.0.0 dev lo
ifconfig lo multicast
```
-### App Servers
+### App Servers / Adapter Tests
Lifecycle of application server is always tied to a particular TestClass.
Each *adapter* test class is annotated by `@AppServerContainer("app-server-*")` annotation
that links it to a particular Arquillian container in `arquillian.xml`.
-The `AppServerTestEnricher` then ensures the server is started before and stopped after all tests methods in the class.
+The `AppServerTestEnricher` then ensures the server is started during `BeforeClass` event and stopped during `AfterClass` event for that particular test class.
In case the `@AppServerContainer` annotation has no value it's assumed that the application container
-is the same as the auth server container (a relative adapter test scenario).
+is the same as the auth server container - a "relative" adapter test scenario.
-Adapter tests are separated into submodules because different app containers require different configurations
-(installation of adapter libs, etc.).
-Container entries of app servers are not present in the main `arquillian.xml` in the `base` module.
-Each adapter submodule adds it's own entry before it runs the tests.
+The app-servers with installed Keycloak adapter are prepared in `servers/app-server` submodules, activated by `-Papp-server-MODULE`.
+[More details.](servers/app-server/README.md)
+
+The corresponding adapter test modules are in `tests/other/adapters` submodules, and are activated by the same profiles.
## SuiteContext and TestContext
@@ -130,32 +133,6 @@ It automatically modifies imported test realms and deployments' adapter configs
2. **Bundled** - In the deployed war in `/WEB-INF/keycloak.json`. **Default.**
-## Maven Modules Structure
-
-```
-integration-arquillian
-│
-├──servers
-│ ├──wildfly (activated by -Pauth-server-wildfly)
-│ └──eap7 (activated by -Pauth-server-eap7)
-│
-└──tests (common settings for all test modules)
- ├──base (custom ARQ extensions + base functional tests)
- └──other (common settings for all modules dependent on base)
- │
- ├──adapters (common settings for all adapter submodules)
- │ ├──wildfly (activated by -Papp-server-wildfly)
- │ ├──wildfly-relative (activated by -Papp-server-wildfly-relative,auth-server-wildfly)
- │ ├──...
- │ ├──tomcat (activated by -Papp-server-tomcat)
- │ └──karaf (activated by -Papp-server-karaf)
- │
- ├──console (activated by -Pconsole-ui-tests)
- ├──mod_auth_mellon (activated by -Pmod_auth_mellon)
- ├──console_no_users (activated by -Pconsole-ui-no-users-tests)
- └──...
-```
-
## Custom Arquillian Extensions
Custom extensions are registered in `META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension`.
@@ -173,6 +150,37 @@ Custom extensions are registered in `META-INF/services/org.jboss.arquillian.core
* `DeploymentTargetModifier` - Ensures all test app deployments are targeted at app server containers.
* `URLProvider` - Fixes URLs injected by Arquillian Graphene which contain value `127.0.0.1` instead of required `localhost`.
-### CustomKarafContainerExtension
+## Modules Overview
+
+```
+integration-arquillian
+│
+├──servers (preconfigured test servers)
+│ │
+│ ├──auth-server
+│ │ ├──jboss (wildfly/eap)
+│ │ └──undertow (arq. extension)
+│ │
+│ ├──app-server
+│ │ ├──jboss (wildfly/eap/as)
+│ │ ├──tomcat
+│ │ └──karaf
+│ │
+│ └──wildfly-balancer
+│
+└──tests (common settings for all test modules)
+ │
+ ├──base (custom ARQ extensions + base functional tests)
+ │
+ └──other (common settings for all test modules dependent on base)
+ │
+ ├──adapters (common settings for all adapter test modules)
+ │ ├──jboss
+ │ ├──tomcat
+ │ └──karaf
+ │
+ ├──console
+ ├──console_no_users
+ └──...
+```
-Extension for executing karaf commands after container is started. Used for installation of bundles (test apps and adapter libs).
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/app-server/README.md b/testsuite/integration-arquillian/servers/app-server/README.md
index 6905f03..008e4f5 100644
--- a/testsuite/integration-arquillian/servers/app-server/README.md
+++ b/testsuite/integration-arquillian/servers/app-server/README.md
@@ -1,6 +1,9 @@
-# Keycloak Arquillian Integration TestSuite - Test Servers
+# Keycloak Arquillian Integration TestSuite - Test Servers - App Servers
-[Up...](../README.md)
+- [Keycloak Arquillian Integration TestSuite](../../README.md)
+- [Keycloak Arquillian Integration TestSuite - Test Servers](../README.md)
+- [Keycloak Arquillian Integration TestSuite - Test Servers - Auth Server](../auth-server/README.md)
+- Keycloak Arquillian Integration TestSuite - Test Servers - App Servers
## App Server - JBoss
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/build.xml b/testsuite/integration-arquillian/servers/auth-server/jboss/build.xml
new file mode 100644
index 0000000..b34fc7d
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/build.xml
@@ -0,0 +1,42 @@
+<!--
+ ~ 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.
+ -->
+
+<project name="inject-provider" basedir="." default="inject-provider">
+
+ <scriptdef name="inject-provider" language="javascript" manager="bsf">
+ <attribute name="path"/>
+ <![CDATA[
+ importClass(Packages.java.io.File);
+ importClass(Packages.org.keycloak.util.JsonSerialization);
+
+ path = attributes.get("path");
+ file = new File(path);
+ root = JsonSerialization.mapper.readTree(file);
+
+ // inject provider
+ providers = root.withArray("providers");
+ providers.add("module:org.keycloak.testsuite.integration-arquillian-event-queue");
+
+ // save file
+ JsonSerialization.prettyMapper.writeValue(file, root);
+ ]]>
+ </scriptdef>
+
+ <target name="inject-provider">
+ <inject-provider path="${auth.server.home}/standalone/configuration/keycloak-server.json"/>
+ </target>
+</project>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
index 72c2f98..c19cc1b 100644
--- a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
@@ -57,6 +57,9 @@
<unpacked.artifact.version>${auth.server.dist.version}</unpacked.artifact.version>
<auth.server.home>${project.build.directory}/unpacked/${auth.server.dist.unpacked.folder.name}</auth.server.home>
+
+ <!--used in profile auth-server-cluster. profile jpa sets this to true-->
+ <skip.h2.tcp>false</skip.h2.tcp>
</properties>
<profiles>
@@ -97,13 +100,102 @@
</artifactItems>
</configuration>
</execution>
+ <execution>
+ <id>copy-event-queue-provider</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-event-queue</artifactId>
+ <version>${project.version}</version>
+ <type>jar</type>
+ <overWrite>false</overWrite>
+ <outputDirectory>${auth.server.home}/modules/org/keycloak/testsuite/integration-arquillian-event-queue/main</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ <execution>
+ <id>install-event-queue-module</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-event-queue</artifactId>
+ <version>${project.version}</version>
+ <type>jar</type>
+ <outputDirectory>${auth.server.home}/modules</outputDirectory>
+ <includes>**/module.xml</includes>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
</executions>
</plugin>
<plugin>
- <artifactId>maven-enforcer-plugin</artifactId>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.8</version>
+ <executions>
+ <execution>
+ <id>inject-into-keycloak-server-json</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+ <target>
+ <ant antfile="../build.xml" inheritRefs="true">
+ <target name="inject-provider"/>
+ </ant>
+ </target>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>ant-contrib</groupId>
+ <artifactId>ant-contrib</artifactId>
+ <version>1.0b3</version>
+ <exclusions>
+ <exclusion>
+ <groupId>ant</groupId>
+ <artifactId>ant</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ant</groupId>
+ <artifactId>ant-apache-bsf</artifactId>
+ <version>1.9.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.bsf</groupId>
+ <artifactId>bsf-api</artifactId>
+ <version>3.1</version>
+ </dependency>
+ <dependency>
+ <groupId>rhino</groupId>
+ <artifactId>js</artifactId>
+ <version>1.7R2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
</plugin>
<plugin>
- <artifactId>maven-antrun-plugin</artifactId>
+ <artifactId>maven-enforcer-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
@@ -268,6 +360,7 @@
<id>jpa</id>
<properties>
<jdbc.mvn.driver.deployment.dir>${auth.server.home}/modules/system/layers/base/com/${jdbc.mvn.artifactId}/main</jdbc.mvn.driver.deployment.dir>
+ <skip.h2.tcp>true</skip.h2.tcp>
</properties>
<build>
<pluginManagement>
@@ -344,7 +437,7 @@
<!-- create module.xml in modules -->
<transformationSet>
<dir>${auth.server.home}/modules/system/layers/base/com/h2database/h2/main</dir>
- <stylesheet>src/main/resources/module.xsl</stylesheet>
+ <stylesheet>src/main/resources/xslt/module.xsl</stylesheet>
<includes>
<include>module.xml</include>
</includes>
@@ -366,6 +459,7 @@
<stylesheet>${common.resources}/datasource.xsl</stylesheet>
<includes>
<include>standalone.xml</include>
+ <include>standalone-ha.xml</include>
</includes>
<outputDir>${auth.server.home}/standalone/configuration</outputDir>
<parameters>
@@ -421,14 +515,13 @@
<artifactId>xml-maven-plugin</artifactId>
<executions>
<execution>
- <id>configure-wildfly-datasource</id>
+ <id>jpa-h2-tcp</id>
<phase>process-resources</phase>
<goals>
<goal>transform</goal>
</goals>
<configuration>
<transformationSets>
- <!-- point KeycloakDS datasource to H2 running on TCP port -->
<transformationSet>
<dir>${auth.server.home}/standalone/configuration</dir>
<includes>
@@ -447,6 +540,18 @@
</parameter>
</parameters>
</transformationSet>
+ </transformationSets>
+ <skip>${skip.h2.tcp}</skip>
+ </configuration>
+ </execution>
+ <execution>
+ <id>keycloak-ispn-caches</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>transform</goal>
+ </goals>
+ <configuration>
+ <transformationSets>
<transformationSet>
<dir>${auth.server.home}/standalone/configuration</dir>
<includes>
diff --git a/testsuite/integration-arquillian/servers/auth-server/pom.xml b/testsuite/integration-arquillian/servers/auth-server/pom.xml
index d9f8222..f37234d 100644
--- a/testsuite/integration-arquillian/servers/auth-server/pom.xml
+++ b/testsuite/integration-arquillian/servers/auth-server/pom.xml
@@ -30,6 +30,7 @@
<name>Auth Server</name>
<modules>
+ <module>services</module>
<module>jboss</module>
<module>undertow</module>
</modules>
diff --git a/testsuite/integration-arquillian/servers/auth-server/README.md b/testsuite/integration-arquillian/servers/auth-server/README.md
index 87f9a20..1fed446 100644
--- a/testsuite/integration-arquillian/servers/auth-server/README.md
+++ b/testsuite/integration-arquillian/servers/auth-server/README.md
@@ -1,16 +1,21 @@
-# Keycloak Arquillian Integration TestSuite - Test Servers
+# Keycloak Arquillian Integration TestSuite - Test Servers - Auth Server
-[Up...](../README.md)
+- [Keycloak Arquillian Integration TestSuite](../../README.md)
+- [Keycloak Arquillian Integration TestSuite - Test Servers](../README.md)
+- Keycloak Arquillian Integration TestSuite - Test Servers - Auth Server
+- [Keycloak Arquillian Integration TestSuite - Test Servers - App Servers](../app-server/README.md)
-## Auth Server - JBoss `auth-server/jboss`
+## Auth Server - JBoss
-### Modules
+Common configurations of Keycloak server on JBoss-based container (Wildfly/EAP).
-* __`wildfly` Wildfly 10__
+### Submodules
+
+#### `wildfly` Wildfly 10
- Builds keycloak server on top of latest Wildfly.
- Activated by __`-Pauth-server-wildfly`__
-* __`eap` EAP 7__
+#### `eap` EAP 7
- Builds keycloak server on top of latest EAP.
- Activated by __`-Pauth-server-eap`__
- Requires access to product repo.
@@ -39,6 +44,6 @@ Configures in `standalone-ha.xml`:
See profile `auth-server-cluster`.
-## Auth Server - Undertow `auth-server/undertow`
+## Auth Server - Undertow
Arquillian extension for running Keycloak server in embedded Undertow.
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/pom.xml
new file mode 100644
index 0000000..47ae3e4
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.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.
+ -->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-servers-auth-server-services</artifactId>
+ <version>1.9.2.Final-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>integration-arquillian-event-queue</artifactId>
+ <name>Auth Server Services - Event Queue</name>
+
+ <dependencies>
+
+ <!-- Keycloak deps for tests -->
+
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-dependencies-server-all</artifactId>
+ <type>pom</type>
+ </dependency>
+
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-services</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.undertow</groupId>
+ <artifactId>undertow-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.undertow</groupId>
+ <artifactId>undertow-servlet</artifactId>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </build>
+</project>
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/AssertEventsServletFilter.java b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/AssertEventsServletFilter.java
new file mode 100644
index 0000000..6d0d4ed
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/AssertEventsServletFilter.java
@@ -0,0 +1,66 @@
+/*
+ * 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.events;
+
+import org.keycloak.events.Event;
+import org.keycloak.util.JsonSerialization;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.concurrent.BlockingQueue;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public class AssertEventsServletFilter implements Filter {
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest req = (HttpServletRequest) servletRequest;
+
+ if ("/event-queue".equals(req.getRequestURI().substring(req.getContextPath().length()))) {
+ BlockingQueue<Event> events = EventsListenerProvider.getInstance();
+ HttpServletResponse resp = (HttpServletResponse) servletResponse;
+ resp.setContentType("application/json");
+
+ Event event = events.poll();
+ if (event != null) {
+ JsonSerialization.writeValueToStream(servletResponse.getOutputStream(), event);
+ } else {
+ resp.setStatus(204);
+ }
+ } else {
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ }
+
+ @Override
+ public void destroy() {
+ }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsListenerProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsListenerProvider.java
new file mode 100644
index 0000000..a4a1a40
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsListenerProvider.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.events;
+
+import org.keycloak.events.Event;
+import org.keycloak.events.EventListenerProvider;
+import org.keycloak.events.admin.AdminEvent;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public class EventsListenerProvider implements EventListenerProvider {
+
+ private static final BlockingQueue<Event> events = new LinkedBlockingQueue<Event>();
+
+ @Override
+ public void onEvent(Event event) {
+ events.add(event);
+ }
+
+ @Override
+ public void onEvent(AdminEvent event, boolean includeRepresentation) {
+ // TODO: implement if needed
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ public static BlockingQueue<Event> getInstance() {
+ return events;
+ }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsListenerProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsListenerProviderFactory.java
new file mode 100644
index 0000000..aecc01a
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsListenerProviderFactory.java
@@ -0,0 +1,58 @@
+/*
+ * 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.events;
+
+import org.keycloak.Config;
+import org.keycloak.events.EventListenerProvider;
+import org.keycloak.events.EventListenerProviderFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public class EventsListenerProviderFactory implements EventListenerProviderFactory {
+
+ private static final EventsListenerProvider INSTANCE = new EventsListenerProvider();
+
+ private EventsServer server = new EventsServer();
+
+ @Override
+ public EventListenerProvider create(KeycloakSession session) {
+ return INSTANCE;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ server.start();
+ }
+
+ @Override
+ public void close() {
+ server.stop();
+ }
+
+ @Override
+ public String getId() {
+ return "event-queue";
+ }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsServer.java b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsServer.java
new file mode 100644
index 0000000..0b04ee3
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsServer.java
@@ -0,0 +1,95 @@
+/*
+ * 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.events;
+
+import io.undertow.Undertow;
+import io.undertow.server.handlers.PathHandler;
+import io.undertow.servlet.Servlets;
+import io.undertow.servlet.api.DeploymentInfo;
+import io.undertow.servlet.api.DeploymentManager;
+import io.undertow.servlet.api.FilterInfo;
+import io.undertow.servlet.api.ServletContainer;
+import io.undertow.servlet.api.ServletContainer.Factory;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletException;
+import java.util.logging.Logger;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public class EventsServer {
+
+ private static final Logger log = Logger.getLogger(EventsServer.class.getName());
+
+ private String rootPath = "/";
+ private int port;
+ private Undertow server;
+
+ public EventsServer() {
+ int eventsPort = Integer.parseInt(System.getProperty("auth.server.events.http.port", "8089"));
+ int portOffset = Integer.parseInt(System.getProperty("auth.server.port.offset", "0"));
+ int jbossPortOffset = Integer.parseInt(System.getProperty("jboss.socket.binding.port-offset", "-1"));
+
+ log.fine("Configuration:");
+ log.fine(" auth.server.events.http.port: " + eventsPort);
+ log.fine(" auth.server.port.offset: " + portOffset);
+ log.fine(" jboss.socket.binding.port-offset: " + jbossPortOffset);
+ port = eventsPort + (jbossPortOffset != -1 ? jbossPortOffset : portOffset);
+ }
+
+ public void start() {
+
+ PathHandler root = new PathHandler();
+ this.server = Undertow.builder().addHttpListener(port, "localhost").setHandler(root).build();
+ this.server.start();
+
+ ServletContainer container = Factory.newInstance();
+
+ DeploymentInfo di = new DeploymentInfo();
+ di.setClassLoader(getClass().getClassLoader());
+ di.setContextPath(rootPath);
+ di.setDeploymentName("testing-event-queue");
+
+ FilterInfo filter = Servlets.filter("EventsFilter", AssertEventsServletFilter.class);
+ di.addFilter(filter);
+ di.addFilterUrlMapping("EventsFilter", "/event-queue", DispatcherType.REQUEST);
+
+ DeploymentManager manager = container.addDeployment(di);
+ manager.deploy();
+
+ try {
+ root.addPrefixPath(rootPath, manager.start());
+ } catch (ServletException e) {
+ throw new RuntimeException(e);
+ }
+ log.info("Started EventsServer on port: " + port);
+ }
+
+ public void stop() {
+ this.server.stop();
+ }
+
+ public void setRootPath(String rootPath) {
+ this.rootPath = rootPath;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory
new file mode 100644
index 0000000..e995149
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory
@@ -0,0 +1,35 @@
+#
+# Copyright 2016 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Copyright 2016 Red Hat, Inc. and/or its affiliates
+# and other contributors as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.keycloak.testsuite.events.EventsListenerProviderFactory
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/org/keycloak/testsuite/integration-arquillian-event-queue/main/module.xml b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/org/keycloak/testsuite/integration-arquillian-event-queue/main/module.xml
new file mode 100644
index 0000000..19f3d41
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/org/keycloak/testsuite/integration-arquillian-event-queue/main/module.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ 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.
+ -->
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.testsuite.integration-arquillian-event-queue">
+ <resources>
+ <resource-root path="integration-arquillian-event-queue-${project.version}.jar"/>
+ </resources>
+ <dependencies>
+ <module name="javax.api"/>
+ <module name="javax.servlet.api"/>
+ <module name="io.undertow.core"/>
+ <module name="io.undertow.servlet"/>
+ <module name="org.keycloak.keycloak-core"/>
+ <module name="org.keycloak.keycloak-server-spi"/>
+ </dependencies>
+</module>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/pom.xml
new file mode 100644
index 0000000..4684ec0
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/pom.xml
@@ -0,0 +1,36 @@
+<?xml version="1.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.
+ -->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-servers-auth-server</artifactId>
+ <version>1.9.2.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-servers-auth-server-services</artifactId>
+ <packaging>pom</packaging>
+ <name>Auth Server Services</name>
+
+ <modules>
+ <module>event-queue</module>
+ </modules>
+
+</project>
diff --git a/testsuite/integration-arquillian/servers/README.md b/testsuite/integration-arquillian/servers/README.md
index 835e171..d29f470 100644
--- a/testsuite/integration-arquillian/servers/README.md
+++ b/testsuite/integration-arquillian/servers/README.md
@@ -1,6 +1,9 @@
-# Keycloak Arquillian Integration TestSuite
+# Keycloak Arquillian Integration TestSuite - Test Servers
-[Keycloak Arquillian Integration TestSuite](../README.md)
+- [Keycloak Arquillian Integration TestSuite](../README.md)
+- Keycloak Arquillian Integration TestSuite - Test Servers
+- [Keycloak Arquillian Integration TestSuite - Test Servers - Auth Server](auth-server/README.md)
+- [Keycloak Arquillian Integration TestSuite - Test Servers - App Servers](app-server/README.md)
## Test Servers
@@ -14,8 +17,6 @@ The artifacts are used by the Arquillian TestSuite.
- EAP 7
- Undertow
-[Details...](auth-server/README.md)
-
### App Server
@@ -30,7 +31,6 @@ The artifacts are used by the Arquillian TestSuite.
- Tomcat
- Tomcat 7, 8
-[Details...](app-server/README.md)
### Load Balancer
diff --git a/testsuite/integration-arquillian/test-apps/js-console/example-realm.json b/testsuite/integration-arquillian/test-apps/js-console/example-realm.json
new file mode 100755
index 0000000..659e5b4
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-console/example-realm.json
@@ -0,0 +1,66 @@
+{
+ "realm": "example",
+ "enabled": true,
+ "sslRequired": "external",
+ "registrationAllowed": true,
+ "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+ "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "requiredCredentials": [ "password" ],
+ "users" : [
+ {
+ "username" : "user",
+ "enabled": true,
+ "email" : "sample-user@example",
+ "firstName": "Sample",
+ "lastName": "User",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": [ "user" ],
+ "clientRoles": {
+ "account": ["view-profile", "manage-account"]
+ }
+ }
+ ],
+ "roles" : {
+ "realm" : [
+ {
+ "name": "user",
+ "description": "User privileges"
+ },
+ {
+ "name": "admin",
+ "description": "Administrator privileges"
+ }
+ ]
+ },
+ "scopeMappings": [
+ {
+ "client": "js-console",
+ "roles": ["user"]
+ }
+ ],
+ "clients": [
+ {
+ "clientId": "js-console",
+ "enabled": true,
+ "publicClient": true,
+ "baseUrl": "/js-console",
+ "redirectUris": [
+ "/js-console/*"
+ ],
+ "webOrigins": [
+ "http://localhost:8280"
+ ]
+ }
+ ],
+ "clientScopeMappings": {
+ "account": [
+ {
+ "client": "js-console",
+ "roles": ["view-profile"]
+ }
+ ]
+ }
+}
diff --git a/testsuite/integration-arquillian/test-apps/js-console/pom.xml b/testsuite/integration-arquillian/test-apps/js-console/pom.xml
new file mode 100755
index 0000000..2f1396c
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-console/pom.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-test-apps</artifactId>
+ <version>1.9.2.Final-SNAPSHOT</version>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>integration-arquillian-test-apps-js-console</artifactId>
+ <packaging>war</packaging>
+ <name>JS Console</name>
+ <description/>
+
+ <build>
+ <finalName>js-console</finalName>
+ <plugins>
+ <plugin>
+ <groupId>org.jboss.as.plugins</groupId>
+ <artifactId>jboss-as-maven-plugin</artifactId>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.wildfly.plugins</groupId>
+ <artifactId>wildfly-maven-plugin</artifactId>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/testsuite/integration-arquillian/test-apps/js-console/README.md b/testsuite/integration-arquillian/test-apps/js-console/README.md
new file mode 100644
index 0000000..fa9a02d
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-console/README.md
@@ -0,0 +1,17 @@
+Basic JavaScript Example
+========================
+
+Start and configure Keycloak
+----------------------------
+
+Start Keycloak:
+
+ bin/standalone.sh
+
+Open the Keycloak admin console, click on Add Realm, click on 'Choose a JSON file', selct example-realm.json and click Upload.
+
+Deploy the JS Console to Keycloak by running:
+
+ mvn install wildfly:deploy
+
+Open the console at http://localhost:8080/js-console and login with username: 'user', and password: 'password'.
diff --git a/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html
new file mode 100755
index 0000000..1c41fcf
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html
@@ -0,0 +1,156 @@
+<!--
+ ~ 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.
+ -->
+
+<html>
+<head>
+ <script src="http://localhost:8180/auth/js/keycloak.js"></script>
+</head>
+<body>
+
+<div>
+ <button onclick="keycloakInit()">Init</button>
+ <button onclick="keycloak.login()">Login</button>
+ <button onclick="keycloak.logout()">Logout</button>
+ <button onclick="keycloak.register()">Register</button>
+ <button onclick="refreshToken(9999)">Refresh Token</button>
+ <button onclick="refreshToken(30)">Refresh Token (if <30s validity)</button>
+ <button onclick="loadProfile()">Get Profile</button>
+ <button onclick="loadUserInfo()">Get User Info</button>
+ <button onclick="output(keycloak.tokenParsed)">Show Token</button>
+ <button onclick="output(keycloak.refreshTokenParsed)">Show Refresh Token</button>
+ <button onclick="output(keycloak.idTokenParsed)">Show ID Token</button>
+ <button onclick="showExpires()">Show Expires</button>
+ <button onclick="output(keycloak)">Show Details</button>
+ <button onclick="output(keycloak.createLoginUrl())">Show Login URL</button>
+ <button onclick="output(keycloak.createLogoutUrl())">Show Logout URL</button>
+ <button onclick="output(keycloak.createRegisterUrl())">Show Register URL</button>
+ <select id="flowSelect">
+ <option value="standard">standard</option>
+ <option value="implicit">implicit</option>
+ </select>
+
+ <select id="responseModeSelect">
+ <option value="fragment">fragment</option>
+ <option value="query">query</option>
+ </select>
+</div>
+
+<h2>Result</h2>
+<pre style="background-color: #ddd; border: 1px solid #ccc; padding: 10px;" id="output"></pre>
+
+<h2>Events</h2>
+<pre style="background-color: #ddd; border: 1px solid #ccc; padding: 10px;" id="events"></pre>
+
+
+<script>
+ function loadProfile() {
+ keycloak.loadUserProfile().success(function(profile) {
+ output(profile);
+ }).error(function() {
+ output('Failed to load profile');
+ });
+ }
+
+ function loadUserInfo() {
+ keycloak.loadUserInfo().success(function(userInfo) {
+ output(userInfo);
+ }).error(function() {
+ output('Failed to load user info');
+ });
+ }
+
+ function refreshToken(minValidity) {
+ keycloak.updateToken(minValidity).success(function(refreshed) {
+ if (refreshed) {
+ output(keycloak.tokenParsed);
+ } else {
+ output('Token not refreshed, valid for ' + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds');
+ }
+ }).error(function() {
+ output('Failed to refresh token');
+ });
+ }
+
+ function showExpires() {
+ if (!keycloak.tokenParsed) {
+ output("Not authenticated");
+ return;
+ }
+
+ var o = 'Token Expires:\t\t' + new Date((keycloak.tokenParsed.exp + keycloak.timeSkew) * 1000).toLocaleString() + '\n';
+ o += 'Token Expires in:\t' + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds\n';
+
+ if (keycloak.refreshTokenParsed) {
+ o += 'Refresh Token Expires:\t' + new Date((keycloak.refreshTokenParsed.exp + keycloak.timeSkew) * 1000).toLocaleString() + '\n';
+ o += 'Refresh Expires in:\t' + Math.round(keycloak.refreshTokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds';
+ }
+
+ output(o);
+ }
+
+ function output(data) {
+ if (typeof data === 'object') {
+ data = JSON.stringify(data, null, ' ');
+ }
+ document.getElementById('output').innerHTML = data;
+ }
+
+ function event(event) {
+ var e = document.getElementById('events').innerHTML;
+ document.getElementById('events').innerHTML = new Date().toLocaleString() + "\t" + event + "\n" + e;
+ }
+
+ var keycloak;
+
+ function keycloakInit() {
+ keycloak = Keycloak();
+
+ keycloak.onAuthSuccess = function () {
+ event('Auth Success');
+ };
+
+ keycloak.onAuthError = function () {
+ event('Auth Error');
+ };
+
+ keycloak.onAuthRefreshSuccess = function () {
+ event('Auth Refresh Success');
+ };
+
+ keycloak.onAuthRefreshError = function () {
+ event('Auth Refresh Error');
+ };
+
+ keycloak.onAuthLogout = function () {
+ event('Auth Logout');
+ };
+
+ keycloak.onTokenExpired = function () {
+ event('Access token expired.');
+ };
+
+ var initOptions = {flow: document.getElementById("flowSelect").value, responseMode: document.getElementById("responseModeSelect").value}
+ keycloak.init(initOptions).success(function (authenticated) {
+ output('Init Success (' + (authenticated ? 'Authenticated' : 'Not Authenticated') + ')');
+ }).error(function () {
+ output('Init Error');
+ });
+ }
+
+</script>
+</body>
+</html>
diff --git a/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/keycloak.json b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/keycloak.json
new file mode 100644
index 0000000..00f6c9e
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/keycloak.json
@@ -0,0 +1,8 @@
+{
+ "realm" : "example",
+ "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url" : "http://localhost:8180/auth",
+ "ssl-required" : "external",
+ "resource" : "js-console",
+ "public-client" : true
+}
diff --git a/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/WEB-INF/web.xml b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..16e8b23
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <module-name>js-console</module-name>
+</web-app>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/pom.xml b/testsuite/integration-arquillian/test-apps/pom.xml
new file mode 100644
index 0000000..e031856
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/pom.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>integration-arquillian</artifactId>
+ <groupId>org.keycloak.testsuite</groupId>
+ <version>1.9.2.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-test-apps</artifactId>
+ <packaging>pom</packaging>
+
+ <name>Test apps</name>
+
+ <modules>
+ <module>js-console</module>
+ <module>test-apps-dist</module>
+ </modules>
+</project>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/test-apps-dist/assembly.xml b/testsuite/integration-arquillian/test-apps/test-apps-dist/assembly.xml
new file mode 100644
index 0000000..5fd7363
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/test-apps-dist/assembly.xml
@@ -0,0 +1,39 @@
+<!--
+ ~ 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.
+ -->
+
+<assembly>
+ <id>test-apps-dist</id>
+
+ <formats>
+ <format>zip</format>
+ </formats>
+
+ <includeBaseDirectory>true</includeBaseDirectory>
+
+ <fileSets>
+ <fileSet>
+ <directory>target/test-apps</directory>
+ <outputDirectory></outputDirectory>
+ <excludes>
+ <exclude>**/pom.xml.releaseBackup</exclude>
+ <exclude>**/.svn/**</exclude>
+ <exclude>**/target/**</exclude>
+ <exclude>**/*.iml</exclude>
+ </excludes>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml b/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml
new file mode 100755
index 0000000..e48e088
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ 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.
+ -->
+
+<project name="test-apps-dist" basedir="." default="all">
+
+ <target name="all">
+ <delete dir="target/test-apps"/>
+ <copy todir="target/test-apps/js-console" overwrite="true">
+ <fileset dir="../js-console">
+ <exclude name="**/target/**"/>
+ <exclude name="**/*.iml"/>
+ <exclude name="**/*.unconfigured"/>
+ <exclude name="**/subsystem-config.xml"/>
+ </fileset>
+ </copy>
+ </target>
+</project>
diff --git a/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml b/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml
new file mode 100644
index 0000000..a518ebe
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>integration-arquillian-test-apps</artifactId>
+ <groupId>org.keycloak.testsuite</groupId>
+ <version>1.9.2.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-test-apps-dist</artifactId>
+
+ <name>Test apps distribution</name>
+
+ <build>
+ <finalName>${product.name}-${product.version}-test-apps</finalName>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.8.2</version>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.8</version>
+ <inherited>false</inherited>
+ <executions>
+ <execution>
+ <id>build-test-apps</id>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <phase>compile</phase>
+ <configuration>
+ <target>
+ <ant antfile="build.xml" inheritRefs="true">
+ <target name="all"></target>
+ </ant>
+ </target>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>ant-contrib</groupId>
+ <artifactId>ant-contrib</artifactId>
+ <version>1.0b3</version>
+ <exclusions>
+ <exclusion>
+ <groupId>ant</groupId>
+ <artifactId>ant</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>assemble</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>assembly.xml</descriptor>
+ </descriptors>
+ <outputDirectory>target</outputDirectory>
+ <workDirectory>target/assembly/work</workDirectory>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ <version>2.5.5</version>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml
index 662a102..bcec08c 100644
--- a/testsuite/integration-arquillian/tests/base/pom.xml
+++ b/testsuite/integration-arquillian/tests/base/pom.xml
@@ -57,6 +57,11 @@
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
+ <dependency>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-event-queue</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
<build>
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleExample.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleExample.java
old mode 100644
new mode 100755
index dff81a0..15f0308
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleExample.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleExample.java
@@ -22,6 +22,7 @@ import org.jboss.arquillian.test.api.ArquillianResource;
import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
import java.net.URL;
@@ -32,7 +33,7 @@ import java.net.URL;
public class JSConsoleExample extends AbstractPageWithInjectedUrl {
public static final String DEPLOYMENT_NAME = "js-console-example";
- public static final String CLIENT_ID = "js-console";
+ public static final String CLIENT_ID = "integration-arquillian-test-apps-js-console";
@ArquillianResource
@OperateOnDeployment(DEPLOYMENT_NAME)
@@ -45,6 +46,8 @@ public class JSConsoleExample extends AbstractPageWithInjectedUrl {
return fixedUrl != null ? fixedUrl : url;
}
+ @FindBy(xpath = "//button[text() = 'Init']")
+ private WebElement initButton;
@FindBy(xpath = "//button[text() = 'Login']")
private WebElement logInButton;
@FindBy(xpath = "//button[text() = 'Logout']")
@@ -67,6 +70,17 @@ public class JSConsoleExample extends AbstractPageWithInjectedUrl {
@FindBy(xpath = "//button[text() = 'Show Details']")
private WebElement showDetailsButton;
+ @FindBy(id = "flowSelect")
+ private Select flowSelect;
+ @FindBy(id = "responseModeSelect")
+ private Select responseModeSelect;
+
+ @FindBy(id = "output")
+ private WebElement outputArea;
+
+ @FindBy(id = "events")
+ private WebElement eventsArea;
+
public void logIn() {
logInButton.click();
}
@@ -87,4 +101,23 @@ public class JSConsoleExample extends AbstractPageWithInjectedUrl {
getProfileButton.click();
}
+ public void setFlow(String value) {
+ flowSelect.selectByValue(value);
+ }
+
+ public void init() {
+ initButton.click();
+ }
+
+ public void setResponseMode(String value) {
+ responseModeSelect.selectByValue(value);
+ }
+
+ public String getOutputText() {
+ return outputArea.getText();
+ }
+
+ public String getEventsText() {
+ return eventsArea.getText();
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java
index 3a5012a..9e7f4f1 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java
@@ -38,6 +38,7 @@ public abstract class AbstractExampleAdapterTest extends AbstractAdapterTest {
public static final String EXAMPLES_HOME;
public static final String EXAMPLES_VERSION_SUFFIX;
public static final String EXAMPLES_HOME_DIR;
+ public static final String TEST_APPS_HOME_DIR;
public static final String EXAMPLES_WEB_XML;
static {
@@ -49,7 +50,13 @@ public abstract class AbstractExampleAdapterTest extends AbstractAdapterTest {
Assert.assertNotNull("Property ${examples.version.suffix} must bet set.", EXAMPLES_VERSION_SUFFIX);
System.out.println(EXAMPLES_VERSION_SUFFIX);
- EXAMPLES_HOME_DIR = EXAMPLES_HOME + "/keycloak-examples-" + EXAMPLES_VERSION_SUFFIX;
+ if (!System.getProperty("unpacked.container.folder.name","").isEmpty()) {
+ EXAMPLES_HOME_DIR = EXAMPLES_HOME + "/" + System.getProperty("unpacked.container.folder.name","") + "-examples";
+ TEST_APPS_HOME_DIR = EXAMPLES_HOME + "/" + System.getProperty("unpacked.container.folder.name","") + "-test-apps";
+ } else {
+ EXAMPLES_HOME_DIR = EXAMPLES_HOME + "/keycloak-examples-" + EXAMPLES_VERSION_SUFFIX;
+ TEST_APPS_HOME_DIR = EXAMPLES_HOME + "/Keycloak-" + EXAMPLES_VERSION_SUFFIX + "-test-apps";
+ }
EXAMPLES_WEB_XML = EXAMPLES_HOME + "/web.xml";
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
old mode 100644
new mode 100755
index 3251c34..e4c9226
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
@@ -73,7 +73,7 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
@Override
public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
- RealmRepresentation jsConsoleRealm = loadRealm(new File(EXAMPLES_HOME_DIR + "/js-console/example-realm.json"));
+ RealmRepresentation jsConsoleRealm = loadRealm(new File(TEST_APPS_HOME_DIR + "/js-console/example-realm.json"));
fixClientUrisUsingDeploymentUrl(jsConsoleRealm,
JSConsoleExample.CLIENT_ID, jsConsoleExamplePage.buildUri().toASCIIString());
@@ -96,6 +96,7 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
pause(1000);
+ jsConsoleExamplePage.init();
jsConsoleExamplePage.logIn();
testRealmLoginPage.form().login("user", "invalid-password");
assertCurrentUrlDoesntStartWith(jsConsoleExamplePage);
@@ -105,14 +106,17 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
testRealmLoginPage.form().login("user", "password");
assertCurrentUrlStartsWith(jsConsoleExamplePage);
- assertTrue(driver.getPageSource().contains("Init Success (Authenticated)"));
- assertTrue(driver.getPageSource().contains("Auth Success"));
+ jsConsoleExamplePage.init();
+ assertTrue(jsConsoleExamplePage.getOutputText().contains("Init Success (Authenticated)"));
+ assertTrue(jsConsoleExamplePage.getEventsText().contains("Auth Success"));
pause(1000);
jsConsoleExamplePage.logOut();
assertCurrentUrlStartsWith(jsConsoleExamplePage);
- assertTrue(driver.getPageSource().contains("Init Success (Not Authenticated)"));
+ jsConsoleExamplePage.init();
+
+ assertTrue(jsConsoleExamplePage.getOutputText().contains("Init Success (Not Authenticated)"));
}
@Test
@@ -120,38 +124,41 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
jsConsoleExamplePage.navigateTo();
assertCurrentUrlStartsWith(jsConsoleExamplePage);
+ jsConsoleExamplePage.init();
jsConsoleExamplePage.refreshToken();
- assertTrue(driver.getPageSource().contains("Failed to refresh token"));
+ assertTrue(jsConsoleExamplePage.getOutputText().contains("Failed to refresh token"));
jsConsoleExamplePage.logIn();
testRealmLoginPage.form().login("user", "password");
assertCurrentUrlStartsWith(jsConsoleExamplePage);
- assertTrue(driver.getPageSource().contains("Auth Success"));
+ jsConsoleExamplePage.init();
+ assertTrue(jsConsoleExamplePage.getEventsText().contains("Auth Success"));
jsConsoleExamplePage.refreshToken();
- assertTrue(driver.getPageSource().contains("Auth Refresh Success"));
+ assertTrue(jsConsoleExamplePage.getEventsText().contains("Auth Refresh Success"));
}
@Test
public void testRefreshTokenIfUnder30s() {
jsConsoleExamplePage.navigateTo();
assertCurrentUrlStartsWith(jsConsoleExamplePage);
-
+ jsConsoleExamplePage.init();
jsConsoleExamplePage.refreshToken();
- assertTrue(driver.getPageSource().contains("Failed to refresh token"));
+ assertTrue(jsConsoleExamplePage.getOutputText().contains("Failed to refresh token"));
jsConsoleExamplePage.logIn();
testRealmLoginPage.form().login("user", "password");
assertCurrentUrlStartsWith(jsConsoleExamplePage);
- assertTrue(driver.getPageSource().contains("Auth Success"));
+ jsConsoleExamplePage.init();
+ assertTrue(jsConsoleExamplePage.getEventsText().contains("Auth Success"));
jsConsoleExamplePage.refreshTokenIfUnder30s();
- assertTrue(driver.getPageSource().contains("Token not refreshed, valid for"));
+ assertTrue(jsConsoleExamplePage.getOutputText().contains("Token not refreshed, valid for"));
pause((TOKEN_LIFESPAN_LEEWAY + 2) * 1000);
jsConsoleExamplePage.refreshTokenIfUnder30s();
- assertTrue(driver.getPageSource().contains("Auth Refresh Success"));
+ assertTrue(jsConsoleExamplePage.getEventsText().contains("Auth Refresh Success"));
}
@Test
@@ -159,17 +166,18 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
jsConsoleExamplePage.navigateTo();
assertCurrentUrlStartsWith(jsConsoleExamplePage);
+ jsConsoleExamplePage.init();
jsConsoleExamplePage.getProfile();
- assertTrue(driver.getPageSource().contains("Failed to load profile"));
+ assertTrue(jsConsoleExamplePage.getOutputText().contains("Failed to load profile"));
jsConsoleExamplePage.logIn();
testRealmLoginPage.form().login("user", "password");
assertCurrentUrlStartsWith(jsConsoleExamplePage);
- assertTrue(driver.getPageSource().contains("Auth Success"));
+ jsConsoleExamplePage.init();
+ assertTrue(jsConsoleExamplePage.getEventsText().contains("Auth Success"));
jsConsoleExamplePage.getProfile();
- assertTrue(driver.getPageSource().contains("Failed to load profile"));
- assertTrue(driver.getPageSource().contains("\"username\": \"user\""));
+ assertTrue(jsConsoleExamplePage.getOutputText().contains("\"username\": \"user\""));
}
@Test
@@ -194,6 +202,7 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
testRealmResource().update(realm);
jsConsoleExamplePage.navigateTo();
+ jsConsoleExamplePage.init();
jsConsoleExamplePage.logIn();
testRealmLoginPage.form().login("user", "password");
@@ -201,12 +210,14 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
assertTrue(oAuthGrantPage.isCurrent());
oAuthGrantPage.accept();
- assertTrue(driver.getPageSource().contains("Init Success (Authenticated)"));
+ jsConsoleExamplePage.init();
+ assertTrue(jsConsoleExamplePage.getOutputText().contains("Init Success (Authenticated)"));
applicationsPage.navigateTo();
applicationsPage.revokeGrantForApplication("js-console");
jsConsoleExamplePage.navigateTo();
+ jsConsoleExamplePage.init();
jsConsoleExamplePage.logIn();
assertTrue(oAuthGrantPage.isCurrent());
@@ -223,7 +234,7 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
resultList.get(0).findElement(By.xpath(".//td[text()='REVOKE_GRANT']"));
resultList.get(0).findElement(By.xpath(".//td[text()='Client']/../td[text()='account']"));
- resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1']"));
+ resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1' or text()='0:0:0:0:0:0:0:1']"));
resultList.get(0).findElement(By.xpath(".//td[text()='revoked_client']/../td[text()='js-console']"));
loginEventsPage.table().reset();
@@ -235,9 +246,87 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
resultList.get(0).findElement(By.xpath(".//td[text()='LOGIN']"));
resultList.get(0).findElement(By.xpath(".//td[text()='Client']/../td[text()='js-console']"));
- resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1']"));
+ resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1' or text()='0:0:0:0:0:0:0:1']"));
resultList.get(0).findElement(By.xpath(".//td[text()='username']/../td[text()='user']"));
resultList.get(0).findElement(By.xpath(".//td[text()='consent']/../td[text()='consent_granted']"));
}
+
+ @Test
+ public void implicitFlowTest() {
+ jsConsoleExamplePage.navigateTo();
+ jsConsoleExamplePage.setFlow("implicit");
+ jsConsoleExamplePage.init();
+
+ jsConsoleExamplePage.logIn();
+ assertTrue(driver.getPageSource().contains("Implicit flow is disabled for the client"));
+
+ setImplicitFlowFroClient();
+
+ jsConsoleExamplePage.navigateTo();
+ jsConsoleExamplePage.init();
+ jsConsoleExamplePage.logIn();
+ assertTrue(driver.getPageSource().contains("Standard flow is disabled for the client"));
+
+ logInAndInit("implicit");
+
+ assertTrue(jsConsoleExamplePage.getOutputText().contains("Init Success (Authenticated)"));
+ }
+
+ @Test
+ public void implicitFlowQueryTest() {
+ setImplicitFlowFroClient();
+
+ jsConsoleExamplePage.navigateTo();
+ jsConsoleExamplePage.setFlow("implicit");
+ jsConsoleExamplePage.setResponseMode("query");
+ jsConsoleExamplePage.init();
+ jsConsoleExamplePage.logIn();
+ assertTrue(driver.getPageSource().contains("Invalid parameter: response_mode"));
+ }
+
+ @Test
+ public void implicitFlowRefreshTokenTest() {
+ setImplicitFlowFroClient();
+
+ logInAndInit("implicit");
+
+ jsConsoleExamplePage.refreshToken();
+
+ assertTrue(jsConsoleExamplePage.getOutputText().contains("Failed to refresh token"));
+ }
+
+ @Test
+ public void implicitFlowOnTokenExpireTest() {
+ RealmRepresentation realm = testRealmResource().toRepresentation();
+ realm.setAccessTokenLifespanForImplicitFlow(5);
+ testRealmResource().update(realm);
+
+ setImplicitFlowFroClient();
+
+ logInAndInit("implicit");
+
+ pause(5000);
+
+ assertTrue(jsConsoleExamplePage.getEventsText().contains("Access token expired"));
+ }
+
+ private void setImplicitFlowFroClient() {
+ ClientResource clientResource = ApiUtil.findClientResourceByClientId(testRealmResource(), "js-console");
+ ClientRepresentation client = clientResource.toRepresentation();
+ client.setImplicitFlowEnabled(true);
+ client.setStandardFlowEnabled(false);
+ clientResource.update(client);
+ }
+
+ private void logInAndInit(String flow) {
+ jsConsoleExamplePage.navigateTo();
+ jsConsoleExamplePage.setFlow(flow);
+ jsConsoleExamplePage.init();
+ jsConsoleExamplePage.logIn();
+ testRealmLoginPage.form().login("user", "password");
+ jsConsoleExamplePage.setFlow(flow);
+ jsConsoleExamplePage.init();
+ }
+
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AbstractEventTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AbstractEventTest.java
index ba50fe7..597556c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AbstractEventTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AbstractEventTest.java
@@ -38,7 +38,7 @@ public abstract class AbstractEventTest extends AbstractAuthTest {
configRep.setAdminEventsDetailsEnabled(false);
configRep.setAdminEventsEnabled(false);
configRep.setEventsEnabled(false);
- configRep.setEnabledEventTypes(Collections.EMPTY_LIST); // resets to all types
+ configRep.setEnabledEventTypes(Collections.<String>emptyList()); // resets to all types
saveConfig();
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/AbstractGroupTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/AbstractGroupTest.java
new file mode 100644
index 0000000..264551b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/AbstractGroupTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.admin.group;
+
+import org.junit.Before;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.RSATokenVerifier;
+import org.keycloak.events.Details;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.jose.jws.crypto.RSAProvider;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.RefreshToken;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
+
+import java.util.List;
+
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public abstract class AbstractGroupTest extends AbstractKeycloakTest {
+
+ AssertEvents events;
+
+ @Before
+ public void initAssertEvents() throws Exception {
+ events = new AssertEvents(this);
+ }
+
+ AccessToken login(String login, String clientId, String clientSecret, String userId) throws Exception {
+
+ AccessTokenResponse tokenResponse = oauthClient.getToken("test", clientId, clientSecret, login, "password");
+
+ String accessToken = tokenResponse.getToken();
+ String refreshToken = tokenResponse.getRefreshToken();
+
+ AccessToken accessTokenRepresentation = RSATokenVerifier.verifyToken(accessToken, events.getRealmPublicKey(), AuthServerTestEnricher.getAuthServerContextRoot() + "/auth/realms/test");
+
+ JWSInput jws = new JWSInput(refreshToken);
+ if (!RSAProvider.verify(jws, events.getRealmPublicKey())) {
+ throw new RuntimeException("Invalid refresh token");
+ }
+ RefreshToken refreshTokenRepresentation = jws.readJsonContent(RefreshToken.class);
+
+ events.expectLogin()
+ .client(clientId)
+ .user(userId)
+ .session(tokenResponse.getSessionState())
+ .detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
+ .detail(Details.TOKEN_ID, accessTokenRepresentation.getId())
+ .detail(Details.REFRESH_TOKEN_ID, refreshTokenRepresentation.getId())
+ .detail(Details.USERNAME, login)
+ .removeDetail(Details.CODE_ID)
+ .removeDetail(Details.REDIRECT_URI)
+ .removeDetail(Details.CONSENT)
+ .assertEvent();
+
+ return accessTokenRepresentation;
+ }
+
+ RealmRepresentation loadTestRealm(List<RealmRepresentation> testRealms) {
+ RealmRepresentation result = loadRealm("/testrealm.json");
+ testRealms.add(result);
+ return result;
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupMappersTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupMappersTest.java
new file mode 100644
index 0000000..d61a517
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupMappersTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.admin.group;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.protocol.ProtocolMapperUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.mappers.GroupMembershipMapper;
+import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
+import org.keycloak.protocol.oidc.mappers.UserAttributeMapper;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ProtocolMapperRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ */
+public class GroupMappersTest extends AbstractGroupTest {
+
+ @Override
+ public void addTestRealms(List<RealmRepresentation> testRealms) {
+ RealmRepresentation testRealmRep = loadTestRealm(testRealms);
+
+ testRealmRep.setEventsEnabled(true);
+
+ ClientRepresentation client = getClientByAlias(testRealmRep, "test-app");
+ Assert.assertNotNull("test-app client exists", client);
+
+ client.setDirectAccessGrantsEnabled(true);
+
+ List<ProtocolMapperRepresentation> mappers = new LinkedList<>();
+ ProtocolMapperRepresentation mapper = new ProtocolMapperRepresentation();
+ mapper.setName("groups");
+ mapper.setProtocolMapper(GroupMembershipMapper.PROVIDER_ID);
+ mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ mapper.setConsentRequired(false);
+ Map<String, String> config = new HashMap<>();
+ config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "groups");
+ config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
+ config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
+ mapper.setConfig(config);
+ mappers.add(mapper);
+
+ mapper = new ProtocolMapperRepresentation();
+ mapper.setName("topAttribute");
+ mapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID);
+ mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ mapper.setConsentRequired(false);
+ config = new HashMap<>();
+ config.put(ProtocolMapperUtils.USER_ATTRIBUTE, "topAttribute");
+ config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "topAttribute");
+ config.put(OIDCAttributeMapperHelper.JSON_TYPE, ProviderConfigProperty.STRING_TYPE);
+ config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
+ config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
+ mapper.setConfig(config);
+ mappers.add(mapper);
+
+ mapper = new ProtocolMapperRepresentation();
+ mapper.setName("level2Attribute");
+ mapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID);
+ mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ mapper.setConsentRequired(false);
+ config = new HashMap<>();
+ config.put(ProtocolMapperUtils.USER_ATTRIBUTE, "level2Attribute");
+ config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "level2Attribute");
+ config.put(OIDCAttributeMapperHelper.JSON_TYPE, ProviderConfigProperty.STRING_TYPE);
+ config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
+ config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
+ mapper.setConfig(config);
+ mappers.add(mapper);
+
+ client.setProtocolMappers(mappers);
+ }
+
+ private ClientRepresentation getClientByAlias(RealmRepresentation testRealmRep, String alias) {
+ for (ClientRepresentation client: testRealmRep.getClients()) {
+ if (alias.equals(client.getClientId())) {
+ return client;
+ }
+ }
+ return null;
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testGroupMappers() throws Exception {
+ RealmResource realm = adminClient.realms().realm("test");
+ {
+ UserRepresentation user = realm.users().search("topGroupUser", -1, -1).get(0);
+
+ AccessToken token = login(user.getUsername(), "test-app", "password", user.getId());
+ Assert.assertTrue(token.getRealmAccess().getRoles().contains("user"));
+ List<String> groups = (List<String>) token.getOtherClaims().get("groups");
+ Assert.assertNotNull(groups);
+ Assert.assertTrue(groups.size() == 1);
+ Assert.assertEquals("topGroup", groups.get(0));
+ Assert.assertEquals("true", token.getOtherClaims().get("topAttribute"));
+ }
+ {
+ UserRepresentation user = realm.users().search("level2GroupUser", -1, -1).get(0);
+
+ AccessToken token = login(user.getUsername(), "test-app", "password", user.getId());
+ Assert.assertTrue(token.getRealmAccess().getRoles().contains("user"));
+ Assert.assertTrue(token.getRealmAccess().getRoles().contains("admin"));
+ Assert.assertTrue(token.getResourceAccess("test-app").getRoles().contains("customer-user"));
+ List<String> groups = (List<String>) token.getOtherClaims().get("groups");
+ Assert.assertNotNull(groups);
+ Assert.assertTrue(groups.size() == 1);
+ Assert.assertEquals("level2group", groups.get(0));
+ Assert.assertEquals("true", token.getOtherClaims().get("topAttribute"));
+ Assert.assertEquals("true", token.getOtherClaims().get("level2Attribute"));
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java
new file mode 100644
index 0000000..8e4c92a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java
@@ -0,0 +1,365 @@
+/*
+ * 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 org.apache.commons.io.IOUtils;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Assert;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator;
+import org.keycloak.common.util.PemUtils;
+import org.keycloak.events.Details;
+import org.keycloak.events.EventType;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.EventRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.UserSessionRepresentation;
+import org.keycloak.util.JsonSerialization;
+import org.keycloak.util.TokenUtil;
+
+import java.io.IOException;
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AssertEvents {
+
+ static final String DEFAULT_CLIENT_ID = "test-app";
+ static final String DEFAULT_IP_ADDRESS = "127.0.0.1";
+ static final String DEFAULT_REALM = "test";
+ static final String DEFAULT_USERNAME = "test-user@localhost";
+
+ String defaultRedirectUri = "http://localhost:8081/app/auth";
+ String defaultEventsQueueUri = "http://localhost:8092";
+
+ private RealmResource realmResource;
+ private RealmRepresentation realmRep;
+ private AbstractKeycloakTest context;
+ private PublicKey realmPublicKey;
+ private UserRepresentation defaultUser;
+
+ public AssertEvents(AbstractKeycloakTest ctx) throws Exception {
+ context = ctx;
+
+ realmResource = context.adminClient.realms().realm(DEFAULT_REALM);
+ realmRep = realmResource.toRepresentation();
+ String pubKeyString = realmRep.getPublicKey();
+ realmPublicKey = PemUtils.decodePublicKey(pubKeyString);
+
+ defaultUser = getUser(DEFAULT_USERNAME);
+ if (defaultUser == null) {
+ throw new RuntimeException("Default user does not exist: " + DEFAULT_USERNAME + ". Make sure to add it to your test realm.");
+ }
+
+ defaultEventsQueueUri = getAuthServerEventsQueueUri();
+ }
+
+ String getAuthServerEventsQueueUri() {
+ int httpPort = Integer.parseInt(System.getProperty("auth.server.event.http.port", "8089"));
+ int portOffset = Integer.parseInt(System.getProperty("auth.server.port.offset", "0"));
+ return "http://localhost:" + (httpPort + portOffset);
+ }
+
+ public EventRepresentation poll() {
+ EventRepresentation event = fetchNextEvent();
+ Assert.assertNotNull("Event expected", event);
+
+ return event;
+ }
+
+ public void clear() {
+ realmResource.clearEvents();
+ }
+
+ public ExpectedEvent expectRequiredAction(EventType event) {
+ return expectLogin().event(event).removeDetail(Details.CONSENT).session(isUUID());
+ }
+
+ public ExpectedEvent expectLogin() {
+ return expect(EventType.LOGIN)
+ .detail(Details.CODE_ID, isCodeId())
+ //.detail(Details.USERNAME, DEFAULT_USERNAME)
+ //.detail(Details.AUTH_METHOD, OIDCLoginProtocol.LOGIN_PROTOCOL)
+ //.detail(Details.AUTH_TYPE, AuthorizationEndpoint.CODE_AUTH_TYPE)
+ .detail(Details.REDIRECT_URI, defaultRedirectUri)
+ .detail(Details.CONSENT, Details.CONSENT_VALUE_NO_CONSENT_REQUIRED)
+ .session(isUUID());
+ }
+
+ public ExpectedEvent expectClientLogin() {
+ return expect(EventType.CLIENT_LOGIN)
+ .detail(Details.CODE_ID, isCodeId())
+ .detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
+ .detail(Details.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS)
+ .removeDetail(Details.CODE_ID)
+ .session(isUUID());
+ }
+
+ public ExpectedEvent expectSocialLogin() {
+ return expect(EventType.LOGIN)
+ .detail(Details.CODE_ID, isCodeId())
+ .detail(Details.USERNAME, DEFAULT_USERNAME)
+ .detail(Details.AUTH_METHOD, "form")
+ .detail(Details.REDIRECT_URI, defaultRedirectUri)
+ .session(isUUID());
+ }
+
+ public ExpectedEvent expectCodeToToken(String codeId, String sessionId) {
+ return expect(EventType.CODE_TO_TOKEN)
+ .detail(Details.CODE_ID, codeId)
+ .detail(Details.TOKEN_ID, isUUID())
+ .detail(Details.REFRESH_TOKEN_ID, isUUID())
+ .detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_REFRESH)
+ .detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
+ .session(sessionId);
+ }
+
+ public ExpectedEvent expectRefresh(String refreshTokenId, String sessionId) {
+ return expect(EventType.REFRESH_TOKEN)
+ .detail(Details.TOKEN_ID, isUUID())
+ .detail(Details.REFRESH_TOKEN_ID, refreshTokenId)
+ .detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_REFRESH)
+ .detail(Details.UPDATED_REFRESH_TOKEN_ID, isUUID())
+ .detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
+ .session(sessionId);
+ }
+
+ public ExpectedEvent expectLogout(String sessionId) {
+ return expect(EventType.LOGOUT).client((String) null)
+ .detail(Details.REDIRECT_URI, defaultRedirectUri)
+ .session(sessionId);
+ }
+
+ public ExpectedEvent expectRegister(String username, String email) {
+ UserRepresentation user = username != null ? getUser(username) : null;
+ return expect(EventType.REGISTER)
+ .user(user != null ? user.getId() : null)
+ .detail(Details.USERNAME, username)
+ .detail(Details.EMAIL, email)
+ .detail(Details.REGISTER_METHOD, "form")
+ .detail(Details.REDIRECT_URI, defaultRedirectUri);
+ }
+
+ public ExpectedEvent expectAccount(EventType event) {
+ return expect(event).client("account");
+ }
+
+ public ExpectedEvent expect(EventType event) {
+ return new ExpectedEvent()
+ .realm(realmRep.getId())
+ .client(DEFAULT_CLIENT_ID)
+ .user(defaultUser.getId())
+ .ipAddress(DEFAULT_IP_ADDRESS)
+ .session((String) null)
+ .event(event);
+ }
+
+ UserRepresentation getUser(String username) {
+ List<UserRepresentation> result = realmResource.users().search(username, null, null, null, 0, 1);
+ return result.size() > 0 ? result.get(0) : null;
+ }
+
+ public PublicKey getRealmPublicKey() {
+ return realmPublicKey;
+ }
+
+ public class ExpectedEvent {
+ private EventRepresentation expected = new EventRepresentation();
+ private Matcher<String> userId;
+ private Matcher<String> sessionId;
+ private HashMap<String, Matcher<String>> details;
+
+ public ExpectedEvent realm(RealmRepresentation realm) {
+ expected.setRealmId(realm.getId());
+ return this;
+ }
+
+ public ExpectedEvent realm(String realmId) {
+ expected.setRealmId(realmId);
+ return this;
+ }
+
+ public ExpectedEvent client(ClientRepresentation client) {
+ expected.setClientId(client.getClientId());
+ return this;
+ }
+
+ public ExpectedEvent client(String clientId) {
+ expected.setClientId(clientId);
+ return this;
+ }
+
+ public ExpectedEvent user(UserRepresentation user) {
+ return user(user.getId());
+ }
+
+ public ExpectedEvent user(String userId) {
+ return user(CoreMatchers.equalTo(userId));
+ }
+
+ public ExpectedEvent user(Matcher<String> userId) {
+ this.userId = userId;
+ return this;
+ }
+
+ public ExpectedEvent session(UserSessionRepresentation session) {
+ return session(session.getId());
+ }
+
+ public ExpectedEvent session(String sessionId) {
+ return session(CoreMatchers.equalTo(sessionId));
+ }
+
+ public ExpectedEvent session(Matcher<String> sessionId) {
+ this.sessionId = sessionId;
+ return this;
+ }
+
+ public ExpectedEvent ipAddress(String ipAddress) {
+ expected.setIpAddress(ipAddress);
+ return this;
+ }
+
+ public ExpectedEvent event(EventType e) {
+ expected.setType(e.name());
+ return this;
+ }
+
+ public ExpectedEvent detail(String key, String value) {
+ return detail(key, CoreMatchers.equalTo(value));
+ }
+
+ public ExpectedEvent detail(String key, Matcher<String> matcher) {
+ if (details == null) {
+ details = new HashMap<String, Matcher<String>>();
+ }
+ details.put(key, matcher);
+ return this;
+ }
+
+ public ExpectedEvent removeDetail(String key) {
+ if (details != null) {
+ details.remove(key);
+ }
+ return this;
+ }
+
+ public ExpectedEvent clearDetails() {
+ if (details != null) details.clear();
+ return this;
+ }
+
+ public ExpectedEvent error(String error) {
+ expected.setError(error);
+ return this;
+ }
+
+ public EventRepresentation assertEvent() {
+ return assertEvent(poll());
+ }
+
+ public EventRepresentation assertEvent(EventRepresentation actual) {
+ if (expected.getError() != null && !expected.getType().toString().endsWith("_ERROR")) {
+ expected.setType(expected.getType() + "_ERROR");
+ }
+ Assert.assertEquals(expected.getType(), actual.getType());
+ Assert.assertEquals(expected.getRealmId(), actual.getRealmId());
+ Assert.assertEquals(expected.getClientId(), actual.getClientId());
+ Assert.assertEquals(expected.getError(), actual.getError());
+ Assert.assertEquals(expected.getIpAddress(), actual.getIpAddress());
+ Assert.assertThat(actual.getUserId(), userId);
+ Assert.assertThat(actual.getSessionId(), sessionId);
+
+ if (details == null || details.isEmpty()) {
+// Assert.assertNull(actual.getDetails());
+ } else {
+ Assert.assertNotNull(actual.getDetails());
+ for (Map.Entry<String, Matcher<String>> d : details.entrySet()) {
+ String actualValue = actual.getDetails().get(d.getKey());
+ if (!actual.getDetails().containsKey(d.getKey())) {
+ Assert.fail(d.getKey() + " missing");
+ }
+
+ Assert.assertThat("Unexpected value for " + d.getKey(), actualValue, d.getValue());
+ }
+ /*
+ for (String k : actual.getDetails().keySet()) {
+ if (!details.containsKey(k)) {
+ Assert.fail(k + " was not expected");
+ }
+ }
+ */
+ }
+
+ return actual;
+ }
+ }
+
+ public static Matcher<String> isCodeId() {
+ return isUUID();
+ }
+
+ public static Matcher<String> isUUID() {
+ return new TypeSafeMatcher<String>() {
+ @Override
+ protected boolean matchesSafely(String item) {
+ return 36 == item.length() && item.charAt(8) == '-' && item.charAt(13) == '-' && item.charAt(18) == '-' && item.charAt(23) == '-';
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Not an UUID");
+ }
+ };
+ }
+
+ private EventRepresentation fetchNextEvent() {
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ try {
+ HttpPost post = new HttpPost(defaultEventsQueueUri + "/event-queue");
+ CloseableHttpResponse response = httpclient.execute(post);
+ if (response.getStatusLine().getStatusCode() != 200) {
+ throw new RuntimeException("Failed to retrieve event from " + post.getURI() + ": " + response.getStatusLine().toString() + " / " + IOUtils.toString(response.getEntity().getContent()));
+ }
+
+ return JsonSerialization.readValue(response.getEntity().getContent(), EventRepresentation.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ finally {
+ try {
+ httpclient.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/URLAssert.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/URLAssert.java
index c66fd70..6ef0bbe 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/URLAssert.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/URLAssert.java
@@ -18,14 +18,27 @@
package org.keycloak.testsuite.util;
import javax.ws.rs.core.UriBuilder;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.junit.Assert;
import org.keycloak.testsuite.page.AbstractPage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.keycloak.testsuite.auth.page.login.PageWithLoginUrl;
import org.openqa.selenium.WebDriver;
-import org.openqa.selenium.support.ui.ExpectedCondition;
-import org.openqa.selenium.support.ui.WebDriverWait;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.net.URI;
+import java.nio.charset.Charset;
/**
*
@@ -104,9 +117,68 @@ public class URLAssert {
public static void assertCurrentUrlStartsWithLoginUrlOf(PageWithLoginUrl page) {
assertCurrentUrlStartsWithLoginUrlOf(page.getDriver(), page);
}
-
+
public static void assertCurrentUrlStartsWithLoginUrlOf(WebDriver driver, PageWithLoginUrl page) {
assertCurrentUrlStartsWith(driver, page.getOIDCLoginUrl().toString());
}
+ public static void assertGetURL(URI url, String accessToken, AssertResponseHandler handler) {
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ try {
+ HttpGet get = new HttpGet(url);
+ get.setHeader("Authorization", "Bearer " + accessToken);
+
+ CloseableHttpResponse response = httpclient.execute(get);
+
+ if (response.getStatusLine().getStatusCode() != 200) {
+ throw new RuntimeException("Response status error: " + response.getStatusLine().getStatusCode() + ": " + url);
+ }
+
+ handler.assertResponse(response);
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ finally {
+ try {
+ httpclient.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public interface AssertResponseHandler {
+ void assertResponse(CloseableHttpResponse response) throws IOException;
+ }
+
+ public static abstract class AssertJSONResponseHandler implements AssertResponseHandler {
+
+ @Override
+ public void assertResponse(CloseableHttpResponse response) throws IOException {
+ HttpEntity entity = response.getEntity();
+ Header contentType = entity.getContentType();
+ Assert.assertEquals("application/json", contentType.getValue());
+
+ char [] buf = new char[8192];
+ StringWriter out = new StringWriter();
+ Reader in = new InputStreamReader(entity.getContent(), Charset.forName("utf-8"));
+ int rc = 0;
+ try {
+ while ((rc = in.read(buf)) != -1) {
+ out.write(buf, 0, rc);
+ }
+ } finally {
+ try {
+ in.close();
+ } catch (Exception ignored) {}
+
+ out.close();
+ }
+
+ assertResponseBody(out.toString());
+ }
+
+ protected abstract void assertResponseBody(String body) throws IOException;
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
index ae0f5f9..f5d64a5 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
@@ -11,7 +11,8 @@
"jboss-logging" : {
"success-level": "debug",
"error-level": "warn"
- }
+ },
+ "event-queue": {}
},
"realm": {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json
new file mode 100644
index 0000000..845adda
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json
@@ -0,0 +1,186 @@
+{
+ "id": "test",
+ "realm": "test",
+ "enabled": true,
+ "sslRequired": "external",
+ "registrationAllowed": true,
+ "resetPasswordAllowed": true,
+ "editUsernameAllowed" : true,
+ "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+ "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "requiredCredentials": [ "password" ],
+ "defaultRoles": [ "user" ],
+ "smtpServer": {
+ "from": "auto@keycloak.org",
+ "host": "localhost",
+ "port":"3025"
+ },
+ "users" : [
+ {
+ "username" : "test-user@localhost",
+ "enabled": true,
+ "email" : "test-user@localhost",
+ "firstName": "Tom",
+ "lastName": "Brady",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": ["user", "offline_access"],
+ "clientRoles": {
+ "test-app": [ "customer-user" ],
+ "account": [ "view-profile", "manage-account" ]
+ }
+ },
+ {
+ "username" : "john-doh@localhost",
+ "enabled": true,
+ "email" : "john-doh@localhost",
+ "firstName": "John",
+ "lastName": "Doh",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": ["user"],
+ "clientRoles": {
+ "test-app": [ "customer-user" ],
+ "account": [ "view-profile", "manage-account" ]
+ }
+ },
+ {
+ "username" : "keycloak-user@localhost",
+ "enabled": true,
+ "email" : "keycloak-user@localhost",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": ["user"],
+ "clientRoles": {
+ "test-app": [ "customer-user" ],
+ "account": [ "view-profile", "manage-account" ]
+ }
+ },
+ {
+ "username" : "topGroupUser",
+ "enabled": true,
+ "email" : "top@redhat.com",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "groups": [
+ "/topGroup"
+ ]
+ },
+ {
+ "username" : "level2GroupUser",
+ "enabled": true,
+ "email" : "level2@redhat.com",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "groups": [
+ "/topGroup/level2group"
+ ]
+ }
+ ],
+ "scopeMappings": [
+ {
+ "client": "third-party",
+ "roles": ["user"]
+ },
+ {
+ "client": "test-app",
+ "roles": ["user"]
+ }
+ ],
+ "clients": [
+ {
+ "clientId": "test-app",
+ "enabled": true,
+ "baseUrl": "http://localhost:8081/app",
+ "redirectUris": [
+ "http://localhost:8081/app/*"
+ ],
+ "adminUrl": "http://localhost:8081/app/logout",
+ "secret": "password"
+ },
+ {
+ "clientId" : "third-party",
+ "enabled": true,
+ "consentRequired": true,
+
+ "redirectUris": [
+ "http://localhost:8081/app/*"
+ ],
+ "secret": "password"
+ }
+ ],
+ "roles" : {
+ "realm" : [
+ {
+ "name": "user",
+ "description": "Have User privileges"
+ },
+ {
+ "name": "admin",
+ "description": "Have Administrator privileges"
+ }
+ ],
+ "client" : {
+ "test-app" : [
+ {
+ "name": "customer-user",
+ "description": "Have Customer User privileges"
+ },
+ {
+ "name": "customer-admin",
+ "description": "Have Customer Admin privileges"
+ }
+ ]
+ }
+
+ },
+ "groups" : [
+ {
+ "name": "topGroup",
+ "attributes": {
+ "topAttribute": ["true"]
+
+ },
+ "realmRoles": ["user"],
+
+ "subGroups": [
+ {
+ "name": "level2group",
+ "realmRoles": ["admin"],
+ "clientRoles": {
+ "test-app": ["customer-user"]
+ },
+ "attributes": {
+ "level2Attribute": ["true"]
+
+ }
+ }
+ ]
+ }
+ ],
+
+
+ "clientScopeMappings": {
+ "test-app": [
+ {
+ "client": "third-party",
+ "roles": ["customer-user"]
+ }
+ ]
+ },
+
+ "internationalizationEnabled": true,
+ "supportedLocales": ["en", "de"],
+ "defaultLocale": "en",
+ "eventsListeners": ["jboss-logging", "event-queue"]
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/EAPJSConsoleExampleAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/EAPJSConsoleExampleAdapterTest.java
new file mode 100644
index 0000000..0674eea
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/EAPJSConsoleExampleAdapterTest.java
@@ -0,0 +1,13 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+import org.junit.Ignore;
+/**
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-eap")
+//@AdapterLibsLocationProperty("adapter.libs.eap")
+public class EAPJSConsoleExampleAdapterTest extends AbstractJSConsoleExampleAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/EAP6JSConsoleExampleAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/EAP6JSConsoleExampleAdapterTest.java
index 20bd52e..4f167ba 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/EAP6JSConsoleExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/EAP6JSConsoleExampleAdapterTest.java
@@ -1,5 +1,6 @@
package org.keycloak.testsuite.adapter.example;
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
import org.junit.Ignore;
/**
@@ -7,7 +8,6 @@ import org.junit.Ignore;
*/
@AppServerContainer("app-server-eap6")
//@AdapterLibsLocationProperty("adapter.libs.eap6")
-@Ignore //jsconsole example has hardcoded relative path to keycloak.js
public class EAP6JSConsoleExampleAdapterTest extends AbstractJSConsoleExampleAdapterTest {
}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyJSConsoleExampleAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyJSConsoleExampleAdapterTest.java
new file mode 100644
index 0000000..7834461
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyJSConsoleExampleAdapterTest.java
@@ -0,0 +1,16 @@
+
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-wildfly")
+//@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyJSConsoleExampleAdapterTest extends AbstractJSConsoleExampleAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
index f46954a..a2e20fd 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
@@ -220,8 +220,8 @@
<type>war</type>
</artifactItem>
<artifactItem>
- <groupId>org.keycloak.example.demo</groupId>
- <artifactId>js-console</artifactId>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-test-apps-js-console</artifactId>
<version>${project.version}</version>
<type>war</type>
</artifactItem>
@@ -287,6 +287,13 @@
<type>zip</type>
<includes>**/*realm.json,**/testsaml.json</includes>
</artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-test-apps-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ <includes>**/*realm.json,**/testsaml.json</includes>
+ </artifactItem>
</artifactItems>
<outputDirectory>${examples.home}</outputDirectory>
<overWriteIfNewer>true</overWriteIfNewer>
diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml
index 2fcd713..727b2ec 100644
--- a/testsuite/integration-arquillian/tests/pom.xml
+++ b/testsuite/integration-arquillian/tests/pom.xml
@@ -48,6 +48,7 @@
<auth.server.port.offset>100</auth.server.port.offset>
<auth.server.http.port>8180</auth.server.http.port>
+ <auth.server.events.http.port>8089</auth.server.events.http.port>
<auth.server.https.port>8543</auth.server.https.port>
<auth.server.management.port>10090</auth.server.management.port>
<auth.server.management.port.jmx>10099</auth.server.management.port.jmx>
@@ -140,6 +141,7 @@
<auth.server.port.offset>${auth.server.port.offset}</auth.server.port.offset>
<auth.server.http.port>${auth.server.http.port}</auth.server.http.port>
+ <auth.server.events.http.port>${auth.server.events.http.port}</auth.server.events.http.port>
<auth.server.https.port>${auth.server.https.port}</auth.server.https.port>
<auth.server.management.port>${auth.server.management.port}</auth.server.management.port>
<auth.server.management.port.jmx>${auth.server.management.port.jmx}</auth.server.management.port.jmx>