KEYCLOAK-4816 KEYCLOAK-4817 Move javascript tests to base testsuite and (#4964)
* KEYCLOAK-4816 KEYCLOAK-4817 Move javascript tests to base testsuite and use JavascriptExecutor * Use PhantomJS 2.1.1 instead of 1.9.8 in Travis CI
This commit is contained in:
parent
f604449188
commit
e2ad59a74d
41 changed files with 1379 additions and 1758 deletions
10
.travis.yml
10
.travis.yml
|
@ -22,6 +22,16 @@ jdk:
|
|||
|
||||
install: true
|
||||
|
||||
before_install:
|
||||
- "export PHANTOMJS_VERSION=2.1.1"
|
||||
- "phantomjs --version"
|
||||
- "export PATH=$PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64/bin:$PATH"
|
||||
- "phantomjs --version"
|
||||
- "if [ $(phantomjs --version) != '$PHANTOMJS_VERSION' ]; then rm -rf $PWD/travis_phantomjs; mkdir -p $PWD/travis_phantomjs; fi"
|
||||
- "if [ $(phantomjs --version) != '$PHANTOMJS_VERSION' ]; then wget https://github.com/Medium/phantomjs/releases/download/v$PHANTOMJS_VERSION/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 -O $PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2; fi"
|
||||
- "if [ $(phantomjs --version) != '$PHANTOMJS_VERSION' ]; then tar -xvf $PWD/travis_phantomjs/phantomjs-$PHANTOMJS_VERSION-linux-x86_64.tar.bz2 -C $PWD/travis_phantomjs; fi"
|
||||
- "phantomjs --version"
|
||||
|
||||
script:
|
||||
- ./travis-run-tests.sh $TESTS
|
||||
|
||||
|
|
|
@ -30,6 +30,10 @@
|
|||
<artifactId>integration-arquillian-testsuite-providers</artifactId>
|
||||
<name>Auth Server Services - Testsuite Providers</name>
|
||||
|
||||
<properties>
|
||||
<js-adapter.version>${project.version}</js-adapter.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Keycloak deps for tests -->
|
||||
|
@ -81,5 +85,32 @@
|
|||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack-javascript-adapter</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-js-adapter</artifactId>
|
||||
<version>${js-adapter.version}</version>
|
||||
<type>jar</type>
|
||||
<outputDirectory>${pom.basedir}/src/main/resources/javascript</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<includes>**/keycloak.js</includes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
|
@ -62,6 +62,7 @@ import org.keycloak.testsuite.forms.PassThroughAuthenticator;
|
|||
import org.keycloak.testsuite.forms.PassThroughClientAuthenticator;
|
||||
import org.keycloak.testsuite.rest.representation.AuthenticatorState;
|
||||
import org.keycloak.testsuite.rest.resource.TestCacheResource;
|
||||
import org.keycloak.testsuite.rest.resource.TestJavascriptResource;
|
||||
import org.keycloak.testsuite.rest.resource.TestingExportImportResource;
|
||||
import org.keycloak.testsuite.runonserver.ModuleUtil;
|
||||
import org.keycloak.testsuite.runonserver.FetchOnServer;
|
||||
|
@ -759,6 +760,11 @@ public class TestingResourceProvider implements RealmResourceProvider {
|
|||
}
|
||||
}
|
||||
|
||||
@Path("/javascript")
|
||||
public TestJavascriptResource getJavascriptResource() {
|
||||
return new TestJavascriptResource();
|
||||
}
|
||||
|
||||
private RealmModel getRealmByName(String realmName) {
|
||||
RealmProvider realmProvider = session.getProvider(RealmProvider.class);
|
||||
return realmProvider.getRealmByName(realmName);
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package org.keycloak.testsuite.rest.resource;
|
||||
|
||||
import org.keycloak.testsuite.rest.TestingResourceProvider;
|
||||
|
||||
import javax.print.attribute.standard.Media;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
public class TestJavascriptResource {
|
||||
|
||||
@GET
|
||||
@Path("/js/keycloak.js")
|
||||
@Produces("application/javascript")
|
||||
public String getJavascriptAdapter() throws IOException {
|
||||
return resourceToString("/javascript/keycloak.js");
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/index.html")
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
public String getJavascriptTestingEnvironment() throws IOException {
|
||||
return resourceToString("/javascript/index.html");
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/keycloak.json")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public String getKeycloakJSON() throws IOException {
|
||||
return resourceToString("/javascript/keycloak.json");
|
||||
}
|
||||
|
||||
private String resourceToString(String path) throws IOException {
|
||||
InputStream is = TestingResourceProvider.class.getResourceAsStream(path);
|
||||
BufferedReader buf = new BufferedReader(new InputStreamReader(is));
|
||||
String line = buf.readLine();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (line != null) {
|
||||
sb.append(line).append("\n");
|
||||
line = buf.readLine();
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<!--
|
||||
~ 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="js/keycloak.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<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 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 showError() {
|
||||
output("Error: " + getParameterByName("error") + "\n" + "Error description: " + getParameterByName("error_description"));
|
||||
}
|
||||
|
||||
function getParameterByName(name, url) {
|
||||
if (!url) url = window.location.href;
|
||||
name = name.replace(/[\[\]]/g, "\\$&");
|
||||
var regex = new RegExp("[?&#]" + name + "(=([^&#]*)|&|#|$)"),
|
||||
results = regex.exec(url);
|
||||
if (!results) return null;
|
||||
if (!results[2]) return '';
|
||||
return decodeURIComponent(results[2].replace(/\+/g, " "));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"realm" : "example",
|
||||
"realm" : "test",
|
||||
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||
"auth-server-url" : "http://localhost:8180/auth",
|
||||
"ssl-required" : "external",
|
|
@ -1,17 +0,0 @@
|
|||
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'.
|
|
@ -1,88 +0,0 @@
|
|||
{
|
||||
"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": {
|
||||
"realm-management" : [ "view-realm", "manage-users" ],
|
||||
"account": ["view-profile", "manage-account"]
|
||||
}
|
||||
},{
|
||||
"username" : "unauthorized",
|
||||
"enabled": true,
|
||||
"email" : "sample-user2@example",
|
||||
"firstName": "Sample",
|
||||
"lastName": "User",
|
||||
"credentials" : [
|
||||
{ "type" : "password",
|
||||
"value" : "password" }
|
||||
],
|
||||
"realmRoles": [],
|
||||
"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",
|
||||
"https://localhost:8643"
|
||||
]
|
||||
},{
|
||||
"clientId": "js-database",
|
||||
"enabled": true,
|
||||
"adminUrl": "/database",
|
||||
"baseUrl": "/database",
|
||||
"bearerOnly": true
|
||||
}
|
||||
],
|
||||
"clientScopeMappings": {
|
||||
"account": [
|
||||
{
|
||||
"client": "js-console",
|
||||
"roles": ["view-profile"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
<?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>4.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/>
|
||||
|
||||
<properties>
|
||||
<js-adapter.version>${project.version}</js-adapter.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<finalName>js-console</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack-javascript-adapter</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-js-adapter</artifactId>
|
||||
<version>${js-adapter.version}</version>
|
||||
<type>jar</type>
|
||||
<outputDirectory>${pom.basedir}/target/js-console/js</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<includes>**/keycloak.js</includes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<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>
|
|
@ -1,25 +0,0 @@
|
|||
<?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>
|
|
@ -1,363 +0,0 @@
|
|||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<script src="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="refreshToken(5)">Refresh Token (if <5s validity)</button>
|
||||
<button onclick="showError()">Show Error Response</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>
|
||||
<button onclick="createBearerRequest()">Create Bearer Request</button>
|
||||
<button onclick="output(showTime())">Show current time</button>
|
||||
<button onclick="cert()">Cert request</button>
|
||||
<button onclick="addToTimeSkew()">timeSkew offset</button>
|
||||
<button onclick="refreshTimeSkew()">refresh timeSkew</button>
|
||||
<button onclick="sendBearerToKeycloak()">Bearer to keycloak</button>
|
||||
<button onclick="createUser()">Create user</button>
|
||||
<button onclick="reentrancyCallback()">Reentrancy callback</button>
|
||||
<button onclick='initWithDifferentRealmName(getInput())'>Init with different realm name</button>
|
||||
<button onclick='keycloakInit(null, {refreshToken: getInput2()})'>Init with refresh token</button>
|
||||
<button onclick='keycloakInit(null, {token: getInput(), refreshToken: getInput2()})'>Init with both tokens</button>
|
||||
<button onclick='keycloakInit(null, {token: getInput(), refreshToken: getInput2(), timeSkew: parseInt(getInput3())})'>Init with TimeSkew</button>
|
||||
<input id="inputField" value="input"/>
|
||||
<input id="inputField2" value="input2"/>
|
||||
<input id="inputField3" value="input3"/>
|
||||
|
||||
|
||||
<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>
|
||||
<select id="onLoad">
|
||||
<option value="check-sso">check-sso</option>
|
||||
<option value="login-required">login-required</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>
|
||||
|
||||
<h2>Info</h2>
|
||||
TimeSkew:
|
||||
<div id="timeSkew"></div>
|
||||
|
||||
|
||||
<script>
|
||||
function getInput() {
|
||||
return document.getElementById("inputField").value;
|
||||
}
|
||||
function getInput2() {
|
||||
return document.getElementById("inputField2").value;
|
||||
}
|
||||
function getInput3() {
|
||||
return document.getElementById("inputField3").value;
|
||||
}
|
||||
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 showError() {
|
||||
output("Error: " + getParameterByName("error") + "\n" + "Error description: " + getParameterByName("error_description"));
|
||||
}
|
||||
|
||||
function getParameterByName(name, url) {
|
||||
if (!url) url = window.location.href;
|
||||
name = name.replace(/[\[\]]/g, "\\$&");
|
||||
var regex = new RegExp("[?&#]" + name + "(=([^&#]*)|&|#|$)"),
|
||||
results = regex.exec(url);
|
||||
if (!results) return null;
|
||||
if (!results[2]) return '';
|
||||
return decodeURIComponent(results[2].replace(/\+/g, " "));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function addToTimeSkew() {
|
||||
keycloak.timeSkew += parseInt(getInput());
|
||||
document.getElementById("timeSkew").innerHTML = keycloak.timeSkew;
|
||||
}
|
||||
|
||||
function refreshTimeSkew() {
|
||||
document.getElementById("timeSkew").innerHTML = keycloak.timeSkew;
|
||||
}
|
||||
|
||||
function createBearerRequest() {
|
||||
|
||||
var url = 'http://localhost:8280/js-database/customers';
|
||||
if (window.location.href.indexOf("8643") > -1) {
|
||||
url = url.replace("8280", "8643");
|
||||
url = url.replace("http", "https");
|
||||
}
|
||||
|
||||
if (window.location.href.indexOf("8180") > -1) {
|
||||
url = url.replace("8280", "8180");
|
||||
}
|
||||
|
||||
var req = new XMLHttpRequest();
|
||||
req.open('GET', url, true);
|
||||
req.setRequestHeader('Accept', 'application/json');
|
||||
req.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);
|
||||
|
||||
req.onreadystatechange = function () {
|
||||
if (req.readyState == 4) {
|
||||
if (req.status == 200) {
|
||||
output(req.responseText);
|
||||
} else if (req.status == 403) {
|
||||
output('Forbidden');
|
||||
} else if (req.status == 401) {
|
||||
output('Unauthorized');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
req.send();
|
||||
}
|
||||
|
||||
function sendBearerToKeycloak() {
|
||||
var url = 'http://localhost:8180/auth/admin/realms/example/roles';
|
||||
if (window.location.href.indexOf("8643") > -1) {
|
||||
url = url.replace("8180", "8543");
|
||||
url = url.replace("http", "https");
|
||||
}
|
||||
|
||||
var req = new XMLHttpRequest();
|
||||
req.open('GET', url, true);
|
||||
req.setRequestHeader('Accept', 'application/json');
|
||||
req.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);
|
||||
|
||||
req.onreadystatechange = function () {
|
||||
if (req.readyState == 4) {
|
||||
if (req.status == 200) {
|
||||
output('Success');
|
||||
} else if (req.status == 403) {
|
||||
output('Forbidden');
|
||||
} else if (req.status == 401) {
|
||||
output('Unauthorized');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
req.send();
|
||||
}
|
||||
|
||||
function cert() {
|
||||
var url = 'http://localhost:8180/auth/realms/example/protocol/openid-connect/certs';
|
||||
if (window.location.href.indexOf("8643") > -1) {
|
||||
url = url.replace("8180", "8543");
|
||||
url = url.replace("http", "https");
|
||||
}
|
||||
var req = new XMLHttpRequest();
|
||||
req.open('GET', url, true);
|
||||
req.setRequestHeader('Accept', 'application/json');
|
||||
req.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);
|
||||
req.onreadystatechange = function () {
|
||||
if (req.readyState == 4) {
|
||||
if (req.status == 200) {
|
||||
output('Success');
|
||||
} else if (req.status == 403) {
|
||||
output('Forbidden');
|
||||
} else if (req.status == 401) {
|
||||
output('Unauthorized');
|
||||
}
|
||||
}
|
||||
};
|
||||
req.send();
|
||||
}
|
||||
|
||||
function createUser() {
|
||||
var user = JSON.parse('{"emailVerified" : false, "enabled" : true, "username": "mhajas", "firstName" :"First", "lastName":"Last","email":"email@redhat.com", "attributes": {}}');
|
||||
|
||||
var url = "http://localhost:8180/auth/admin/realms/example/users";
|
||||
if (window.location.href.indexOf("8643") > -1) {
|
||||
url = url.replace("8180", "8543");
|
||||
url = url.replace("http", "https");
|
||||
}
|
||||
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("POST", url, true);
|
||||
req.setRequestHeader('Accept', 'application/json');
|
||||
req.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);
|
||||
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||
req.onreadystatechange = function () {
|
||||
if (req.readyState == 4) {
|
||||
if (req.status == 201) {
|
||||
output("Response headers: " + req.getAllResponseHeaders().toLowerCase());
|
||||
} else if (req.status == 403) {
|
||||
output('Forbidden');
|
||||
} else if (req.status == 401) {
|
||||
output('Unauthorized');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
req.send(JSON.stringify(user));
|
||||
}
|
||||
|
||||
function reentrancyCallback() {
|
||||
keycloak.updateToken(60).success(function () {
|
||||
event("First callback");
|
||||
keycloak.updateToken(60).success(function () {
|
||||
event("Second callback")
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function initWithDifferentRealmName(realmName) {
|
||||
var url = 'http://localhost:8180/auth';
|
||||
if (window.location.href.indexOf("8643") > -1) {
|
||||
url = url.replace("8180","8543");
|
||||
url = url.replace("http","https");
|
||||
}
|
||||
|
||||
keycloakInit({
|
||||
url: url,
|
||||
realm: realmName,
|
||||
clientId: 'js-console'
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
var keycloak;
|
||||
|
||||
function keycloakInit(configuration, initOptions) {
|
||||
if (!configuration)
|
||||
keycloak = Keycloak();
|
||||
else
|
||||
keycloak = Keycloak(configuration);
|
||||
|
||||
keycloak.onAuthSuccess = function () {
|
||||
event('Auth Success');
|
||||
};
|
||||
|
||||
keycloak.onAuthError = function () {
|
||||
event('Auth Error');
|
||||
};
|
||||
|
||||
keycloak.onAuthRefreshSuccess = function () {
|
||||
event('Auth Refresh Success');
|
||||
document.getElementById("timeSkew").innerHTML = keycloak.timeSkew;
|
||||
};
|
||||
|
||||
keycloak.onAuthRefreshError = function () {
|
||||
event('Auth Refresh Error');
|
||||
};
|
||||
|
||||
keycloak.onAuthLogout = function () {
|
||||
event('Auth Logout');
|
||||
};
|
||||
|
||||
keycloak.onTokenExpired = function () {
|
||||
event('Access token expired.');
|
||||
};
|
||||
|
||||
if (!initOptions) {
|
||||
var initOptions = {
|
||||
onLoad: document.getElementById("onLoad").value,
|
||||
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>
|
|
@ -1,74 +0,0 @@
|
|||
<?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>4.0.0.CR1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>integration-arquillian-test-apps-js-database</artifactId>
|
||||
<name>JAX-RS Database Service Using OAuth Bearer Tokens</name>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jboss</id>
|
||||
<name>jboss repo</name>
|
||||
<url>https://repository.jboss.org/nexus/content/groups/public/</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jaxrs</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.spec.javax.servlet</groupId>
|
||||
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-adapter-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>js-database</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>
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.example.oauth;
|
||||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@Path("customers")
|
||||
public class CustomerService {
|
||||
|
||||
@Context
|
||||
private HttpRequest httpRequest;
|
||||
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
@NoCache
|
||||
public List<String> getCustomers() {
|
||||
// Just to show how to user info from access token in REST endpoint
|
||||
KeycloakSecurityContext securityContext = (KeycloakSecurityContext) httpRequest.getAttribute(KeycloakSecurityContext.class.getName());
|
||||
AccessToken accessToken = securityContext.getToken();
|
||||
System.out.println(String.format("User '%s' with email '%s' made request to CustomerService REST endpoint", accessToken.getPreferredUsername(), accessToken.getEmail()));
|
||||
|
||||
ArrayList<String> rtn = new ArrayList<String>();
|
||||
rtn.add("Bill Burke");
|
||||
rtn.add("Stian Thorgersen");
|
||||
rtn.add("Stan Silvert");
|
||||
rtn.add("Gabriel Cardoso");
|
||||
rtn.add("Viliam Rockai");
|
||||
rtn.add("Marek Posolda");
|
||||
rtn.add("Boleslaw Dawidowicz");
|
||||
return rtn;
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"realm" : "example",
|
||||
"resource" : "js-database",
|
||||
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||
"auth-server-url": "http://localhost:8180/auth",
|
||||
"bearer-only" : true,
|
||||
"ssl-required" : "external"
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
<?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-database</module-name>
|
||||
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</web-resource-collection>
|
||||
<!-- <user-data-constraint>
|
||||
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
|
||||
</user-data-constraint> -->
|
||||
<auth-constraint>
|
||||
<role-name>user</role-name>
|
||||
</auth-constraint>
|
||||
</security-constraint>
|
||||
|
||||
<login-config>
|
||||
<auth-method>KEYCLOAK</auth-method>
|
||||
<realm-name>example</realm-name>
|
||||
</login-config>
|
||||
|
||||
<security-role>
|
||||
<role-name>user</role-name>
|
||||
</security-role>
|
||||
</web-app>
|
|
@ -15,9 +15,7 @@
|
|||
<name>Test apps</name>
|
||||
|
||||
<modules>
|
||||
<module>js-console</module>
|
||||
<module>test-apps-dist</module>
|
||||
<module>js-database</module>
|
||||
<module>photoz</module>
|
||||
<module>hello-world-authz-service</module>
|
||||
<module>servlet-authz</module>
|
||||
|
|
|
@ -19,14 +19,6 @@
|
|||
|
||||
<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>
|
||||
<copy todir="target/test-apps/photoz" overwrite="true">
|
||||
<fileset dir="../photoz">
|
||||
<exclude name="**/target/**"/>
|
||||
|
|
|
@ -1,245 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.testsuite.adapter.page;
|
||||
|
||||
import org.jboss.arquillian.container.test.api.OperateOnDeployment;
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public class JSConsoleTestApp extends AbstractPageWithInjectedUrl {
|
||||
|
||||
public static final String DEPLOYMENT_NAME = "js-console-example";
|
||||
public static final String CLIENT_ID = "integration-arquillian-test-apps-js-console";
|
||||
|
||||
@ArquillianResource
|
||||
@OperateOnDeployment(DEPLOYMENT_NAME)
|
||||
private URL url;
|
||||
|
||||
@Override
|
||||
public URL getInjectedUrl() {
|
||||
//EAP6 URL fix
|
||||
URL fixedUrl = createInjectedURL("js-console");
|
||||
return fixedUrl != null ? fixedUrl : url;
|
||||
}
|
||||
|
||||
@FindBy(xpath = "//button[text() = 'Init']")
|
||||
private WebElement initButton;
|
||||
@FindBy(xpath = "//button[text() = 'Init with both tokens']")
|
||||
private WebElement initWithBothTokens;
|
||||
@FindBy(xpath = "//button[text() = 'Init with refresh token']")
|
||||
private WebElement initWithRefreshToken;
|
||||
@FindBy(xpath = "//button[text() = 'Init with TimeSkew']")
|
||||
private WebElement initWithTimeSkew;
|
||||
@FindBy(xpath = "//button[text() = 'Init with different realm name']")
|
||||
private WebElement initWithDifferentRealmName;
|
||||
@FindBy(xpath = "//button[text() = 'Login']")
|
||||
private WebElement logInButton;
|
||||
@FindBy(xpath = "//button[text() = 'Logout']")
|
||||
private WebElement logOutButton;
|
||||
@FindBy(xpath = "//button[text() = 'Refresh Token']")
|
||||
private WebElement refreshTokenButton;
|
||||
@FindBy(xpath = "//button[contains(text(),'Refresh Token (if <30s')]")
|
||||
private WebElement refreshTokenIfUnder30sButton;
|
||||
@FindBy(xpath = "//button[contains(text(),'Refresh Token (if <5s')]")
|
||||
private WebElement refreshTokenIfUnder5sButton;
|
||||
@FindBy(xpath = "//button[text() = 'Get Profile']")
|
||||
private WebElement getProfileButton;
|
||||
|
||||
@FindBy(xpath = "//button[text() = 'Show Error Response']")
|
||||
private WebElement showErrorButton;
|
||||
@FindBy(xpath = "//button[text() = 'Show Token']")
|
||||
private WebElement showTokenButton;
|
||||
@FindBy(xpath = "//button[text() = 'Show Refresh Token']")
|
||||
private WebElement showRefreshTokenButton;
|
||||
@FindBy(xpath = "//button[text() = 'Show ID Token']")
|
||||
private WebElement showIdTokenButton;
|
||||
@FindBy(xpath = "//button[text() = 'Show Expires']")
|
||||
private WebElement showExpiresButton;
|
||||
@FindBy(xpath = "//button[text() = 'Show Details']")
|
||||
private WebElement showDetailsButton;
|
||||
@FindBy(xpath = "//button[text() = 'Create Bearer Request']")
|
||||
private WebElement createBearerRequest;
|
||||
@FindBy(xpath = "//button[text() = 'Bearer to keycloak']")
|
||||
private WebElement createBearerRequestToKeycloakButton;
|
||||
@FindBy(xpath = "//button[text() = 'Cert request']")
|
||||
private WebElement certRequestButton;
|
||||
@FindBy(xpath = "//button[text() = 'refresh timeSkew']")
|
||||
private WebElement refreshTimeSkewButton;
|
||||
@FindBy(xpath = "//button[text() = 'Create user']")
|
||||
private WebElement createUserButton;
|
||||
@FindBy(xpath = "//button[text() = 'Reentrancy callback']")
|
||||
private WebElement reentrancyCallbackButton;
|
||||
|
||||
@FindBy(id = "timeSkew")
|
||||
private WebElement timeSkewValue;
|
||||
@FindBy(id = "inputField")
|
||||
private WebElement generalInput;
|
||||
@FindBy(id = "inputField2")
|
||||
private WebElement generalInput2;
|
||||
@FindBy(id = "inputField3")
|
||||
private WebElement generalInput3;
|
||||
@FindBy(xpath = "//button[text() = 'timeSkew offset']")
|
||||
private WebElement timeSkewButton;
|
||||
|
||||
@FindBy(id = "flowSelect")
|
||||
private Select flowSelect;
|
||||
@FindBy(id = "responseModeSelect")
|
||||
private Select responseModeSelect;
|
||||
@FindBy(id = "onLoad")
|
||||
private Select onLoad;
|
||||
|
||||
|
||||
@FindBy(id = "output")
|
||||
private WebElement outputArea;
|
||||
|
||||
@FindBy(id = "events")
|
||||
private WebElement eventsArea;
|
||||
|
||||
public void logIn() {
|
||||
logInButton.click();
|
||||
}
|
||||
|
||||
public void logOut() {
|
||||
logOutButton.click();
|
||||
}
|
||||
|
||||
public void refreshToken() {
|
||||
refreshTokenButton.click();
|
||||
}
|
||||
|
||||
public void refreshTokenIfUnder30s() {
|
||||
refreshTokenIfUnder30sButton.click();
|
||||
}
|
||||
|
||||
public void refreshTokenIfUnder5s() {
|
||||
refreshTokenIfUnder5sButton.click();
|
||||
}
|
||||
|
||||
public void getProfile() {
|
||||
getProfileButton.click();
|
||||
}
|
||||
|
||||
public void setFlow(String value) {
|
||||
flowSelect.selectByValue(value);
|
||||
}
|
||||
|
||||
public void setOnLoad(String value) {
|
||||
onLoad.selectByValue(value);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
initButton.click();
|
||||
}
|
||||
|
||||
public void initWithBothTokens() {
|
||||
initWithBothTokens.click();
|
||||
}
|
||||
|
||||
public void initWithRefreshToken() {
|
||||
initWithRefreshToken.click();
|
||||
}
|
||||
|
||||
public void initWithTimeSkew() {
|
||||
initWithTimeSkew.click();
|
||||
}
|
||||
|
||||
public void initWithDifferentRealmName() {
|
||||
initWithDifferentRealmName.click();
|
||||
}
|
||||
|
||||
public void createBearerRequest() {
|
||||
createBearerRequest.click();
|
||||
}
|
||||
|
||||
public void createBearerRequestToKeycloak() {
|
||||
createBearerRequestToKeycloakButton.click();
|
||||
}
|
||||
|
||||
public void setResponseMode(String value) {
|
||||
responseModeSelect.selectByValue(value);
|
||||
}
|
||||
|
||||
public WebElement getOutputElement() {
|
||||
return outputArea;
|
||||
}
|
||||
|
||||
public WebElement getEventsElement() {
|
||||
return eventsArea;
|
||||
}
|
||||
|
||||
public WebElement getInitButtonElement() {
|
||||
return initButton;
|
||||
}
|
||||
|
||||
public void showErrorResponse() {
|
||||
showErrorButton.click();
|
||||
}
|
||||
|
||||
public WebElement getTimeSkewValue() {
|
||||
return timeSkewValue;
|
||||
}
|
||||
|
||||
public void setInput(String value) {
|
||||
generalInput.clear();
|
||||
generalInput.sendKeys(value);
|
||||
}
|
||||
|
||||
public void setInput2(String value) {
|
||||
generalInput2.clear();
|
||||
generalInput2.sendKeys(value);
|
||||
}
|
||||
|
||||
public void setInput3(String value) {
|
||||
generalInput3.clear();
|
||||
generalInput3.sendKeys(value);
|
||||
}
|
||||
|
||||
public void setInput(int value) {
|
||||
setInput(Integer.toString(value));
|
||||
}
|
||||
|
||||
public void setTimeSkew(int value) {
|
||||
setInput(value);
|
||||
timeSkewButton.click();
|
||||
}
|
||||
|
||||
public void refreshTimeSkew() {
|
||||
refreshTimeSkewButton.click();
|
||||
}
|
||||
|
||||
public void createUserRequest() {
|
||||
createUserButton.click();
|
||||
}
|
||||
|
||||
public void sendCertRequest() {
|
||||
certRequestButton.click();
|
||||
}
|
||||
|
||||
public void callReentrancyCallback() {
|
||||
reentrancyCallbackButton.click();
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.testsuite.adapter.page;
|
||||
|
||||
import org.jboss.arquillian.container.test.api.OperateOnDeployment;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public class JSDatabaseTestApp extends AbstractPageWithInjectedUrl {
|
||||
|
||||
public static final String DEPLOYMENT_NAME = "js-database-example";
|
||||
public static final String CLIENT_ID = "integration-arquillian-test-apps-js-database";
|
||||
|
||||
@ArquillianResource
|
||||
@OperateOnDeployment(DEPLOYMENT_NAME)
|
||||
private URL url;
|
||||
|
||||
@Override
|
||||
public URL getInjectedUrl() {
|
||||
//EAP6 URL fix
|
||||
URL fixedUrl = createInjectedURL("js-database");
|
||||
return fixedUrl != null ? fixedUrl : url;
|
||||
}
|
||||
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.keycloak.testsuite.auth.page.login;
|
||||
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
|
||||
|
@ -38,8 +39,14 @@ public class OAuthGrant extends LoginActions {
|
|||
cancelButton.click();
|
||||
}
|
||||
|
||||
|
||||
public boolean isCurrent(WebDriver driver1) {
|
||||
if (driver1 == null) driver1 = driver;
|
||||
return driver1.getPageSource().contains("Do you grant these access privileges");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCurrent() {
|
||||
return driver.getPageSource().contains("Do you grant these access privileges");
|
||||
return isCurrent(null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -286,4 +286,14 @@ public interface TestingResource {
|
|||
@Produces(MediaType.TEXT_PLAIN_UTF_8)
|
||||
String runOnServer(String runOnServer);
|
||||
|
||||
@GET
|
||||
@Path("js/keycloak.js")
|
||||
@Produces(MediaType.TEXT_HTML_UTF_8)
|
||||
String getJavascriptAdapter();
|
||||
|
||||
@GET
|
||||
@Path("/get-javascript-testing-environment")
|
||||
@Produces(MediaType.TEXT_HTML_UTF_8)
|
||||
String getJavascriptTestingEnvironment();
|
||||
|
||||
}
|
||||
|
|
|
@ -14,17 +14,21 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.testsuite.util;
|
||||
|
||||
package org.keycloak.example.oauth;
|
||||
import org.jboss.arquillian.drone.api.annotation.Qualifier;
|
||||
|
||||
import javax.ws.rs.ApplicationPath;
|
||||
import javax.ws.rs.core.Application;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*
|
||||
* @author <a href="mailto:pmensik@redhat.com">Petr Mensik</a>
|
||||
*/
|
||||
@ApplicationPath("/")
|
||||
public class DataApplication extends Application
|
||||
{
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.PARAMETER})
|
||||
@Qualifier
|
||||
public @interface JavascriptBrowser {
|
||||
}
|
|
@ -50,6 +50,7 @@ import org.keycloak.representations.AccessToken;
|
|||
import org.keycloak.representations.IDToken;
|
||||
import org.keycloak.representations.RefreshToken;
|
||||
import org.keycloak.representations.idm.KeysMetadataRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
|
||||
import org.keycloak.util.BasicAuthHelper;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
@ -69,6 +70,8 @@ import java.security.KeyStore;
|
|||
import java.security.PublicKey;
|
||||
import java.util.*;
|
||||
|
||||
import static org.keycloak.testsuite.admin.Users.getPasswordOf;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
|
||||
|
@ -197,6 +200,10 @@ public class OAuthClient {
|
|||
origin = null;
|
||||
}
|
||||
|
||||
public void setDriver(WebDriver driver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
public AuthorizationEndpointResponse doLogin(String username, String password) {
|
||||
openLoginForm();
|
||||
fillLoginForm(username, password);
|
||||
|
@ -204,6 +211,11 @@ public class OAuthClient {
|
|||
return new AuthorizationEndpointResponse(this);
|
||||
}
|
||||
|
||||
public AuthorizationEndpointResponse doLogin(UserRepresentation user) {
|
||||
|
||||
return doLogin(user.getUsername(), getPasswordOf(user));
|
||||
}
|
||||
|
||||
public void fillLoginForm(String username, String password) {
|
||||
WaitUtils.waitForPageToLoad();
|
||||
String src = driver.getPageSource();
|
||||
|
|
|
@ -1,594 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.testsuite.adapter.example;
|
||||
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
|
||||
import org.keycloak.testsuite.adapter.page.JSConsoleTestApp;
|
||||
import org.keycloak.testsuite.adapter.page.JSDatabaseTestApp;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.auth.page.account.Applications;
|
||||
import org.keycloak.testsuite.auth.page.login.OAuthGrant;
|
||||
import org.keycloak.testsuite.console.page.events.Config;
|
||||
import org.keycloak.testsuite.console.page.events.LoginEvents;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.keycloak.testsuite.util.RealmBuilder;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.TimeoutException;
|
||||
import org.openqa.selenium.WebElement;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.keycloak.testsuite.auth.page.AuthRealm.EXAMPLE;
|
||||
import static org.keycloak.testsuite.util.IOUtil.loadRealm;
|
||||
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
|
||||
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
|
||||
import static org.keycloak.testsuite.util.WaitUtils.pause;
|
||||
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
|
||||
|
||||
public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampleAdapterTest {
|
||||
|
||||
@Page
|
||||
private JSConsoleTestApp jsConsoleTestAppPage;
|
||||
|
||||
@Page
|
||||
private Config configPage;
|
||||
|
||||
@Page
|
||||
private LoginEvents loginEventsPage;
|
||||
|
||||
@Page
|
||||
private OAuthGrant oAuthGrantPage;
|
||||
|
||||
@Page
|
||||
private Applications applicationsPage;
|
||||
|
||||
private static int TIME_SKEW_TOLERANCE = 3;
|
||||
|
||||
public static int TOKEN_LIFESPAN_LEEWAY = 3; // seconds
|
||||
|
||||
@Deployment(name = JSConsoleTestApp.DEPLOYMENT_NAME)
|
||||
private static WebArchive jsConsoleTestApp() throws IOException {
|
||||
return exampleDeployment(JSConsoleTestApp.CLIENT_ID);
|
||||
}
|
||||
|
||||
@Deployment(name = JSDatabaseTestApp.DEPLOYMENT_NAME)
|
||||
private static WebArchive jsDbApp() throws IOException {
|
||||
return exampleDeployment(JSDatabaseTestApp.CLIENT_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
|
||||
RealmRepresentation jsConsoleRealm = loadRealm(new File(TEST_APPS_HOME_DIR + "/js-console/example-realm.json"));
|
||||
|
||||
fixClientUrisUsingDeploymentUrl(jsConsoleRealm,
|
||||
JSConsoleTestApp.CLIENT_ID, jsConsoleTestAppPage.buildUri().toASCIIString());
|
||||
|
||||
jsConsoleRealm.setAccessTokenLifespan(30 + TOKEN_LIFESPAN_LEEWAY); // seconds
|
||||
|
||||
testRealms.add(jsConsoleRealm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultPageUriParameters() {
|
||||
super.setDefaultPageUriParameters();
|
||||
testRealmPage.setAuthRealm(EXAMPLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJSConsoleAuth() {
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
assertCurrentUrlStartsWith(jsConsoleTestAppPage);
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getInitButtonElement()).is().present();
|
||||
|
||||
jsConsoleTestAppPage.init();
|
||||
jsConsoleTestAppPage.logIn();
|
||||
testRealmLoginPage.form().login("user", "invalid-password");
|
||||
assertCurrentUrlDoesntStartWith(jsConsoleTestAppPage);
|
||||
|
||||
testRealmLoginPage.form().login("invalid-user", "password");
|
||||
assertCurrentUrlDoesntStartWith(jsConsoleTestAppPage);
|
||||
|
||||
testRealmLoginPage.form().login("user", "password");
|
||||
assertCurrentUrlStartsWith(jsConsoleTestAppPage);
|
||||
jsConsoleTestAppPage.init();
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
|
||||
waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Success");
|
||||
|
||||
jsConsoleTestAppPage.logOut();
|
||||
assertCurrentUrlStartsWith(jsConsoleTestAppPage);
|
||||
waitUntilElement(jsConsoleTestAppPage.getInitButtonElement()).is().present();
|
||||
jsConsoleTestAppPage.init();
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Not Authenticated)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshToken() {
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
assertCurrentUrlStartsWith(jsConsoleTestAppPage);
|
||||
|
||||
jsConsoleTestAppPage.init();
|
||||
jsConsoleTestAppPage.refreshToken();
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Failed to refresh token");
|
||||
|
||||
jsConsoleTestAppPage.logIn();
|
||||
testRealmLoginPage.form().login("user", "password");
|
||||
assertCurrentUrlStartsWith(jsConsoleTestAppPage);
|
||||
jsConsoleTestAppPage.init();
|
||||
waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Success");
|
||||
|
||||
jsConsoleTestAppPage.refreshToken();
|
||||
waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Refresh Success");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshTokenIfUnder30s() {
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
assertCurrentUrlStartsWith(jsConsoleTestAppPage);
|
||||
jsConsoleTestAppPage.init();
|
||||
jsConsoleTestAppPage.refreshToken();
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Failed to refresh token");
|
||||
|
||||
jsConsoleTestAppPage.logIn();
|
||||
testRealmLoginPage.form().login("user", "password");
|
||||
assertCurrentUrlStartsWith(jsConsoleTestAppPage);
|
||||
jsConsoleTestAppPage.init();
|
||||
waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Success");
|
||||
|
||||
jsConsoleTestAppPage.refreshTokenIfUnder30s();
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Token not refreshed, valid for");
|
||||
|
||||
pause((TOKEN_LIFESPAN_LEEWAY + 2) * 1000);
|
||||
|
||||
jsConsoleTestAppPage.refreshTokenIfUnder30s();
|
||||
waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Refresh Success");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProfile() {
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
assertCurrentUrlStartsWith(jsConsoleTestAppPage);
|
||||
|
||||
jsConsoleTestAppPage.init();
|
||||
jsConsoleTestAppPage.getProfile();
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Failed to load profile");
|
||||
|
||||
jsConsoleTestAppPage.logIn();
|
||||
testRealmLoginPage.form().login("user", "password");
|
||||
assertCurrentUrlStartsWith(jsConsoleTestAppPage);
|
||||
jsConsoleTestAppPage.init();
|
||||
waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Success");
|
||||
|
||||
jsConsoleTestAppPage.getProfile();
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("\"username\": \"user\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCertEndpoint() {
|
||||
logInAndInit("standard");
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
|
||||
|
||||
jsConsoleTestAppPage.sendCertRequest();
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Success");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void grantBrowserBasedApp() {
|
||||
testRealmPage.setAuthRealm(EXAMPLE);
|
||||
testRealmLoginPage.setAuthRealm(EXAMPLE);
|
||||
configPage.setConsoleRealm(EXAMPLE);
|
||||
loginEventsPage.setConsoleRealm(EXAMPLE);
|
||||
applicationsPage.setAuthRealm(EXAMPLE);
|
||||
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
driver.manage().deleteAllCookies();
|
||||
|
||||
ClientResource clientResource = ApiUtil.findClientResourceByClientId(testRealmResource(), "js-console");
|
||||
ClientRepresentation client = clientResource.toRepresentation();
|
||||
client.setConsentRequired(true);
|
||||
clientResource.update(client);
|
||||
|
||||
RealmRepresentation realm = testRealmResource().toRepresentation();
|
||||
realm.setEventsEnabled(true);
|
||||
realm.setEnabledEventTypes(Arrays.asList("REVOKE_GRANT", "LOGIN"));
|
||||
testRealmResource().update(realm);
|
||||
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
jsConsoleTestAppPage.init();
|
||||
jsConsoleTestAppPage.logIn();
|
||||
|
||||
testRealmLoginPage.form().login("user", "password");
|
||||
|
||||
assertTrue(oAuthGrantPage.isCurrent());
|
||||
oAuthGrantPage.accept();
|
||||
|
||||
jsConsoleTestAppPage.init();
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
|
||||
|
||||
applicationsPage.navigateTo();
|
||||
applicationsPage.revokeGrantForApplication("js-console");
|
||||
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
jsConsoleTestAppPage.setOnLoad("login-required");
|
||||
jsConsoleTestAppPage.init();
|
||||
|
||||
waitUntilElement(By.tagName("body")).is().visible();
|
||||
assertTrue(oAuthGrantPage.isCurrent());
|
||||
|
||||
loginEventsPage.navigateTo();
|
||||
loginPage.form().login(adminUser);
|
||||
loginEventsPage.table().filter();
|
||||
loginEventsPage.table().filterForm().addEventType("REVOKE_GRANT");
|
||||
loginEventsPage.table().update();
|
||||
|
||||
List<WebElement> resultList = loginEventsPage.table().rows();
|
||||
|
||||
assertEquals(1, resultList.size());
|
||||
|
||||
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' 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();
|
||||
loginEventsPage.table().filterForm().addEventType("LOGIN");
|
||||
loginEventsPage.table().update();
|
||||
resultList = loginEventsPage.table().rows();
|
||||
|
||||
assertEquals(1, resultList.size());
|
||||
|
||||
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' 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() {
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
jsConsoleTestAppPage.setFlow("implicit");
|
||||
jsConsoleTestAppPage.init();
|
||||
|
||||
jsConsoleTestAppPage.logIn();
|
||||
assertResponseError("Implicit flow is disabled for the client");
|
||||
|
||||
setImplicitFlowForClient();
|
||||
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
jsConsoleTestAppPage.init();
|
||||
jsConsoleTestAppPage.logIn();
|
||||
assertResponseError("Standard flow is disabled for the client");
|
||||
|
||||
logInAndInit("implicit");
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void implicitFlowQueryTest() {
|
||||
setImplicitFlowForClient();
|
||||
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
jsConsoleTestAppPage.setFlow("implicit");
|
||||
jsConsoleTestAppPage.setResponseMode("query");
|
||||
jsConsoleTestAppPage.init();
|
||||
jsConsoleTestAppPage.logIn();
|
||||
assertResponseError("Response_mode 'query' not allowed");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void implicitFlowRefreshTokenTest() {
|
||||
setImplicitFlowForClient();
|
||||
|
||||
logInAndInit("implicit");
|
||||
|
||||
jsConsoleTestAppPage.refreshToken();
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Failed to refresh token");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void implicitFlowOnTokenExpireTest() {
|
||||
RealmRepresentation realm = testRealmResource().toRepresentation();
|
||||
realm.setAccessTokenLifespanForImplicitFlow(5);
|
||||
testRealmResource().update(realm);
|
||||
|
||||
setImplicitFlowForClient();
|
||||
|
||||
logInAndInit("implicit");
|
||||
|
||||
pause(6000);
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Access token expired");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void implicitFlowCertEndpoint() {
|
||||
setImplicitFlowForClient();
|
||||
logInAndInit("implicit");
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
|
||||
|
||||
jsConsoleTestAppPage.sendCertRequest();
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Success");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBearerRequest() {
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
jsConsoleTestAppPage.init();
|
||||
jsConsoleTestAppPage.createBearerRequest();
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Unauthorized");
|
||||
|
||||
logInAndInit("standard", "unauthorized");
|
||||
jsConsoleTestAppPage.createBearerRequest();
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Forbidden");
|
||||
|
||||
jsConsoleTestAppPage.logOut();
|
||||
logInAndInit("standard");
|
||||
jsConsoleTestAppPage.createBearerRequest();
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("[\"Bill Burke\",\"Stian Thorgersen\",\"Stan Silvert\",\"Gabriel Cardoso\",\"Viliam Rockai\",\"Marek Posolda\",\"Boleslaw Dawidowicz\"]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginRequiredAction() {
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
jsConsoleTestAppPage.setOnLoad("login-required");
|
||||
jsConsoleTestAppPage.init();
|
||||
|
||||
assertCurrentUrlStartsWith(testRealmLoginPage);
|
||||
testRealmLoginPage.form().login("user", "password");
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getInitButtonElement()).is().present();
|
||||
jsConsoleTestAppPage.init();
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateToken() {
|
||||
logInAndInit("standard");
|
||||
|
||||
jsConsoleTestAppPage.setTimeSkew(-33);
|
||||
setTimeOffset(33);
|
||||
|
||||
jsConsoleTestAppPage.refreshTokenIfUnder5s();
|
||||
|
||||
jsConsoleTestAppPage.setTimeSkew(-34);
|
||||
setTimeOffset(67);
|
||||
|
||||
jsConsoleTestAppPage.refreshTokenIfUnder5s();
|
||||
jsConsoleTestAppPage.createBearerRequestToKeycloak();
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Success");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeSkewTest() {
|
||||
logInAndInit("standard");
|
||||
|
||||
jsConsoleTestAppPage.refreshTimeSkew();
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getTimeSkewValue()).text().not().contains("undefined");
|
||||
|
||||
int timeSkew = Integer.parseInt(jsConsoleTestAppPage.getTimeSkewValue().getText());
|
||||
assertTrue("TimeSkew was: " + timeSkew + ", but should be ~0", timeSkew >= 0 - TIME_SKEW_TOLERANCE);
|
||||
assertTrue("TimeSkew was: " + timeSkew + ", but should be ~0", timeSkew <= TIME_SKEW_TOLERANCE);
|
||||
|
||||
setTimeOffset(40);
|
||||
jsConsoleTestAppPage.refreshToken();
|
||||
jsConsoleTestAppPage.refreshTimeSkew();
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getTimeSkewValue()).text().not().contains("undefined");
|
||||
|
||||
timeSkew = Integer.parseInt(jsConsoleTestAppPage.getTimeSkewValue().getText());
|
||||
assertTrue("TimeSkew was: " + timeSkew + ", but should be ~-40", timeSkew + 40 >= 0 - TIME_SKEW_TOLERANCE);
|
||||
assertTrue("TimeSkew was: " + timeSkew + ", but should be ~-40", timeSkew + 40 <= TIME_SKEW_TOLERANCE);
|
||||
}
|
||||
|
||||
// KEYCLOAK-4179
|
||||
@Test
|
||||
public void testOneSecondTimeSkewTokenUpdate() {
|
||||
setTimeOffset(1);
|
||||
|
||||
logInAndInit("standard");
|
||||
|
||||
jsConsoleTestAppPage.refreshToken();
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Refresh Success");
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().not().contains("Failed to refresh token");
|
||||
|
||||
try {
|
||||
// The events element should contain "Auth logout" but we need to wait for it
|
||||
// and text().not().contains() doesn't wait. With KEYCLOAK-4179 it took some time for "Auth Logout" to be present
|
||||
waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Logout");
|
||||
|
||||
throw new RuntimeException("The events element shouldn't contain \"Auth Logout\" text");
|
||||
} catch (TimeoutException e) {
|
||||
// OK
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationHeaderInResponse() {
|
||||
logInAndInit("standard");
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
|
||||
|
||||
jsConsoleTestAppPage.createUserRequest();
|
||||
|
||||
UsersResource userResource = testRealmResource().users();
|
||||
|
||||
List<UserRepresentation> users = userResource.search("mhajas", 0, 1);
|
||||
assertEquals("There should be created user mhajas", 1, users.size());
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text()
|
||||
.contains("location: " + authServerContextRootPage.toString() + "/auth/admin/realms/" + EXAMPLE + "/users/" + users.get(0).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void spaceInRealmNameTest() {
|
||||
String SPACE_REALM_NAME = "Example realm";
|
||||
adminClient.realm(EXAMPLE).update(RealmBuilder.edit(adminClient.realm(EXAMPLE).toRepresentation()).name(SPACE_REALM_NAME).build());
|
||||
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
jsConsoleTestAppPage.setInput(SPACE_REALM_NAME);
|
||||
jsConsoleTestAppPage.initWithDifferentRealmName();
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Not Authenticated)");
|
||||
jsConsoleTestAppPage.logIn();
|
||||
waitUntilElement(By.xpath("//body")).is().present();
|
||||
testRealmLoginPage.form().login("user", "password");
|
||||
jsConsoleTestAppPage.setInput(SPACE_REALM_NAME);
|
||||
jsConsoleTestAppPage.initWithDifferentRealmName();
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
|
||||
waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Success");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initializeWithTokenTest() {
|
||||
oauth.realm(EXAMPLE);
|
||||
oauth.clientId("js-console");
|
||||
oauth.redirectUri(appServerContextRootPage + "/js-console");
|
||||
oauth.doLogin("user", "password");
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
|
||||
String token = tokenResponse.getAccessToken();
|
||||
String refreshToken = tokenResponse.getRefreshToken();
|
||||
|
||||
//String refreshToken = oauth.doRefreshTokenRequest(token, "password").getRefreshToken();
|
||||
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
jsConsoleTestAppPage.setInput(token);
|
||||
jsConsoleTestAppPage.setInput2(refreshToken);
|
||||
|
||||
jsConsoleTestAppPage.initWithBothTokens();
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
|
||||
|
||||
jsConsoleTestAppPage.refreshToken();
|
||||
waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Refresh Success");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initializeWithTimeSkew() {
|
||||
setTimeOffset(600);
|
||||
oauth.realm(EXAMPLE);
|
||||
oauth.clientId("js-console");
|
||||
oauth.redirectUri(appServerContextRootPage + "/js-console");
|
||||
oauth.doLogin("user", "password");
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
|
||||
String token = tokenResponse.getAccessToken();
|
||||
String refreshToken = tokenResponse.getRefreshToken();
|
||||
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
jsConsoleTestAppPage.setInput(token);
|
||||
jsConsoleTestAppPage.setInput2(refreshToken);
|
||||
jsConsoleTestAppPage.setInput3("-600");
|
||||
|
||||
jsConsoleTestAppPage.initWithTimeSkew();
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
|
||||
|
||||
jsConsoleTestAppPage.refreshToken();
|
||||
waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Refresh Success");
|
||||
|
||||
setTimeOffset(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
// KEYCLOAK-4503
|
||||
public void initializeWithRefreshToken() {
|
||||
oauth.realm(EXAMPLE);
|
||||
oauth.clientId("js-console");
|
||||
oauth.redirectUri("http://localhost:8280/js-console");
|
||||
oauth.doLogin("user", "password");
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
String token = oauth.doAccessTokenRequest(code, "password").getAccessToken();
|
||||
String refreshToken = oauth.doRefreshTokenRequest(token, "password").getRefreshToken();
|
||||
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
jsConsoleTestAppPage.setInput2(refreshToken);
|
||||
|
||||
jsConsoleTestAppPage.initWithRefreshToken();
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Not Authenticated)");
|
||||
waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().not().contains("Auth Success");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reentrancyCallbackTest() {
|
||||
logInAndInit("standard");
|
||||
|
||||
jsConsoleTestAppPage.callReentrancyCallback();
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("First callback");
|
||||
waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Second callback");
|
||||
|
||||
waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().not().contains("Auth Logout");
|
||||
}
|
||||
|
||||
private void setImplicitFlowForClient() {
|
||||
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, String user) {
|
||||
jsConsoleTestAppPage.navigateTo();
|
||||
jsConsoleTestAppPage.setFlow(flow);
|
||||
jsConsoleTestAppPage.init();
|
||||
jsConsoleTestAppPage.logIn();
|
||||
waitUntilElement(By.xpath("//body")).is().present();
|
||||
testRealmLoginPage.form().login(user, "password");
|
||||
jsConsoleTestAppPage.setFlow(flow);
|
||||
jsConsoleTestAppPage.init();
|
||||
}
|
||||
|
||||
private void logInAndInit(String flow) {
|
||||
logInAndInit(flow, "user");
|
||||
}
|
||||
|
||||
private void assertResponseError(String errorDescription) {
|
||||
jsConsoleTestAppPage.showErrorResponse();
|
||||
assertThat(jsConsoleTestAppPage.getOutputElement().getText(), containsString(errorDescription));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
package org.keycloak.testsuite.adapter.javascript;
|
||||
|
||||
import org.jboss.arquillian.drone.api.annotation.Drone;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Before;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.AbstractAuthTest;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.auth.page.login.OIDCLogin;
|
||||
import org.keycloak.testsuite.util.ClientBuilder;
|
||||
import org.keycloak.testsuite.util.JavascriptBrowser;
|
||||
import org.keycloak.testsuite.util.RealmBuilder;
|
||||
import org.keycloak.testsuite.util.RolesBuilder;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.JavascriptExecutor;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.collection.IsMapContaining.hasEntry;
|
||||
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
|
||||
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
public abstract class AbstractJavascriptTest extends AbstractAuthTest {
|
||||
|
||||
@FunctionalInterface
|
||||
interface QuadFunction<T, U, V, W> {
|
||||
void apply(T a, U b, V c, W d);
|
||||
}
|
||||
|
||||
public static final String CLIENT_ID = "js-console";
|
||||
public static final String REALM_NAME = "test";
|
||||
public static final String SPACE_REALM_NAME = "Example realm";
|
||||
public static final String JAVASCRIPT_URL = "/auth/realms/" + REALM_NAME + "/testing/javascript";
|
||||
public static final String JAVASCRIPT_ENCODED_SPACE_URL = "/auth/realms/Example%20realm/testing/javascript";
|
||||
public static final String JAVASCRIPT_SPACE_URL = "/auth/realms/Example realm/testing/javascript";
|
||||
public static int TOKEN_LIFESPAN_LEEWAY = 3; // seconds
|
||||
|
||||
|
||||
@Drone
|
||||
@JavascriptBrowser
|
||||
protected WebDriver jsDriver;
|
||||
|
||||
protected JavascriptExecutor jsExecutor;
|
||||
|
||||
@Page
|
||||
@JavascriptBrowser
|
||||
protected OIDCLogin testRealmLoginPage;
|
||||
|
||||
@FindBy(id = "output")
|
||||
@JavascriptBrowser
|
||||
protected WebElement outputArea;
|
||||
|
||||
@FindBy(id = "events")
|
||||
@JavascriptBrowser
|
||||
protected WebElement eventsArea;
|
||||
|
||||
public static final UserRepresentation testUser;
|
||||
public static final UserRepresentation unauthorizedUser;
|
||||
|
||||
static {
|
||||
testUser = UserBuilder.create().username("test-user@localhost").password("password").build();
|
||||
unauthorizedUser = UserBuilder.create().username("unauthorized").password("password").build();
|
||||
}
|
||||
|
||||
|
||||
@Before
|
||||
public void beforeJavascriptTest() {
|
||||
jsExecutor = (JavascriptExecutor) jsDriver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||
testRealms.add(updateRealm(RealmBuilder.create()
|
||||
.name(REALM_NAME)
|
||||
.roles(
|
||||
RolesBuilder.create()
|
||||
.realmRole(new RoleRepresentation("user", "", false))
|
||||
.realmRole(new RoleRepresentation("admin", "", false))
|
||||
)
|
||||
.user(
|
||||
UserBuilder.create()
|
||||
.username("test-user@localhost").password("password")
|
||||
.addRoles("user")
|
||||
.role("realm-management", "view-realm")
|
||||
.role("realm-management", "manage-users")
|
||||
.role("account", "view-profile")
|
||||
.role("account", "manage-account")
|
||||
)
|
||||
.user(
|
||||
UserBuilder.create()
|
||||
.username("unauthorized").password("password")
|
||||
)
|
||||
.client(
|
||||
ClientBuilder.create()
|
||||
.clientId(CLIENT_ID)
|
||||
.redirectUris(JAVASCRIPT_URL + "/*", JAVASCRIPT_ENCODED_SPACE_URL + "/*")
|
||||
.publicClient()
|
||||
)
|
||||
.accessTokenLifespan(30 + TOKEN_LIFESPAN_LEEWAY)
|
||||
.testEventListener()
|
||||
));
|
||||
}
|
||||
|
||||
protected <T> JavascriptStateValidator buildFunction(QuadFunction<T, WebDriver, Object, WebElement> f, T x) {
|
||||
return (y,z,w) -> f.apply(x, y, z, w);
|
||||
}
|
||||
|
||||
protected void setImplicitFlowForClient() {
|
||||
ClientResource clientResource = ApiUtil.findClientResourceByClientId(adminClient.realms().realm(REALM_NAME), CLIENT_ID);
|
||||
ClientRepresentation client = clientResource.toRepresentation();
|
||||
client.setImplicitFlowEnabled(true);
|
||||
client.setStandardFlowEnabled(false);
|
||||
clientResource.update(client);
|
||||
}
|
||||
|
||||
protected void setStandardFlowForClient() {
|
||||
ClientResource clientResource = ApiUtil.findClientResourceByClientId(adminClient.realms().realm(REALM_NAME), CLIENT_ID);
|
||||
ClientRepresentation client = clientResource.toRepresentation();
|
||||
client.setImplicitFlowEnabled(false);
|
||||
client.setStandardFlowEnabled(true);
|
||||
clientResource.update(client);
|
||||
}
|
||||
|
||||
protected abstract RealmRepresentation updateRealm(RealmBuilder builder);
|
||||
|
||||
protected void assertSuccessfullyLoggedIn(WebDriver driver1, Object output, WebElement events) {
|
||||
buildFunction(this::assertOutputContains, "Init Success (Authenticated)").validate(driver1, output, events);
|
||||
waitUntilElement(events).text().contains("Auth Success");
|
||||
}
|
||||
|
||||
protected void assertInitNotAuth(WebDriver driver1, Object output, WebElement events) {
|
||||
buildFunction(this::assertOutputContains, "Init Success (Not Authenticated)").validate(driver1, output, events);
|
||||
}
|
||||
|
||||
protected void assertOnLoginPage(WebDriver driver1, Object output, WebElement events) {
|
||||
waitUntilElement(By.tagName("body")).is().present();
|
||||
assertCurrentUrlStartsWith(testRealmLoginPage, driver1);
|
||||
}
|
||||
|
||||
public void assertOutputWebElementContains(String value, WebDriver driver1, Object output, WebElement events) {
|
||||
waitUntilElement((WebElement) output).text().contains(value);
|
||||
}
|
||||
|
||||
public void assertOutputContains(String value, WebDriver driver1, Object output, WebElement events) {
|
||||
if (output instanceof WebElement) {
|
||||
waitUntilElement((WebElement) output).text().contains(value);
|
||||
} else {
|
||||
Assert.assertThat((String) output, containsString(value));
|
||||
}
|
||||
}
|
||||
|
||||
public void assertEventsWebElementContains(String value, WebDriver driver1, Object output, WebElement events) {
|
||||
waitUntilElement(events).text().contains(value);
|
||||
}
|
||||
|
||||
public ResponseValidator assertResponseStatus(long status) {
|
||||
return output -> Assert.assertThat(output, hasEntry("status", status));
|
||||
}
|
||||
|
||||
public JavascriptStateValidator assertOutputContains(String text) {
|
||||
return buildFunction(this::assertOutputContains, text);
|
||||
}
|
||||
|
||||
public JavascriptStateValidator assertEventsContains(String text) {
|
||||
return buildFunction(this::assertEventsWebElementContains, text);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package org.keycloak.testsuite.adapter.javascript;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
public class JSObjectBuilder {
|
||||
|
||||
private Map<String, Object> arguments;
|
||||
|
||||
|
||||
public static JSObjectBuilder create() {
|
||||
return new JSObjectBuilder();
|
||||
}
|
||||
|
||||
private JSObjectBuilder() {
|
||||
arguments = new HashMap<>();
|
||||
}
|
||||
|
||||
public JSObjectBuilder defaultSettings() {
|
||||
standardFlow();
|
||||
fragmentResponse();
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSObjectBuilder standardFlow() {
|
||||
arguments.put("flow", "standard");
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSObjectBuilder implicitFlow() {
|
||||
arguments.put("flow", "implicit");
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSObjectBuilder fragmentResponse() {
|
||||
arguments.put("responseMode", "fragment");
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSObjectBuilder queryResponse() {
|
||||
arguments.put("responseMode", "query");
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSObjectBuilder checkSSOOnLoad() {
|
||||
arguments.put("onLoad", "check-sso");
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSObjectBuilder loginRequiredOnLoad() {
|
||||
arguments.put("onLoad", "login-required");
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSObjectBuilder add(String key, Object value) {
|
||||
arguments.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isLoginRequired() {
|
||||
return arguments.get("onLoad").equals("login-required");
|
||||
}
|
||||
|
||||
|
||||
public String build() {
|
||||
StringBuilder argument = new StringBuilder("{");
|
||||
String comma = "";
|
||||
for (Map.Entry<String, Object> option : arguments.entrySet()) {
|
||||
argument.append(comma)
|
||||
.append(option.getKey())
|
||||
.append(" : ");
|
||||
|
||||
if (!(option.getValue() instanceof Integer)) argument.append("\"");
|
||||
|
||||
argument.append(option.getValue());
|
||||
|
||||
if (!(option.getValue() instanceof Integer)) argument.append("\"");
|
||||
comma = ",";
|
||||
}
|
||||
|
||||
argument.append("}");
|
||||
|
||||
return argument.toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,517 @@
|
|||
package org.keycloak.testsuite.adapter.javascript;
|
||||
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
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.testsuite.Assert;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.auth.page.account.Applications;
|
||||
import org.keycloak.testsuite.auth.page.login.OAuthGrant;
|
||||
import org.keycloak.testsuite.util.JavascriptBrowser;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.keycloak.testsuite.util.RealmBuilder;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
import org.openqa.selenium.TimeoutException;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebDriverException;
|
||||
import org.openqa.selenium.WebElement;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.lang.Math.toIntExact;
|
||||
import static org.hamcrest.CoreMatchers.both;
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.hamcrest.collection.IsMapContaining.hasEntry;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
|
||||
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
|
||||
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
public class JavascriptAdapterTest extends AbstractJavascriptTest {
|
||||
|
||||
private String testAppUrl;
|
||||
private JavascriptTestExecutor testExecutor;
|
||||
private static int TIME_SKEW_TOLERANCE = 3;
|
||||
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(this);
|
||||
|
||||
@Page
|
||||
@JavascriptBrowser
|
||||
private Applications applicationsPage;
|
||||
|
||||
@Page
|
||||
@JavascriptBrowser
|
||||
private OAuthGrant oAuthGrantPage;
|
||||
|
||||
@Override
|
||||
protected RealmRepresentation updateRealm(RealmBuilder builder) {
|
||||
return builder.accessTokenLifespan(30 + TOKEN_LIFESPAN_LEEWAY).build();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setDefaultEnvironment() {
|
||||
testAppUrl = authServerContextRootPage + JAVASCRIPT_URL + "/index.html";
|
||||
|
||||
testRealmLoginPage.setAuthRealm(REALM_NAME);
|
||||
oAuthGrantPage.setAuthRealm(REALM_NAME);
|
||||
applicationsPage.setAuthRealm(REALM_NAME);
|
||||
|
||||
jsDriver.navigate().to(testAppUrl);
|
||||
testExecutor = JavascriptTestExecutor.create(jsDriver, testRealmLoginPage);
|
||||
|
||||
waitUntilElement(outputArea).is().present();
|
||||
assertCurrentUrlStartsWith(testAppUrl, jsDriver);
|
||||
|
||||
jsDriver.manage().deleteAllCookies();
|
||||
}
|
||||
|
||||
private JSObjectBuilder defaultArguments() {
|
||||
return JSObjectBuilder.create().defaultSettings();
|
||||
}
|
||||
|
||||
private void assertOnTestAppUrl(WebDriver jsDriver, Object output, WebElement events) {
|
||||
assertCurrentUrlStartsWith(testAppUrl, jsDriver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJSConsoleAuth() {
|
||||
testExecutor.init(defaultArguments(), this::assertInitNotAuth)
|
||||
.login(this::assertOnLoginPage)
|
||||
.loginForm( UserBuilder.create().username("user").password("invalid-password").build(),
|
||||
(driver1, output, events) -> assertCurrentUrlDoesntStartWith(testAppUrl, driver1))
|
||||
.loginForm(UserBuilder.create().username("invalid-user").password("password").build(),
|
||||
(driver1, output, events) -> assertCurrentUrlDoesntStartWith(testAppUrl, driver1))
|
||||
.loginForm(testUser, this::assertOnTestAppUrl)
|
||||
.init(defaultArguments(), this::assertSuccessfullyLoggedIn)
|
||||
.logout(this::assertOnTestAppUrl)
|
||||
.init(defaultArguments(), this::assertInitNotAuth);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshToken() {
|
||||
testExecutor.init(defaultArguments(), this::assertInitNotAuth)
|
||||
.refreshToken(9999, assertOutputContains("Failed to refresh token"))
|
||||
.login(this::assertOnLoginPage)
|
||||
.loginForm(testUser, this::assertOnTestAppUrl)
|
||||
.init(defaultArguments(), this::assertSuccessfullyLoggedIn)
|
||||
.refreshToken(9999, assertEventsContains("Auth Refresh Success"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshTokenIfUnder30s() {
|
||||
testExecutor.init(defaultArguments(), this::assertInitNotAuth)
|
||||
.login(this::assertOnLoginPage)
|
||||
.loginForm(testUser, this::assertOnTestAppUrl)
|
||||
.init(defaultArguments(), this::assertSuccessfullyLoggedIn)
|
||||
.refreshToken(30, assertOutputContains("Token not refreshed, valid for"))
|
||||
.addTimeSkew(-5) // instead of wait move in time
|
||||
.refreshToken(30, assertEventsContains("Auth Refresh Success"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProfile() {
|
||||
testExecutor.init(defaultArguments(), this::assertInitNotAuth)
|
||||
.getProfile(assertOutputContains("Failed to load profile"))
|
||||
.login(this::assertOnLoginPage)
|
||||
.loginForm(testUser, this::assertOnTestAppUrl)
|
||||
.init(defaultArguments(), this::assertSuccessfullyLoggedIn)
|
||||
.getProfile((driver1, output, events) -> Assert.assertThat((Map<String, String>) output, hasEntry("username", testUser.getUsername())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void grantBrowserBasedApp() {
|
||||
ClientResource clientResource = ApiUtil.findClientResourceByClientId(adminClient.realm(REALM_NAME), CLIENT_ID);
|
||||
ClientRepresentation client = clientResource.toRepresentation();
|
||||
client.setConsentRequired(true);
|
||||
clientResource.update(client);
|
||||
|
||||
testExecutor.init(defaultArguments(), this::assertInitNotAuth)
|
||||
.login(this::assertOnLoginPage)
|
||||
.loginForm(testUser, (driver1, output, events) -> assertTrue(oAuthGrantPage.isCurrent(driver1))
|
||||
// I am not sure why is this driver1 argument to isCurrent necessary, but I got exception without it
|
||||
);
|
||||
|
||||
oAuthGrantPage.accept();
|
||||
|
||||
EventRepresentation loginEvent = events.expectLogin()
|
||||
.client(CLIENT_ID)
|
||||
.detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED)
|
||||
.detail(Details.REDIRECT_URI, testAppUrl)
|
||||
.detail(Details.USERNAME, testUser.getUsername())
|
||||
.assertEvent();
|
||||
String codeId = loginEvent.getDetails().get(Details.CODE_ID);
|
||||
|
||||
testExecutor.init(defaultArguments(), this::assertSuccessfullyLoggedIn);
|
||||
|
||||
applicationsPage.navigateTo();
|
||||
events.expectCodeToToken(codeId, loginEvent.getSessionId()).client(CLIENT_ID).assertEvent();
|
||||
|
||||
applicationsPage.revokeGrantForApplication(CLIENT_ID);
|
||||
events.expect(EventType.REVOKE_GRANT)
|
||||
.client("account")
|
||||
.detail(Details.REVOKED_CLIENT, CLIENT_ID)
|
||||
.assertEvent();
|
||||
|
||||
jsDriver.navigate().to(testAppUrl);
|
||||
testExecutor.configure() // need to configure because we refreshed page
|
||||
.init(defaultArguments(), this::assertInitNotAuth)
|
||||
.login((driver1, output, events) -> assertTrue(oAuthGrantPage.isCurrent(driver1)));
|
||||
|
||||
// Clean
|
||||
client.setConsentRequired(false);
|
||||
clientResource.update(client);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void implicitFlowTest() {
|
||||
testExecutor.init(defaultArguments().implicitFlow(), this::assertInitNotAuth)
|
||||
.login(this::assertOnTestAppUrl)
|
||||
.errorResponse(assertOutputContains("Implicit flow is disabled for the client"));
|
||||
|
||||
setImplicitFlowForClient();
|
||||
jsDriver.navigate().to(testAppUrl);
|
||||
|
||||
testExecutor.init(defaultArguments(), this::assertInitNotAuth)
|
||||
.login(this::assertOnTestAppUrl)
|
||||
.errorResponse(assertOutputContains("Standard flow is disabled for the client"));
|
||||
jsDriver.navigate().to(testAppUrl);
|
||||
|
||||
testExecutor.init(defaultArguments().implicitFlow(), this::assertInitNotAuth)
|
||||
.login(this::assertOnLoginPage)
|
||||
.loginForm(testUser, this::assertOnTestAppUrl)
|
||||
.init(defaultArguments().implicitFlow(), this::assertSuccessfullyLoggedIn);
|
||||
|
||||
setStandardFlowForClient();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCertEndpoint() {
|
||||
testExecutor.logInAndInit(defaultArguments(), testUser, this::assertSuccessfullyLoggedIn)
|
||||
.sendXMLHttpRequest(XMLHttpRequest.create()
|
||||
.url(authServerContextRootPage + "/auth/realms/" + REALM_NAME + "/protocol/openid-connect/certs")
|
||||
.method("GET")
|
||||
.addHeader("Accept", "application/json")
|
||||
.addHeader("Authorization", "Bearer ' + keycloak.token + '"),
|
||||
assertResponseStatus(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void implicitFlowQueryTest() {
|
||||
setImplicitFlowForClient();
|
||||
testExecutor.init(defaultArguments().implicitFlow().queryResponse(), this::assertInitNotAuth)
|
||||
.login(((driver1, output, events) ->
|
||||
Assert.assertThat(driver1.getCurrentUrl(), containsString("Response_mode+%27query%27+not+allowed"))));
|
||||
setStandardFlowForClient();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void implicitFlowRefreshTokenTest() {
|
||||
setImplicitFlowForClient();
|
||||
testExecutor.logInAndInit(defaultArguments().implicitFlow(), testUser, this::assertSuccessfullyLoggedIn)
|
||||
.refreshToken(9999, assertOutputContains("Failed to refresh token"));
|
||||
setStandardFlowForClient();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void implicitFlowOnTokenExpireTest() {
|
||||
RealmRepresentation realm = adminClient.realms().realm(REALM_NAME).toRepresentation();
|
||||
Integer storeAccesTokenLifespan = realm.getAccessTokenLifespanForImplicitFlow();
|
||||
realm.setAccessTokenLifespanForImplicitFlow(5);
|
||||
adminClient.realms().realm(REALM_NAME).update(realm);
|
||||
|
||||
setImplicitFlowForClient();
|
||||
testExecutor.logInAndInit(defaultArguments().implicitFlow(), testUser, this::assertSuccessfullyLoggedIn)
|
||||
.addTimeSkew(-5); // Move in time instead of wait
|
||||
|
||||
waitUntilElement(eventsArea).text().contains("Access token expired");
|
||||
|
||||
// Get to origin state
|
||||
realm.setAccessTokenLifespanForImplicitFlow(storeAccesTokenLifespan);
|
||||
adminClient.realms().realm(REALM_NAME).update(realm);
|
||||
setStandardFlowForClient();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void implicitFlowCertEndpoint() {
|
||||
setImplicitFlowForClient();
|
||||
testExecutor.logInAndInit(defaultArguments().implicitFlow(), testUser, this::assertSuccessfullyLoggedIn)
|
||||
.sendXMLHttpRequest(XMLHttpRequest.create()
|
||||
.url(authServerContextRootPage + "/auth/realms/" + REALM_NAME + "/protocol/openid-connect/certs")
|
||||
.method("GET")
|
||||
.addHeader("Accept", "application/json")
|
||||
.addHeader("Authorization", "Bearer ' + keycloak.token + '"),
|
||||
assertResponseStatus(200));
|
||||
setStandardFlowForClient();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBearerRequest() {
|
||||
XMLHttpRequest request = XMLHttpRequest.create()
|
||||
.url(authServerContextRootPage + "/auth/admin/realms/" + REALM_NAME + "/roles")
|
||||
.method("GET")
|
||||
.addHeader("Accept", "application/json")
|
||||
.addHeader("Authorization", "Bearer ' + keycloak.token + '");
|
||||
|
||||
testExecutor.init(defaultArguments())
|
||||
.sendXMLHttpRequest(request, assertResponseStatus(401))
|
||||
.refresh();
|
||||
if (!"phantomjs".equals(System.getProperty("js.browser"))) {
|
||||
// I have no idea why, but this request doesn't work with phantomjs, it works in chrome
|
||||
testExecutor.logInAndInit(defaultArguments(), unauthorizedUser, this::assertSuccessfullyLoggedIn)
|
||||
.sendXMLHttpRequest(request, output -> Assert.assertThat(output, hasEntry("status", 403L)))
|
||||
.logout(this::assertOnTestAppUrl)
|
||||
.refresh();
|
||||
}
|
||||
testExecutor.logInAndInit(defaultArguments(), testUser, this::assertSuccessfullyLoggedIn)
|
||||
.sendXMLHttpRequest(request, assertResponseStatus(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginRequiredAction() {
|
||||
try {
|
||||
testExecutor.init(defaultArguments().loginRequiredOnLoad());
|
||||
// This throws exception because when JavascriptExecutor waits for AsyncScript to finish
|
||||
// it is redirected to login page and executor gets no response
|
||||
|
||||
throw new RuntimeException("Probably the login-required OnLoad mode doesn't work, because testExecutor should fail with error that page was redirected.");
|
||||
} catch (WebDriverException ex) {
|
||||
// should happen
|
||||
}
|
||||
|
||||
testExecutor.loginForm(testUser, this::assertOnTestAppUrl)
|
||||
.init(defaultArguments(), this::assertSuccessfullyLoggedIn);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateToken() {
|
||||
XMLHttpRequest request = XMLHttpRequest.create()
|
||||
.url(authServerContextRootPage + "/auth/admin/realms/" + REALM_NAME + "/roles")
|
||||
.method("GET")
|
||||
.addHeader("Accept", "application/json")
|
||||
.addHeader("Authorization", "Bearer ' + keycloak.token + '");
|
||||
|
||||
testExecutor.logInAndInit(defaultArguments(), testUser, this::assertSuccessfullyLoggedIn)
|
||||
.addTimeSkew(-33);
|
||||
setTimeOffset(33);
|
||||
testExecutor.refreshToken(5, assertEventsContains("Auth Refresh Success"));
|
||||
|
||||
setTimeOffset(67);
|
||||
testExecutor.addTimeSkew(-34)
|
||||
.sendXMLHttpRequest(request, assertResponseStatus(401))
|
||||
.refreshToken(5, assertEventsContains("Auth Refresh Success"))
|
||||
.sendXMLHttpRequest(request, assertResponseStatus(200));
|
||||
|
||||
setTimeOffset(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeSkewTest() {
|
||||
testExecutor.logInAndInit(defaultArguments(), testUser, this::assertSuccessfullyLoggedIn)
|
||||
.checkTimeSkew((driver1, output, events) -> assertThat(toIntExact((long) output),
|
||||
is(
|
||||
both(greaterThan(0 - TIME_SKEW_TOLERANCE))
|
||||
.and(lessThan(TIME_SKEW_TOLERANCE))
|
||||
)
|
||||
));
|
||||
|
||||
setTimeOffset(40);
|
||||
|
||||
testExecutor.refreshToken(9999, assertEventsContains("Auth Refresh Success"))
|
||||
.checkTimeSkew((driver1, output, events) -> assertThat(toIntExact((long) output),
|
||||
is(
|
||||
both(greaterThan(-40 - TIME_SKEW_TOLERANCE))
|
||||
.and(lessThan(-40 + TIME_SKEW_TOLERANCE))
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneSecondTimeSkewTokenUpdate() {
|
||||
setTimeOffset(1);
|
||||
|
||||
testExecutor.logInAndInit(defaultArguments(), testUser, this::assertSuccessfullyLoggedIn)
|
||||
.refreshToken(9999, assertEventsContains("Auth Refresh Success"));
|
||||
|
||||
try {
|
||||
// The events element should contain "Auth logout" but we need to wait for it
|
||||
// and text().not().contains() doesn't wait. With KEYCLOAK-4179 it took some time for "Auth Logout" to be present
|
||||
waitUntilElement(eventsArea).text().contains("Auth Logout");
|
||||
|
||||
throw new RuntimeException("The events element shouldn't contain \"Auth Logout\" text");
|
||||
} catch (TimeoutException e) {
|
||||
// OK
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationHeaderInResponse() {
|
||||
XMLHttpRequest request = XMLHttpRequest.create()
|
||||
.url(authServerContextRootPage + "/auth/admin/realms/" + REALM_NAME + "/users")
|
||||
.method("POST")
|
||||
.content("JSON.stringify(JSON.parse('{\"emailVerified\" : false, \"enabled\" : true, \"username\": \"mhajas\", \"firstName\" :\"First\", \"lastName\":\"Last\",\"email\":\"email@redhat.com\", \"attributes\": {}}'))")
|
||||
.addHeader("Accept", "application/json")
|
||||
.addHeader("Authorization", "Bearer ' + keycloak.token + '")
|
||||
.addHeader("Content-Type", "application/json; charset=UTF-8");
|
||||
|
||||
testExecutor.logInAndInit(defaultArguments(), testUser, this::assertSuccessfullyLoggedIn)
|
||||
.sendXMLHttpRequest(request, response -> {
|
||||
List<UserRepresentation> users = adminClient.realm(REALM_NAME).users().search("mhajas", 0, 1);
|
||||
assertEquals("There should be created user mhajas", 1, users.size());
|
||||
|
||||
assertThat((String) response.get("responseHeaders"), containsString("location: " + authServerContextRootPage.toString() + "/auth/admin/realms/" + REALM_NAME + "/users/" + users.get(0).getId()));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void spaceInRealmNameTest() {
|
||||
// Unfortunately this test doesn't work on phantomjs
|
||||
// it looks like phantomjs double encode %20 => %25%20
|
||||
Assume.assumeTrue("This test doesn't work with phantomjs", !"phantomjs".equals(System.getProperty("js.browser")));
|
||||
|
||||
adminClient.realm(REALM_NAME).update(RealmBuilder.edit(adminClient.realm(REALM_NAME).toRepresentation()).name(SPACE_REALM_NAME).build());
|
||||
|
||||
JSObjectBuilder configuration = JSObjectBuilder.create()
|
||||
.add("url", authServerContextRootPage + "/auth")
|
||||
.add("realm", SPACE_REALM_NAME)
|
||||
.add("clientId", CLIENT_ID);
|
||||
|
||||
testAppUrl = authServerContextRootPage + JAVASCRIPT_SPACE_URL + "/index.html";
|
||||
jsDriver.navigate().to(testAppUrl);
|
||||
testRealmLoginPage.setAuthRealm(SPACE_REALM_NAME);
|
||||
|
||||
testExecutor.configure(configuration)
|
||||
.init(defaultArguments(), this::assertInitNotAuth)
|
||||
.login(this::assertOnLoginPage)
|
||||
.loginForm(testUser, this::assertOnTestAppUrl)
|
||||
.configure(configuration)
|
||||
.init(defaultArguments(), this::assertSuccessfullyLoggedIn);
|
||||
|
||||
// Clean
|
||||
adminClient.realm(SPACE_REALM_NAME).update(RealmBuilder.edit(adminClient.realm(SPACE_REALM_NAME).toRepresentation()).name(REALM_NAME).build());
|
||||
testRealmLoginPage.setAuthRealm(REALM_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initializeWithTokenTest() {
|
||||
oauth.setDriver(jsDriver);
|
||||
|
||||
oauth.realm(REALM_NAME);
|
||||
oauth.clientId(CLIENT_ID);
|
||||
oauth.redirectUri(testAppUrl);
|
||||
oauth.doLogin(testUser);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
|
||||
String token = tokenResponse.getAccessToken();
|
||||
String refreshToken = tokenResponse.getRefreshToken();
|
||||
|
||||
testExecutor.init(JSObjectBuilder.create()
|
||||
.add("token", token)
|
||||
.add("refreshToken", refreshToken)
|
||||
, this::assertSuccessfullyLoggedIn)
|
||||
.refreshToken(9999, assertEventsContains("Auth Refresh Success"));
|
||||
|
||||
|
||||
oauth.setDriver(driver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initializeWithTimeSkew() {
|
||||
oauth.setDriver(jsDriver); // Oauth need to login with jsDriver
|
||||
|
||||
// Get access token and refresh token to initialize with
|
||||
setTimeOffset(600);
|
||||
oauth.realm(REALM_NAME);
|
||||
oauth.clientId(CLIENT_ID);
|
||||
oauth.redirectUri(testAppUrl);
|
||||
oauth.doLogin(testUser);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
|
||||
String token = tokenResponse.getAccessToken();
|
||||
String refreshToken = tokenResponse.getRefreshToken();
|
||||
|
||||
// Perform test
|
||||
testExecutor.init(JSObjectBuilder.create()
|
||||
.add("token", token)
|
||||
.add("refreshToken", refreshToken)
|
||||
.add("timeSkew", -600)
|
||||
, this::assertSuccessfullyLoggedIn)
|
||||
.checkTimeSkew((driver1, output, events) -> assertThat(output, equalTo(-600L)))
|
||||
.refreshToken(9999, assertEventsContains("Auth Refresh Success"))
|
||||
.checkTimeSkew((driver1, output, events) -> assertThat(output, equalTo(-600L)));
|
||||
|
||||
setTimeOffset(0);
|
||||
|
||||
oauth.setDriver(driver); // Clean
|
||||
}
|
||||
|
||||
@Test
|
||||
// KEYCLOAK-4503
|
||||
public void initializeWithRefreshToken() {
|
||||
oauth.setDriver(jsDriver); // Oauth need to login with jsDriver
|
||||
|
||||
oauth.realm(REALM_NAME);
|
||||
oauth.clientId(CLIENT_ID);
|
||||
oauth.redirectUri(testAppUrl);
|
||||
oauth.doLogin(testUser);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
|
||||
String token = tokenResponse.getAccessToken();
|
||||
String refreshToken = tokenResponse.getRefreshToken();
|
||||
|
||||
testExecutor.init(JSObjectBuilder.create()
|
||||
.add("refreshToken", refreshToken)
|
||||
, (driver1, output, events) -> {
|
||||
assertInitNotAuth(driver1, output, events);
|
||||
waitUntilElement(events).text().not().contains("Auth Success");
|
||||
});
|
||||
|
||||
oauth.setDriver(driver); // Clean
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reentrancyCallbackTest() {
|
||||
testExecutor.logInAndInit(defaultArguments(), testUser, this::assertSuccessfullyLoggedIn)
|
||||
.executeAsyncScript(
|
||||
"var callback = arguments[arguments.length - 1];" +
|
||||
"keycloak.updateToken(60).success(function () {" +
|
||||
" event(\"First callback\");" +
|
||||
" keycloak.updateToken(60).success(function () {" +
|
||||
" event(\"Second callback\");" +
|
||||
" callback(\"Success\");" +
|
||||
" });" +
|
||||
" }" +
|
||||
");"
|
||||
, (driver1, output, events) -> {
|
||||
waitUntilElement(events).text().contains("First callback");
|
||||
waitUntilElement(events).text().contains("Second callback");
|
||||
waitUntilElement(events).text().not().contains("Auth Logout");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package org.keycloak.testsuite.adapter.javascript;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
public interface JavascriptStateValidator extends Serializable {
|
||||
|
||||
void validate(WebDriver driver, Object output, WebElement events);
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
package org.keycloak.testsuite.adapter.javascript;
|
||||
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.auth.page.login.OIDCLogin;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.JavascriptExecutor;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
public class JavascriptTestExecutor {
|
||||
private WebDriver jsDriver;
|
||||
private JavascriptExecutor jsExecutor;
|
||||
private WebElement output;
|
||||
private WebElement events;
|
||||
private OIDCLogin loginPage;
|
||||
private boolean configured;
|
||||
|
||||
public static JavascriptTestExecutor create(WebDriver driver, OIDCLogin loginPage) {
|
||||
return new JavascriptTestExecutor(driver, loginPage);
|
||||
}
|
||||
|
||||
private JavascriptTestExecutor(WebDriver driver, OIDCLogin loginPage) {
|
||||
this.jsDriver = driver;
|
||||
driver.manage().timeouts().setScriptTimeout(10, TimeUnit.SECONDS);
|
||||
jsExecutor = (JavascriptExecutor) driver;
|
||||
events = driver.findElement(By.id("events"));
|
||||
output = driver.findElement(By.id("output"));
|
||||
this.loginPage = loginPage;
|
||||
configured = false;
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor login() {
|
||||
return login(null);
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor login(JavascriptStateValidator validator) {
|
||||
jsExecutor.executeScript("keycloak.login()");
|
||||
|
||||
if (validator != null) {
|
||||
validator.validate(jsDriver, output, events);
|
||||
}
|
||||
|
||||
configured = false; // Getting out of testApp page => loosing keycloak variable etc.
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor loginForm(UserRepresentation user) {
|
||||
return loginForm(user, null);
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor loginForm(UserRepresentation user, JavascriptStateValidator validator) {
|
||||
loginPage.form().login(user);
|
||||
|
||||
if (validator != null) {
|
||||
validator.validate(jsDriver, null, events);
|
||||
}
|
||||
|
||||
configured = false; // Getting out of testApp page => loosing keycloak variable etc.
|
||||
// this is necessary in case we skipped login button for example in login-required mode
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor logout() {
|
||||
return logout(null);
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor logout(JavascriptStateValidator validator) {
|
||||
jsExecutor.executeScript("keycloak.logout()");
|
||||
if (validator != null) {
|
||||
validator.validate(jsDriver, output, events);
|
||||
}
|
||||
|
||||
configured = false; // Loosing keycloak variable so we need to create it when init next session
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor configure() {
|
||||
return configure(null);
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor configure(JSObjectBuilder argumentsBuilder) {
|
||||
if (argumentsBuilder == null) {
|
||||
jsExecutor.executeScript("keycloak = Keycloak()");
|
||||
} else {
|
||||
String configArguments = argumentsBuilder.build();
|
||||
jsExecutor.executeScript("keycloak = Keycloak(" + configArguments + ")");
|
||||
}
|
||||
|
||||
jsExecutor.executeScript("keycloak.onAuthSuccess = function () {event('Auth Success')}"); // event function is declared in index.html
|
||||
jsExecutor.executeScript("keycloak.onAuthError = function () {event('Auth Error')}");
|
||||
jsExecutor.executeScript("keycloak.onAuthRefreshSuccess = function () {event('Auth Refresh Success')}");
|
||||
jsExecutor.executeScript("keycloak.onAuthRefreshError = function () {event('Auth Refresh Error')}");
|
||||
jsExecutor.executeScript("keycloak.onAuthLogout = function () {event('Auth Logout')}");
|
||||
jsExecutor.executeScript("keycloak.onTokenExpired = function () {event('Access token expired.')}");
|
||||
|
||||
configured = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor init(JSObjectBuilder argumentsBuilder) {
|
||||
return init(argumentsBuilder, null);
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor init(JSObjectBuilder argumentsBuilder, JavascriptStateValidator validator) {
|
||||
if(!configured) {
|
||||
configure();
|
||||
}
|
||||
|
||||
String arguments = argumentsBuilder.build();
|
||||
|
||||
Object output = jsExecutor.executeAsyncScript(
|
||||
"var callback = arguments[arguments.length - 1];" +
|
||||
" keycloak.init(" + arguments + ").success(function (authenticated) {" +
|
||||
" callback(\"Init Success (\" + (authenticated ? \"Authenticated\" : \"Not Authenticated\") + \")\");" +
|
||||
" }).error(function () {" +
|
||||
" callback(\"Init Error\");" +
|
||||
" });");
|
||||
|
||||
if (validator != null) {
|
||||
validator.validate(jsDriver, output, events);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor logInAndInit(JSObjectBuilder argumentsBuilder,
|
||||
UserRepresentation user, JavascriptStateValidator validator) {
|
||||
init(argumentsBuilder);
|
||||
login();
|
||||
loginForm(user);
|
||||
init(argumentsBuilder, validator);
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor refreshToken(int value) {
|
||||
return refreshToken(value, null);
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor refreshToken(int value, JavascriptStateValidator validator) {
|
||||
Object output = jsExecutor.executeAsyncScript(
|
||||
"var callback = arguments[arguments.length - 1];" +
|
||||
" keycloak.updateToken(" + Integer.toString(value) + ").success(function (refreshed) {" +
|
||||
" if (refreshed) {" +
|
||||
" callback(keycloak.tokenParsed);" +
|
||||
" } else {" +
|
||||
" callback('Token not refreshed, valid for ' + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds');" +
|
||||
" }" +
|
||||
" }).error(function () {" +
|
||||
" callback('Failed to refresh token');" +
|
||||
" });");
|
||||
|
||||
if(validator != null) {
|
||||
validator.validate(jsDriver, output, events);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor getProfile() {
|
||||
return getProfile(null);
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor getProfile(JavascriptStateValidator validator) {
|
||||
|
||||
Object output = jsExecutor.executeAsyncScript(
|
||||
"var callback = arguments[arguments.length - 1];" +
|
||||
" keycloak.loadUserProfile().success(function (profile) {" +
|
||||
" callback(profile);" +
|
||||
" }).error(function () {" +
|
||||
" callback('Failed to load profile');" +
|
||||
" });");
|
||||
|
||||
if(validator != null) {
|
||||
validator.validate(jsDriver, output, events);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor sendXMLHttpRequest(XMLHttpRequest request, ResponseValidator validator) {
|
||||
validator.validate(request.send(jsExecutor));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor refresh() {
|
||||
jsDriver.navigate().refresh();
|
||||
configured = false; // Refreshing webpage => Loosing keycloak variable
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor addTimeSkew(int addition) {
|
||||
jsExecutor.executeScript("keycloak.timeSkew += " + Integer.toString(addition));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor checkTimeSkew(JavascriptStateValidator validator) {
|
||||
Object timeSkew = jsExecutor.executeScript("return keycloak.timeSkew");
|
||||
|
||||
validator.validate(jsDriver, timeSkew, events);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor executeScript(String script) {
|
||||
return executeScript(script, null);
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor executeScript(String script, JavascriptStateValidator validator) {
|
||||
Object output = jsExecutor.executeScript(script);
|
||||
|
||||
if(validator != null) {
|
||||
validator.validate(jsDriver, output, events);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor executeAsyncScript(String script) {
|
||||
return executeAsyncScript(script, null);
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor executeAsyncScript(String script, JavascriptStateValidator validator) {
|
||||
Object output = jsExecutor.executeAsyncScript(script);
|
||||
|
||||
if(validator != null) {
|
||||
validator.validate(jsDriver, output, events);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JavascriptTestExecutor errorResponse(JavascriptStateValidator validator) {
|
||||
Object output = jsExecutor.executeScript("return \"Error: \" + getParameterByName(\"error\") + \"\\n\" + \"Error description: \" + getParameterByName(\"error_description\")");
|
||||
|
||||
validator.validate(jsDriver, output, events);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package org.keycloak.testsuite.adapter.javascript;
|
||||
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
public interface ResponseValidator extends Serializable {
|
||||
|
||||
void validate(Map<String, Object> response);
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package org.keycloak.testsuite.adapter.javascript;
|
||||
|
||||
import org.openqa.selenium.JavascriptExecutor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
public class XMLHttpRequest {
|
||||
|
||||
private String url;
|
||||
private String method;
|
||||
private Map<String, String> headers;
|
||||
private String content;
|
||||
|
||||
public static XMLHttpRequest create() {
|
||||
return new XMLHttpRequest();
|
||||
}
|
||||
|
||||
private XMLHttpRequest() {}
|
||||
|
||||
public XMLHttpRequest url(String url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
public XMLHttpRequest method(String method) {
|
||||
this.method = method;
|
||||
return this;
|
||||
}
|
||||
|
||||
public XMLHttpRequest content(String content) {
|
||||
this.content = content;
|
||||
return this;
|
||||
}
|
||||
|
||||
public XMLHttpRequest addHeader(String key, String value) {
|
||||
if (headers == null) {
|
||||
headers = new HashMap<>();
|
||||
}
|
||||
|
||||
headers.put(key, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, Object> send(JavascriptExecutor jsExecutor) {
|
||||
String requestCode = "var callback = arguments[arguments.length - 1];" +
|
||||
"var req = new XMLHttpRequest();" +
|
||||
" req.open('" + method + "', '" + url + "', true);" +
|
||||
getHeadersString() +
|
||||
" req.onreadystatechange = function () {" +
|
||||
" if (req.readyState == 4) {" +
|
||||
" callback({\"status\" : req.status, \"reponseText\" : req.reponseText, \"responseHeaders\" : req.getAllResponseHeaders().toLowerCase(), \"res\" : req.response})" +
|
||||
" }" +
|
||||
" };" +
|
||||
" req.send(" + content + ");";
|
||||
|
||||
return (Map<String, Object>) jsExecutor.executeAsyncScript(requestCode);
|
||||
}
|
||||
|
||||
private String getHeadersString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||
builder.append("req.setRequestHeader('")
|
||||
.append(entry.getKey())
|
||||
.append("', '")
|
||||
.append(entry.getValue())
|
||||
.append("');");
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -44,14 +44,23 @@
|
|||
<!-- chrome -->
|
||||
<property name="chromeArguments">${chromeArguments}</property>
|
||||
</extension>
|
||||
|
||||
|
||||
<extension qualifier="graphene">
|
||||
<property name="waitGuiInterval">5</property>
|
||||
<property name="waitAjaxInterval">5</property>
|
||||
<property name="waitModelInterval">10</property>
|
||||
<property name="waitGuardInterval">5</property>
|
||||
</extension>
|
||||
|
||||
|
||||
<extension qualifier="webdriver-javascriptbrowser">
|
||||
<property name="browser">${js.browser}</property>
|
||||
<property name="htmlUnit.version">${htmlUnitBrowserVersion}</property>
|
||||
<property name="firefox_binary">${firefox_binary}</property>
|
||||
<property name="chromeDriverBinary">${webdriver.chrome.driver}</property>
|
||||
<property name="chromeArguments">${js.chromeArguments}</property>
|
||||
<property name="phantomjs.cli.args">${phantomjs.cli.args} --ssl-certificates-path=${client.certificate.ca.path} --ssl-client-certificate-file=${client.certificate.file} --ssl-client-key-file=${client.key.file} --ssl-client-key-passphrase=${client.key.passphrase}</property>
|
||||
</extension>
|
||||
|
||||
<extension qualifier="graphene-secondbrowser">
|
||||
<property name="browser">${browser}</property>
|
||||
<property name="firefox_binary">${firefox_binary}</property>
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
package org.keycloak.testsuite.adapter.example;
|
||||
|
||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
|
||||
/**
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
@AppServerContainer("app-server-eap")
|
||||
public class EAPJSConsoleExampleAdapterTest extends AbstractJSConsoleExampleAdapterTest {
|
||||
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package org.keycloak.testsuite.adapter.example;
|
||||
|
||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
|
||||
/**
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
@AppServerContainer("app-server-eap6")
|
||||
public class EAP6JSConsoleExampleAdapterTest extends AbstractJSConsoleExampleAdapterTest {
|
||||
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package org.keycloak.testsuite.adapter.example;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public class RelativeEAPJSConsoleExampleAdapterTest extends AbstractJSConsoleExampleAdapterTest {
|
||||
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package org.keycloak.testsuite.adapter.example;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public class RelativeWildflyJSConsoleExampleAdapterTest extends AbstractJSConsoleExampleAdapterTest {
|
||||
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package org.keycloak.testsuite.adapter.example;
|
||||
|
||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
|
||||
/**
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
@AppServerContainer("app-server-remote")
|
||||
public class RemoteJSConsoleExampleAdapterTest extends AbstractJSConsoleExampleAdapterTest {
|
||||
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
|
||||
package org.keycloak.testsuite.adapter.example;
|
||||
|
||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
@AppServerContainer("app-server-wildfly")
|
||||
public class WildflyJSConsoleExampleAdapterTest extends AbstractJSConsoleExampleAdapterTest {
|
||||
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
|
||||
package org.keycloak.testsuite.adapter.example;
|
||||
|
||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
@AppServerContainer("app-server-wildfly10")
|
||||
public class Wildfly10JSConsoleExampleAdapterTest extends AbstractJSConsoleExampleAdapterTest {
|
||||
|
||||
}
|
|
@ -267,18 +267,6 @@
|
|||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.keycloak.testsuite</groupId>
|
||||
<artifactId>integration-arquillian-test-apps-js-console</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>war</type>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>org.keycloak.testsuite</groupId>
|
||||
<artifactId>integration-arquillian-test-apps-js-database</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>war</type>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>org.keycloak.testsuite</groupId>
|
||||
<artifactId>hello-world-authz-service</artifactId>
|
||||
|
|
|
@ -103,6 +103,8 @@
|
|||
<github.username/>
|
||||
<github.secretToken/>
|
||||
<ieDriverArch/>
|
||||
<js.browser>phantomjs</js.browser>
|
||||
<js.chromeArguments>--headless</js.chromeArguments>
|
||||
<htmlUnitBrowserVersion>chrome</htmlUnitBrowserVersion>
|
||||
<phantomjs.cli.args>--ignore-ssl-errors=true --web-security=false --ssl-certificates-path=${client.certificate.ca.path} --ssl-client-certificate-file=${client.certificate.file} --ssl-client-key-file=${client.key.file} --ssl-client-key-passphrase=${client.key.passphrase}</phantomjs.cli.args>
|
||||
<firefox_binary>/usr/bin/firefox</firefox_binary>
|
||||
|
@ -268,6 +270,8 @@
|
|||
<test.intermittent>${test.intermittent}</test.intermittent>
|
||||
|
||||
<browser>${browser}</browser>
|
||||
<js.browser>${js.browser}</js.browser>
|
||||
<js.chromeArguments>${js.chromeArguments}</js.chromeArguments>
|
||||
<htmlUnitBrowserVersion>${htmlUnitBrowserVersion}</htmlUnitBrowserVersion>
|
||||
<webdriverDownloadBinaries>${webdriverDownloadBinaries}</webdriverDownloadBinaries>
|
||||
|
||||
|
|
Loading…
Reference in a new issue