resolve conflict
This commit is contained in:
commit
d1552cd6e8
46 changed files with 2330 additions and 231 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1,33 +1,36 @@
|
|||
# Keycloak Arquillian Integration Testsuite
|
||||
|
||||
## Overview
|
||||
|
||||
For overview see the **Modules Overview** section at the bottom of this README.
|
||||
|
||||
|
||||
## Container Lifecycles
|
||||
|
||||
### Keycloak Auth Server
|
||||
### 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`.
|
||||
Keycloak server is automatically started by the testsuite on the `BeforeSuite` event and stopped on `AfterSuite` event.
|
||||
|
||||
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.
|
||||
By default the server runs in embedded Undertow.
|
||||
|
||||
**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.
|
||||
#### Wildfly/EAP
|
||||
|
||||
**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.
|
||||
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
|
||||
|
||||
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).
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
<name>Auth Server</name>
|
||||
|
||||
<modules>
|
||||
<module>services</module>
|
||||
<module>jboss</module>
|
||||
<module>undertow</module>
|
||||
</modules>
|
||||
|
|
|
@ -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>2.0.0.CR1-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>
|
|
@ -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() {
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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>
|
|
@ -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>2.0.0.CR1-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>
|
|
@ -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'.
|
66
testsuite/integration-arquillian/test-apps/js-console/example-realm.json
Executable file
66
testsuite/integration-arquillian/test-apps/js-console/example-realm.json
Executable file
|
@ -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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
52
testsuite/integration-arquillian/test-apps/js-console/pom.xml
Executable file
52
testsuite/integration-arquillian/test-apps/js-console/pom.xml
Executable file
|
@ -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>2.0.0.CR1-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>
|
|
@ -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>
|
156
testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html
Executable file
156
testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html
Executable file
|
@ -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>
|
|
@ -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
|
||||
}
|
21
testsuite/integration-arquillian/test-apps/pom.xml
Normal file
21
testsuite/integration-arquillian/test-apps/pom.xml
Normal file
|
@ -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>2.0.0.CR1-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>
|
|
@ -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>
|
31
testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml
Executable file
31
testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml
Executable file
|
@ -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>
|
|
@ -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>2.0.0.CR1-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>
|
|
@ -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>
|
||||
|
|
35
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleExample.java
Normal file → Executable file
35
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleExample.java
Normal file → Executable file
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,86 +15,66 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.testsuite.model;
|
||||
package org.keycloak.testsuite.admin.group;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.oidc.mappers.GroupMembershipMapper;
|
||||
import org.keycloak.protocol.oidc.mappers.UserAttributeMapper;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.RefreshToken;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.util.URLAssert;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
public class GroupTest {
|
||||
public class GroupTest extends AbstractGroupTest {
|
||||
|
||||
@ClassRule
|
||||
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
ClientModel app = KeycloakModelUtils.createClient(appRealm, "resource-owner");
|
||||
app.setDirectAccessGrantsEnabled(true);
|
||||
app.setSecret("secret");
|
||||
@Override
|
||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||
RealmRepresentation testRealmRep = loadTestRealm(testRealms);
|
||||
|
||||
app = appRealm.getClientByClientId("test-app");
|
||||
app.setDirectAccessGrantsEnabled(true);
|
||||
testRealmRep.setEventsEnabled(true);
|
||||
|
||||
UserModel user = session.users().addUser(appRealm, "direct-login");
|
||||
user.setEmail("direct-login@localhost");
|
||||
user.setEnabled(true);
|
||||
List<UserRepresentation> users = testRealmRep.getUsers();
|
||||
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername("direct-login");
|
||||
user.setEmail("direct-login@localhost");
|
||||
user.setEnabled(true);
|
||||
List<CredentialRepresentation> credentials = new LinkedList<>();
|
||||
CredentialRepresentation credential = new CredentialRepresentation();
|
||||
credential.setType(CredentialRepresentation.PASSWORD);
|
||||
credential.setValue("password");
|
||||
credentials.add(credential);
|
||||
user.setCredentials(credentials);
|
||||
users.add(user);
|
||||
|
||||
|
||||
session.users().updateCredential(appRealm, user, UserCredentialModel.password("password"));
|
||||
keycloak = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", Constants.ADMIN_CLI_CLIENT_ID);
|
||||
}
|
||||
});
|
||||
List<ClientRepresentation> clients = testRealmRep.getClients();
|
||||
|
||||
protected static Keycloak keycloak;
|
||||
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(keycloakRule);
|
||||
|
||||
@Rule
|
||||
public WebRule webRule = new WebRule(this);
|
||||
|
||||
@WebResource
|
||||
protected WebDriver driver;
|
||||
|
||||
@WebResource
|
||||
protected OAuthClient oauth;
|
||||
ClientRepresentation client = new ClientRepresentation();
|
||||
client.setClientId("resource-owner");
|
||||
client.setDirectAccessGrantsEnabled(true);
|
||||
client.setSecret("secret");
|
||||
clients.add(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* KEYCLOAK-2716
|
||||
|
@ -102,7 +82,7 @@ public class GroupTest {
|
|||
*/
|
||||
@Test
|
||||
public void testClientRemoveWithClientRoleGroupMapping() throws Exception {
|
||||
RealmResource realm = keycloak.realms().realm("test");
|
||||
RealmResource realm = adminClient.realms().realm("test");
|
||||
ClientRepresentation client = new ClientRepresentation();
|
||||
client.setClientId("foo");
|
||||
client.setRootUrl("http://foo");
|
||||
|
@ -127,7 +107,7 @@ public class GroupTest {
|
|||
|
||||
@Test
|
||||
public void createAndTestGroups() throws Exception {
|
||||
RealmResource realm = keycloak.realms().realm("test");
|
||||
RealmResource realm = adminClient.realms().realm("test");
|
||||
{
|
||||
RoleRepresentation groupRole = new RoleRepresentation();
|
||||
groupRole.setName("topRole");
|
||||
|
@ -162,6 +142,21 @@ public class GroupTest {
|
|||
level2Group.setName("level2");
|
||||
response = realm.groups().group(topGroup.getId()).subGroup(level2Group);
|
||||
response.close();
|
||||
|
||||
URI location = response.getLocation();
|
||||
final String level2Id = ApiUtil.getCreatedId(response);
|
||||
final GroupRepresentation level2GroupById = realm.groups().group(level2Id).toRepresentation();
|
||||
Assert.assertEquals(level2Id, level2GroupById.getId());
|
||||
Assert.assertEquals(level2Group.getName(), level2GroupById.getName());
|
||||
|
||||
URLAssert.assertGetURL(location, adminClient.tokenManager().getAccessTokenString(), new URLAssert.AssertJSONResponseHandler() {
|
||||
@Override
|
||||
protected void assertResponseBody(String body) throws IOException {
|
||||
GroupRepresentation level2 = JsonSerialization.readValue(body, GroupRepresentation.class);
|
||||
Assert.assertEquals(level2Id, level2.getId());
|
||||
}
|
||||
});
|
||||
|
||||
level2Group = realm.getGroupByPath("/top/level2");
|
||||
Assert.assertNotNull(level2Group);
|
||||
roles.clear();
|
||||
|
@ -237,73 +232,5 @@ public class GroupTest {
|
|||
realm.removeDefaultGroup(level3Group.getId());
|
||||
defaultGroups = realm.getDefaultGroups();
|
||||
Assert.assertEquals(0, defaultGroups.size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroupMappers() throws Exception {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
ClientModel app = appRealm.getClientByClientId("test-app");
|
||||
app.addProtocolMapper(GroupMembershipMapper.create("groups", "groups", false, null, true, true));
|
||||
app.addProtocolMapper(UserAttributeMapper.createClaimMapper("topAttribute", "topAttribute", "topAttribute", ProviderConfigProperty.STRING_TYPE, false, null, true, true, false));
|
||||
app.addProtocolMapper(UserAttributeMapper.createClaimMapper("level2Attribute", "level2Attribute", "level2Attribute", ProviderConfigProperty.STRING_TYPE, false, null, true, true, false));
|
||||
}
|
||||
}, "test");
|
||||
RealmResource realm = keycloak.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"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected AccessToken login(String login, String clientId, String clientSecret, String userId) throws Exception {
|
||||
oauth.clientId(clientId);
|
||||
|
||||
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest(clientSecret, login, "password");
|
||||
|
||||
assertEquals(200, response.getStatusCode());
|
||||
|
||||
AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
|
||||
RefreshToken refreshToken = oauth.verifyRefreshToken(response.getRefreshToken());
|
||||
|
||||
events.expectLogin()
|
||||
.client(clientId)
|
||||
.user(userId)
|
||||
.session(accessToken.getSessionState())
|
||||
.detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
|
||||
.detail(Details.TOKEN_ID, accessToken.getId())
|
||||
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
|
||||
.detail(Details.USERNAME, login)
|
||||
.removeDetail(Details.CODE_ID)
|
||||
.removeDetail(Details.REDIRECT_URI)
|
||||
.removeDetail(Details.CONSENT)
|
||||
.assertEvent();
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
"jboss-logging" : {
|
||||
"success-level": "debug",
|
||||
"error-level": "warn"
|
||||
}
|
||||
},
|
||||
"event-queue": {}
|
||||
},
|
||||
|
||||
"realm": {
|
||||
|
|
|
@ -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"]
|
||||
}
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue