Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
c8aa708cff
908 changed files with 26364 additions and 53229 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -49,3 +49,7 @@ target
|
||||||
# Maven shade
|
# Maven shade
|
||||||
#############
|
#############
|
||||||
*dependency-reduced-pom.xml
|
*dependency-reduced-pom.xml
|
||||||
|
|
||||||
|
# nodejs #
|
||||||
|
##########
|
||||||
|
node_modules
|
26
.travis.yml
26
.travis.yml
|
@ -1,33 +1,27 @@
|
||||||
language: java
|
language: java
|
||||||
|
dist: precise
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
cache: false
|
||||||
- $HOME/.m2
|
|
||||||
|
|
||||||
before_cache:
|
|
||||||
- rm -rf $HOME/.m2/repository/org/keycloak
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- MAVEN_SKIP_RC=true
|
- MAVEN_SKIP_RC=true
|
||||||
- MAVEN_OPTS="-Xms512m -Xmx2048m"
|
- MAVEN_OPTS="-Xms512m -Xmx1536m"
|
||||||
matrix:
|
matrix:
|
||||||
- TESTS=group1
|
- TESTS=unit
|
||||||
- TESTS=group2
|
- TESTS=server-group1
|
||||||
- TESTS=group3
|
- TESTS=server-group2
|
||||||
- TESTS=group4
|
- TESTS=server-group3
|
||||||
|
- TESTS=server-group4
|
||||||
- TESTS=old
|
- TESTS=old
|
||||||
|
|
||||||
jdk:
|
jdk:
|
||||||
- oraclejdk8
|
- oraclejdk8
|
||||||
|
|
||||||
before_script:
|
install: true
|
||||||
- export MAVEN_SKIP_RC=true
|
|
||||||
|
|
||||||
install:
|
script:
|
||||||
- travis_wait 60 mvn install --no-snapshot-updates -Pdistribution -DskipTestsuite -B -V -q
|
|
||||||
|
|
||||||
script:
|
|
||||||
- ./travis-run-tests.sh $TESTS
|
- ./travis-run-tests.sh $TESTS
|
||||||
|
|
||||||
sudo: false
|
sudo: false
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -91,6 +91,8 @@ public class KeycloakDeployment {
|
||||||
// https://tools.ietf.org/html/rfc7636
|
// https://tools.ietf.org/html/rfc7636
|
||||||
protected boolean pkce = false;
|
protected boolean pkce = false;
|
||||||
protected boolean ignoreOAuthQueryParameter;
|
protected boolean ignoreOAuthQueryParameter;
|
||||||
|
|
||||||
|
protected Map<String, String> redirectRewriteRules;
|
||||||
|
|
||||||
public KeycloakDeployment() {
|
public KeycloakDeployment() {
|
||||||
}
|
}
|
||||||
|
@ -446,4 +448,14 @@ public class KeycloakDeployment {
|
||||||
public boolean isOAuthQueryParameterEnabled() {
|
public boolean isOAuthQueryParameterEnabled() {
|
||||||
return !this.ignoreOAuthQueryParameter;
|
return !this.ignoreOAuthQueryParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getRedirectRewriteRules() {
|
||||||
|
return redirectRewriteRules;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRewriteRedirectRules(Map<String, String> redirectRewriteRules) {
|
||||||
|
this.redirectRewriteRules = redirectRewriteRules;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,6 +116,7 @@ public class KeycloakDeploymentBuilder {
|
||||||
deployment.setMinTimeBetweenJwksRequests(adapterConfig.getMinTimeBetweenJwksRequests());
|
deployment.setMinTimeBetweenJwksRequests(adapterConfig.getMinTimeBetweenJwksRequests());
|
||||||
deployment.setPublicKeyCacheTtl(adapterConfig.getPublicKeyCacheTtl());
|
deployment.setPublicKeyCacheTtl(adapterConfig.getPublicKeyCacheTtl());
|
||||||
deployment.setIgnoreOAuthQueryParameter(adapterConfig.isIgnoreOAuthQueryParameter());
|
deployment.setIgnoreOAuthQueryParameter(adapterConfig.isIgnoreOAuthQueryParameter());
|
||||||
|
deployment.setRewriteRedirectRules(adapterConfig.getRedirectRewriteRules());
|
||||||
|
|
||||||
if (realmKeyPem == null && adapterConfig.isBearerOnly() && adapterConfig.getAuthServerUrl() == null) {
|
if (realmKeyPem == null && adapterConfig.isBearerOnly() && adapterConfig.getAuthServerUrl() == null) {
|
||||||
throw new IllegalArgumentException("For bearer auth, you must set the realm-public-key or auth-server-url");
|
throw new IllegalArgumentException("For bearer auth, you must set the realm-public-key or auth-server-url");
|
||||||
|
|
|
@ -25,7 +25,6 @@ import org.keycloak.adapters.spi.AuthChallenge;
|
||||||
import org.keycloak.adapters.spi.AuthOutcome;
|
import org.keycloak.adapters.spi.AuthOutcome;
|
||||||
import org.keycloak.adapters.spi.HttpFacade;
|
import org.keycloak.adapters.spi.HttpFacade;
|
||||||
import org.keycloak.common.VerificationException;
|
import org.keycloak.common.VerificationException;
|
||||||
import org.keycloak.common.util.Encode;
|
|
||||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
import org.keycloak.common.util.KeycloakUriBuilder;
|
||||||
import org.keycloak.common.util.UriUtils;
|
import org.keycloak.common.util.UriUtils;
|
||||||
import org.keycloak.constants.AdapterConstants;
|
import org.keycloak.constants.AdapterConstants;
|
||||||
|
@ -38,7 +37,10 @@ import org.keycloak.representations.IDToken;
|
||||||
import org.keycloak.util.TokenUtil;
|
import org.keycloak.util.TokenUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -141,6 +143,7 @@ public class OAuthRequestAuthenticator {
|
||||||
protected String getRedirectUri(String state) {
|
protected String getRedirectUri(String state) {
|
||||||
String url = getRequestUrl();
|
String url = getRequestUrl();
|
||||||
log.debugf("callback uri: %s", url);
|
log.debugf("callback uri: %s", url);
|
||||||
|
|
||||||
if (!facade.getRequest().isSecure() && deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr())) {
|
if (!facade.getRequest().isSecure() && deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr())) {
|
||||||
int port = sslRedirectPort();
|
int port = sslRedirectPort();
|
||||||
if (port < 0) {
|
if (port < 0) {
|
||||||
|
@ -170,7 +173,7 @@ public class OAuthRequestAuthenticator {
|
||||||
KeycloakUriBuilder redirectUriBuilder = deployment.getAuthUrl().clone()
|
KeycloakUriBuilder redirectUriBuilder = deployment.getAuthUrl().clone()
|
||||||
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
|
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
|
||||||
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
|
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
|
||||||
.queryParam(OAuth2Constants.REDIRECT_URI, Encode.encodeQueryParamAsIs(url)) // Need to encode uri ourselves as queryParam() will not encode % characters.
|
.queryParam(OAuth2Constants.REDIRECT_URI, rewrittenRedirectUri(url))
|
||||||
.queryParam(OAuth2Constants.STATE, state)
|
.queryParam(OAuth2Constants.STATE, state)
|
||||||
.queryParam("login", "true");
|
.queryParam("login", "true");
|
||||||
if(loginHint != null && loginHint.length() > 0){
|
if(loginHint != null && loginHint.length() > 0){
|
||||||
|
@ -320,10 +323,11 @@ public class OAuthRequestAuthenticator {
|
||||||
|
|
||||||
AccessTokenResponse tokenResponse = null;
|
AccessTokenResponse tokenResponse = null;
|
||||||
strippedOauthParametersRequestUri = stripOauthParametersFromRedirect();
|
strippedOauthParametersRequestUri = stripOauthParametersFromRedirect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// For COOKIE store we don't have httpSessionId and single sign-out won't be available
|
// For COOKIE store we don't have httpSessionId and single sign-out won't be available
|
||||||
String httpSessionId = deployment.getTokenStore() == TokenStore.SESSION ? reqAuthenticator.changeHttpSessionId(true) : null;
|
String httpSessionId = deployment.getTokenStore() == TokenStore.SESSION ? reqAuthenticator.changeHttpSessionId(true) : null;
|
||||||
tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, strippedOauthParametersRequestUri, httpSessionId);
|
tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, rewrittenRedirectUri(strippedOauthParametersRequestUri), httpSessionId);
|
||||||
} catch (ServerRequest.HttpFailure failure) {
|
} catch (ServerRequest.HttpFailure failure) {
|
||||||
log.error("failed to turn code into token");
|
log.error("failed to turn code into token");
|
||||||
log.error("status from server: " + failure.getStatus());
|
log.error("status from server: " + failure.getStatus());
|
||||||
|
@ -375,6 +379,23 @@ public class OAuthRequestAuthenticator {
|
||||||
.replaceQueryParam(OAuth2Constants.STATE, null);
|
.replaceQueryParam(OAuth2Constants.STATE, null);
|
||||||
return builder.build().toString();
|
return builder.build().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String rewrittenRedirectUri(String originalUri) {
|
||||||
|
Map<String, String> rewriteRules = deployment.getRedirectRewriteRules();
|
||||||
|
if(rewriteRules != null && !rewriteRules.isEmpty()) {
|
||||||
|
try {
|
||||||
|
URL url = new URL(originalUri);
|
||||||
|
Map.Entry<String, String> rule = rewriteRules.entrySet().iterator().next();
|
||||||
|
StringBuilder redirectUriBuilder = new StringBuilder(url.getProtocol());
|
||||||
|
redirectUriBuilder.append("://"+ url.getAuthority());
|
||||||
|
redirectUriBuilder.append(url.getPath().replaceFirst(rule.getKey(), rule.getValue()));
|
||||||
|
return redirectUriBuilder.toString();
|
||||||
|
} catch (MalformedURLException ex) {
|
||||||
|
log.error("Not a valid request url");
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return originalUri;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,7 +155,9 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
|
||||||
this.refreshToken = response.getRefreshToken();
|
this.refreshToken = response.getRefreshToken();
|
||||||
}
|
}
|
||||||
this.tokenString = tokenString;
|
this.tokenString = tokenString;
|
||||||
tokenStore.refreshCallback(this);
|
if (tokenStore != null) {
|
||||||
|
tokenStore.refreshCallback(this);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,7 @@ public class KeycloakDeploymentBuilderTest {
|
||||||
assertEquals(10, deployment.getTokenMinimumTimeToLive());
|
assertEquals(10, deployment.getTokenMinimumTimeToLive());
|
||||||
assertEquals(20, deployment.getMinTimeBetweenJwksRequests());
|
assertEquals(20, deployment.getMinTimeBetweenJwksRequests());
|
||||||
assertEquals(120, deployment.getPublicKeyCacheTtl());
|
assertEquals(120, deployment.getPublicKeyCacheTtl());
|
||||||
|
assertEquals("/api/$1", deployment.getRedirectRewriteRules().get("^/wsmaster/api/(.*)$"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -33,5 +33,8 @@
|
||||||
"token-minimum-time-to-live": 10,
|
"token-minimum-time-to-live": 10,
|
||||||
"min-time-between-jwks-requests": 20,
|
"min-time-between-jwks-requests": 20,
|
||||||
"public-key-cache-ttl": 120,
|
"public-key-cache-ttl": 120,
|
||||||
"ignore-oauth-query-parameter": true
|
"ignore-oauth-query-parameter": true,
|
||||||
|
"redirect-rewrite-rules" : {
|
||||||
|
"^/wsmaster/api/(.*)$" : "/api/$1"
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-as7-integration-pom</artifactId>
|
<artifactId>keycloak-as7-integration-pom</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-as7-integration-pom</artifactId>
|
<artifactId>keycloak-as7-integration-pom</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-as7-integration-pom</artifactId>
|
<artifactId>keycloak-as7-integration-pom</artifactId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<name>Keycloak AS7 / JBoss EAP 6 Integration</name>
|
<name>Keycloak AS7 / JBoss EAP 6 Integration</name>
|
||||||
|
|
9
adapters/oidc/cli-sso/README.md
Executable file
9
adapters/oidc/cli-sso/README.md
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
CLI Single Sign On
|
||||||
|
===================================
|
||||||
|
|
||||||
|
This java-based utility is meant for providing Keycloak integration to
|
||||||
|
command line applications that are either written in Java or another language. The
|
||||||
|
idea is that the Java app provided by this utility performs a login for a specific
|
||||||
|
client, parses responses, and exports an access token as an environment variable
|
||||||
|
that can be used by the command line utility you are accessing.
|
||||||
|
|
10
adapters/oidc/cli-sso/login.sh
Executable file
10
adapters/oidc/cli-sso/login.sh
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/sh
|
||||||
|
export KC_AUTH_SERVER=http://localhost:8080/auth
|
||||||
|
export KC_REALM=master
|
||||||
|
export KC_CLIENT=cli
|
||||||
|
|
||||||
|
export KC_ACCESS_TOKEN=`java -DKEYCLOAK_AUTH_SERVER=$KC_AUTH_SERVER -DKEYCLOAK_REALM=$KC_REALM -DKEYCLOAK_CLIENT=$KC_CLIENT -jar target/keycloak-cli-sso-3.3.0.CR1-SNAPSHOT.jar login`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
9
adapters/oidc/cli-sso/logout.sh
Normal file
9
adapters/oidc/cli-sso/logout.sh
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
java -DKEYCLOAK_AUTH_SERVER=$KC_AUTH_SERVER -DKEYCLOAK_REALM=$KC_REALM -DKEYCLOAK_CLIENT=$KC_CLIENT -jar target/keycloak-cli-sso-3.3.0.CR1-SNAPSHOT.jar logout
|
||||||
|
|
||||||
|
unset KC_ACCESS_TOKEN
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
84
adapters/oidc/cli-sso/pom.xml
Executable file
84
adapters/oidc/cli-sso/pom.xml
Executable file
|
@ -0,0 +1,84 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
~ and other contributors as indicated by the @author tags.
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<project 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>
|
||||||
|
<artifactId>keycloak-parent</artifactId>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>keycloak-cli-sso</artifactId>
|
||||||
|
<name>Keycloak CLI SSO Framework</name>
|
||||||
|
<description/>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-installed-adapter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>${maven.compiler.source}</source>
|
||||||
|
<target>${maven.compiler.target}</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
<configuration>
|
||||||
|
<transformers>
|
||||||
|
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
|
<mainClass>org.keycloak.adapters.KeycloakCliSsoMain</mainClass>
|
||||||
|
</transformer>
|
||||||
|
</transformers>
|
||||||
|
|
||||||
|
<filters>
|
||||||
|
<filter>
|
||||||
|
<artifact>*:*</artifact>
|
||||||
|
<excludes>
|
||||||
|
<exclude>META-INF/*.SF</exclude>
|
||||||
|
<exclude>META-INF/*.DSA</exclude>
|
||||||
|
<exclude>META-INF/*.RSA</exclude>
|
||||||
|
</excludes>
|
||||||
|
</filter>
|
||||||
|
</filters>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* 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.adapters;
|
||||||
|
|
||||||
|
import org.keycloak.adapters.installed.KeycloakCliSso;
|
||||||
|
import org.keycloak.adapters.installed.KeycloakInstalled;
|
||||||
|
import org.keycloak.common.util.Time;
|
||||||
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
|
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class KeycloakCliSsoMain extends KeycloakCliSso {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
new KeycloakCliSsoMain().mainCmd(args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -0,0 +1,266 @@
|
||||||
|
/*
|
||||||
|
* 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.adapters.installed;
|
||||||
|
|
||||||
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
|
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||||
|
import org.keycloak.adapters.ServerRequest;
|
||||||
|
import org.keycloak.common.util.Time;
|
||||||
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
|
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class KeycloakCliSso {
|
||||||
|
|
||||||
|
public void mainCmd(String[] args) throws Exception {
|
||||||
|
if (args.length != 1) {
|
||||||
|
printHelp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args[0].equalsIgnoreCase("login")) {
|
||||||
|
login();
|
||||||
|
} else if (args[0].equalsIgnoreCase("login-manual")) {
|
||||||
|
loginManual();
|
||||||
|
} else if (args[0].equalsIgnoreCase("token")) {
|
||||||
|
token();
|
||||||
|
} else if (args[0].equalsIgnoreCase("logout")) {
|
||||||
|
logout();
|
||||||
|
} else if (args[0].equalsIgnoreCase("env")) {
|
||||||
|
System.out.println(System.getenv().toString());
|
||||||
|
} else {
|
||||||
|
printHelp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void printHelp() {
|
||||||
|
System.err.println("Commands:");
|
||||||
|
System.err.println(" login - login with desktop browser if available, otherwise do manual login. Output is access token.");
|
||||||
|
System.err.println(" login-manual - manual login");
|
||||||
|
System.err.println(" token - print access token if logged in");
|
||||||
|
System.err.println(" logout - logout.");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AdapterConfig getConfig() {
|
||||||
|
String url = System.getProperty("KEYCLOAK_AUTH_SERVER");
|
||||||
|
if (url == null) {
|
||||||
|
System.err.println("KEYCLOAK_AUTH_SERVER property not set");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
String realm = System.getProperty("KEYCLOAK_REALM");
|
||||||
|
if (realm == null) {
|
||||||
|
System.err.println("KEYCLOAK_REALM property not set");
|
||||||
|
System.exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
String client = System.getProperty("KEYCLOAK_CLIENT");
|
||||||
|
if (client == null) {
|
||||||
|
System.err.println("KEYCLOAK_CLIENT property not set");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
String secret = System.getProperty("KEYCLOAK_CLIENT_SECRET");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AdapterConfig config = new AdapterConfig();
|
||||||
|
config.setAuthServerUrl(url);
|
||||||
|
config.setRealm(realm);
|
||||||
|
config.setResource(client);
|
||||||
|
config.setSslRequired("external");
|
||||||
|
if (secret != null) {
|
||||||
|
Map<String, Object> creds = new HashMap<>();
|
||||||
|
creds.put("secret", secret);
|
||||||
|
config.setCredentials(creds);
|
||||||
|
} else {
|
||||||
|
config.setPublicClient(true);
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkToken() throws Exception {
|
||||||
|
String token = getTokenResponse();
|
||||||
|
if (token == null) return false;
|
||||||
|
|
||||||
|
|
||||||
|
if (token != null) {
|
||||||
|
Matcher m = Pattern.compile("\\{.*\\}\\z").matcher(token);
|
||||||
|
if (m.find()) {
|
||||||
|
String json = m.group(0);
|
||||||
|
try {
|
||||||
|
AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
|
||||||
|
if (Time.currentTime() < tokenResponse.getExpiresIn()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
AdapterConfig config = getConfig();
|
||||||
|
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
|
||||||
|
installed.refreshToken(tokenResponse.getRefreshToken());
|
||||||
|
processResponse(installed);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Error processing existing token");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getTokenResponse() throws IOException {
|
||||||
|
String token = null;
|
||||||
|
File tokenFile = getTokenFilePath();
|
||||||
|
if (tokenFile.exists()) {
|
||||||
|
FileInputStream fis = new FileInputStream(tokenFile);
|
||||||
|
byte[] data = new byte[(int) tokenFile.length()];
|
||||||
|
fis.read(data);
|
||||||
|
fis.close();
|
||||||
|
token = new String(data, "UTF-8");
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void token() throws Exception {
|
||||||
|
String token = getTokenResponse();
|
||||||
|
if (token == null) {
|
||||||
|
System.err.println("There is no token for client");
|
||||||
|
System.exit(1);
|
||||||
|
} else {
|
||||||
|
Matcher m = Pattern.compile("\\{.*\\}\\z").matcher(token);
|
||||||
|
if (m.find()) {
|
||||||
|
String json = m.group(0);
|
||||||
|
try {
|
||||||
|
AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
|
||||||
|
if (Time.currentTime() < tokenResponse.getExpiresIn()) {
|
||||||
|
System.out.println(tokenResponse.getToken());
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
System.err.println("token in response file is expired");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Failure processing token response file");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.err.println("Could not find json within token response file");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void login() throws Exception {
|
||||||
|
if (checkToken()) return;
|
||||||
|
AdapterConfig config = getConfig();
|
||||||
|
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
|
||||||
|
installed.login();
|
||||||
|
processResponse(installed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHome() {
|
||||||
|
String home = System.getenv("HOME");
|
||||||
|
if (home == null) {
|
||||||
|
home = System.getProperty("HOME");
|
||||||
|
if (home == null) {
|
||||||
|
home = Paths.get("").toAbsolutePath().normalize().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return home;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getTokenDirectory() {
|
||||||
|
return Paths.get(getHome(), System.getProperty("basepath", ".keycloak-sso"), System.getProperty("KEYCLOAK_REALM")).toFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getTokenFilePath() {
|
||||||
|
return Paths.get(getHome(), System.getProperty("basepath", ".keycloak-sso"), System.getProperty("KEYCLOAK_REALM"), System.getProperty("KEYCLOAK_CLIENT") + ".json").toFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processResponse(KeycloakInstalled installed) throws IOException {
|
||||||
|
AccessTokenResponse tokenResponse = installed.getTokenResponse();
|
||||||
|
tokenResponse.setExpiresIn(Time.currentTime() + tokenResponse.getExpiresIn());
|
||||||
|
tokenResponse.setIdToken(null);
|
||||||
|
String output = JsonSerialization.writeValueAsString(tokenResponse);
|
||||||
|
getTokenDirectory().mkdirs();
|
||||||
|
FileOutputStream fos = new FileOutputStream(getTokenFilePath());
|
||||||
|
fos.write(output.getBytes("UTF-8"));
|
||||||
|
fos.flush();
|
||||||
|
fos.close();
|
||||||
|
System.out.println(tokenResponse.getToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loginManual() throws Exception {
|
||||||
|
if (checkToken()) return;
|
||||||
|
AdapterConfig config = getConfig();
|
||||||
|
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config);
|
||||||
|
KeycloakInstalled installed = new KeycloakInstalled(deployment);
|
||||||
|
installed.loginManual();
|
||||||
|
processResponse(installed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logout() throws Exception {
|
||||||
|
String token = getTokenResponse();
|
||||||
|
if (token != null) {
|
||||||
|
Matcher m = Pattern.compile("\\{.*\\}\\z").matcher(token);
|
||||||
|
if (m.find()) {
|
||||||
|
String json = m.group(0);
|
||||||
|
try {
|
||||||
|
AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
|
||||||
|
if (Time.currentTime() > tokenResponse.getExpiresIn()) {
|
||||||
|
System.err.println("Login is expired");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
AdapterConfig config = getConfig();
|
||||||
|
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config);
|
||||||
|
ServerRequest.invokeLogout(deployment, tokenResponse.getRefreshToken());
|
||||||
|
for (File fp : getTokenDirectory().listFiles()) fp.delete();
|
||||||
|
System.out.println("logout complete");
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Failure processing token response file");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.err.println("Could not find json within token response file");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.err.println("Not logged in");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.keycloak.adapters.installed;
|
package org.keycloak.adapters.installed;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.Charsets;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.OAuthErrorException;
|
import org.keycloak.OAuthErrorException;
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
|
@ -24,6 +25,7 @@ import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||||
import org.keycloak.adapters.ServerRequest;
|
import org.keycloak.adapters.ServerRequest;
|
||||||
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
|
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
|
||||||
import org.keycloak.common.VerificationException;
|
import org.keycloak.common.VerificationException;
|
||||||
|
import org.keycloak.common.util.KeycloakUriBuilder;
|
||||||
import org.keycloak.jose.jws.JWSInput;
|
import org.keycloak.jose.jws.JWSInput;
|
||||||
import org.keycloak.jose.jws.JWSInputException;
|
import org.keycloak.jose.jws.JWSInputException;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
|
@ -43,6 +45,7 @@ import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@ -51,6 +54,11 @@ import java.util.concurrent.TimeUnit;
|
||||||
*/
|
*/
|
||||||
public class KeycloakInstalled {
|
public class KeycloakInstalled {
|
||||||
|
|
||||||
|
public interface HttpResponseWriter {
|
||||||
|
void success(PrintWriter pw, KeycloakInstalled ki);
|
||||||
|
void failure(PrintWriter pw, KeycloakInstalled ki);
|
||||||
|
}
|
||||||
|
|
||||||
private static final String KEYCLOAK_JSON = "META-INF/keycloak.json";
|
private static final String KEYCLOAK_JSON = "META-INF/keycloak.json";
|
||||||
|
|
||||||
private KeycloakDeployment deployment;
|
private KeycloakDeployment deployment;
|
||||||
|
@ -59,12 +67,18 @@ public class KeycloakInstalled {
|
||||||
LOGGED_MANUAL, LOGGED_DESKTOP
|
LOGGED_MANUAL, LOGGED_DESKTOP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AccessTokenResponse tokenResponse;
|
||||||
private String tokenString;
|
private String tokenString;
|
||||||
private String idTokenString;
|
private String idTokenString;
|
||||||
private IDToken idToken;
|
private IDToken idToken;
|
||||||
private AccessToken token;
|
private AccessToken token;
|
||||||
private String refreshToken;
|
private String refreshToken;
|
||||||
private Status status;
|
private Status status;
|
||||||
|
private Locale locale;
|
||||||
|
private HttpResponseWriter loginResponseWriter;
|
||||||
|
private HttpResponseWriter logoutResponseWriter;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public KeycloakInstalled() {
|
public KeycloakInstalled() {
|
||||||
InputStream config = Thread.currentThread().getContextClassLoader().getResourceAsStream(KEYCLOAK_JSON);
|
InputStream config = Thread.currentThread().getContextClassLoader().getResourceAsStream(KEYCLOAK_JSON);
|
||||||
|
@ -75,6 +89,92 @@ public class KeycloakInstalled {
|
||||||
deployment = KeycloakDeploymentBuilder.build(config);
|
deployment = KeycloakDeploymentBuilder.build(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KeycloakInstalled(KeycloakDeployment deployment) {
|
||||||
|
this.deployment = deployment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HttpResponseWriter defaultLoginWriter = new HttpResponseWriter() {
|
||||||
|
@Override
|
||||||
|
public void success(PrintWriter pw, KeycloakInstalled ki) {
|
||||||
|
pw.println("HTTP/1.1 200 OK");
|
||||||
|
pw.println("Content-Type: text/html");
|
||||||
|
pw.println();
|
||||||
|
pw.println("<html><h1>Login completed.</h1><div>");
|
||||||
|
pw.println("This browser will remain logged in until you close it, logout, or the session expires.");
|
||||||
|
pw.println("</div></html>");
|
||||||
|
pw.flush();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failure(PrintWriter pw, KeycloakInstalled ki) {
|
||||||
|
pw.println("HTTP/1.1 200 OK");
|
||||||
|
pw.println("Content-Type: text/html");
|
||||||
|
pw.println();
|
||||||
|
pw.println("<html><h1>Login attempt failed.</h1><div>");
|
||||||
|
pw.println("</div></html>");
|
||||||
|
pw.flush();
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private static HttpResponseWriter defaultLogoutWriter = new HttpResponseWriter() {
|
||||||
|
@Override
|
||||||
|
public void success(PrintWriter pw, KeycloakInstalled ki) {
|
||||||
|
pw.println("HTTP/1.1 200 OK");
|
||||||
|
pw.println("Content-Type: text/html");
|
||||||
|
pw.println();
|
||||||
|
pw.println("<html><h1>Logout completed.</h1><div>");
|
||||||
|
pw.println("You may close this browser tab.");
|
||||||
|
pw.println("</div></html>");
|
||||||
|
pw.flush();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failure(PrintWriter pw, KeycloakInstalled ki) {
|
||||||
|
pw.println("HTTP/1.1 200 OK");
|
||||||
|
pw.println("Content-Type: text/html");
|
||||||
|
pw.println();
|
||||||
|
pw.println("<html><h1>Logout failed.</h1><div>");
|
||||||
|
pw.println("You may close this browser tab.");
|
||||||
|
pw.println("</div></html>");
|
||||||
|
pw.flush();
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public HttpResponseWriter getLoginResponseWriter() {
|
||||||
|
if (loginResponseWriter == null) {
|
||||||
|
return defaultLoginWriter;
|
||||||
|
} else {
|
||||||
|
return loginResponseWriter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpResponseWriter getLogoutResponseWriter() {
|
||||||
|
if (logoutResponseWriter == null) {
|
||||||
|
return defaultLogoutWriter;
|
||||||
|
} else {
|
||||||
|
return logoutResponseWriter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoginResponseWriter(HttpResponseWriter loginResponseWriter) {
|
||||||
|
this.loginResponseWriter = loginResponseWriter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogoutResponseWriter(HttpResponseWriter logoutResponseWriter) {
|
||||||
|
this.logoutResponseWriter = logoutResponseWriter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Locale getLocale() {
|
||||||
|
return locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocale(Locale locale) {
|
||||||
|
this.locale = locale;
|
||||||
|
}
|
||||||
|
|
||||||
public void login() throws IOException, ServerRequest.HttpFailure, VerificationException, InterruptedException, OAuthErrorException, URISyntaxException {
|
public void login() throws IOException, ServerRequest.HttpFailure, VerificationException, InterruptedException, OAuthErrorException, URISyntaxException {
|
||||||
if (isDesktopSupported()) {
|
if (isDesktopSupported()) {
|
||||||
loginDesktop();
|
loginDesktop();
|
||||||
|
@ -108,19 +208,22 @@ public class KeycloakInstalled {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loginDesktop() throws IOException, VerificationException, OAuthErrorException, URISyntaxException, ServerRequest.HttpFailure, InterruptedException {
|
public void loginDesktop() throws IOException, VerificationException, OAuthErrorException, URISyntaxException, ServerRequest.HttpFailure, InterruptedException {
|
||||||
CallbackListener callback = new CallbackListener();
|
CallbackListener callback = new CallbackListener(getLoginResponseWriter());
|
||||||
callback.start();
|
callback.start();
|
||||||
|
|
||||||
String redirectUri = "http://localhost:" + callback.server.getLocalPort();
|
String redirectUri = "http://localhost:" + callback.server.getLocalPort();
|
||||||
String state = UUID.randomUUID().toString();
|
String state = UUID.randomUUID().toString();
|
||||||
|
|
||||||
String authUrl = deployment.getAuthUrl().clone()
|
KeycloakUriBuilder builder = deployment.getAuthUrl().clone()
|
||||||
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
|
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
|
||||||
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
|
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
|
||||||
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
|
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
|
||||||
.queryParam(OAuth2Constants.STATE, state)
|
.queryParam(OAuth2Constants.STATE, state)
|
||||||
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
|
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID);
|
||||||
.build().toString();
|
if (locale != null) {
|
||||||
|
builder.queryParam(OAuth2Constants.UI_LOCALES_PARAM, locale.getLanguage());
|
||||||
|
}
|
||||||
|
String authUrl = builder.build().toString();
|
||||||
|
|
||||||
Desktop.getDesktop().browse(new URI(authUrl));
|
Desktop.getDesktop().browse(new URI(authUrl));
|
||||||
|
|
||||||
|
@ -144,7 +247,7 @@ public class KeycloakInstalled {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logoutDesktop() throws IOException, URISyntaxException, InterruptedException {
|
private void logoutDesktop() throws IOException, URISyntaxException, InterruptedException {
|
||||||
CallbackListener callback = new CallbackListener();
|
CallbackListener callback = new CallbackListener(getLogoutResponseWriter());
|
||||||
callback.start();
|
callback.start();
|
||||||
|
|
||||||
String redirectUri = "http://localhost:" + callback.server.getLocalPort();
|
String redirectUri = "http://localhost:" + callback.server.getLocalPort();
|
||||||
|
@ -167,9 +270,6 @@ public class KeycloakInstalled {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loginManual(PrintStream printer, Reader reader) throws IOException, ServerRequest.HttpFailure, VerificationException {
|
public void loginManual(PrintStream printer, Reader reader) throws IOException, ServerRequest.HttpFailure, VerificationException {
|
||||||
CallbackListener callback = new CallbackListener();
|
|
||||||
callback.start();
|
|
||||||
|
|
||||||
String redirectUri = "urn:ietf:wg:oauth:2.0:oob";
|
String redirectUri = "urn:ietf:wg:oauth:2.0:oob";
|
||||||
|
|
||||||
String authUrl = deployment.getAuthUrl().clone()
|
String authUrl = deployment.getAuthUrl().clone()
|
||||||
|
@ -208,7 +308,14 @@ public class KeycloakInstalled {
|
||||||
parseAccessToken(tokenResponse);
|
parseAccessToken(tokenResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void refreshToken(String refreshToken) throws IOException, ServerRequest.HttpFailure, VerificationException {
|
||||||
|
AccessTokenResponse tokenResponse = ServerRequest.invokeRefresh(deployment, refreshToken);
|
||||||
|
parseAccessToken(tokenResponse);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private void parseAccessToken(AccessTokenResponse tokenResponse) throws VerificationException {
|
private void parseAccessToken(AccessTokenResponse tokenResponse) throws VerificationException {
|
||||||
|
this.tokenResponse = tokenResponse;
|
||||||
tokenString = tokenResponse.getToken();
|
tokenString = tokenResponse.getToken();
|
||||||
refreshToken = tokenResponse.getRefreshToken();
|
refreshToken = tokenResponse.getRefreshToken();
|
||||||
idTokenString = tokenResponse.getIdToken();
|
idTokenString = tokenResponse.getIdToken();
|
||||||
|
@ -240,6 +347,10 @@ public class KeycloakInstalled {
|
||||||
return refreshToken;
|
return refreshToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AccessTokenResponse getTokenResponse() {
|
||||||
|
return tokenResponse;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isDesktopSupported() {
|
public boolean isDesktopSupported() {
|
||||||
return Desktop.isDesktopSupported();
|
return Desktop.isDesktopSupported();
|
||||||
}
|
}
|
||||||
|
@ -248,6 +359,8 @@ public class KeycloakInstalled {
|
||||||
return deployment;
|
return deployment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void processCode(String code, String redirectUri) throws IOException, ServerRequest.HttpFailure, VerificationException {
|
private void processCode(String code, String redirectUri) throws IOException, ServerRequest.HttpFailure, VerificationException {
|
||||||
AccessTokenResponse tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, redirectUri, null);
|
AccessTokenResponse tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, redirectUri, null);
|
||||||
parseAccessToken(tokenResponse);
|
parseAccessToken(tokenResponse);
|
||||||
|
@ -269,6 +382,7 @@ public class KeycloakInstalled {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class CallbackListener extends Thread {
|
public class CallbackListener extends Thread {
|
||||||
|
|
||||||
private ServerSocket server;
|
private ServerSocket server;
|
||||||
|
@ -283,14 +397,19 @@ public class KeycloakInstalled {
|
||||||
|
|
||||||
private String state;
|
private String state;
|
||||||
|
|
||||||
public CallbackListener() throws IOException {
|
private Socket socket;
|
||||||
|
|
||||||
|
private HttpResponseWriter writer;
|
||||||
|
|
||||||
|
public CallbackListener(HttpResponseWriter writer) throws IOException {
|
||||||
|
this.writer = writer;
|
||||||
server = new ServerSocket(0);
|
server = new ServerSocket(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
Socket socket = server.accept();
|
socket = server.accept();
|
||||||
|
|
||||||
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||||
String request = br.readLine();
|
String request = br.readLine();
|
||||||
|
@ -314,10 +433,15 @@ public class KeycloakInstalled {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
|
OutputStreamWriter out = new OutputStreamWriter(socket.getOutputStream());
|
||||||
pw.println("Please close window and return to application");
|
PrintWriter pw = new PrintWriter(out);
|
||||||
pw.flush();
|
|
||||||
|
|
||||||
|
if (error == null) {
|
||||||
|
writer.success(pw, KeycloakInstalled.this);
|
||||||
|
} else {
|
||||||
|
writer.failure(pw, KeycloakInstalled.this);
|
||||||
|
}
|
||||||
|
pw.flush();
|
||||||
socket.close();
|
socket.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
errorException = e;
|
errorException = e;
|
||||||
|
@ -328,6 +452,8 @@ public class KeycloakInstalled {
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<name>Keycloak Jetty Integration</name>
|
<name>Keycloak Jetty Integration</name>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -33,6 +33,13 @@
|
||||||
interval: 5
|
interval: 5
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var scripts = document.getElementsByTagName('script');
|
||||||
|
for (var i = 0; i < scripts.length; i++) {
|
||||||
|
if ((scripts[i].src.indexOf('keycloak.js') !== -1 || scripts[i].src.indexOf('keycloak.min.js') !== -1) && scripts[i].src.indexOf('version=') !== -1) {
|
||||||
|
kc.iframeVersion = scripts[i].src.substring(scripts[i].src.indexOf('version=') + 8).split('&')[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
kc.init = function (initOptions) {
|
kc.init = function (initOptions) {
|
||||||
kc.authenticated = false;
|
kc.authenticated = false;
|
||||||
|
|
||||||
|
@ -831,6 +838,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
|
var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
|
||||||
|
if (kc.iframeVersion) {
|
||||||
|
src = src + '?version=' + kc.iframeVersion;
|
||||||
|
}
|
||||||
|
|
||||||
iframe.setAttribute('src', src );
|
iframe.setAttribute('src', src );
|
||||||
iframe.style.display = 'none';
|
iframe.style.display = 'none';
|
||||||
document.body.appendChild(iframe);
|
document.body.appendChild(iframe);
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
} else if (!init) {
|
} else if (!init) {
|
||||||
var req = new XMLHttpRequest();
|
var req = new XMLHttpRequest();
|
||||||
|
|
||||||
var url = location.href + "/init";
|
var url = location.href.split("?")[0] + "/init";
|
||||||
url += "?client_id=" + encodeURIComponent(clientId);
|
url += "?client_id=" + encodeURIComponent(clientId);
|
||||||
url += "&origin=" + encodeURIComponent(origin);
|
url += "&origin=" + encodeURIComponent(origin);
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<name>Keycloak OIDC Client Adapter Modules</name>
|
<name>Keycloak OIDC Client Adapter Modules</name>
|
||||||
|
@ -34,6 +34,7 @@
|
||||||
<module>adapter-core</module>
|
<module>adapter-core</module>
|
||||||
<module>as7-eap6</module>
|
<module>as7-eap6</module>
|
||||||
<module>installed</module>
|
<module>installed</module>
|
||||||
|
<module>cli-sso</module>
|
||||||
<module>jaxrs-oauth-client</module>
|
<module>jaxrs-oauth-client</module>
|
||||||
<module>jetty</module>
|
<module>jetty</module>
|
||||||
<module>js</module>
|
<module>js</module>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -54,72 +54,96 @@ import java.util.regex.Pattern;
|
||||||
*/
|
*/
|
||||||
public class KeycloakOIDCFilter implements Filter {
|
public class KeycloakOIDCFilter implements Filter {
|
||||||
|
|
||||||
|
private final static Logger log = Logger.getLogger("" + KeycloakOIDCFilter.class);
|
||||||
|
|
||||||
public static final String SKIP_PATTERN_PARAM = "keycloak.config.skipPattern";
|
public static final String SKIP_PATTERN_PARAM = "keycloak.config.skipPattern";
|
||||||
|
|
||||||
|
public static final String CONFIG_RESOLVER_PARAM = "keycloak.config.resolver";
|
||||||
|
|
||||||
|
public static final String CONFIG_FILE_PARAM = "keycloak.config.file";
|
||||||
|
|
||||||
|
public static final String CONFIG_PATH_PARAM = "keycloak.config.path";
|
||||||
|
|
||||||
protected AdapterDeploymentContext deploymentContext;
|
protected AdapterDeploymentContext deploymentContext;
|
||||||
|
|
||||||
protected SessionIdMapper idMapper = new InMemorySessionIdMapper();
|
protected SessionIdMapper idMapper = new InMemorySessionIdMapper();
|
||||||
|
|
||||||
protected NodesRegistrationManagement nodesRegistrationManagement;
|
protected NodesRegistrationManagement nodesRegistrationManagement;
|
||||||
|
|
||||||
protected Pattern skipPattern;
|
protected Pattern skipPattern;
|
||||||
|
|
||||||
private final static Logger log = Logger.getLogger(""+KeycloakOIDCFilter.class);
|
private final KeycloakConfigResolver definedconfigResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor that can be used to define a {@code KeycloakConfigResolver} that will be used at initialization to
|
||||||
|
* provide the {@code KeycloakDeployment}.
|
||||||
|
* @param definedconfigResolver the resolver
|
||||||
|
*/
|
||||||
|
public KeycloakOIDCFilter(KeycloakConfigResolver definedconfigResolver) {
|
||||||
|
this.definedconfigResolver = definedconfigResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeycloakOIDCFilter() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(final FilterConfig filterConfig) throws ServletException {
|
public void init(final FilterConfig filterConfig) throws ServletException {
|
||||||
|
|
||||||
String skipPatternDefinition = filterConfig.getInitParameter(SKIP_PATTERN_PARAM);
|
String skipPatternDefinition = filterConfig.getInitParameter(SKIP_PATTERN_PARAM);
|
||||||
if (skipPatternDefinition != null) {
|
if (skipPatternDefinition != null) {
|
||||||
skipPattern = Pattern.compile(skipPatternDefinition, Pattern.DOTALL);
|
skipPattern = Pattern.compile(skipPatternDefinition, Pattern.DOTALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
String configResolverClass = filterConfig.getInitParameter("keycloak.config.resolver");
|
if (definedconfigResolver != null) {
|
||||||
if (configResolverClass != null) {
|
deploymentContext = new AdapterDeploymentContext(definedconfigResolver);
|
||||||
try {
|
log.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", definedconfigResolver.getClass());
|
||||||
KeycloakConfigResolver configResolver = (KeycloakConfigResolver) getClass().getClassLoader().loadClass(configResolverClass).newInstance();
|
|
||||||
deploymentContext = new AdapterDeploymentContext(configResolver);
|
|
||||||
log.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
log.log(Level.FINE, "The specified resolver {0} could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: {1}", new Object[]{configResolverClass, ex.getMessage()});
|
|
||||||
deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
String fp = filterConfig.getInitParameter("keycloak.config.file");
|
String configResolverClass = filterConfig.getInitParameter(CONFIG_RESOLVER_PARAM);
|
||||||
InputStream is = null;
|
if (configResolverClass != null) {
|
||||||
if (fp != null) {
|
|
||||||
try {
|
try {
|
||||||
is = new FileInputStream(fp);
|
KeycloakConfigResolver configResolver = (KeycloakConfigResolver) getClass().getClassLoader().loadClass(configResolverClass).newInstance();
|
||||||
} catch (FileNotFoundException e) {
|
deploymentContext = new AdapterDeploymentContext(configResolver);
|
||||||
throw new RuntimeException(e);
|
log.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.log(Level.FINE, "The specified resolver {0} could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: {1}", new Object[]{configResolverClass, ex.getMessage()});
|
||||||
|
deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
String path = "/WEB-INF/keycloak.json";
|
String fp = filterConfig.getInitParameter(CONFIG_FILE_PARAM);
|
||||||
String pathParam = filterConfig.getInitParameter("keycloak.config.path");
|
InputStream is = null;
|
||||||
if (pathParam != null) path = pathParam;
|
if (fp != null) {
|
||||||
is = filterConfig.getServletContext().getResourceAsStream(path);
|
try {
|
||||||
|
is = new FileInputStream(fp);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String path = "/WEB-INF/keycloak.json";
|
||||||
|
String pathParam = filterConfig.getInitParameter(CONFIG_PATH_PARAM);
|
||||||
|
if (pathParam != null) path = pathParam;
|
||||||
|
is = filterConfig.getServletContext().getResourceAsStream(path);
|
||||||
|
}
|
||||||
|
KeycloakDeployment kd = createKeycloakDeploymentFrom(is);
|
||||||
|
deploymentContext = new AdapterDeploymentContext(kd);
|
||||||
|
log.fine("Keycloak is using a per-deployment configuration.");
|
||||||
}
|
}
|
||||||
KeycloakDeployment kd = createKeycloakDeploymentFrom(is);
|
|
||||||
deploymentContext = new AdapterDeploymentContext(kd);
|
|
||||||
log.fine("Keycloak is using a per-deployment configuration.");
|
|
||||||
}
|
}
|
||||||
filterConfig.getServletContext().setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext);
|
filterConfig.getServletContext().setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext);
|
||||||
nodesRegistrationManagement = new NodesRegistrationManagement();
|
nodesRegistrationManagement = new NodesRegistrationManagement();
|
||||||
}
|
}
|
||||||
|
|
||||||
private KeycloakDeployment createKeycloakDeploymentFrom(InputStream is) {
|
private KeycloakDeployment createKeycloakDeploymentFrom(InputStream is) {
|
||||||
|
|
||||||
if (is == null) {
|
if (is == null) {
|
||||||
log.fine("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
|
log.fine("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
|
||||||
return new KeycloakDeployment();
|
return new KeycloakDeployment();
|
||||||
}
|
}
|
||||||
|
|
||||||
return KeycloakDeploymentBuilder.build(is);
|
return KeycloakDeploymentBuilder.build(is);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
|
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
|
||||||
|
|
||||||
log.fine("Keycloak OIDC Filter");
|
log.fine("Keycloak OIDC Filter");
|
||||||
//System.err.println("Keycloak OIDC Filter: " + ((HttpServletRequest)req).getRequestURL().toString());
|
|
||||||
HttpServletRequest request = (HttpServletRequest) req;
|
HttpServletRequest request = (HttpServletRequest) req;
|
||||||
HttpServletResponse response = (HttpServletResponse) res;
|
HttpServletResponse response = (HttpServletResponse) res;
|
||||||
|
|
||||||
|
@ -201,7 +225,7 @@ public class KeycloakOIDCFilter implements Filter {
|
||||||
*
|
*
|
||||||
* @param request the request to check
|
* @param request the request to check
|
||||||
* @return {@code true} if the request should not be handled,
|
* @return {@code true} if the request should not be handled,
|
||||||
* {@code false} otherwise.
|
* {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
private boolean shouldSkip(HttpServletRequest request) {
|
private boolean shouldSkip(HttpServletRequest request) {
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>spring-boot-container-bundle</artifactId>
|
<artifactId>spring-boot-container-bundle</artifactId>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -19,6 +19,9 @@ package org.keycloak.adapters.springsecurity.facade;
|
||||||
|
|
||||||
import org.keycloak.KeycloakSecurityContext;
|
import org.keycloak.KeycloakSecurityContext;
|
||||||
import org.keycloak.adapters.OIDCHttpFacade;
|
import org.keycloak.adapters.OIDCHttpFacade;
|
||||||
|
import org.keycloak.adapters.spi.KeycloakAccount;
|
||||||
|
import org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount;
|
||||||
|
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
@ -57,7 +60,8 @@ public class SimpleHttpFacade implements OIDCHttpFacade {
|
||||||
SecurityContext context = SecurityContextHolder.getContext();
|
SecurityContext context = SecurityContextHolder.getContext();
|
||||||
|
|
||||||
if (context != null && context.getAuthentication() != null) {
|
if (context != null && context.getAuthentication() != null) {
|
||||||
return (KeycloakSecurityContext) context.getAuthentication().getDetails();
|
KeycloakAuthenticationToken authentication = (KeycloakAuthenticationToken) context.getAuthentication();
|
||||||
|
return authentication.getAccount().getKeycloakSecurityContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
package org.keycloak.adapters.springsecurity.facade;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||||
|
import org.keycloak.adapters.spi.KeycloakAccount;
|
||||||
|
import org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount;
|
||||||
|
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||||
|
import org.mockito.internal.util.collections.Sets;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
public class SimpleHttpFacadeTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
SecurityContext springSecurityContext = SecurityContextHolder.createEmptyContext();
|
||||||
|
SecurityContextHolder.setContext(springSecurityContext);
|
||||||
|
Set<String> roles = Sets.newSet("user");
|
||||||
|
Principal principal = mock(Principal.class);
|
||||||
|
RefreshableKeycloakSecurityContext keycloakSecurityContext = mock(RefreshableKeycloakSecurityContext.class);
|
||||||
|
KeycloakAccount account = new SimpleKeycloakAccount(principal, roles, keycloakSecurityContext);
|
||||||
|
KeycloakAuthenticationToken token = new KeycloakAuthenticationToken(account);
|
||||||
|
springSecurityContext.setAuthentication(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldRetrieveKeycloakSecurityContext() {
|
||||||
|
SimpleHttpFacade facade = new SimpleHttpFacade(new MockHttpServletRequest(), new MockHttpServletResponse());
|
||||||
|
|
||||||
|
assertNotNull(facade.getSecurityContext());
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<name>Keycloak Tomcat Integration</name>
|
<name>Keycloak Tomcat Integration</name>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -90,6 +90,11 @@ class ElytronHttpFacade implements OIDCHttpFacade {
|
||||||
|
|
||||||
void authenticationComplete() {
|
void authenticationComplete() {
|
||||||
if (securityIdentity != null) {
|
if (securityIdentity != null) {
|
||||||
|
HttpScope requestScope = request.getScope(Scope.EXCHANGE);
|
||||||
|
RefreshableKeycloakSecurityContext keycloakSecurityContext = account.getKeycloakSecurityContext();
|
||||||
|
|
||||||
|
requestScope.setAttachment(KeycloakSecurityContext.class.getName(), keycloakSecurityContext);
|
||||||
|
|
||||||
this.request.authenticationComplete(response -> {
|
this.request.authenticationComplete(response -> {
|
||||||
if (!restored) {
|
if (!restored) {
|
||||||
responseConsumer.accept(response);
|
responseConsumer.accept(response);
|
||||||
|
|
|
@ -71,7 +71,7 @@ class KeycloakHttpServerAuthenticationMechanism implements HttpServerAuthenticat
|
||||||
AdapterDeploymentContext deploymentContext = getDeploymentContext(request);
|
AdapterDeploymentContext deploymentContext = getDeploymentContext(request);
|
||||||
|
|
||||||
if (deploymentContext == null) {
|
if (deploymentContext == null) {
|
||||||
LOGGER.debugf("Ignoring request for path [%s] from mechanism [%s]. No deployment context found.", request.getRequestURI());
|
LOGGER.debugf("Ignoring request for path [%s] from mechanism [%s]. No deployment context found.", request.getRequestURI(), getMechanismName());
|
||||||
request.noAuthenticationInProgress();
|
request.noAuthenticationInProgress();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<name>Keycloak WildFly Integration</name>
|
<name>Keycloak WildFly Integration</name>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,8 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD
|
||||||
public final class KeycloakAdapterConfigService {
|
public final class KeycloakAdapterConfigService {
|
||||||
|
|
||||||
private static final String CREDENTIALS_JSON_NAME = "credentials";
|
private static final String CREDENTIALS_JSON_NAME = "credentials";
|
||||||
|
|
||||||
|
private static final String REDIRECT_REWRITE_RULE_JSON_NAME = "redirect-rewrite-rule";
|
||||||
|
|
||||||
private static final KeycloakAdapterConfigService INSTANCE = new KeycloakAdapterConfigService();
|
private static final KeycloakAdapterConfigService INSTANCE = new KeycloakAdapterConfigService();
|
||||||
|
|
||||||
|
@ -129,6 +131,56 @@ public final class KeycloakAdapterConfigService {
|
||||||
ModelNode deployment = this.secureDeployments.get(deploymentNameFromOp(operation));
|
ModelNode deployment = this.secureDeployments.get(deploymentNameFromOp(operation));
|
||||||
return deployment.get(CREDENTIALS_JSON_NAME);
|
return deployment.get(CREDENTIALS_JSON_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addRedirectRewriteRule(ModelNode operation, ModelNode model) {
|
||||||
|
ModelNode redirectRewritesRules = redirectRewriteRuleFromOp(operation);
|
||||||
|
if (!redirectRewritesRules.isDefined()) {
|
||||||
|
redirectRewritesRules = new ModelNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
String redirectRewriteRuleName = redirectRewriteRule(operation);
|
||||||
|
if (!redirectRewriteRuleName.contains(".")) {
|
||||||
|
redirectRewritesRules.get(redirectRewriteRuleName).set(model.get("value").asString());
|
||||||
|
} else {
|
||||||
|
String[] parts = redirectRewriteRuleName.split("\\.");
|
||||||
|
String provider = parts[0];
|
||||||
|
String property = parts[1];
|
||||||
|
ModelNode redirectRewriteRule = redirectRewritesRules.get(provider);
|
||||||
|
if (!redirectRewriteRule.isDefined()) {
|
||||||
|
redirectRewriteRule = new ModelNode();
|
||||||
|
}
|
||||||
|
redirectRewriteRule.get(property).set(model.get("value").asString());
|
||||||
|
redirectRewritesRules.set(provider, redirectRewriteRule);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelNode deployment = this.secureDeployments.get(deploymentNameFromOp(operation));
|
||||||
|
deployment.get(REDIRECT_REWRITE_RULE_JSON_NAME).set(redirectRewritesRules);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeRedirectRewriteRule(ModelNode operation) {
|
||||||
|
ModelNode redirectRewritesRules = redirectRewriteRuleFromOp(operation);
|
||||||
|
if (!redirectRewritesRules.isDefined()) {
|
||||||
|
throw new RuntimeException("Can not remove redirect rewrite rule. No rules defined for deployment in op " + operation.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
String ruleName = credentialNameFromOp(operation);
|
||||||
|
redirectRewritesRules.remove(ruleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateRedirectRewriteRule(ModelNode operation, String attrName, ModelNode resolvedValue) {
|
||||||
|
ModelNode redirectRewritesRules = redirectRewriteRuleFromOp(operation);
|
||||||
|
if (!redirectRewritesRules.isDefined()) {
|
||||||
|
throw new RuntimeException("Can not update redirect rewrite rule. No rules defined for deployment in op " + operation.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
String ruleName = credentialNameFromOp(operation);
|
||||||
|
redirectRewritesRules.get(ruleName).set(resolvedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ModelNode redirectRewriteRuleFromOp(ModelNode operation) {
|
||||||
|
ModelNode deployment = this.secureDeployments.get(deploymentNameFromOp(operation));
|
||||||
|
return deployment.get(REDIRECT_REWRITE_RULE_JSON_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
private String realmNameFromOp(ModelNode operation) {
|
private String realmNameFromOp(ModelNode operation) {
|
||||||
return valueFromOpAddress(RealmDefinition.TAG_NAME, operation);
|
return valueFromOpAddress(RealmDefinition.TAG_NAME, operation);
|
||||||
|
@ -141,6 +193,10 @@ public final class KeycloakAdapterConfigService {
|
||||||
private String credentialNameFromOp(ModelNode operation) {
|
private String credentialNameFromOp(ModelNode operation) {
|
||||||
return valueFromOpAddress(CredentialDefinition.TAG_NAME, operation);
|
return valueFromOpAddress(CredentialDefinition.TAG_NAME, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String redirectRewriteRule(ModelNode operation) {
|
||||||
|
return valueFromOpAddress(RedirecRewritetRuleDefinition.TAG_NAME, operation);
|
||||||
|
}
|
||||||
|
|
||||||
private String valueFromOpAddress(String addrElement, ModelNode operation) {
|
private String valueFromOpAddress(String addrElement, ModelNode operation) {
|
||||||
String deploymentName = getValueOfAddrElement(operation.get(ADDRESS), addrElement);
|
String deploymentName = getValueOfAddrElement(operation.get(ADDRESS), addrElement);
|
||||||
|
|
|
@ -48,6 +48,7 @@ public class KeycloakExtension implements Extension {
|
||||||
static final RealmDefinition REALM_DEFINITION = new RealmDefinition();
|
static final RealmDefinition REALM_DEFINITION = new RealmDefinition();
|
||||||
static final SecureDeploymentDefinition SECURE_DEPLOYMENT_DEFINITION = new SecureDeploymentDefinition();
|
static final SecureDeploymentDefinition SECURE_DEPLOYMENT_DEFINITION = new SecureDeploymentDefinition();
|
||||||
static final CredentialDefinition CREDENTIAL_DEFINITION = new CredentialDefinition();
|
static final CredentialDefinition CREDENTIAL_DEFINITION = new CredentialDefinition();
|
||||||
|
static final RedirecRewritetRuleDefinition REDIRECT_RULE_DEFINITON = new RedirecRewritetRuleDefinition();
|
||||||
|
|
||||||
public static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
|
public static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
|
||||||
StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
|
StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
|
||||||
|
@ -77,6 +78,7 @@ public class KeycloakExtension implements Extension {
|
||||||
registration.registerSubModel(REALM_DEFINITION);
|
registration.registerSubModel(REALM_DEFINITION);
|
||||||
ManagementResourceRegistration secureDeploymentRegistration = registration.registerSubModel(SECURE_DEPLOYMENT_DEFINITION);
|
ManagementResourceRegistration secureDeploymentRegistration = registration.registerSubModel(SECURE_DEPLOYMENT_DEFINITION);
|
||||||
secureDeploymentRegistration.registerSubModel(CREDENTIAL_DEFINITION);
|
secureDeploymentRegistration.registerSubModel(CREDENTIAL_DEFINITION);
|
||||||
|
secureDeploymentRegistration.registerSubModel(REDIRECT_RULE_DEFINITON);
|
||||||
|
|
||||||
subsystem.registerXMLElementWriter(PARSER);
|
subsystem.registerXMLElementWriter(PARSER);
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,12 +96,17 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
|
||||||
PathElement.pathElement(SecureDeploymentDefinition.TAG_NAME, name));
|
PathElement.pathElement(SecureDeploymentDefinition.TAG_NAME, name));
|
||||||
addSecureDeployment.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
|
addSecureDeployment.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
|
||||||
List<ModelNode> credentialsToAdd = new ArrayList<ModelNode>();
|
List<ModelNode> credentialsToAdd = new ArrayList<ModelNode>();
|
||||||
|
List<ModelNode> redirectRulesToAdd = new ArrayList<ModelNode>();
|
||||||
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
|
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
|
||||||
String tagName = reader.getLocalName();
|
String tagName = reader.getLocalName();
|
||||||
if (tagName.equals(CredentialDefinition.TAG_NAME)) {
|
if (tagName.equals(CredentialDefinition.TAG_NAME)) {
|
||||||
readCredential(reader, addr, credentialsToAdd);
|
readCredential(reader, addr, credentialsToAdd);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (tagName.equals(RedirecRewritetRuleDefinition.TAG_NAME)) {
|
||||||
|
readRewriteRule(reader, addr, redirectRulesToAdd);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
SimpleAttributeDefinition def = SecureDeploymentDefinition.lookup(tagName);
|
SimpleAttributeDefinition def = SecureDeploymentDefinition.lookup(tagName);
|
||||||
if (def == null) throw new XMLStreamException("Unknown secure-deployment tag " + tagName);
|
if (def == null) throw new XMLStreamException("Unknown secure-deployment tag " + tagName);
|
||||||
|
@ -111,6 +116,7 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
|
||||||
// Must add credentials after the deployment is added.
|
// Must add credentials after the deployment is added.
|
||||||
resourcesToAdd.add(addSecureDeployment);
|
resourcesToAdd.add(addSecureDeployment);
|
||||||
resourcesToAdd.addAll(credentialsToAdd);
|
resourcesToAdd.addAll(credentialsToAdd);
|
||||||
|
resourcesToAdd.addAll(redirectRulesToAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readCredential(XMLExtendedStreamReader reader, PathAddress parent, List<ModelNode> credentialsToAdd) throws XMLStreamException {
|
public void readCredential(XMLExtendedStreamReader reader, PathAddress parent, List<ModelNode> credentialsToAdd) throws XMLStreamException {
|
||||||
|
@ -149,6 +155,43 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void readRewriteRule(XMLExtendedStreamReader reader, PathAddress parent, List<ModelNode> rewriteRuleToToAdd) throws XMLStreamException {
|
||||||
|
String name = readNameAttribute(reader);
|
||||||
|
|
||||||
|
Map<String, String> values = new HashMap<>();
|
||||||
|
String textValue = null;
|
||||||
|
while (reader.hasNext()) {
|
||||||
|
int next = reader.next();
|
||||||
|
if (next == CHARACTERS) {
|
||||||
|
// text value of redirect rule element
|
||||||
|
String text = reader.getText();
|
||||||
|
if (text == null || text.trim().isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
textValue = text;
|
||||||
|
} else if (next == START_ELEMENT) {
|
||||||
|
String key = reader.getLocalName();
|
||||||
|
reader.next();
|
||||||
|
String value = reader.getText();
|
||||||
|
reader.next();
|
||||||
|
|
||||||
|
values.put(key, value);
|
||||||
|
} else if (next == END_ELEMENT) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textValue != null) {
|
||||||
|
ModelNode addRedirectRule = getRedirectRuleToAdd(parent, name, textValue);
|
||||||
|
rewriteRuleToToAdd.add(addRedirectRule);
|
||||||
|
} else {
|
||||||
|
for (Map.Entry<String, String> entry : values.entrySet()) {
|
||||||
|
ModelNode addRedirectRule = getRedirectRuleToAdd(parent, name + "." + entry.getKey(), entry.getValue());
|
||||||
|
rewriteRuleToToAdd.add(addRedirectRule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ModelNode getCredentialToAdd(PathAddress parent, String name, String value) {
|
private ModelNode getCredentialToAdd(PathAddress parent, String name, String value) {
|
||||||
ModelNode addCredential = new ModelNode();
|
ModelNode addCredential = new ModelNode();
|
||||||
|
@ -158,6 +201,15 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
|
||||||
addCredential.get(CredentialDefinition.VALUE.getName()).set(value);
|
addCredential.get(CredentialDefinition.VALUE.getName()).set(value);
|
||||||
return addCredential;
|
return addCredential;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ModelNode getRedirectRuleToAdd(PathAddress parent, String name, String value) {
|
||||||
|
ModelNode addRedirectRule = new ModelNode();
|
||||||
|
addRedirectRule.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
|
||||||
|
PathAddress addr = PathAddress.pathAddress(parent, PathElement.pathElement(RedirecRewritetRuleDefinition.TAG_NAME, name));
|
||||||
|
addRedirectRule.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
|
||||||
|
addRedirectRule.get(RedirecRewritetRuleDefinition.VALUE.getName()).set(value);
|
||||||
|
return addRedirectRule;
|
||||||
|
}
|
||||||
|
|
||||||
// expects that the current tag will have one single attribute called "name"
|
// expects that the current tag will have one single attribute called "name"
|
||||||
private String readNameAttribute(XMLExtendedStreamReader reader) throws XMLStreamException {
|
private String readNameAttribute(XMLExtendedStreamReader reader) throws XMLStreamException {
|
||||||
|
@ -219,6 +271,11 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
|
||||||
if (credentials.isDefined()) {
|
if (credentials.isDefined()) {
|
||||||
writeCredentials(writer, credentials);
|
writeCredentials(writer, credentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModelNode redirectRewriteRule = deploymentElements.get(RedirecRewritetRuleDefinition.TAG_NAME);
|
||||||
|
if (redirectRewriteRule.isDefined()) {
|
||||||
|
writeRedirectRules(writer, redirectRewriteRule);
|
||||||
|
}
|
||||||
|
|
||||||
writer.writeEndElement();
|
writer.writeEndElement();
|
||||||
}
|
}
|
||||||
|
@ -265,6 +322,34 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
|
||||||
writer.writeEndElement();
|
writer.writeEndElement();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void writeRedirectRules(XMLExtendedStreamWriter writer, ModelNode redirectRules) throws XMLStreamException {
|
||||||
|
Map<String, Object> parsed = new LinkedHashMap<>();
|
||||||
|
for (Property redirectRule : redirectRules.asPropertyList()) {
|
||||||
|
String ruleName = redirectRule.getName();
|
||||||
|
String ruleValue = redirectRule.getValue().get(RedirecRewritetRuleDefinition.VALUE.getName()).asString();
|
||||||
|
parsed.put(ruleName, ruleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, Object> entry : parsed.entrySet()) {
|
||||||
|
writer.writeStartElement(RedirecRewritetRuleDefinition.TAG_NAME);
|
||||||
|
writer.writeAttribute("name", entry.getKey());
|
||||||
|
|
||||||
|
Object value = entry.getValue();
|
||||||
|
if (value instanceof String) {
|
||||||
|
writeCharacters(writer, (String) value);
|
||||||
|
} else {
|
||||||
|
Map<String, String> redirectRulesProps = (Map<String, String>) value;
|
||||||
|
for (Map.Entry<String, String> prop : redirectRulesProps.entrySet()) {
|
||||||
|
writer.writeStartElement(prop.getKey());
|
||||||
|
writeCharacters(writer, prop.getValue());
|
||||||
|
writer.writeEndElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.writeEndElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// code taken from org.jboss.as.controller.AttributeMarshaller
|
// code taken from org.jboss.as.controller.AttributeMarshaller
|
||||||
private void writeCharacters(XMLExtendedStreamWriter writer, String content) throws XMLStreamException {
|
private void writeCharacters(XMLExtendedStreamWriter writer, String content) throws XMLStreamException {
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* 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.subsystem.adapter.extension;
|
||||||
|
|
||||||
|
import org.jboss.as.controller.AttributeDefinition;
|
||||||
|
import org.jboss.as.controller.PathElement;
|
||||||
|
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
|
||||||
|
import org.jboss.as.controller.SimpleResourceDefinition;
|
||||||
|
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
|
||||||
|
import org.jboss.as.controller.operations.validation.StringLengthValidator;
|
||||||
|
import org.jboss.as.controller.registry.ManagementResourceRegistration;
|
||||||
|
import org.jboss.dmr.ModelType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author sblanc
|
||||||
|
*/
|
||||||
|
public class RedirecRewritetRuleDefinition extends SimpleResourceDefinition {
|
||||||
|
|
||||||
|
public static final String TAG_NAME = "redirect-rewrite-rule";
|
||||||
|
|
||||||
|
protected static final AttributeDefinition VALUE =
|
||||||
|
new SimpleAttributeDefinitionBuilder("value", ModelType.STRING, false)
|
||||||
|
.setAllowExpression(true)
|
||||||
|
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, false, true))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
public RedirecRewritetRuleDefinition() {
|
||||||
|
super(PathElement.pathElement(TAG_NAME),
|
||||||
|
KeycloakExtension.getResourceDescriptionResolver(TAG_NAME),
|
||||||
|
new RedirectRewriteRuleAddHandler(VALUE),
|
||||||
|
RedirectRewriteRuleRemoveHandler.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
|
||||||
|
super.registerOperations(resourceRegistration);
|
||||||
|
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
|
||||||
|
super.registerAttributes(resourceRegistration);
|
||||||
|
resourceRegistration.registerReadWriteAttribute(VALUE, null, new RedirectRewriteRuleReadWriteAttributeHandler());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* 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.subsystem.adapter.extension;
|
||||||
|
|
||||||
|
import org.jboss.as.controller.AbstractAddStepHandler;
|
||||||
|
import org.jboss.as.controller.AttributeDefinition;
|
||||||
|
import org.jboss.as.controller.OperationContext;
|
||||||
|
import org.jboss.as.controller.OperationFailedException;
|
||||||
|
import org.jboss.dmr.ModelNode;
|
||||||
|
|
||||||
|
public class RedirectRewriteRuleAddHandler extends AbstractAddStepHandler {
|
||||||
|
|
||||||
|
public RedirectRewriteRuleAddHandler(AttributeDefinition... attributes) {
|
||||||
|
super(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
|
||||||
|
KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
|
||||||
|
ckService.addRedirectRewriteRule(operation, context.resolveExpressions(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* 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.subsystem.adapter.extension;
|
||||||
|
|
||||||
|
import org.jboss.as.controller.AbstractWriteAttributeHandler;
|
||||||
|
import org.jboss.as.controller.OperationContext;
|
||||||
|
import org.jboss.as.controller.OperationFailedException;
|
||||||
|
import org.jboss.dmr.ModelNode;
|
||||||
|
|
||||||
|
public class RedirectRewriteRuleReadWriteAttributeHandler extends AbstractWriteAttributeHandler<KeycloakAdapterConfigService> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean applyUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
|
||||||
|
ModelNode resolvedValue, ModelNode currentValue, AbstractWriteAttributeHandler.HandbackHolder<KeycloakAdapterConfigService> hh) throws OperationFailedException {
|
||||||
|
|
||||||
|
KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
|
||||||
|
ckService.updateRedirectRewriteRule(operation, attributeName, resolvedValue);
|
||||||
|
|
||||||
|
hh.setHandback(ckService);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void revertUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
|
||||||
|
ModelNode valueToRestore, ModelNode valueToRevert, KeycloakAdapterConfigService ckService) throws OperationFailedException {
|
||||||
|
ckService.updateRedirectRewriteRule(operation, attributeName, valueToRestore);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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.subsystem.adapter.extension;
|
||||||
|
|
||||||
|
import org.jboss.as.controller.AbstractRemoveStepHandler;
|
||||||
|
import org.jboss.as.controller.OperationContext;
|
||||||
|
import org.jboss.as.controller.OperationFailedException;
|
||||||
|
import org.jboss.dmr.ModelNode;
|
||||||
|
|
||||||
|
public class RedirectRewriteRuleRemoveHandler extends AbstractRemoveStepHandler {
|
||||||
|
|
||||||
|
public static RedirectRewriteRuleRemoveHandler INSTANCE = new RedirectRewriteRuleRemoveHandler();
|
||||||
|
|
||||||
|
private RedirectRewriteRuleRemoveHandler() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
|
||||||
|
KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
|
||||||
|
ckService.removeRedirectRewriteRule(operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -65,6 +65,7 @@ keycloak.secure-deployment.connection-pool-size=Connection pool size for the cli
|
||||||
keycloak.secure-deployment.resource=Application name
|
keycloak.secure-deployment.resource=Application name
|
||||||
keycloak.secure-deployment.use-resource-role-mappings=Use resource level permissions from token
|
keycloak.secure-deployment.use-resource-role-mappings=Use resource level permissions from token
|
||||||
keycloak.secure-deployment.credentials=Adapter credentials
|
keycloak.secure-deployment.credentials=Adapter credentials
|
||||||
|
keycloak.secure-deployment.redirect-rewrite-rule=Apply a rewrite rule for the redirect URI
|
||||||
keycloak.secure-deployment.bearer-only=Bearer Token Auth only
|
keycloak.secure-deployment.bearer-only=Bearer Token Auth only
|
||||||
keycloak.secure-deployment.enable-basic-auth=Enable Basic Authentication
|
keycloak.secure-deployment.enable-basic-auth=Enable Basic Authentication
|
||||||
keycloak.secure-deployment.public-client=Public client
|
keycloak.secure-deployment.public-client=Public client
|
||||||
|
@ -94,4 +95,9 @@ keycloak.secure-deployment.credential=Credential value
|
||||||
keycloak.credential=Credential
|
keycloak.credential=Credential
|
||||||
keycloak.credential.value=Credential value
|
keycloak.credential.value=Credential value
|
||||||
keycloak.credential.add=Credential add
|
keycloak.credential.add=Credential add
|
||||||
keycloak.credential.remove=Credential remove
|
keycloak.credential.remove=Credential remove
|
||||||
|
|
||||||
|
keycloak.redirect-rewrite-rule=redirect-rewrite-rule
|
||||||
|
keycloak.redirect-rewrite-rule.value=redirect-rewrite-rule value
|
||||||
|
keycloak.redirect-rewrite-rule.add=redirect-rewrite-rule add
|
||||||
|
keycloak.redirect-rewrite-rule.remove=redirect-rewrite-rule remove
|
|
@ -101,6 +101,7 @@
|
||||||
<xs:element name="ssl-required" type="xs:string" minOccurs="0" maxOccurs="1" />
|
<xs:element name="ssl-required" type="xs:string" minOccurs="0" maxOccurs="1" />
|
||||||
<xs:element name="realm-public-key" type="xs:string" minOccurs="1" maxOccurs="1"/>
|
<xs:element name="realm-public-key" type="xs:string" minOccurs="1" maxOccurs="1"/>
|
||||||
<xs:element name="credential" type="credential-type" minOccurs="1" maxOccurs="1"/>
|
<xs:element name="credential" type="credential-type" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xs:element name="redirect-rewrite-rule" type="redirect-rewrite-rule-type" minOccurs="1" maxOccurs="1"/>
|
||||||
<xs:element name="auth-server-url-for-backend-requests" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
<xs:element name="auth-server-url-for-backend-requests" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||||
<xs:element name="always-refresh-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
|
<xs:element name="always-refresh-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
|
||||||
<xs:element name="register-node-at-startup" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
|
<xs:element name="register-node-at-startup" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
|
||||||
|
@ -127,4 +128,10 @@
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
<xs:attribute name="name" type="xs:string" use="required" />
|
<xs:attribute name="name" type="xs:string" use="required" />
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
<xs:complexType name="redirect-rewrite-rule-type" mixed="true">
|
||||||
|
<xs:sequence maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:any processContents="lax"></xs:any>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="name" type="xs:string" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
</xs:schema>
|
</xs:schema>
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
<auth-server-url>http://localhost:8080/auth</auth-server-url>
|
<auth-server-url>http://localhost:8080/auth</auth-server-url>
|
||||||
<ssl-required>EXTERNAL</ssl-required>
|
<ssl-required>EXTERNAL</ssl-required>
|
||||||
<credential name="secret">0aa31d98-e0aa-404c-b6e0-e771dba1e798</credential>
|
<credential name="secret">0aa31d98-e0aa-404c-b6e0-e771dba1e798</credential>
|
||||||
|
<redirect-rewrite-rule name="^/wsmaster/api/(.*)$">api/$1/</redirect-rewrite-rule>
|
||||||
</secure-deployment>
|
</secure-deployment>
|
||||||
<secure-deployment name="http-endpoint">
|
<secure-deployment name="http-endpoint">
|
||||||
<realm>master</realm>
|
<realm>master</realm>
|
||||||
|
@ -66,5 +67,6 @@
|
||||||
<credential name="jwt">
|
<credential name="jwt">
|
||||||
<client-keystore-file>/tmp/keystore.jks</client-keystore-file>
|
<client-keystore-file>/tmp/keystore.jks</client-keystore-file>
|
||||||
</credential>
|
</credential>
|
||||||
|
<redirect-rewrite-rule name="^/wsmaster/api/(.*)$">/api/$1/</redirect-rewrite-rule>
|
||||||
</secure-deployment>
|
</secure-deployment>
|
||||||
</subsystem>
|
</subsystem>
|
|
@ -20,7 +20,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<name>Keycloak Adapters</name>
|
<name>Keycloak Adapters</name>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-saml-eap-integration-pom</artifactId>
|
<artifactId>keycloak-saml-eap-integration-pom</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<name>Keycloak SAML EAP Integration</name>
|
<name>Keycloak SAML EAP Integration</name>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-saml-eap-integration-pom</artifactId>
|
<artifactId>keycloak-saml-eap-integration-pom</artifactId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ public final class FormattingXMLStreamWriter implements XMLExtendedStreamWriter,
|
||||||
public void writeStartElement(final String localName) throws XMLStreamException {
|
public void writeStartElement(final String localName) throws XMLStreamException {
|
||||||
ArrayDeque<String> namespaces = unspecifiedNamespaces;
|
ArrayDeque<String> namespaces = unspecifiedNamespaces;
|
||||||
String namespace = namespaces.getFirst();
|
String namespace = namespaces.getFirst();
|
||||||
if (namespace != NO_NAMESPACE) {
|
if (namespace == null ? NO_NAMESPACE != null : ! namespace.equals(NO_NAMESPACE)) {
|
||||||
writeStartElement(namespace, localName);
|
writeStartElement(namespace, localName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -140,9 +140,9 @@ public final class FormattingXMLStreamWriter implements XMLExtendedStreamWriter,
|
||||||
attrQueue.add(new ArgRunnable() {
|
attrQueue.add(new ArgRunnable() {
|
||||||
public void run(int arg) throws XMLStreamException {
|
public void run(int arg) throws XMLStreamException {
|
||||||
if (arg == 0) {
|
if (arg == 0) {
|
||||||
delegate.writeStartElement(prefix, namespaceURI, localName);
|
delegate.writeStartElement(prefix, localName, namespaceURI);
|
||||||
} else {
|
} else {
|
||||||
delegate.writeEmptyElement(prefix, namespaceURI, localName);
|
delegate.writeEmptyElement(prefix, localName, namespaceURI);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -165,14 +165,14 @@ public final class FormattingXMLStreamWriter implements XMLExtendedStreamWriter,
|
||||||
runAttrQueue();
|
runAttrQueue();
|
||||||
nl();
|
nl();
|
||||||
indent();
|
indent();
|
||||||
delegate.writeEmptyElement(prefix, namespaceURI, localName);
|
delegate.writeEmptyElement(prefix, localName, namespaceURI);
|
||||||
state = END_ELEMENT;
|
state = END_ELEMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeEmptyElement(final String localName) throws XMLStreamException {
|
public void writeEmptyElement(final String localName) throws XMLStreamException {
|
||||||
String namespace = unspecifiedNamespaces.getFirst();
|
String namespace = unspecifiedNamespaces.getFirst();
|
||||||
if (namespace != NO_NAMESPACE) {
|
if (namespace == null ? NO_NAMESPACE != null : ! namespace.equals(NO_NAMESPACE)) {
|
||||||
writeEmptyElement(namespace, localName);
|
writeEmptyElement(namespace, localName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -18,7 +18,10 @@
|
||||||
package org.keycloak.adapters.saml;
|
package org.keycloak.adapters.saml;
|
||||||
|
|
||||||
import org.keycloak.adapters.spi.AuthenticationError;
|
import org.keycloak.adapters.spi.AuthenticationError;
|
||||||
|
import org.keycloak.dom.saml.v2.protocol.StatusCodeType;
|
||||||
import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
|
import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
|
||||||
|
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object that describes the SAML error that happened.
|
* Object that describes the SAML error that happened.
|
||||||
|
@ -27,6 +30,7 @@ import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class SamlAuthenticationError implements AuthenticationError {
|
public class SamlAuthenticationError implements AuthenticationError {
|
||||||
|
|
||||||
public static enum Reason {
|
public static enum Reason {
|
||||||
EXTRACTION_FAILURE,
|
EXTRACTION_FAILURE,
|
||||||
INVALID_SIGNATURE,
|
INVALID_SIGNATURE,
|
||||||
|
@ -59,7 +63,18 @@ public class SamlAuthenticationError implements AuthenticationError {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "SamlAuthenticationError [reason=" + reason + ", status=" + status + "]";
|
return "SamlAuthenticationError [reason=" + reason + ", status="
|
||||||
|
+ ((status == null || status.getStatus() == null) ? "UNKNOWN" : extractStatusCode(status.getStatus().getStatusCode()))
|
||||||
|
+ "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String extractStatusCode(StatusCodeType statusCode) {
|
||||||
|
if (statusCode == null || statusCode.getValue() == null) {
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
if (Objects.equals(JBossSAMLURIConstants.STATUS_RESPONDER.get(), statusCode.getValue().toString())) {
|
||||||
|
return extractStatusCode(statusCode.getStatusCode());
|
||||||
|
}
|
||||||
|
return statusCode.getValue().toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
import org.keycloak.saml.common.constants.JBossSAMLConstants;
|
import org.keycloak.saml.common.constants.JBossSAMLConstants;
|
||||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||||
|
import org.keycloak.saml.common.util.DocumentUtil;
|
||||||
import org.keycloak.saml.processing.core.util.NamespaceContext;
|
import org.keycloak.saml.processing.core.util.NamespaceContext;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
|
@ -65,9 +66,7 @@ public class SamlDescriptorIDPKeysExtractor {
|
||||||
MultivaluedHashMap<String, KeyInfo> res = new MultivaluedHashMap<>();
|
MultivaluedHashMap<String, KeyInfo> res = new MultivaluedHashMap<>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
DocumentBuilder builder = DocumentUtil.getDocumentBuilder();
|
||||||
factory.setNamespaceAware(true);
|
|
||||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
|
||||||
Document doc = builder.parse(stream);
|
Document doc = builder.parse(stream);
|
||||||
|
|
||||||
XPathExpression expr = xpath.compile("/m:EntitiesDescriptor/m:EntityDescriptor/m:IDPSSODescriptor/m:KeyDescriptor");
|
XPathExpression expr = xpath.compile("/m:EntitiesDescriptor/m:EntityDescriptor/m:IDPSSODescriptor/m:KeyDescriptor");
|
||||||
|
|
|
@ -407,8 +407,8 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
|
||||||
|
|
||||||
SubjectType subject = assertion.getSubject();
|
SubjectType subject = assertion.getSubject();
|
||||||
SubjectType.STSubType subType = subject.getSubType();
|
SubjectType.STSubType subType = subject.getSubType();
|
||||||
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
|
NameIDType subjectNameID = subType == null ? null : (NameIDType) subType.getBaseID();
|
||||||
String principalName = subjectNameID.getValue();
|
String principalName = subjectNameID == null ? null : subjectNameID.getValue();
|
||||||
|
|
||||||
final Set<String> roles = new HashSet<>();
|
final Set<String> roles = new HashSet<>();
|
||||||
MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
|
MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
|
||||||
|
@ -473,7 +473,7 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
URI nameFormat = subjectNameID.getFormat();
|
URI nameFormat = subjectNameID == null ? null : subjectNameID.getFormat();
|
||||||
String nameFormatString = nameFormat == null ? JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get() : nameFormat.toString();
|
String nameFormatString = nameFormat == null ? JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get() : nameFormat.toString();
|
||||||
final SamlPrincipal principal = new SamlPrincipal(assertion, principalName, principalName, nameFormatString, attributes, friendlyAttributes);
|
final SamlPrincipal principal = new SamlPrincipal(assertion, principalName, principalName, nameFormatString, attributes, friendlyAttributes);
|
||||||
String index = authn == null ? null : authn.getSessionIndex();
|
String index = authn == null ? null : authn.getSessionIndex();
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<name>Keycloak SAML Jetty Integration</name>
|
<name>Keycloak SAML Jetty Integration</name>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<name>Keycloak SAML Client Adapter Modules</name>
|
<name>Keycloak SAML Client Adapter Modules</name>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<name>Keycloak SAML Tomcat Integration</name>
|
<name>Keycloak SAML Tomcat Integration</name>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-saml-tomcat-integration-pom</artifactId>
|
<artifactId>keycloak-saml-tomcat-integration-pom</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-saml-tomcat-integration-pom</artifactId>
|
<artifactId>keycloak-saml-tomcat-integration-pom</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-saml-tomcat-integration-pom</artifactId>
|
<artifactId>keycloak-saml-tomcat-integration-pom</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-saml-tomcat-integration-pom</artifactId>
|
<artifactId>keycloak-saml-tomcat-integration-pom</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -41,10 +41,20 @@ import java.util.List;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class SamlAuthenticatorValve extends AbstractSamlAuthenticatorValve {
|
public class SamlAuthenticatorValve extends AbstractSamlAuthenticatorValve {
|
||||||
|
/**
|
||||||
|
* Method called by Tomcat < 8.5.5
|
||||||
|
*/
|
||||||
public boolean authenticate(Request request, HttpServletResponse response) throws IOException {
|
public boolean authenticate(Request request, HttpServletResponse response) throws IOException {
|
||||||
return authenticateInternal(request, response, request.getContext().getLoginConfig());
|
return authenticateInternal(request, response, request.getContext().getLoginConfig());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method called by Tomcat >= 8.5.5
|
||||||
|
*/
|
||||||
|
protected boolean doAuthenticate(Request request, HttpServletResponse response) throws IOException {
|
||||||
|
return this.authenticate(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean forwardToErrorPageInternal(Request request, HttpServletResponse response, Object loginConfig) throws IOException {
|
protected boolean forwardToErrorPageInternal(Request request, HttpServletResponse response, Object loginConfig) throws IOException {
|
||||||
if (loginConfig == null) return false;
|
if (loginConfig == null) return false;
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -47,10 +47,8 @@ import org.wildfly.security.auth.callback.AnonymousAuthorizationCallback;
|
||||||
import org.wildfly.security.auth.callback.AuthenticationCompleteCallback;
|
import org.wildfly.security.auth.callback.AuthenticationCompleteCallback;
|
||||||
import org.wildfly.security.auth.callback.SecurityIdentityCallback;
|
import org.wildfly.security.auth.callback.SecurityIdentityCallback;
|
||||||
import org.wildfly.security.auth.server.SecurityIdentity;
|
import org.wildfly.security.auth.server.SecurityIdentity;
|
||||||
import org.wildfly.security.http.HttpAuthenticationException;
|
|
||||||
import org.wildfly.security.http.HttpScope;
|
import org.wildfly.security.http.HttpScope;
|
||||||
import org.wildfly.security.http.HttpServerCookie;
|
import org.wildfly.security.http.HttpServerCookie;
|
||||||
import org.wildfly.security.http.HttpServerMechanismsResponder;
|
|
||||||
import org.wildfly.security.http.HttpServerRequest;
|
import org.wildfly.security.http.HttpServerRequest;
|
||||||
import org.wildfly.security.http.HttpServerResponse;
|
import org.wildfly.security.http.HttpServerResponse;
|
||||||
import org.wildfly.security.http.Scope;
|
import org.wildfly.security.http.Scope;
|
||||||
|
@ -87,11 +85,14 @@ class ElytronHttpFacade implements HttpFacade {
|
||||||
|
|
||||||
void authenticationComplete() {
|
void authenticationComplete() {
|
||||||
this.securityIdentity = SecurityIdentityUtil.authorize(this.callbackHandler, samlSession.getPrincipal());
|
this.securityIdentity = SecurityIdentityUtil.authorize(this.callbackHandler, samlSession.getPrincipal());
|
||||||
this.request.authenticationComplete(response -> {
|
|
||||||
if (!restored) {
|
if (this.securityIdentity != null) {
|
||||||
responseConsumer.accept(response);
|
this.request.authenticationComplete(response -> {
|
||||||
}
|
if (!restored) {
|
||||||
}, () -> ((ElytronTokeStore) sessionStore).logout(true));
|
responseConsumer.accept(response);
|
||||||
|
}
|
||||||
|
}, () -> ((ElytronTokeStore) sessionStore).logout(true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void authenticationCompleteAnonymous() {
|
void authenticationCompleteAnonymous() {
|
||||||
|
|
|
@ -65,7 +65,7 @@ class KeycloakHttpServerAuthenticationMechanism implements HttpServerAuthenticat
|
||||||
SamlDeploymentContext deploymentContext = getDeploymentContext(request);
|
SamlDeploymentContext deploymentContext = getDeploymentContext(request);
|
||||||
|
|
||||||
if (deploymentContext == null) {
|
if (deploymentContext == null) {
|
||||||
LOGGER.debugf("Ignoring request for path [%s] from mechanism [%s]. No deployment context found.", request.getRequestURI());
|
LOGGER.debugf("Ignoring request for path [%s] from mechanism [%s]. No deployment context found.", request.getRequestURI(), getMechanismName());
|
||||||
request.noAuthenticationInProgress();
|
request.noAuthenticationInProgress();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<name>Keycloak SAML Wildfly Integration</name>
|
<name>Keycloak SAML Wildfly Integration</name>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
<version>3.3.0.CR1-SNAPSHOT</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue