Compare commits
243
Commits
3.2.0.CR1
...
3.3.0.Final
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3fa471223 | ||
|
|
302d5a9334 | ||
|
|
8b8db265ba | ||
|
|
1bc75509e3 | ||
|
|
b896c2eb57 | ||
|
|
fed39d4953 | ||
|
|
88e77f7b08 | ||
|
|
e201f205d1 | ||
|
|
9f38e246df | ||
|
|
6ea9ed9be8 | ||
|
|
96fe460806 | ||
|
|
d7edee8a06 | ||
|
|
366ba829ab | ||
|
|
79cd646ac7 | ||
|
|
a3a33c2359 | ||
|
|
9f3babb66c | ||
|
|
4dd469933c | ||
|
|
570ea94a33 | ||
|
|
cc36cadebd | ||
|
|
f45ef2ccc8 | ||
|
|
0caf93dfd9 | ||
|
|
4baa838e3d | ||
|
|
8cc1d02d46 | ||
|
|
794c508b10 | ||
|
|
3f8083e34c | ||
|
|
a2ccf4efa3 | ||
|
|
31be564fa3 | ||
|
|
53fe96db78 | ||
|
|
29df73df4c | ||
|
|
a76e4434fa | ||
|
|
e3a61c580e | ||
|
|
9ee8f72be9 | ||
|
|
23560d9e41 | ||
|
|
859cd30c3a | ||
|
|
36c5490250 | ||
|
|
34648c6f0c | ||
|
|
9a7d56097a | ||
|
|
05c8c74c96 | ||
|
|
9b858907ec | ||
|
|
d58c6ad4e0 | ||
|
|
e173bf33ba | ||
|
|
30ea556a7a | ||
|
|
5484e088aa | ||
|
|
83bb2ce06b | ||
|
|
155d7fddd7 | ||
|
|
9d35806538 | ||
|
|
1103b1eb40 | ||
|
|
6696c44dc0 | ||
|
|
7a57723c01 | ||
|
|
43ce466aa6 | ||
|
|
8bb473147c | ||
|
|
543f61048f | ||
|
|
b7c9c397d6 | ||
|
|
a58aca1a80 | ||
|
|
16305418f0 | ||
|
|
3ff38e22cf | ||
|
|
b4530cfbe9 | ||
|
|
245f6c0c84 | ||
|
|
010dd5709c | ||
|
|
eb5a5023c6 | ||
|
|
da9a88c45c | ||
|
|
8e5752c9db | ||
|
|
44efe24bf0 | ||
|
|
5ec178acfc | ||
|
|
fe5891fbdb | ||
|
|
287a86938e | ||
|
|
d5ae6bf77a | ||
|
|
a6dfb4ccdd | ||
|
|
d3e559453b | ||
|
|
8d3384666a | ||
|
|
2ae812420d | ||
|
|
bb10dc8c02 | ||
|
|
ef32585f57 | ||
|
|
20ac70d3fd | ||
|
|
b5ed8961f7 | ||
|
|
5b179420fd | ||
|
|
7f7c0bac1e | ||
|
|
83d3dfbc54 | ||
|
|
a6a6a62dc0 | ||
|
|
d58499acfb | ||
|
|
0c21b8c3a6 | ||
|
|
efd44d86c5 | ||
|
|
ae54dd848d | ||
|
|
5f97f23c12 | ||
|
|
1084170b52 | ||
|
|
089514d8a6 | ||
|
|
6119572934 | ||
|
|
31f82243fc | ||
|
|
672dd295f5 | ||
|
|
f97ca7c531 | ||
|
|
fc777e166c | ||
|
|
a80808c5c9 | ||
|
|
c4bb29b4bb | ||
|
|
1e4958774f | ||
|
|
61ab6d5b8b | ||
|
|
3aae4d22f6 | ||
|
|
6ff118e95c | ||
|
|
28af722a85 | ||
|
|
868e76fcf3 | ||
|
|
1289e84cdb | ||
|
|
16954fc370 | ||
|
|
4df7fd644d | ||
|
|
41cdd9db70 | ||
|
|
fbeef3e75f | ||
|
|
b36f0d89bc | ||
|
|
29386f7976 | ||
|
|
b928de0a10 | ||
|
|
0f7440d344 | ||
|
|
9f6d9f7403 | ||
|
|
cc9fb4c12e | ||
|
|
12db976e53 | ||
|
|
45eac1093d | ||
|
|
3470b1839d | ||
|
|
2876317f08 | ||
|
|
2fa55550f3 | ||
|
|
6bdc49048a | ||
|
|
07044eeeaa | ||
|
|
a72c297d5d | ||
|
|
6d003555ea | ||
|
|
52de3fff06 | ||
|
|
afc972278c | ||
|
|
9ca72dc5c6 | ||
|
|
80177e8712 | ||
|
|
125b178297 | ||
|
|
430fe60533 | ||
|
|
ed5e880931 | ||
|
|
adf5da0b2c | ||
|
|
c9b7504e3f | ||
|
|
d82acacd96 | ||
|
|
251b41a7ac | ||
|
|
3fce14d9ce | ||
|
|
5423e0534b | ||
|
|
2365445a3e | ||
|
|
3b5ca2bac0 | ||
|
|
cf0ee31bc5 | ||
|
|
77cc3db1c5 | ||
|
|
9f761a224d | ||
|
|
82affa3fb7 | ||
|
|
4583a45e78 | ||
|
|
8c93fdfb62 | ||
|
|
56bb37a9cf | ||
|
|
8f542618f7 | ||
|
|
d761085318 | ||
|
|
070bb9bf6a | ||
|
|
d105aa92e2 | ||
|
|
486a0c9528 | ||
|
|
02f043d9a6 | ||
|
|
6b991b850e | ||
|
|
852e9274d4 | ||
|
|
db9b1bcb21 | ||
|
|
a955364f0e | ||
|
|
ca9956c36b | ||
|
|
07e2136b3b | ||
|
|
1b83928652 | ||
|
|
0406d6738f | ||
|
|
121ec2603b | ||
|
|
9fa50bded5 | ||
|
|
96bdd32bd0 | ||
|
|
d8b77895db | ||
|
|
ab05216730 | ||
|
|
36080b9833 | ||
|
|
dd6a7b23c3 | ||
|
|
f8a60d5e27 | ||
|
|
3c537f5f28 | ||
|
|
8d81a4a2e4 | ||
|
|
527c61b287 | ||
|
|
c7046b6325 | ||
|
|
ec89aab8fb | ||
|
|
bed3040f5d | ||
|
|
4b9ea5cbe0 | ||
|
|
f938856230 | ||
|
|
81d0ae1160 | ||
|
|
69fa9de4d8 | ||
|
|
c8068b688d | ||
|
|
f5d7f9b09b | ||
|
|
79a64657f7 | ||
|
|
4e1eab99a2 | ||
|
|
9482a2b997 | ||
|
|
18ddb58f72 | ||
|
|
657b4cbeb2 | ||
|
|
4825a857d9 | ||
|
|
5456514499 | ||
|
|
a192b6f50a | ||
|
|
d52d685161 | ||
|
|
c6f922412f | ||
|
|
9d1ccaeefa | ||
|
|
c5b01ca9cb | ||
|
|
91ba048c2a | ||
|
|
11ff5a05e9 | ||
|
|
c36074c7f3 | ||
|
|
7406a095b9 | ||
|
|
876edc0186 | ||
|
|
27b4f0e25d | ||
|
|
a7940c6ffa | ||
|
|
1e059e3fa3 | ||
|
|
01152144bb | ||
|
|
b10e7b8c31 | ||
|
|
f68754290f | ||
|
|
ddcbee2bff | ||
|
|
badba7adaf | ||
|
|
286fb688eb | ||
|
|
b0a33c9765 | ||
|
|
b86079c589 | ||
|
|
3a13981d3f | ||
|
|
3fca731395 | ||
|
|
d2274fa49f | ||
|
|
7e7d67464e | ||
|
|
936efe872a | ||
|
|
2b427dfcd2 | ||
|
|
2f5d11087e | ||
|
|
7be2c55f61 | ||
|
|
7dbb439cf3 | ||
|
|
dac381058c | ||
|
|
48eaebf1c3 | ||
|
|
5b2e390f57 | ||
|
|
86b915d862 | ||
|
|
50424b2ad3 | ||
|
|
1721ccc85e | ||
|
|
e99b08c6da | ||
|
|
cff8a1ecc6 | ||
|
|
9531b9e4ea | ||
|
|
0aeba74185 | ||
|
|
6865b4bbb1 | ||
|
|
81c0e62d79 | ||
|
|
db269e28d6 | ||
|
|
d84ab4f90b | ||
|
|
65251748c7 | ||
|
|
5fbb362710 | ||
|
|
4b7c61111c | ||
|
|
8cb8678525 | ||
|
|
c95aace6e0 | ||
|
|
eba8dac47d | ||
|
|
e1622006c3 | ||
|
|
9a9f4137e5 | ||
|
|
adffe16cb8 | ||
|
|
1e6f099eb0 | ||
|
|
32b16717a7 | ||
|
|
09ec642543 | ||
|
|
41011a95e1 | ||
|
|
fba264433a | ||
|
|
a228c9b32b | ||
|
|
454c5f4d83 | ||
|
|
0e0140d88b |
No files matched your search
@@ -49,3 +49,9 @@ target
|
|||||||
# Maven shade
|
# Maven shade
|
||||||
#############
|
#############
|
||||||
*dependency-reduced-pom.xml
|
*dependency-reduced-pom.xml
|
||||||
|
|
||||||
|
# nodejs #
|
||||||
|
##########
|
||||||
|
# KEYCLOAK-5391: We will re-exclude node_modules when node_modules handling is worked out.
|
||||||
|
# For now, we keep our js libraries checked into GitHub, so we don't ignore.
|
||||||
|
#node_modules
|
||||||
+9
-15
@@ -1,31 +1,25 @@
|
|||||||
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:
|
|
||||||
- travis_wait 60 mvn install --no-snapshot-updates -Pdistribution -DskipTestsuite -B -V -q
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./travis-run-tests.sh $TESTS
|
- ./travis-run-tests.sh $TESTS
|
||||||
|
|||||||
@@ -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.Final</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
+6
-1
@@ -164,7 +164,12 @@ public class BearerTokenRequestAuthenticator {
|
|||||||
OIDCAuthenticationError error = new OIDCAuthenticationError(reason, description);
|
OIDCAuthenticationError error = new OIDCAuthenticationError(reason, description);
|
||||||
facade.getRequest().setError(error);
|
facade.getRequest().setError(error);
|
||||||
facade.getResponse().addHeader("WWW-Authenticate", challenge);
|
facade.getResponse().addHeader("WWW-Authenticate", challenge);
|
||||||
facade.getResponse().sendError(401);
|
if(deployment.isDelegateBearerErrorResponseSending()){
|
||||||
|
facade.getResponse().setStatus(401);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
facade.getResponse().sendError(401);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -94,6 +94,8 @@ public class KeycloakDeployment {
|
|||||||
|
|
||||||
protected Map<String, String> redirectRewriteRules;
|
protected Map<String, String> redirectRewriteRules;
|
||||||
|
|
||||||
|
protected boolean delegateBearerErrorResponseSending = false;
|
||||||
|
|
||||||
public KeycloakDeployment() {
|
public KeycloakDeployment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,5 +459,11 @@ public class KeycloakDeployment {
|
|||||||
this.redirectRewriteRules = redirectRewriteRules;
|
this.redirectRewriteRules = redirectRewriteRules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isDelegateBearerErrorResponseSending() {
|
||||||
|
return delegateBearerErrorResponseSending;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDelegateBearerErrorResponseSending(boolean delegateBearerErrorResponseSending) {
|
||||||
|
this.delegateBearerErrorResponseSending = delegateBearerErrorResponseSending;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+3
-1
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -89,7 +89,7 @@ class PathMatcher {
|
|||||||
pathString = "/";
|
pathString = "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchingUri.equals(targetUri)) {
|
if (matchingUri.equals(targetUri) || pathString.equals(targetUri)) {
|
||||||
cache.put(targetUri, entry);
|
cache.put(targetUri, entry);
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.Final</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.Final</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.Final</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.Final</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>
|
||||||
|
|||||||
Executable
+9
@@ -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.
|
||||||
|
|
||||||
Executable
+10
@@ -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`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Executable
+84
@@ -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.Final</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.Final</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
+266
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+139
-13
@@ -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.Final</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.Final</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.Final</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.Final</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.Final</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.Final</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.Final</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.Final</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.Final</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
@@ -38,6 +38,11 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>com.samaxes.maven</groupId>
|
<groupId>com.samaxes.maven</groupId>
|
||||||
<artifactId>minify-maven-plugin</artifactId>
|
<artifactId>minify-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<jsEngine>CLOSURE</jsEngine>
|
||||||
|
<closureLanguageIn>ECMASCRIPT5</closureLanguageIn>
|
||||||
|
<closureCreateSourceMap>true</closureCreateSourceMap>
|
||||||
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>min-js</id>
|
<id>min-js</id>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -214,7 +221,7 @@
|
|||||||
var callbackState = {
|
var callbackState = {
|
||||||
state: state,
|
state: state,
|
||||||
nonce: nonce,
|
nonce: nonce,
|
||||||
redirectUri: encodeURIComponent(redirectUri),
|
redirectUri: encodeURIComponent(redirectUri)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options && options.prompt) {
|
if (options && options.prompt) {
|
||||||
@@ -831,7 +838,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
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.setAttribute('title', 'keycloak-session-iframe' );
|
||||||
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.Final</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.Final</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.Final</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.Final</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.Final</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.Final</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
@@ -31,7 +31,9 @@
|
|||||||
<description/>
|
<description/>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<spring-boot.version>1.3.0.RELEASE</spring-boot.version>
|
<spring-boot.version>1.4.0.RELEASE</spring-boot.version>
|
||||||
|
<spring.version>4.1.6.RELEASE</spring.version>
|
||||||
|
<mockito.version>1.9.5</mockito.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@@ -98,7 +100,19 @@
|
|||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-test</artifactId>
|
||||||
|
<version>${spring.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-all</artifactId>
|
||||||
|
<version>${mockito.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
|
|||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
package org.keycloak.adapters.springboot.client;
|
||||||
|
|
||||||
|
import org.springframework.boot.web.client.RestTemplateCustomizer;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
public class KeycloakRestTemplateCustomizer implements RestTemplateCustomizer {
|
||||||
|
|
||||||
|
private final KeycloakSecurityContextClientRequestInterceptor keycloakInterceptor;
|
||||||
|
|
||||||
|
public KeycloakRestTemplateCustomizer() {
|
||||||
|
this(new KeycloakSecurityContextClientRequestInterceptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected KeycloakRestTemplateCustomizer(
|
||||||
|
KeycloakSecurityContextClientRequestInterceptor keycloakInterceptor
|
||||||
|
) {
|
||||||
|
this.keycloakInterceptor = keycloakInterceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(RestTemplate restTemplate) {
|
||||||
|
restTemplate.getInterceptors().add(keycloakInterceptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package org.keycloak.adapters.springboot.client;
|
||||||
|
|
||||||
|
import org.keycloak.KeycloakPrincipal;
|
||||||
|
import org.keycloak.KeycloakSecurityContext;
|
||||||
|
import org.springframework.http.HttpRequest;
|
||||||
|
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||||
|
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||||
|
import org.springframework.http.client.ClientHttpResponse;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.Principal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interceptor for {@link ClientHttpRequestExecution} objects created for server to server secured
|
||||||
|
* communication using OAuth2 bearer tokens issued by Keycloak.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jmcshan1@gmail.com">James McShane</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class KeycloakSecurityContextClientRequestInterceptor implements ClientHttpRequestInterceptor {
|
||||||
|
|
||||||
|
private static final String AUTHORIZATION_HEADER = "Authorization";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link KeycloakSecurityContext} from the Spring {@link ServletRequestAttributes}'s {@link Principal}.
|
||||||
|
*
|
||||||
|
* The principal must support retrieval of the KeycloakSecurityContext, so at this point, only {@link KeycloakPrincipal}
|
||||||
|
* values are supported
|
||||||
|
*
|
||||||
|
* @return the current <code>KeycloakSecurityContext</code>
|
||||||
|
*/
|
||||||
|
protected KeycloakSecurityContext getKeycloakSecurityContext() {
|
||||||
|
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||||
|
Principal principal = attributes.getRequest().getUserPrincipal();
|
||||||
|
if (principal == null) {
|
||||||
|
throw new IllegalStateException("Cannot set authorization header because there is no authenticated principal");
|
||||||
|
}
|
||||||
|
if (!(principal instanceof KeycloakPrincipal)) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
String.format(
|
||||||
|
"Cannot set authorization header because the principal type %s does not provide the KeycloakSecurityContext",
|
||||||
|
principal.getClass()));
|
||||||
|
}
|
||||||
|
return ((KeycloakPrincipal) principal).getKeycloakSecurityContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
|
||||||
|
KeycloakSecurityContext context = this.getKeycloakSecurityContext();
|
||||||
|
httpRequest.getHeaders().set(AUTHORIZATION_HEADER, "Bearer " + context.getTokenString());
|
||||||
|
return clientHttpRequestExecution.execute(httpRequest, bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package org.keycloak.adapters.springboot.client;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
public class KeycloakRestTemplateCustomizerTest {
|
||||||
|
|
||||||
|
private KeycloakRestTemplateCustomizer customizer;
|
||||||
|
private KeycloakSecurityContextClientRequestInterceptor interceptor =
|
||||||
|
mock(KeycloakSecurityContextClientRequestInterceptor.class);
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
customizer = new KeycloakRestTemplateCustomizer(interceptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void interceptorIsAddedToRequest() {
|
||||||
|
RestTemplate restTemplate = new RestTemplate();
|
||||||
|
customizer.customize(restTemplate);
|
||||||
|
assertTrue(restTemplate.getInterceptors().contains(interceptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+87
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* 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.springboot.client;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.KeycloakPrincipal;
|
||||||
|
import org.keycloak.KeycloakSecurityContext;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keycloak spring boot client request factory tests.
|
||||||
|
*/
|
||||||
|
public class KeycloakSecurityContextClientRequestInterceptorTest {
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
private KeycloakSecurityContextClientRequestInterceptor factory;
|
||||||
|
|
||||||
|
private MockHttpServletRequest servletRequest;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private KeycloakSecurityContext keycloakSecurityContext;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private KeycloakPrincipal keycloakPrincipal;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
servletRequest = new MockHttpServletRequest();
|
||||||
|
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(servletRequest));
|
||||||
|
servletRequest.setUserPrincipal(keycloakPrincipal);
|
||||||
|
when(keycloakPrincipal.getKeycloakSecurityContext()).thenReturn(keycloakSecurityContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetKeycloakSecurityContext() throws Exception {
|
||||||
|
KeycloakSecurityContext context = factory.getKeycloakSecurityContext();
|
||||||
|
assertNotNull(context);
|
||||||
|
assertEquals(keycloakSecurityContext, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void testGetKeycloakSecurityContextInvalidPrincipal() throws Exception {
|
||||||
|
servletRequest.setUserPrincipal(new MarkerPrincipal());
|
||||||
|
factory.getKeycloakSecurityContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void testGetKeycloakSecurityContextNullAuthentication() throws Exception {
|
||||||
|
servletRequest.setUserPrincipal(null);
|
||||||
|
factory.getKeycloakSecurityContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MarkerPrincipal implements Principal {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.Final</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
+5
-1
@@ -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;
|
||||||
|
|||||||
+4
@@ -134,6 +134,10 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
|
|||||||
|
|
||||||
HttpFacade facade = new SimpleHttpFacade(request, response);
|
HttpFacade facade = new SimpleHttpFacade(request, response);
|
||||||
KeycloakDeployment deployment = adapterDeploymentContext.resolveDeployment(facade);
|
KeycloakDeployment deployment = adapterDeploymentContext.resolveDeployment(facade);
|
||||||
|
|
||||||
|
// using Spring authenticationFailureHandler
|
||||||
|
deployment.setDelegateBearerErrorResponseSending(true);
|
||||||
|
|
||||||
AdapterTokenStore tokenStore = adapterTokenStoreFactory.createAdapterTokenStore(deployment, request);
|
AdapterTokenStore tokenStore = adapterTokenStoreFactory.createAdapterTokenStore(deployment, request);
|
||||||
RequestAuthenticator authenticator
|
RequestAuthenticator authenticator
|
||||||
= new SpringSecurityRequestAuthenticator(facade, request, deployment, tokenStore, -1);
|
= new SpringSecurityRequestAuthenticator(facade, request, deployment, tokenStore, -1);
|
||||||
|
|||||||
+41
@@ -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.Final</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.Final</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.Final</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.Final</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.Final</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.Final</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.Final</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
+20
-1
@@ -50,7 +50,9 @@ import java.net.InetSocketAddress;
|
|||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,6 +68,7 @@ class ElytronHttpFacade implements OIDCHttpFacade {
|
|||||||
private ElytronAccount account;
|
private ElytronAccount account;
|
||||||
private SecurityIdentity securityIdentity;
|
private SecurityIdentity securityIdentity;
|
||||||
private boolean restored;
|
private boolean restored;
|
||||||
|
private final Map<String, String> headers = new HashMap<>();
|
||||||
|
|
||||||
public ElytronHttpFacade(HttpServerRequest request, AdapterDeploymentContext deploymentContext, CallbackHandler handler) {
|
public ElytronHttpFacade(HttpServerRequest request, AdapterDeploymentContext deploymentContext, CallbackHandler handler) {
|
||||||
this.request = request;
|
this.request = request;
|
||||||
@@ -90,6 +93,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);
|
||||||
@@ -256,6 +264,7 @@ class ElytronHttpFacade implements OIDCHttpFacade {
|
|||||||
@Override
|
@Override
|
||||||
public Response getResponse() {
|
public Response getResponse() {
|
||||||
return new Response() {
|
return new Response() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setStatus(final int status) {
|
public void setStatus(final int status) {
|
||||||
responseConsumer = responseConsumer.andThen(response -> response.setStatusCode(status));
|
responseConsumer = responseConsumer.andThen(response -> response.setStatusCode(status));
|
||||||
@@ -263,7 +272,17 @@ class ElytronHttpFacade implements OIDCHttpFacade {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addHeader(final String name, final String value) {
|
public void addHeader(final String name, final String value) {
|
||||||
responseConsumer = responseConsumer.andThen(response -> response.addResponseHeader(name, value));
|
headers.put(name, value);
|
||||||
|
responseConsumer = responseConsumer.andThen(new Consumer<HttpServerResponse>() {
|
||||||
|
@Override
|
||||||
|
public void accept(HttpServerResponse response) {
|
||||||
|
String latestValue = headers.get(name);
|
||||||
|
|
||||||
|
if (latestValue.equals(value)) {
|
||||||
|
response.addResponseHeader(name, latestValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+1
-1
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -17,6 +17,7 @@
|
|||||||
package org.keycloak.adapters.elytron;
|
package org.keycloak.adapters.elytron;
|
||||||
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.keycloak.KeycloakPrincipal;
|
import org.keycloak.KeycloakPrincipal;
|
||||||
@@ -54,7 +55,7 @@ public class KeycloakSecurityRealm implements SecurityRealm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName) throws RealmUnavailableException {
|
public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
|
||||||
return SupportLevel.UNSUPPORTED;
|
return SupportLevel.UNSUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +93,7 @@ public class KeycloakSecurityRealm implements SecurityRealm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName) throws RealmUnavailableException {
|
public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
|
||||||
return SupportLevel.UNSUPPORTED;
|
return SupportLevel.UNSUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.Final</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.Final</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.Final</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.Final</version>
|
||||||
<relativePath>../../../../pom.xml</relativePath>
|
<relativePath>../../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
@@ -68,6 +68,10 @@
|
|||||||
<artifactId>wildfly-web-common</artifactId>
|
<artifactId>wildfly-web-common</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.wildfly.security</groupId>
|
||||||
|
<artifactId>wildfly-elytron</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jboss.logging</groupId>
|
<groupId>org.jboss.logging</groupId>
|
||||||
<artifactId>jboss-logging-annotations</artifactId>
|
<artifactId>jboss-logging-annotations</artifactId>
|
||||||
@@ -101,5 +105,9 @@
|
|||||||
<artifactId>keycloak-wildfly-adapter</artifactId>
|
<artifactId>keycloak-wildfly-adapter</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-wildfly-elytron-oidc-adapter</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
+10
-19
@@ -17,41 +17,32 @@
|
|||||||
|
|
||||||
package org.keycloak.subsystem.adapter.extension;
|
package org.keycloak.subsystem.adapter.extension;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.jboss.as.controller.AbstractAddStepHandler;
|
import org.jboss.as.controller.AbstractAddStepHandler;
|
||||||
import org.jboss.as.controller.AttributeDefinition;
|
|
||||||
import org.jboss.as.controller.OperationContext;
|
import org.jboss.as.controller.OperationContext;
|
||||||
import org.jboss.as.controller.OperationFailedException;
|
import org.jboss.as.controller.OperationFailedException;
|
||||||
|
import org.jboss.as.controller.SimpleAttributeDefinition;
|
||||||
|
import org.jboss.as.controller.capability.RuntimeCapability;
|
||||||
import org.jboss.dmr.ModelNode;
|
import org.jboss.dmr.ModelNode;
|
||||||
|
|
||||||
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
|
|
||||||
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a deployment to a realm.
|
* Add a deployment to a realm.
|
||||||
*
|
*
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
|
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
|
||||||
*/
|
*/
|
||||||
public final class SecureDeploymentAddHandler extends AbstractAddStepHandler {
|
abstract class AbstractAdapterConfigurationAddHandler extends AbstractAddStepHandler {
|
||||||
|
|
||||||
public static SecureDeploymentAddHandler INSTANCE = new SecureDeploymentAddHandler();
|
private final boolean elytronEnabled;
|
||||||
|
|
||||||
private SecureDeploymentAddHandler() {}
|
AbstractAdapterConfigurationAddHandler(RuntimeCapability<Void> runtimeCapability, List<SimpleAttributeDefinition> attributes) {
|
||||||
|
super(runtimeCapability, attributes);
|
||||||
@Override
|
elytronEnabled = runtimeCapability != null;
|
||||||
protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
|
|
||||||
// TODO: localize exception. get id number
|
|
||||||
if (!operation.get(OP).asString().equals(ADD)) {
|
|
||||||
throw new OperationFailedException("Unexpected operation for add secure deployment. operation=" + operation.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (AttributeDefinition attr : SecureDeploymentDefinition.ALL_ATTRIBUTES) {
|
|
||||||
attr.validateAndSet(operation, model);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
|
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
|
||||||
KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
|
KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
|
||||||
ckService.addSecureDeployment(operation, context.resolveExpressions(model));
|
ckService.addSecureDeployment(operation, context.resolveExpressions(model), elytronEnabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+164
@@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* JBoss, Home of Professional Open Source.
|
||||||
|
* Copyright 2016 Red Hat, Inc., and individual 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.SimpleAttributeDefinition;
|
||||||
|
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.IntRangeValidator;
|
||||||
|
import org.jboss.as.controller.operations.validation.StringLengthValidator;
|
||||||
|
import org.jboss.as.controller.registry.ManagementResourceRegistration;
|
||||||
|
import org.jboss.dmr.ModelNode;
|
||||||
|
import org.jboss.dmr.ModelType;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines attributes and operations for a secure-deployment.
|
||||||
|
*
|
||||||
|
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
|
||||||
|
*/
|
||||||
|
abstract class AbstractAdapterConfigurationDefinition extends SimpleResourceDefinition {
|
||||||
|
|
||||||
|
protected static final SimpleAttributeDefinition REALM =
|
||||||
|
new SimpleAttributeDefinitionBuilder("realm", ModelType.STRING, true)
|
||||||
|
.setXmlName("realm")
|
||||||
|
.setAllowExpression(true)
|
||||||
|
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
|
||||||
|
.build();
|
||||||
|
protected static final SimpleAttributeDefinition RESOURCE =
|
||||||
|
new SimpleAttributeDefinitionBuilder("resource", ModelType.STRING, true)
|
||||||
|
.setXmlName("resource")
|
||||||
|
.setAllowExpression(true)
|
||||||
|
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
|
||||||
|
.build();
|
||||||
|
protected static final SimpleAttributeDefinition USE_RESOURCE_ROLE_MAPPINGS =
|
||||||
|
new SimpleAttributeDefinitionBuilder("use-resource-role-mappings", ModelType.BOOLEAN, true)
|
||||||
|
.setXmlName("use-resource-role-mappings")
|
||||||
|
.setAllowExpression(true)
|
||||||
|
.setDefaultValue(new ModelNode(false))
|
||||||
|
.build();
|
||||||
|
protected static final SimpleAttributeDefinition BEARER_ONLY =
|
||||||
|
new SimpleAttributeDefinitionBuilder("bearer-only", ModelType.BOOLEAN, true)
|
||||||
|
.setXmlName("bearer-only")
|
||||||
|
.setAllowExpression(true)
|
||||||
|
.setDefaultValue(new ModelNode(false))
|
||||||
|
.build();
|
||||||
|
protected static final SimpleAttributeDefinition ENABLE_BASIC_AUTH =
|
||||||
|
new SimpleAttributeDefinitionBuilder("enable-basic-auth", ModelType.BOOLEAN, true)
|
||||||
|
.setXmlName("enable-basic-auth")
|
||||||
|
.setAllowExpression(true)
|
||||||
|
.setDefaultValue(new ModelNode(false))
|
||||||
|
.build();
|
||||||
|
protected static final SimpleAttributeDefinition PUBLIC_CLIENT =
|
||||||
|
new SimpleAttributeDefinitionBuilder("public-client", ModelType.BOOLEAN, true)
|
||||||
|
.setXmlName("public-client")
|
||||||
|
.setAllowExpression(true)
|
||||||
|
.setDefaultValue(new ModelNode(false))
|
||||||
|
.build();
|
||||||
|
protected static final SimpleAttributeDefinition TURN_OFF_CHANGE_SESSION =
|
||||||
|
new SimpleAttributeDefinitionBuilder("turn-off-change-session-id-on-login", ModelType.BOOLEAN, true)
|
||||||
|
.setXmlName("turn-off-change-session-id-on-login")
|
||||||
|
.setAllowExpression(true)
|
||||||
|
.setDefaultValue(new ModelNode(false))
|
||||||
|
.build();
|
||||||
|
protected static final SimpleAttributeDefinition TOKEN_MINIMUM_TIME_TO_LIVE =
|
||||||
|
new SimpleAttributeDefinitionBuilder("token-minimum-time-to-live", ModelType.INT, true)
|
||||||
|
.setXmlName("token-minimum-time-to-live")
|
||||||
|
.setValidator(new IntRangeValidator(-1, true))
|
||||||
|
.setAllowExpression(true)
|
||||||
|
.build();
|
||||||
|
protected static final SimpleAttributeDefinition MIN_TIME_BETWEEN_JWKS_REQUESTS =
|
||||||
|
new SimpleAttributeDefinitionBuilder("min-time-between-jwks-requests", ModelType.INT, true)
|
||||||
|
.setXmlName("min-time-between-jwks-requests")
|
||||||
|
.setValidator(new IntRangeValidator(-1, true))
|
||||||
|
.setAllowExpression(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
static final List<SimpleAttributeDefinition> DEPLOYMENT_ONLY_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
DEPLOYMENT_ONLY_ATTRIBUTES.add(REALM);
|
||||||
|
DEPLOYMENT_ONLY_ATTRIBUTES.add(RESOURCE);
|
||||||
|
DEPLOYMENT_ONLY_ATTRIBUTES.add(USE_RESOURCE_ROLE_MAPPINGS);
|
||||||
|
DEPLOYMENT_ONLY_ATTRIBUTES.add(BEARER_ONLY);
|
||||||
|
DEPLOYMENT_ONLY_ATTRIBUTES.add(ENABLE_BASIC_AUTH);
|
||||||
|
DEPLOYMENT_ONLY_ATTRIBUTES.add(PUBLIC_CLIENT);
|
||||||
|
DEPLOYMENT_ONLY_ATTRIBUTES.add(TURN_OFF_CHANGE_SESSION);
|
||||||
|
DEPLOYMENT_ONLY_ATTRIBUTES.add(TOKEN_MINIMUM_TIME_TO_LIVE);
|
||||||
|
DEPLOYMENT_ONLY_ATTRIBUTES.add(MIN_TIME_BETWEEN_JWKS_REQUESTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList();
|
||||||
|
|
||||||
|
static {
|
||||||
|
ALL_ATTRIBUTES.addAll(DEPLOYMENT_ONLY_ATTRIBUTES);
|
||||||
|
ALL_ATTRIBUTES.addAll(SharedAttributeDefinitons.ATTRIBUTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final Map<String, SimpleAttributeDefinition> XML_ATTRIBUTES = new HashMap<String, SimpleAttributeDefinition>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
|
||||||
|
XML_ATTRIBUTES.put(def.getXmlName(), def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Map<String, SimpleAttributeDefinition> DEFINITION_LOOKUP = new HashMap<String, SimpleAttributeDefinition>();
|
||||||
|
static {
|
||||||
|
for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
|
||||||
|
DEFINITION_LOOKUP.put(def.getXmlName(), def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final AbstractAdapterConfigurationWriteAttributeHandler attrWriteHandler;
|
||||||
|
private final List<SimpleAttributeDefinition> attributes;
|
||||||
|
|
||||||
|
protected AbstractAdapterConfigurationDefinition(String name, List<SimpleAttributeDefinition> attributes, AbstractAdapterConfigurationAddHandler addHandler, AbstractAdapterConfigurationRemoveHandler removeHandler, AbstractAdapterConfigurationWriteAttributeHandler attrWriteHandler) {
|
||||||
|
super(PathElement.pathElement(name),
|
||||||
|
KeycloakExtension.getResourceDescriptionResolver(name),
|
||||||
|
addHandler,
|
||||||
|
removeHandler);
|
||||||
|
this.attributes = attributes;
|
||||||
|
this.attrWriteHandler = attrWriteHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
|
||||||
|
super.registerOperations(resourceRegistration);
|
||||||
|
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
|
||||||
|
super.registerAttributes(resourceRegistration);
|
||||||
|
for (AttributeDefinition attrDef : this.attributes) {
|
||||||
|
resourceRegistration.registerReadWriteAttribute(attrDef, null, this.attrWriteHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SimpleAttributeDefinition lookup(String name) {
|
||||||
|
return DEFINITION_LOOKUP.get(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-5
@@ -27,11 +27,7 @@ import org.jboss.dmr.ModelNode;
|
|||||||
*
|
*
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
|
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
|
||||||
*/
|
*/
|
||||||
public final class SecureDeploymentRemoveHandler extends AbstractRemoveStepHandler {
|
abstract class AbstractAdapterConfigurationRemoveHandler extends AbstractRemoveStepHandler {
|
||||||
|
|
||||||
public static SecureDeploymentRemoveHandler INSTANCE = new SecureDeploymentRemoveHandler();
|
|
||||||
|
|
||||||
private SecureDeploymentRemoveHandler() {}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
|
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
|
||||||
+3
-7
@@ -31,14 +31,10 @@ import java.util.List;
|
|||||||
*
|
*
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
|
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
|
||||||
*/
|
*/
|
||||||
public class SecureDeploymentWriteAttributeHandler extends AbstractWriteAttributeHandler<KeycloakAdapterConfigService> {
|
abstract class AbstractAdapterConfigurationWriteAttributeHandler extends AbstractWriteAttributeHandler<KeycloakAdapterConfigService> {
|
||||||
|
|
||||||
public SecureDeploymentWriteAttributeHandler(List<SimpleAttributeDefinition> definitions) {
|
AbstractAdapterConfigurationWriteAttributeHandler(List<SimpleAttributeDefinition> definitions) {
|
||||||
this(definitions.toArray(new AttributeDefinition[definitions.size()]));
|
super(definitions.toArray(new AttributeDefinition[definitions.size()]));
|
||||||
}
|
|
||||||
|
|
||||||
public SecureDeploymentWriteAttributeHandler(AttributeDefinition... definitions) {
|
|
||||||
super(definitions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
+34
@@ -25,7 +25,9 @@ import org.jboss.as.web.common.WarMetaData;
|
|||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.metadata.javaee.spec.ParamValueMetaData;
|
import org.jboss.metadata.javaee.spec.ParamValueMetaData;
|
||||||
import org.jboss.metadata.web.jboss.JBossWebMetaData;
|
import org.jboss.metadata.web.jboss.JBossWebMetaData;
|
||||||
|
import org.jboss.metadata.web.spec.ListenerMetaData;
|
||||||
import org.jboss.metadata.web.spec.LoginConfigMetaData;
|
import org.jboss.metadata.web.spec.LoginConfigMetaData;
|
||||||
|
import org.keycloak.adapters.elytron.KeycloakConfigurationServletListener;
|
||||||
import org.keycloak.subsystem.adapter.logging.KeycloakLogger;
|
import org.keycloak.subsystem.adapter.logging.KeycloakLogger;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -69,6 +71,9 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
|
|||||||
KeycloakAdapterConfigService service = KeycloakAdapterConfigService.getInstance();
|
KeycloakAdapterConfigService service = KeycloakAdapterConfigService.getInstance();
|
||||||
if (service.isSecureDeployment(deploymentUnit) && service.isDeploymentConfigured(deploymentUnit)) {
|
if (service.isSecureDeployment(deploymentUnit) && service.isDeploymentConfigured(deploymentUnit)) {
|
||||||
addKeycloakAuthData(phaseContext, service);
|
addKeycloakAuthData(phaseContext, service);
|
||||||
|
} else if (service.isElytronEnabled(deploymentUnit)) {
|
||||||
|
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
|
||||||
|
addConfigurationListener(warMetaData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FYI, Undertow Extension will find deployments that have auth-method set to KEYCLOAK
|
// FYI, Undertow Extension will find deployments that have auth-method set to KEYCLOAK
|
||||||
@@ -99,6 +104,10 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
|
|||||||
loginConfig.setAuthMethod("KEYCLOAK");
|
loginConfig.setAuthMethod("KEYCLOAK");
|
||||||
loginConfig.setRealmName(service.getRealmName(deploymentUnit));
|
loginConfig.setRealmName(service.getRealmName(deploymentUnit));
|
||||||
KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentUnit.getName());
|
KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentUnit.getName());
|
||||||
|
|
||||||
|
if (service.isElytronEnabled(deploymentUnit)) {
|
||||||
|
addConfigurationListener(warMetaData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addJSONData(String json, WarMetaData warMetaData) {
|
private void addJSONData(String json, WarMetaData warMetaData) {
|
||||||
@@ -121,6 +130,31 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
|
|||||||
webMetaData.setContextParams(contextParams);
|
webMetaData.setContextParams(contextParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addConfigurationListener(WarMetaData warMetaData) {
|
||||||
|
if (warMetaData == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
|
||||||
|
if (webMetaData == null) {
|
||||||
|
webMetaData = new JBossWebMetaData();
|
||||||
|
warMetaData.setMergedJBossWebMetaData(webMetaData);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoginConfigMetaData loginConfig = webMetaData.getLoginConfig();
|
||||||
|
if (loginConfig == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!loginConfig.getAuthMethod().equals("KEYCLOAK")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ListenerMetaData listenerMetaData = new ListenerMetaData();
|
||||||
|
|
||||||
|
listenerMetaData.setListenerClass(KeycloakConfigurationServletListener.class.getName());
|
||||||
|
|
||||||
|
webMetaData.getListeners().add(listenerMetaData);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void undeploy(DeploymentUnit du) {
|
public void undeploy(DeploymentUnit du) {
|
||||||
|
|
||||||
|
|||||||
+46
-8
@@ -23,8 +23,11 @@ import org.jboss.dmr.ModelNode;
|
|||||||
import org.jboss.dmr.Property;
|
import org.jboss.dmr.Property;
|
||||||
import org.jboss.metadata.web.jboss.JBossWebMetaData;
|
import org.jboss.metadata.web.jboss.JBossWebMetaData;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
|
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
|
||||||
|
|
||||||
@@ -50,6 +53,7 @@ public final class KeycloakAdapterConfigService {
|
|||||||
|
|
||||||
// keycloak-secured deployments
|
// keycloak-secured deployments
|
||||||
private final Map<String, ModelNode> secureDeployments = new HashMap<String, ModelNode>();
|
private final Map<String, ModelNode> secureDeployments = new HashMap<String, ModelNode>();
|
||||||
|
private final Set<String> elytronEnabledDeployments = new HashSet<>();
|
||||||
|
|
||||||
|
|
||||||
private KeycloakAdapterConfigService() {
|
private KeycloakAdapterConfigService() {
|
||||||
@@ -68,9 +72,13 @@ public final class KeycloakAdapterConfigService {
|
|||||||
this.realms.remove(realmNameFromOp(operation));
|
this.realms.remove(realmNameFromOp(operation));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSecureDeployment(ModelNode operation, ModelNode model) {
|
public void addSecureDeployment(ModelNode operation, ModelNode model, boolean elytronEnabled) {
|
||||||
ModelNode deployment = model.clone();
|
ModelNode deployment = model.clone();
|
||||||
this.secureDeployments.put(deploymentNameFromOp(operation), deployment);
|
String name = deploymentNameFromOp(operation);
|
||||||
|
this.secureDeployments.put(name, deployment);
|
||||||
|
if (elytronEnabled) {
|
||||||
|
elytronEnabledDeployments.add(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateSecureDeployment(ModelNode operation, String attrName, ModelNode resolvedValue) {
|
public void updateSecureDeployment(ModelNode operation, String attrName, ModelNode resolvedValue) {
|
||||||
@@ -79,7 +87,9 @@ public final class KeycloakAdapterConfigService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void removeSecureDeployment(ModelNode operation) {
|
public void removeSecureDeployment(ModelNode operation) {
|
||||||
this.secureDeployments.remove(deploymentNameFromOp(operation));
|
String name = deploymentNameFromOp(operation);
|
||||||
|
this.secureDeployments.remove(name);
|
||||||
|
elytronEnabledDeployments.remove(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addCredential(ModelNode operation, ModelNode model) {
|
public void addCredential(ModelNode operation, ModelNode model) {
|
||||||
@@ -187,7 +197,19 @@ public final class KeycloakAdapterConfigService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String deploymentNameFromOp(ModelNode operation) {
|
private String deploymentNameFromOp(ModelNode operation) {
|
||||||
return valueFromOpAddress(SecureDeploymentDefinition.TAG_NAME, operation);
|
String deploymentName = valueFromOpAddress(SecureDeploymentDefinition.TAG_NAME, operation);
|
||||||
|
|
||||||
|
if (deploymentName == null) {
|
||||||
|
deploymentName = valueFromOpAddress(KeycloakHttpServerAuthenticationMechanismFactoryDefinition.TAG_NAME, operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deploymentName == null) {
|
||||||
|
deploymentName = valueFromOpAddress(SecureServerDefinition.TAG_NAME, operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deploymentName == null) throw new RuntimeException("Can't find deployment name in address " + operation);
|
||||||
|
|
||||||
|
return deploymentName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String credentialNameFromOp(ModelNode operation) {
|
private String credentialNameFromOp(ModelNode operation) {
|
||||||
@@ -199,9 +221,7 @@ public final class KeycloakAdapterConfigService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String valueFromOpAddress(String addrElement, ModelNode operation) {
|
private String valueFromOpAddress(String addrElement, ModelNode operation) {
|
||||||
String deploymentName = getValueOfAddrElement(operation.get(ADDRESS), addrElement);
|
return getValueOfAddrElement(operation.get(ADDRESS), addrElement);
|
||||||
if (deploymentName == null) throw new RuntimeException("Can't find '" + addrElement + "' in address " + operation.toString());
|
|
||||||
return deploymentName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getValueOfAddrElement(ModelNode address, String elementName) {
|
private String getValueOfAddrElement(ModelNode address, String elementName) {
|
||||||
@@ -241,8 +261,22 @@ public final class KeycloakAdapterConfigService {
|
|||||||
return json.toJSONString(true);
|
return json.toJSONString(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getJSON(String deploymentName) {
|
||||||
|
ModelNode deployment = this.secureDeployments.get(deploymentName);
|
||||||
|
String realmName = deployment.get(RealmDefinition.TAG_NAME).asString();
|
||||||
|
ModelNode realm = this.realms.get(realmName);
|
||||||
|
|
||||||
|
ModelNode json = new ModelNode();
|
||||||
|
json.get(RealmDefinition.TAG_NAME).set(realmName);
|
||||||
|
|
||||||
|
// Realm values set first. Some can be overridden by deployment values.
|
||||||
|
if (realm != null) setJSONValues(json, realm);
|
||||||
|
setJSONValues(json, deployment);
|
||||||
|
return json.toJSONString(true);
|
||||||
|
}
|
||||||
|
|
||||||
private void setJSONValues(ModelNode json, ModelNode values) {
|
private void setJSONValues(ModelNode json, ModelNode values) {
|
||||||
for (Property prop : values.asPropertyList()) {
|
for (Property prop : new ArrayList<>(values.asPropertyList())) {
|
||||||
String name = prop.getName();
|
String name = prop.getName();
|
||||||
ModelNode value = prop.getValue();
|
ModelNode value = prop.getValue();
|
||||||
if (value.isDefined()) {
|
if (value.isDefined()) {
|
||||||
@@ -258,6 +292,10 @@ public final class KeycloakAdapterConfigService {
|
|||||||
return this.secureDeployments.containsKey(deploymentName);
|
return this.secureDeployments.containsKey(deploymentName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isElytronEnabled(DeploymentUnit deploymentUnit) {
|
||||||
|
return elytronEnabledDeployments.contains(preferredDeploymentName(deploymentUnit));
|
||||||
|
}
|
||||||
|
|
||||||
private ModelNode getSecureDeployment(DeploymentUnit deploymentUnit) {
|
private ModelNode getSecureDeployment(DeploymentUnit deploymentUnit) {
|
||||||
String deploymentName = preferredDeploymentName(deploymentUnit);
|
String deploymentName = preferredDeploymentName(deploymentUnit);
|
||||||
return this.secureDeployments.containsKey(deploymentName)
|
return this.secureDeployments.containsKey(deploymentName)
|
||||||
|
|||||||
+2
-1
@@ -29,13 +29,14 @@ import org.jboss.modules.ModuleLoader;
|
|||||||
*/
|
*/
|
||||||
public class KeycloakDependencyProcessorWildFly extends KeycloakDependencyProcessor {
|
public class KeycloakDependencyProcessorWildFly extends KeycloakDependencyProcessor {
|
||||||
|
|
||||||
|
private static final ModuleIdentifier KEYCLOAK_ELYTRON_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-wildfly-elytron-oidc-adapter");
|
||||||
private static final ModuleIdentifier KEYCLOAK_WILDFLY_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-wildfly-adapter");
|
private static final ModuleIdentifier KEYCLOAK_WILDFLY_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-wildfly-adapter");
|
||||||
private static final ModuleIdentifier KEYCLOAK_UNDERTOW_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-undertow-adapter");
|
private static final ModuleIdentifier KEYCLOAK_UNDERTOW_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-undertow-adapter");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addPlatformSpecificModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader) {
|
protected void addPlatformSpecificModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader) {
|
||||||
// ModuleDependency(ModuleLoader moduleLoader, ModuleIdentifier identifier, boolean optional, boolean export, boolean importServices, boolean userSpecified)
|
|
||||||
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_WILDFLY_ADAPTER, false, false, true, false));
|
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_WILDFLY_ADAPTER, false, false, true, false));
|
||||||
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_UNDERTOW_ADAPTER, false, false, false, false));
|
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_UNDERTOW_ADAPTER, false, false, false, false));
|
||||||
|
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_ELYTRON_ADAPTER, true, false, false, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+6
-1
@@ -47,8 +47,9 @@ public class KeycloakExtension implements Extension {
|
|||||||
private static final ResourceDefinition KEYCLOAK_SUBSYSTEM_RESOURCE = new KeycloakSubsystemDefinition();
|
private static final ResourceDefinition KEYCLOAK_SUBSYSTEM_RESOURCE = new KeycloakSubsystemDefinition();
|
||||||
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 SecureServerDefinition SECURE_SERVER_DEFINITION = new SecureServerDefinition();
|
||||||
static final CredentialDefinition CREDENTIAL_DEFINITION = new CredentialDefinition();
|
static final CredentialDefinition CREDENTIAL_DEFINITION = new CredentialDefinition();
|
||||||
static final RedirecRewritetRuleDefinition REDIRECT_RULE_DEFINITON = new RedirecRewritetRuleDefinition();
|
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);
|
||||||
@@ -80,6 +81,10 @@ public class KeycloakExtension implements Extension {
|
|||||||
secureDeploymentRegistration.registerSubModel(CREDENTIAL_DEFINITION);
|
secureDeploymentRegistration.registerSubModel(CREDENTIAL_DEFINITION);
|
||||||
secureDeploymentRegistration.registerSubModel(REDIRECT_RULE_DEFINITON);
|
secureDeploymentRegistration.registerSubModel(REDIRECT_RULE_DEFINITON);
|
||||||
|
|
||||||
|
ManagementResourceRegistration secureServerRegistration = registration.registerSubModel(SECURE_SERVER_DEFINITION);
|
||||||
|
secureServerRegistration.registerSubModel(CREDENTIAL_DEFINITION);
|
||||||
|
secureServerRegistration.registerSubModel(REDIRECT_RULE_DEFINITON);
|
||||||
|
|
||||||
subsystem.registerXMLElementWriter(PARSER);
|
subsystem.registerXMLElementWriter(PARSER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* JBoss, Home of Professional Open Source.
|
||||||
|
* Copyright 2016 Red Hat, Inc., and individual 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.msc.service.Service;
|
||||||
|
import org.jboss.msc.service.StartContext;
|
||||||
|
import org.jboss.msc.service.StartException;
|
||||||
|
import org.jboss.msc.service.StopContext;
|
||||||
|
import org.jboss.msc.value.InjectedValue;
|
||||||
|
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||||
|
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||||
|
import org.keycloak.adapters.elytron.KeycloakHttpServerAuthenticationMechanismFactory;
|
||||||
|
import org.wildfly.security.auth.server.SecurityDomain;
|
||||||
|
import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory;
|
||||||
|
import org.wildfly.security.http.util.SetMechanismInformationMechanismFactory;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
public class KeycloakHttpAuthenticationFactoryService implements Service<HttpServerAuthenticationMechanismFactory> {
|
||||||
|
|
||||||
|
private final String factoryName;
|
||||||
|
private HttpServerAuthenticationMechanismFactory httpAuthenticationFactory;
|
||||||
|
|
||||||
|
public KeycloakHttpAuthenticationFactoryService(String factoryName) {
|
||||||
|
this.factoryName = factoryName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(StartContext context) throws StartException {
|
||||||
|
KeycloakAdapterConfigService adapterConfigService = KeycloakAdapterConfigService.getInstance();
|
||||||
|
String config = adapterConfigService.getJSON(this.factoryName);
|
||||||
|
this.httpAuthenticationFactory = new KeycloakHttpServerAuthenticationMechanismFactory(createDeploymentContext(config.getBytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop(StopContext context) {
|
||||||
|
this.httpAuthenticationFactory = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpServerAuthenticationMechanismFactory getValue() throws IllegalStateException, IllegalArgumentException {
|
||||||
|
return new SetMechanismInformationMechanismFactory(this.httpAuthenticationFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AdapterDeploymentContext createDeploymentContext(byte[] config) {
|
||||||
|
return new AdapterDeploymentContext(KeycloakDeploymentBuilder.build(new ByteArrayInputStream(config)));
|
||||||
|
}
|
||||||
|
}
|
||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* JBoss, Home of Professional Open Source.
|
||||||
|
* Copyright 2016 Red Hat, Inc., and individual 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 static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
|
||||||
|
import static org.keycloak.subsystem.adapter.extension.KeycloakHttpServerAuthenticationMechanismFactoryDefinition.KeycloakHttpServerAuthenticationMechanismFactoryAddHandler.HTTP_SERVER_AUTHENTICATION_CAPABILITY;
|
||||||
|
|
||||||
|
import org.jboss.as.controller.OperationContext;
|
||||||
|
import org.jboss.as.controller.OperationFailedException;
|
||||||
|
import org.jboss.as.controller.PathAddress;
|
||||||
|
import org.jboss.as.controller.SimpleResourceDefinition;
|
||||||
|
import org.jboss.as.controller.capability.RuntimeCapability;
|
||||||
|
import org.jboss.dmr.ModelNode;
|
||||||
|
import org.jboss.msc.service.ServiceController;
|
||||||
|
import org.jboss.msc.service.ServiceName;
|
||||||
|
import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link SimpleResourceDefinition} that can be used to configure a {@link org.keycloak.adapters.elytron.KeycloakHttpServerAuthenticationMechanismFactory}
|
||||||
|
* and expose it as a capability for other subsystems.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
class KeycloakHttpServerAuthenticationMechanismFactoryDefinition extends AbstractAdapterConfigurationDefinition {
|
||||||
|
|
||||||
|
static final String TAG_NAME = "http-server-mechanism-factory";
|
||||||
|
|
||||||
|
KeycloakHttpServerAuthenticationMechanismFactoryDefinition() {
|
||||||
|
this(TAG_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
KeycloakHttpServerAuthenticationMechanismFactoryDefinition(String tagName) {
|
||||||
|
super(tagName, ALL_ATTRIBUTES, new KeycloakHttpServerAuthenticationMechanismFactoryAddHandler(), new KeycloakHttpServerAuthenticationMechanismFactoryRemoveHandler(), new KeycloakHttpServerAuthenticationMechanismFactoryWriteHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link AbstractAdapterConfigurationAddHandler} that exposes a {@link KeycloakHttpServerAuthenticationMechanismFactoryDefinition}
|
||||||
|
* as a capability through the installation of a {@link KeycloakHttpAuthenticationFactoryService}.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
static final class KeycloakHttpServerAuthenticationMechanismFactoryAddHandler extends AbstractAdapterConfigurationAddHandler {
|
||||||
|
|
||||||
|
static final String HTTP_SERVER_AUTHENTICATION_CAPABILITY = "org.wildfly.security.http-server-mechanism-factory";
|
||||||
|
static final RuntimeCapability<Void> HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY = RuntimeCapability
|
||||||
|
.Builder.of(HTTP_SERVER_AUTHENTICATION_CAPABILITY, true, HttpServerAuthenticationMechanismFactory.class)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
KeycloakHttpServerAuthenticationMechanismFactoryAddHandler() {
|
||||||
|
super(HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY, ALL_ATTRIBUTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
|
||||||
|
super.performRuntime(context, operation, model);
|
||||||
|
installCapability(context, operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void installCapability(OperationContext context, ModelNode operation) {
|
||||||
|
PathAddress pathAddress = PathAddress.pathAddress(operation.get(OP_ADDR));
|
||||||
|
String factoryName = pathAddress.getLastElement().getValue();
|
||||||
|
ServiceName serviceName = context.getCapabilityServiceName(HTTP_SERVER_AUTHENTICATION_CAPABILITY, factoryName, HttpServerAuthenticationMechanismFactory.class);
|
||||||
|
KeycloakHttpAuthenticationFactoryService service = new KeycloakHttpAuthenticationFactoryService(factoryName);
|
||||||
|
context.getServiceTarget().addService(serviceName, service).setInitialMode(ServiceController.Mode.ACTIVE).install();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link AbstractAdapterConfigurationRemoveHandler} that handles the removal of {@link KeycloakHttpServerAuthenticationMechanismFactoryDefinition}.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
static final class KeycloakHttpServerAuthenticationMechanismFactoryRemoveHandler extends AbstractAdapterConfigurationRemoveHandler {
|
||||||
|
@Override
|
||||||
|
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
|
||||||
|
super.performRuntime(context, operation, model);
|
||||||
|
PathAddress pathAddress = PathAddress.pathAddress(operation.get(OP_ADDR));
|
||||||
|
String factoryName = pathAddress.getLastElement().getValue();
|
||||||
|
ServiceName serviceName = context.getCapabilityServiceName(HTTP_SERVER_AUTHENTICATION_CAPABILITY, factoryName, HttpServerAuthenticationMechanismFactory.class);
|
||||||
|
|
||||||
|
context.removeService(serviceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void recoverServices(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
|
||||||
|
super.recoverServices(context, operation, model);
|
||||||
|
KeycloakHttpServerAuthenticationMechanismFactoryAddHandler.installCapability(context, operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link AbstractAdapterConfigurationWriteAttributeHandler} that updates attributes on a {@link KeycloakHttpServerAuthenticationMechanismFactoryDefinition}.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
static final class KeycloakHttpServerAuthenticationMechanismFactoryWriteHandler extends AbstractAdapterConfigurationWriteAttributeHandler {
|
||||||
|
KeycloakHttpServerAuthenticationMechanismFactoryWriteHandler() {
|
||||||
|
super(ALL_ATTRIBUTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
-10
@@ -62,6 +62,9 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
|
|||||||
else if (reader.getLocalName().equals(SecureDeploymentDefinition.TAG_NAME)) {
|
else if (reader.getLocalName().equals(SecureDeploymentDefinition.TAG_NAME)) {
|
||||||
readDeployment(reader, list);
|
readDeployment(reader, list);
|
||||||
}
|
}
|
||||||
|
else if (reader.getLocalName().equals(SecureServerDefinition.TAG_NAME)) {
|
||||||
|
readSecureServer(reader, list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,27 +92,35 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void readDeployment(XMLExtendedStreamReader reader, List<ModelNode> resourcesToAdd) throws XMLStreamException {
|
private void readDeployment(XMLExtendedStreamReader reader, List<ModelNode> resourcesToAdd) throws XMLStreamException {
|
||||||
|
readSecureResource(KeycloakExtension.SECURE_DEPLOYMENT_DEFINITION.TAG_NAME, KeycloakExtension.SECURE_DEPLOYMENT_DEFINITION, reader, resourcesToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readSecureServer(XMLExtendedStreamReader reader, List<ModelNode> resourcesToAdd) throws XMLStreamException {
|
||||||
|
readSecureResource(KeycloakExtension.SECURE_SERVER_DEFINITION.TAG_NAME, KeycloakExtension.SECURE_SERVER_DEFINITION, reader, resourcesToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readSecureResource(String tagName, AbstractAdapterConfigurationDefinition resource, XMLExtendedStreamReader reader, List<ModelNode> resourcesToAdd) throws XMLStreamException {
|
||||||
String name = readNameAttribute(reader);
|
String name = readNameAttribute(reader);
|
||||||
ModelNode addSecureDeployment = new ModelNode();
|
ModelNode addSecureDeployment = new ModelNode();
|
||||||
addSecureDeployment.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
|
addSecureDeployment.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
|
||||||
PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
|
PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
|
||||||
PathElement.pathElement(SecureDeploymentDefinition.TAG_NAME, name));
|
PathElement.pathElement(tagName, 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>();
|
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 localName = reader.getLocalName();
|
||||||
if (tagName.equals(CredentialDefinition.TAG_NAME)) {
|
if (localName.equals(CredentialDefinition.TAG_NAME)) {
|
||||||
readCredential(reader, addr, credentialsToAdd);
|
readCredential(reader, addr, credentialsToAdd);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (tagName.equals(RedirecRewritetRuleDefinition.TAG_NAME)) {
|
if (localName.equals(RedirecRewritetRuleDefinition.TAG_NAME)) {
|
||||||
readRewriteRule(reader, addr, redirectRulesToAdd);
|
readRewriteRule(reader, addr, redirectRulesToAdd);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SimpleAttributeDefinition def = SecureDeploymentDefinition.lookup(tagName);
|
SimpleAttributeDefinition def = resource.lookup(localName);
|
||||||
if (def == null) throw new XMLStreamException("Unknown secure-deployment tag " + tagName);
|
if (def == null) throw new XMLStreamException("Unknown secure-deployment tag " + localName);
|
||||||
def.parseAndSetParameter(reader.getElementText(), addSecureDeployment, reader);
|
def.parseAndSetParameter(reader.getElementText(), addSecureDeployment, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,6 +247,7 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
|
|||||||
context.startSubsystemElement(KeycloakExtension.NAMESPACE, false);
|
context.startSubsystemElement(KeycloakExtension.NAMESPACE, false);
|
||||||
writeRealms(writer, context);
|
writeRealms(writer, context);
|
||||||
writeSecureDeployments(writer, context);
|
writeSecureDeployments(writer, context);
|
||||||
|
writeSecureServers(writer, context);
|
||||||
writer.writeEndElement();
|
writer.writeEndElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,14 +268,22 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void writeSecureDeployments(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
|
private void writeSecureDeployments(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
|
||||||
if (!context.getModelNode().get(SecureDeploymentDefinition.TAG_NAME).isDefined()) {
|
writeSecureResource(SecureDeploymentDefinition.TAG_NAME, SecureDeploymentDefinition.ALL_ATTRIBUTES, writer, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeSecureServers(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
|
||||||
|
writeSecureResource(SecureServerDefinition.TAG_NAME, SecureServerDefinition.ALL_ATTRIBUTES, writer, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeSecureResource(String tagName, List<SimpleAttributeDefinition> attributes, XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
|
||||||
|
if (!context.getModelNode().get(tagName).isDefined()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (Property deployment : context.getModelNode().get(SecureDeploymentDefinition.TAG_NAME).asPropertyList()) {
|
for (Property deployment : context.getModelNode().get(tagName).asPropertyList()) {
|
||||||
writer.writeStartElement(SecureDeploymentDefinition.TAG_NAME);
|
writer.writeStartElement(tagName);
|
||||||
writer.writeAttribute("name", deployment.getName());
|
writer.writeAttribute("name", deployment.getName());
|
||||||
ModelNode deploymentElements = deployment.getValue();
|
ModelNode deploymentElements = deployment.getValue();
|
||||||
for (AttributeDefinition element : SecureDeploymentDefinition.ALL_ATTRIBUTES) {
|
for (AttributeDefinition element : attributes) {
|
||||||
element.marshallAsElement(deploymentElements, writer);
|
element.marshallAsElement(deploymentElements, writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+67
-117
@@ -16,138 +16,88 @@
|
|||||||
*/
|
*/
|
||||||
package org.keycloak.subsystem.adapter.extension;
|
package org.keycloak.subsystem.adapter.extension;
|
||||||
|
|
||||||
import org.jboss.as.controller.AttributeDefinition;
|
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
|
||||||
import org.jboss.as.controller.PathElement;
|
|
||||||
import org.jboss.as.controller.SimpleAttributeDefinition;
|
|
||||||
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.IntRangeValidator;
|
|
||||||
import org.jboss.as.controller.operations.validation.StringLengthValidator;
|
|
||||||
import org.jboss.as.controller.registry.ManagementResourceRegistration;
|
|
||||||
import org.jboss.dmr.ModelNode;
|
|
||||||
import org.jboss.dmr.ModelType;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import org.jboss.as.controller.OperationContext;
|
||||||
import java.util.HashMap;
|
import org.jboss.as.controller.OperationFailedException;
|
||||||
import java.util.List;
|
import org.jboss.as.controller.PathAddress;
|
||||||
import java.util.Map;
|
import org.jboss.as.controller.capability.RuntimeCapability;
|
||||||
|
import org.jboss.dmr.ModelNode;
|
||||||
|
import org.jboss.msc.service.ServiceController;
|
||||||
|
import org.jboss.msc.service.ServiceName;
|
||||||
|
import org.jboss.msc.service.ServiceTarget;
|
||||||
|
import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines attributes and operations for a secure-deployment.
|
* Defines attributes and operations for a secure-deployment.
|
||||||
*
|
*
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
|
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
|
||||||
*/
|
*/
|
||||||
public class SecureDeploymentDefinition extends SimpleResourceDefinition {
|
final class SecureDeploymentDefinition extends AbstractAdapterConfigurationDefinition {
|
||||||
|
|
||||||
public static final String TAG_NAME = "secure-deployment";
|
static final String TAG_NAME = "secure-deployment";
|
||||||
|
|
||||||
protected static final SimpleAttributeDefinition REALM =
|
|
||||||
new SimpleAttributeDefinitionBuilder("realm", ModelType.STRING, true)
|
|
||||||
.setXmlName("realm")
|
|
||||||
.setAllowExpression(true)
|
|
||||||
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
|
|
||||||
.build();
|
|
||||||
protected static final SimpleAttributeDefinition RESOURCE =
|
|
||||||
new SimpleAttributeDefinitionBuilder("resource", ModelType.STRING, true)
|
|
||||||
.setXmlName("resource")
|
|
||||||
.setAllowExpression(true)
|
|
||||||
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
|
|
||||||
.build();
|
|
||||||
protected static final SimpleAttributeDefinition USE_RESOURCE_ROLE_MAPPINGS =
|
|
||||||
new SimpleAttributeDefinitionBuilder("use-resource-role-mappings", ModelType.BOOLEAN, true)
|
|
||||||
.setXmlName("use-resource-role-mappings")
|
|
||||||
.setAllowExpression(true)
|
|
||||||
.setDefaultValue(new ModelNode(false))
|
|
||||||
.build();
|
|
||||||
protected static final SimpleAttributeDefinition BEARER_ONLY =
|
|
||||||
new SimpleAttributeDefinitionBuilder("bearer-only", ModelType.BOOLEAN, true)
|
|
||||||
.setXmlName("bearer-only")
|
|
||||||
.setAllowExpression(true)
|
|
||||||
.setDefaultValue(new ModelNode(false))
|
|
||||||
.build();
|
|
||||||
protected static final SimpleAttributeDefinition ENABLE_BASIC_AUTH =
|
|
||||||
new SimpleAttributeDefinitionBuilder("enable-basic-auth", ModelType.BOOLEAN, true)
|
|
||||||
.setXmlName("enable-basic-auth")
|
|
||||||
.setAllowExpression(true)
|
|
||||||
.setDefaultValue(new ModelNode(false))
|
|
||||||
.build();
|
|
||||||
protected static final SimpleAttributeDefinition PUBLIC_CLIENT =
|
|
||||||
new SimpleAttributeDefinitionBuilder("public-client", ModelType.BOOLEAN, true)
|
|
||||||
.setXmlName("public-client")
|
|
||||||
.setAllowExpression(true)
|
|
||||||
.setDefaultValue(new ModelNode(false))
|
|
||||||
.build();
|
|
||||||
protected static final SimpleAttributeDefinition TURN_OFF_CHANGE_SESSION =
|
|
||||||
new SimpleAttributeDefinitionBuilder("turn-off-change-session-id-on-login", ModelType.BOOLEAN, true)
|
|
||||||
.setXmlName("turn-off-change-session-id-on-login")
|
|
||||||
.setAllowExpression(true)
|
|
||||||
.setDefaultValue(new ModelNode(false))
|
|
||||||
.build();
|
|
||||||
protected static final SimpleAttributeDefinition TOKEN_MINIMUM_TIME_TO_LIVE =
|
|
||||||
new SimpleAttributeDefinitionBuilder("token-minimum-time-to-live", ModelType.INT, true)
|
|
||||||
.setXmlName("token-minimum-time-to-live")
|
|
||||||
.setValidator(new IntRangeValidator(-1, true))
|
|
||||||
.setAllowExpression(true)
|
|
||||||
.build();
|
|
||||||
protected static final SimpleAttributeDefinition MIN_TIME_BETWEEN_JWKS_REQUESTS =
|
|
||||||
new SimpleAttributeDefinitionBuilder("min-time-between-jwks-requests", ModelType.INT, true)
|
|
||||||
.setXmlName("min-time-between-jwks-requests")
|
|
||||||
.setValidator(new IntRangeValidator(-1, true))
|
|
||||||
.setAllowExpression(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
|
|
||||||
protected static final List<SimpleAttributeDefinition> DEPLOYMENT_ONLY_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
|
|
||||||
static {
|
|
||||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(REALM);
|
|
||||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(RESOURCE);
|
|
||||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(USE_RESOURCE_ROLE_MAPPINGS);
|
|
||||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(BEARER_ONLY);
|
|
||||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(ENABLE_BASIC_AUTH);
|
|
||||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(PUBLIC_CLIENT);
|
|
||||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(TURN_OFF_CHANGE_SESSION);
|
|
||||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(TOKEN_MINIMUM_TIME_TO_LIVE);
|
|
||||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(MIN_TIME_BETWEEN_JWKS_REQUESTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
|
|
||||||
static {
|
|
||||||
ALL_ATTRIBUTES.addAll(DEPLOYMENT_ONLY_ATTRIBUTES);
|
|
||||||
ALL_ATTRIBUTES.addAll(SharedAttributeDefinitons.ATTRIBUTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Map<String, SimpleAttributeDefinition> DEFINITION_LOOKUP = new HashMap<String, SimpleAttributeDefinition>();
|
|
||||||
static {
|
|
||||||
for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
|
|
||||||
DEFINITION_LOOKUP.put(def.getXmlName(), def);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SecureDeploymentWriteAttributeHandler attrHandler = new SecureDeploymentWriteAttributeHandler(ALL_ATTRIBUTES);
|
|
||||||
|
|
||||||
public SecureDeploymentDefinition() {
|
public SecureDeploymentDefinition() {
|
||||||
super(PathElement.pathElement(TAG_NAME),
|
super(TAG_NAME, ALL_ATTRIBUTES, new SecureDeploymentAddHandler(), new SecureDeploymentRemoveHandler(), new SecureDeploymentWriteAttributeHandler());
|
||||||
KeycloakExtension.getResourceDescriptionResolver(TAG_NAME),
|
|
||||||
SecureDeploymentAddHandler.INSTANCE,
|
|
||||||
SecureDeploymentRemoveHandler.INSTANCE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
|
* Add a deployment to a realm.
|
||||||
super.registerOperations(resourceRegistration);
|
*
|
||||||
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
|
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
|
||||||
}
|
*/
|
||||||
|
static final class SecureDeploymentAddHandler extends AbstractAdapterConfigurationAddHandler {
|
||||||
|
|
||||||
@Override
|
static final String HTTP_SERVER_AUTHENTICATION_CAPABILITY = "org.wildfly.security.http-server-mechanism-factory";
|
||||||
public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
|
static RuntimeCapability<Void> HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY;
|
||||||
super.registerAttributes(resourceRegistration);
|
|
||||||
for (AttributeDefinition attrDef : ALL_ATTRIBUTES) {
|
static {
|
||||||
resourceRegistration.registerReadWriteAttribute(attrDef, null, attrHandler);
|
try {
|
||||||
|
HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY = RuntimeCapability
|
||||||
|
.Builder.of(HTTP_SERVER_AUTHENTICATION_CAPABILITY, true, HttpServerAuthenticationMechanismFactory.class)
|
||||||
|
.build();
|
||||||
|
} catch (NoClassDefFoundError ncfe) {
|
||||||
|
// ignore, Elytron not present thus no capability will be published by this resource definition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SecureDeploymentAddHandler() {
|
||||||
|
super(HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY, ALL_ATTRIBUTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
|
||||||
|
super.performRuntime(context, operation, model);
|
||||||
|
if (HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY != null) {
|
||||||
|
installCapability(context, operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void installCapability(OperationContext context, ModelNode operation) {
|
||||||
|
PathAddress pathAddress = PathAddress.pathAddress(operation.get(OP_ADDR));
|
||||||
|
String factoryName = pathAddress.getLastElement().getValue();
|
||||||
|
ServiceName serviceName = context.getCapabilityServiceName(HTTP_SERVER_AUTHENTICATION_CAPABILITY, factoryName, HttpServerAuthenticationMechanismFactory.class);
|
||||||
|
KeycloakHttpAuthenticationFactoryService service = new KeycloakHttpAuthenticationFactoryService(factoryName);
|
||||||
|
ServiceTarget serviceTarget = context.getServiceTarget();
|
||||||
|
serviceTarget.addService(serviceName, service).setInitialMode(ServiceController.Mode.ACTIVE).install();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SimpleAttributeDefinition lookup(String name) {
|
/**
|
||||||
return DEFINITION_LOOKUP.get(name);
|
* Remove a secure-deployment from a realm.
|
||||||
|
*
|
||||||
|
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
|
||||||
|
*/
|
||||||
|
static final class SecureDeploymentRemoveHandler extends AbstractAdapterConfigurationRemoveHandler {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an attribute on a secure-deployment.
|
||||||
|
*
|
||||||
|
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
|
||||||
|
*/
|
||||||
|
static final class SecureDeploymentWriteAttributeHandler extends AbstractAdapterConfigurationWriteAttributeHandler {
|
||||||
|
SecureDeploymentWriteAttributeHandler() {
|
||||||
|
super(ALL_ATTRIBUTES);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+258
@@ -0,0 +1,258 @@
|
|||||||
|
/*
|
||||||
|
* 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 static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
|
||||||
|
import static org.keycloak.subsystem.adapter.extension.KeycloakHttpServerAuthenticationMechanismFactoryDefinition.KeycloakHttpServerAuthenticationMechanismFactoryAddHandler.HTTP_SERVER_AUTHENTICATION_CAPABILITY;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.undertow.io.IoCallback;
|
||||||
|
import io.undertow.io.Sender;
|
||||||
|
import io.undertow.server.HttpServerExchange;
|
||||||
|
import io.undertow.server.handlers.resource.Resource;
|
||||||
|
import io.undertow.server.handlers.resource.ResourceChangeListener;
|
||||||
|
import io.undertow.server.handlers.resource.ResourceManager;
|
||||||
|
import io.undertow.util.ETag;
|
||||||
|
import io.undertow.util.MimeMappings;
|
||||||
|
import org.jboss.as.controller.OperationContext;
|
||||||
|
import org.jboss.as.controller.OperationFailedException;
|
||||||
|
import org.jboss.as.controller.PathAddress;
|
||||||
|
import org.jboss.as.controller.capability.RuntimeCapability;
|
||||||
|
import org.jboss.as.server.mgmt.domain.ExtensibleHttpManagement;
|
||||||
|
import org.jboss.dmr.ModelNode;
|
||||||
|
import org.jboss.msc.service.Service;
|
||||||
|
import org.jboss.msc.service.ServiceController.Mode;
|
||||||
|
import org.jboss.msc.service.ServiceName;
|
||||||
|
import org.jboss.msc.service.ServiceTarget;
|
||||||
|
import org.jboss.msc.service.StartContext;
|
||||||
|
import org.jboss.msc.service.StartException;
|
||||||
|
import org.jboss.msc.service.StopContext;
|
||||||
|
import org.jboss.msc.value.InjectedValue;
|
||||||
|
import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines attributes and operations for a secure-deployment.
|
||||||
|
*
|
||||||
|
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
|
||||||
|
*/
|
||||||
|
final class SecureServerDefinition extends AbstractAdapterConfigurationDefinition {
|
||||||
|
|
||||||
|
public static final String TAG_NAME = "secure-server";
|
||||||
|
|
||||||
|
SecureServerDefinition() {
|
||||||
|
super(TAG_NAME, ALL_ATTRIBUTES, new SecureServerAddHandler(), new SecureServerRemoveHandler(), new SecureServerWriteHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link AbstractAdapterConfigurationAddHandler} that exposes a {@link SecureServerDefinition}
|
||||||
|
* as a capability through the installation of a {@link KeycloakHttpAuthenticationFactoryService}.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
static final class SecureServerAddHandler extends AbstractAdapterConfigurationAddHandler {
|
||||||
|
|
||||||
|
static final String HTTP_SERVER_AUTHENTICATION_CAPABILITY = "org.wildfly.security.http-server-mechanism-factory";
|
||||||
|
static final String HTTP_MANAGEMENT_HTTP_EXTENSIBLE_CAPABILITY = "org.wildfly.management.http.extensible";
|
||||||
|
static RuntimeCapability<Void> HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY = RuntimeCapability
|
||||||
|
.Builder.of(HTTP_SERVER_AUTHENTICATION_CAPABILITY, true, HttpServerAuthenticationMechanismFactory.class)
|
||||||
|
.build();
|
||||||
|
} catch (NoClassDefFoundError ncfe) {
|
||||||
|
// ignore, Elytron not present thus no capability will be published by this resource definition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SecureServerAddHandler() {
|
||||||
|
super(HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY, ALL_ATTRIBUTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
|
||||||
|
super.performRuntime(context, operation, model);
|
||||||
|
if (HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY != null) {
|
||||||
|
installCapability(context, operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void installCapability(OperationContext context, ModelNode operation) throws OperationFailedException {
|
||||||
|
PathAddress pathAddress = PathAddress.pathAddress(operation.get(OP_ADDR));
|
||||||
|
String factoryName = pathAddress.getLastElement().getValue();
|
||||||
|
ServiceName serviceName = context.getCapabilityServiceName(HTTP_SERVER_AUTHENTICATION_CAPABILITY, factoryName, HttpServerAuthenticationMechanismFactory.class);
|
||||||
|
boolean publicClient = SecureServerDefinition.PUBLIC_CLIENT.resolveModelAttribute(context, operation).asBoolean(false);
|
||||||
|
|
||||||
|
if (!publicClient) {
|
||||||
|
throw new OperationFailedException("Only public clients are allowed to have their configuration exposed through the management interface");
|
||||||
|
}
|
||||||
|
|
||||||
|
KeycloakHttpAuthenticationFactoryService service = new KeycloakHttpAuthenticationFactoryService(factoryName);
|
||||||
|
ServiceTarget serviceTarget = context.getServiceTarget();
|
||||||
|
InjectedValue<ExtensibleHttpManagement> injectedValue = new InjectedValue<>();
|
||||||
|
serviceTarget.addService(serviceName.append("http-management-context"), createHttpManagementConfigContextService(factoryName, injectedValue))
|
||||||
|
.addDependency(context.getCapabilityServiceName(HTTP_MANAGEMENT_HTTP_EXTENSIBLE_CAPABILITY, ExtensibleHttpManagement.class), ExtensibleHttpManagement.class, injectedValue).setInitialMode(Mode.ACTIVE).install();
|
||||||
|
serviceTarget.addService(serviceName, service).setInitialMode(Mode.ACTIVE).install();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link AbstractAdapterConfigurationRemoveHandler} that handles the removal of {@link SecureServerDefinition}.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
static final class SecureServerRemoveHandler extends AbstractAdapterConfigurationRemoveHandler {
|
||||||
|
@Override
|
||||||
|
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
|
||||||
|
super.performRuntime(context, operation, model);
|
||||||
|
PathAddress pathAddress = PathAddress.pathAddress(operation.get(OP_ADDR));
|
||||||
|
String factoryName = pathAddress.getLastElement().getValue();
|
||||||
|
ServiceName serviceName = context.getCapabilityServiceName(HTTP_SERVER_AUTHENTICATION_CAPABILITY, factoryName, HttpServerAuthenticationMechanismFactory.class);
|
||||||
|
context.removeService(serviceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void recoverServices(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
|
||||||
|
super.recoverServices(context, operation, model);
|
||||||
|
SecureServerDefinition.SecureServerAddHandler.installCapability(context, operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link AbstractAdapterConfigurationWriteAttributeHandler} that updates attributes on a {@link SecureServerDefinition}.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
static final class SecureServerWriteHandler extends AbstractAdapterConfigurationWriteAttributeHandler {
|
||||||
|
SecureServerWriteHandler() {
|
||||||
|
super(ALL_ATTRIBUTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Service<Void> createHttpManagementConfigContextService(final String factoryName, final InjectedValue<ExtensibleHttpManagement> httpConfigContext) {
|
||||||
|
final String contextName = "/keycloak/adapter/" + factoryName + "/";
|
||||||
|
return new Service<Void>() {
|
||||||
|
public void start(StartContext startContext) throws StartException {
|
||||||
|
ExtensibleHttpManagement extensibleHttpManagement = (ExtensibleHttpManagement)httpConfigContext.getValue();
|
||||||
|
extensibleHttpManagement.addStaticContext(contextName, new ResourceManager() {
|
||||||
|
public Resource getResource(final String path) throws IOException {
|
||||||
|
KeycloakAdapterConfigService adapterConfigService = KeycloakAdapterConfigService.getInstance();
|
||||||
|
final String config = adapterConfigService.getJSON(factoryName);
|
||||||
|
|
||||||
|
if (config == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Resource() {
|
||||||
|
public String getPath() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLastModified() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastModifiedString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ETag getETag() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDirectory() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Resource> list() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContentType(MimeMappings mimeMappings) {
|
||||||
|
return "application/json";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void serve(Sender sender, HttpServerExchange exchange, IoCallback completionCallback) {
|
||||||
|
sender.send(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getContentLength() {
|
||||||
|
return Long.valueOf((long)config.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCacheKey() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getFile() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path getFilePath() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getResourceManagerRoot() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path getResourceManagerRootPath() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getUrl() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isResourceChangeListenerSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerResourceChangeListener(ResourceChangeListener listener) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeResourceChangeListener(ResourceChangeListener listener) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop(StopContext stopContext) {
|
||||||
|
((ExtensibleHttpManagement)httpConfigContext.getValue()).removeContext(contextName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Void getValue() throws IllegalStateException, IllegalArgumentException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
+43
@@ -20,6 +20,8 @@ keycloak.subsystem.add=Operation Adds Keycloak adapter subsystem
|
|||||||
keycloak.subsystem.remove=Operation removes Keycloak adapter subsystem
|
keycloak.subsystem.remove=Operation removes Keycloak adapter subsystem
|
||||||
keycloak.subsystem.realm=A Keycloak realm.
|
keycloak.subsystem.realm=A Keycloak realm.
|
||||||
keycloak.subsystem.secure-deployment=A deployment secured by Keycloak.
|
keycloak.subsystem.secure-deployment=A deployment secured by Keycloak.
|
||||||
|
keycloak.subsystem.secure-server=A configuration exposed to the server.
|
||||||
|
keycloak.subsystem.http-server-mechanism-factory=A http-server-mechanism-factory exposed to the server.
|
||||||
|
|
||||||
keycloak.realm=A Keycloak realm.
|
keycloak.realm=A Keycloak realm.
|
||||||
keycloak.realm.add=Add a realm definition to the subsystem.
|
keycloak.realm.add=Add a realm definition to the subsystem.
|
||||||
@@ -90,7 +92,48 @@ keycloak.secure-deployment.token-minimum-time-to-live=The adapter will refresh t
|
|||||||
keycloak.secure-deployment.min-time-between-jwks-requests=If adapter recognize token signed by unknown public key, it will try to download new public key from keycloak server. However it won't try to download if already tried it in less than 'min-time-between-jwks-requests' seconds
|
keycloak.secure-deployment.min-time-between-jwks-requests=If adapter recognize token signed by unknown public key, it will try to download new public key from keycloak server. However it won't try to download if already tried it in less than 'min-time-between-jwks-requests' seconds
|
||||||
keycloak.secure-deployment.ignore-oauth-query-parameter=disable query parameter parsing for access_token
|
keycloak.secure-deployment.ignore-oauth-query-parameter=disable query parameter parsing for access_token
|
||||||
|
|
||||||
|
keycloak.secure-server=A deployment secured by Keycloak
|
||||||
|
keycloak.secure-server.add=Add a deployment to be secured by Keycloak
|
||||||
|
keycloak.secure-server.realm=Keycloak realm
|
||||||
|
keycloak.secure-server.remove=Remove a deployment to be secured by Keycloak
|
||||||
|
keycloak.secure-server.realm-public-key=Public key of the realm
|
||||||
|
keycloak.secure-server.auth-server-url=Base URL of the Realm Auth Server
|
||||||
|
keycloak.secure-server.disable-trust-manager=Adapter will not use a trust manager when making adapter HTTPS requests
|
||||||
|
keycloak.secure-server.ssl-required=Specify if SSL is required (valid values are all, external and none)
|
||||||
|
keycloak.secure-server.allow-any-hostname=SSL Setting
|
||||||
|
keycloak.secure-server.truststore=Truststore used for adapter client HTTPS requests
|
||||||
|
keycloak.secure-server.truststore-password=Password of the Truststore
|
||||||
|
keycloak.secure-server.connection-pool-size=Connection pool size for the client used by the adapter
|
||||||
|
keycloak.secure-server.resource=Application name
|
||||||
|
keycloak.secure-server.use-resource-role-mappings=Use resource level permissions from token
|
||||||
|
keycloak.secure-server.credentials=Adapter credentials
|
||||||
|
keycloak.secure-server.redirect-rewrite-rule=Apply a rewrite rule for the redirect URI
|
||||||
|
keycloak.secure-server.bearer-only=Bearer Token Auth only
|
||||||
|
keycloak.secure-server.enable-basic-auth=Enable Basic Authentication
|
||||||
|
keycloak.secure-server.public-client=Public client
|
||||||
|
keycloak.secure-server.enable-cors=Enable Keycloak CORS support
|
||||||
|
keycloak.secure-server.autodetect-bearer-only=autodetect bearer-only requests
|
||||||
|
keycloak.secure-server.client-keystore=n/a
|
||||||
|
keycloak.secure-server.client-keystore-password=n/a
|
||||||
|
keycloak.secure-server.client-key-password=n/a
|
||||||
|
keycloak.secure-server.cors-max-age=CORS max-age header
|
||||||
|
keycloak.secure-server.cors-allowed-headers=CORS allowed headers
|
||||||
|
keycloak.secure-server.cors-allowed-methods=CORS allowed methods
|
||||||
|
keycloak.secure-server.cors-exposed-headers=CORS exposed headers
|
||||||
|
keycloak.secure-server.expose-token=Enable secure URL that exposes access token
|
||||||
|
keycloak.secure-server.auth-server-url-for-backend-requests=URL to use to make background calls to auth server
|
||||||
|
keycloak.secure-server.always-refresh-token=Refresh token on every single web request
|
||||||
|
keycloak.secure-server.register-node-at-startup=Cluster setting
|
||||||
|
keycloak.secure-server.register-node-period=how often to re-register node
|
||||||
|
keycloak.secure-server.token-store=cookie or session storage for auth session data
|
||||||
|
keycloak.secure-server.principal-attribute=token attribute to use to set Principal name
|
||||||
|
keycloak.secure-server.turn-off-change-session-id-on-login=The session id is changed by default on a successful login. Change this to true if you want to turn this off
|
||||||
|
keycloak.secure-server.token-minimum-time-to-live=The adapter will refresh the token if the current token is expired OR will expire in 'token-minimum-time-to-live' seconds or less
|
||||||
|
keycloak.secure-server.min-time-between-jwks-requests=If adapter recognize token signed by unknown public key, it will try to download new public key from keycloak server. However it won't try to download if already tried it in less than 'min-time-between-jwks-requests' seconds
|
||||||
|
keycloak.secure-server.ignore-oauth-query-parameter=disable query parameter parsing for access_token
|
||||||
|
|
||||||
keycloak.secure-deployment.credential=Credential value
|
keycloak.secure-deployment.credential=Credential value
|
||||||
|
keycloak.secure-server.credential=Credential value
|
||||||
|
|
||||||
keycloak.credential=Credential
|
keycloak.credential=Credential
|
||||||
keycloak.credential.value=Credential value
|
keycloak.credential.value=Credential value
|
||||||
|
|||||||
+5
-4
@@ -38,6 +38,7 @@
|
|||||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
<xs:element name="realm" maxOccurs="unbounded" minOccurs="0" type="realm-type"/>
|
<xs:element name="realm" maxOccurs="unbounded" minOccurs="0" type="realm-type"/>
|
||||||
<xs:element name="secure-deployment" maxOccurs="unbounded" minOccurs="0" type="secure-deployment-type"/>
|
<xs:element name="secure-deployment" maxOccurs="unbounded" minOccurs="0" type="secure-deployment-type"/>
|
||||||
|
<xs:element name="secure-server" maxOccurs="unbounded" minOccurs="0" type="secure-deployment-type"/>
|
||||||
</xs:choice>
|
</xs:choice>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
|
||||||
@@ -84,7 +85,7 @@
|
|||||||
<xs:element name="allow-any-hostname" type="xs:boolean" minOccurs="0" maxOccurs="1" />
|
<xs:element name="allow-any-hostname" type="xs:boolean" minOccurs="0" maxOccurs="1" />
|
||||||
<xs:element name="use-resource-role-mappings" type="xs:boolean" minOccurs="0" maxOccurs="1" />
|
<xs:element name="use-resource-role-mappings" type="xs:boolean" minOccurs="0" maxOccurs="1" />
|
||||||
<xs:element name="cors-max-age" type="xs:integer" minOccurs="0" maxOccurs="1"/>
|
<xs:element name="cors-max-age" type="xs:integer" minOccurs="0" maxOccurs="1"/>
|
||||||
<xs:element name="auth-server-url" type="xs:string" minOccurs="1" maxOccurs="1"/>
|
<xs:element name="auth-server-url" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||||
<xs:element name="realm" type="xs:string" minOccurs="1" maxOccurs="1"/>
|
<xs:element name="realm" type="xs:string" minOccurs="1" maxOccurs="1"/>
|
||||||
<xs:element name="disable-trust-manager" type="xs:boolean" minOccurs="0" maxOccurs="1" />
|
<xs:element name="disable-trust-manager" type="xs:boolean" minOccurs="0" maxOccurs="1" />
|
||||||
<xs:element name="cors-allowed-methods" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
<xs:element name="cors-allowed-methods" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||||
@@ -99,9 +100,9 @@
|
|||||||
<xs:element name="connection-pool-size" type="xs:integer" minOccurs="0" maxOccurs="1"/>
|
<xs:element name="connection-pool-size" type="xs:integer" minOccurs="0" maxOccurs="1"/>
|
||||||
<xs:element name="expose-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
|
<xs:element name="expose-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
|
||||||
<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="0" maxOccurs="1"/>
|
||||||
<xs:element name="credential" type="credential-type" minOccurs="1" maxOccurs="1"/>
|
<xs:element name="credential" type="credential-type" minOccurs="0" maxOccurs="1"/>
|
||||||
<xs:element name="redirect-rewrite-rule" type="redirect-rewrite-rule-type" minOccurs="1" maxOccurs="1"/>
|
<xs:element name="redirect-rewrite-rule" type="redirect-rewrite-rule-type" minOccurs="0" 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"/>
|
||||||
|
|||||||
+1
-1
@@ -73,7 +73,7 @@ public class SubsystemParsingTestCase extends AbstractSubsystemBaseTest {
|
|||||||
ModelNode deployment = new ModelNode();
|
ModelNode deployment = new ModelNode();
|
||||||
deployment.get("realm").set("demo");
|
deployment.get("realm").set("demo");
|
||||||
deployment.get("resource").set("customer-portal");
|
deployment.get("resource").set("customer-portal");
|
||||||
service.addSecureDeployment(deploymentOp, deployment);
|
service.addSecureDeployment(deploymentOp, deployment, false);
|
||||||
|
|
||||||
addCredential(addr, service, "secret", "secret1");
|
addCredential(addr, service, "secret", "secret1");
|
||||||
addCredential(addr, service, "jwt.client-keystore-file", "/tmp/foo.jks");
|
addCredential(addr, service, "jwt.client-keystore-file", "/tmp/foo.jks");
|
||||||
|
|||||||
+16
@@ -40,6 +40,10 @@
|
|||||||
<token-store>session</token-store>
|
<token-store>session</token-store>
|
||||||
<principal-attribute>sub</principal-attribute>
|
<principal-attribute>sub</principal-attribute>
|
||||||
</realm>
|
</realm>
|
||||||
|
<realm name="jboss-infra">
|
||||||
|
<realm-public-key>MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqKoq+a9MgXepmsPJDmo45qswuChW9pWjanX68oIBuI4hGvhQxFHryCow230A+sr7tFdMQMt8f1l/ysmV/fYAuW29WaoY4kI4Ou1yYPuwywKSsxT6PooTs83hKyZ1h4LZMj5DkLGDDDyVRHob2WmPaYg9RGVRw3iGGsD/p+Yb+L/gnBYQnZZ7lYqmN7h36p5CkzzlgXQA1Ha8sQxL+rJNH8+sZm0vBrKsoII3Of7TqHGsm1RwFV3XCuGJ7S61AbjJMXL5DQgJl9Z5scvxGAyoRLKC294UgMnQdzyBTMPw2GybxkRKmiK2KjQKmcopmrJp/Bt6fBR6ZkGSs9qUlxGHgwIDAQAB</realm-public-key>
|
||||||
|
<auth-server-url>http://localhost:8180/auth</auth-server-url>
|
||||||
|
</realm>
|
||||||
<secure-deployment name="web-console">
|
<secure-deployment name="web-console">
|
||||||
<realm>master</realm>
|
<realm>master</realm>
|
||||||
<resource>web-console</resource>
|
<resource>web-console</resource>
|
||||||
@@ -69,4 +73,16 @@
|
|||||||
</credential>
|
</credential>
|
||||||
<redirect-rewrite-rule name="^/wsmaster/api/(.*)$">/api/$1/</redirect-rewrite-rule>
|
<redirect-rewrite-rule name="^/wsmaster/api/(.*)$">/api/$1/</redirect-rewrite-rule>
|
||||||
</secure-deployment>
|
</secure-deployment>
|
||||||
|
<secure-deployment name="wildfly-management">
|
||||||
|
<realm>jboss-infra</realm>
|
||||||
|
<resource>wildfly-management</resource>
|
||||||
|
<bearer-only>true</bearer-only>
|
||||||
|
<ssl-required>EXTERNAL</ssl-required>
|
||||||
|
<principal-attribute>preferred_username</principal-attribute>
|
||||||
|
</secure-deployment>
|
||||||
|
<secure-server name="wildfly-console">
|
||||||
|
<realm>jboss-infra</realm>
|
||||||
|
<resource>wildfly-console</resource>
|
||||||
|
<public-client>true</public-client>
|
||||||
|
</secure-server>
|
||||||
</subsystem>
|
</subsystem>
|
||||||
+1
-1
@@ -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.Final</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.Final</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
@@ -78,6 +78,18 @@
|
|||||||
<version>7.1.2.Final</version>
|
<version>7.1.2.Final</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.infinispan</groupId>
|
||||||
|
<artifactId>infinispan-core</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
<version>5.2.20.Final</version> <!-- override version to match EAP's -->
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.infinispan</groupId>
|
||||||
|
<artifactId>infinispan-cachestore-remote</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
<version>5.2.20.Final</version> <!-- override version to match EAP's -->
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-saml-tomcat-adapter-core</artifactId>
|
<artifactId>keycloak-saml-tomcat-adapter-core</artifactId>
|
||||||
|
|||||||
+42
-32
@@ -16,9 +16,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.keycloak.adapters.saml.jbossweb.infinispan;
|
package org.keycloak.adapters.saml.jbossweb.infinispan;
|
||||||
|
|
||||||
|
import org.keycloak.adapters.saml.AdapterConstants;
|
||||||
import org.keycloak.adapters.spi.SessionIdMapper;
|
import org.keycloak.adapters.spi.SessionIdMapper;
|
||||||
import org.keycloak.adapters.spi.SessionIdMapperUpdater;
|
import org.keycloak.adapters.spi.SessionIdMapperUpdater;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import javax.naming.InitialContext;
|
import javax.naming.InitialContext;
|
||||||
import javax.naming.NamingException;
|
import javax.naming.NamingException;
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
@@ -26,6 +28,8 @@ import org.apache.catalina.Context;
|
|||||||
import org.infinispan.Cache;
|
import org.infinispan.Cache;
|
||||||
import org.infinispan.configuration.cache.CacheMode;
|
import org.infinispan.configuration.cache.CacheMode;
|
||||||
import org.infinispan.configuration.cache.Configuration;
|
import org.infinispan.configuration.cache.Configuration;
|
||||||
|
import org.infinispan.loaders.CacheLoaderManager;
|
||||||
|
import org.infinispan.loaders.remote.RemoteCacheStore;
|
||||||
import org.infinispan.manager.EmbeddedCacheManager;
|
import org.infinispan.manager.EmbeddedCacheManager;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
@@ -37,24 +41,12 @@ public class InfinispanSessionCacheIdMapperUpdater {
|
|||||||
|
|
||||||
private static final Logger LOG = Logger.getLogger(InfinispanSessionCacheIdMapperUpdater.class);
|
private static final Logger LOG = Logger.getLogger(InfinispanSessionCacheIdMapperUpdater.class);
|
||||||
|
|
||||||
public static final String DEFAULT_CACHE_CONTAINER_JNDI_NAME = "java:jboss/infinispan/container/web";
|
public static final String DEFAULT_CACHE_CONTAINER_JNDI_NAME = "java:jboss/infinispan/container";
|
||||||
|
|
||||||
private static final String DEPLOYMENT_CACHE_CONTAINER_JNDI_NAME_PARAM_NAME = "keycloak.sessionIdMapperUpdater.infinispan.cacheContainerJndi";
|
|
||||||
private static final String DEPLOYMENT_CACHE_NAME_PARAM_NAME = "keycloak.sessionIdMapperUpdater.infinispan.deploymentCacheName";
|
|
||||||
private static final String SSO_CACHE_NAME_PARAM_NAME = "keycloak.sessionIdMapperUpdater.infinispan.cacheName";
|
|
||||||
|
|
||||||
public static SessionIdMapperUpdater addTokenStoreUpdaters(Context context, SessionIdMapper mapper, SessionIdMapperUpdater previousIdMapperUpdater) {
|
public static SessionIdMapperUpdater addTokenStoreUpdaters(Context context, SessionIdMapper mapper, SessionIdMapperUpdater previousIdMapperUpdater) {
|
||||||
boolean distributable = context.getDistributable();
|
|
||||||
|
|
||||||
if (! distributable) {
|
|
||||||
LOG.warnv("Deployment {0} does not use supported distributed session cache mechanism", context.getName());
|
|
||||||
return previousIdMapperUpdater;
|
|
||||||
}
|
|
||||||
|
|
||||||
ServletContext servletContext = context.getServletContext();
|
ServletContext servletContext = context.getServletContext();
|
||||||
String cacheContainerLookup = (servletContext != null && servletContext.getInitParameter(DEPLOYMENT_CACHE_CONTAINER_JNDI_NAME_PARAM_NAME) != null)
|
String containerName = servletContext == null ? null : servletContext.getInitParameter(AdapterConstants.REPLICATION_CONFIG_CONTAINER_PARAM_NAME);
|
||||||
? servletContext.getInitParameter(DEPLOYMENT_CACHE_CONTAINER_JNDI_NAME_PARAM_NAME)
|
String cacheName = servletContext == null ? null : servletContext.getInitParameter(AdapterConstants.REPLICATION_CONFIG_SSO_CACHE_PARAM_NAME);
|
||||||
: DEFAULT_CACHE_CONTAINER_JNDI_NAME;
|
|
||||||
|
|
||||||
// the following is based on https://github.com/jbossas/jboss-as/blob/7.2.0.Final/clustering/web-infinispan/src/main/java/org/jboss/as/clustering/web/infinispan/DistributedCacheManagerFactory.java#L116-L122
|
// the following is based on https://github.com/jbossas/jboss-as/blob/7.2.0.Final/clustering/web-infinispan/src/main/java/org/jboss/as/clustering/web/infinispan/DistributedCacheManagerFactory.java#L116-L122
|
||||||
String host = context.getParent() == null ? "" : context.getParent().getName();
|
String host = context.getParent() == null ? "" : context.getParent().getName();
|
||||||
@@ -62,43 +54,48 @@ public class InfinispanSessionCacheIdMapperUpdater {
|
|||||||
if ("/".equals(contextPath)) {
|
if ("/".equals(contextPath)) {
|
||||||
contextPath = "/ROOT";
|
contextPath = "/ROOT";
|
||||||
}
|
}
|
||||||
|
String deploymentSessionCacheName = host + contextPath;
|
||||||
|
|
||||||
boolean deploymentSessionCacheNamePreset = servletContext != null && servletContext.getInitParameter(DEPLOYMENT_CACHE_NAME_PARAM_NAME) != null;
|
if (containerName == null || cacheName == null || deploymentSessionCacheName == null) {
|
||||||
String deploymentSessionCacheName = deploymentSessionCacheNamePreset
|
LOG.warnv("Cannot determine parameters of SSO cache for deployment {0}.", host + contextPath);
|
||||||
? servletContext.getInitParameter(DEPLOYMENT_CACHE_NAME_PARAM_NAME)
|
|
||||||
: host + contextPath;
|
return previousIdMapperUpdater;
|
||||||
boolean ssoCacheNamePreset = servletContext != null && servletContext.getInitParameter(SSO_CACHE_NAME_PARAM_NAME) != null;
|
}
|
||||||
String ssoCacheName = ssoCacheNamePreset
|
|
||||||
? servletContext.getInitParameter(SSO_CACHE_NAME_PARAM_NAME)
|
String cacheContainerLookup = DEFAULT_CACHE_CONTAINER_JNDI_NAME + "/" + containerName;
|
||||||
: deploymentSessionCacheName + ".ssoCache";
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
EmbeddedCacheManager cacheManager = (EmbeddedCacheManager) new InitialContext().lookup(cacheContainerLookup);
|
EmbeddedCacheManager cacheManager = (EmbeddedCacheManager) new InitialContext().lookup(cacheContainerLookup);
|
||||||
|
|
||||||
Configuration ssoCacheConfiguration = cacheManager.getCacheConfiguration(ssoCacheName);
|
Configuration ssoCacheConfiguration = cacheManager.getCacheConfiguration(cacheName);
|
||||||
if (ssoCacheConfiguration == null) {
|
if (ssoCacheConfiguration == null) {
|
||||||
Configuration cacheConfiguration = cacheManager.getCacheConfiguration(deploymentSessionCacheName);
|
Configuration cacheConfiguration = cacheManager.getCacheConfiguration(deploymentSessionCacheName);
|
||||||
if (cacheConfiguration == null) {
|
if (cacheConfiguration == null) {
|
||||||
LOG.debugv("Using default cache container configuration for SSO cache. lookup={0}, looked up configuration of cache={1}", cacheContainerLookup, deploymentSessionCacheName);
|
LOG.debugv("Using default configuration for SSO cache {0}.{1}.", containerName, cacheName);
|
||||||
ssoCacheConfiguration = cacheManager.getDefaultCacheConfiguration();
|
ssoCacheConfiguration = cacheManager.getDefaultCacheConfiguration();
|
||||||
} else {
|
} else {
|
||||||
LOG.debugv("Using distributed HTTP session cache configuration for SSO cache. lookup={0}, configuration taken from cache={1}", cacheContainerLookup, deploymentSessionCacheName);
|
LOG.debugv("Using distributed HTTP session cache configuration for SSO cache {0}.{1}, configuration taken from cache {2}",
|
||||||
|
containerName, cacheName, deploymentSessionCacheName);
|
||||||
ssoCacheConfiguration = cacheConfiguration;
|
ssoCacheConfiguration = cacheConfiguration;
|
||||||
cacheManager.defineConfiguration(ssoCacheName, ssoCacheConfiguration);
|
cacheManager.defineConfiguration(cacheName, ssoCacheConfiguration);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG.debugv("Using custom configuration for SSO cache. lookup={0}, cache name={1}", cacheContainerLookup, ssoCacheName);
|
LOG.debugv("Using custom configuration of SSO cache {0}.{1}.", containerName, cacheName);
|
||||||
}
|
}
|
||||||
|
|
||||||
CacheMode ssoCacheMode = ssoCacheConfiguration.clustering().cacheMode();
|
CacheMode ssoCacheMode = ssoCacheConfiguration.clustering().cacheMode();
|
||||||
if (ssoCacheMode != CacheMode.REPL_ASYNC && ssoCacheMode != CacheMode.REPL_SYNC) {
|
if (ssoCacheMode != CacheMode.REPL_ASYNC && ssoCacheMode != CacheMode.REPL_SYNC) {
|
||||||
LOG.warnv("SSO cache mode is {0}, it is recommended to use replicated mode instead", ssoCacheConfiguration.clustering().cacheModeString());
|
LOG.warnv("SSO cache mode is {0}, it is recommended to use replicated mode instead.", ssoCacheConfiguration.clustering().cacheModeString());
|
||||||
}
|
}
|
||||||
|
|
||||||
Cache<String, String[]> ssoCache = cacheManager.getCache(ssoCacheName, true);
|
Cache<String, String[]> ssoCache = cacheManager.getCache(cacheName, true);
|
||||||
ssoCache.addListener(new SsoSessionCacheListener(mapper));
|
final SsoSessionCacheListener listener = new SsoSessionCacheListener(ssoCache, mapper);
|
||||||
|
ssoCache.addListener(listener);
|
||||||
|
|
||||||
LOG.debugv("Added distributed SSO session cache, lookup={0}, cache name={1}", cacheContainerLookup, deploymentSessionCacheName);
|
// Not possible to add listener for cross-DC support because of too old Infinispan in AS 7
|
||||||
|
warnIfRemoteStoreIsUsed(ssoCache);
|
||||||
|
|
||||||
|
LOG.debugv("Added distributed SSO session cache, lookup={0}, cache name={1}", cacheContainerLookup, cacheName);
|
||||||
|
|
||||||
SsoCacheSessionIdMapperUpdater updater = new SsoCacheSessionIdMapperUpdater(ssoCache, previousIdMapperUpdater);
|
SsoCacheSessionIdMapperUpdater updater = new SsoCacheSessionIdMapperUpdater(ssoCache, previousIdMapperUpdater);
|
||||||
|
|
||||||
@@ -108,4 +105,17 @@ public class InfinispanSessionCacheIdMapperUpdater {
|
|||||||
return previousIdMapperUpdater;
|
return previousIdMapperUpdater;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void warnIfRemoteStoreIsUsed(Cache<String, String[]> ssoCache) {
|
||||||
|
final List<RemoteCacheStore> stores = getRemoteStores(ssoCache);
|
||||||
|
if (stores == null || stores.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.warnv("Unable to listen for events on remote stores configured for cache {0} (unsupported in this Infinispan limitations), logouts will not be propagated.", ssoCache.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<RemoteCacheStore> getRemoteStores(Cache ssoCache) {
|
||||||
|
return ssoCache.getAdvancedCache().getComponentRegistry().getComponent(CacheLoaderManager.class).getCacheLoaders(RemoteCacheStore.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+46
-27
@@ -20,6 +20,7 @@ import org.keycloak.adapters.spi.SessionIdMapper;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
import org.infinispan.Cache;
|
||||||
import org.infinispan.notifications.Listener;
|
import org.infinispan.notifications.Listener;
|
||||||
import org.infinispan.notifications.cachelistener.annotation.*;
|
import org.infinispan.notifications.cachelistener.annotation.*;
|
||||||
import org.infinispan.notifications.cachelistener.event.*;
|
import org.infinispan.notifications.cachelistener.event.*;
|
||||||
@@ -43,9 +44,12 @@ public class SsoSessionCacheListener {
|
|||||||
|
|
||||||
private final SessionIdMapper idMapper;
|
private final SessionIdMapper idMapper;
|
||||||
|
|
||||||
|
private final Cache<String, String[]> ssoCache;
|
||||||
|
|
||||||
private ExecutorService executor = Executors.newSingleThreadExecutor();
|
private ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
public SsoSessionCacheListener(SessionIdMapper idMapper) {
|
public SsoSessionCacheListener(Cache<String, String[]> ssoCache, SessionIdMapper idMapper) {
|
||||||
|
this.ssoCache = ssoCache;
|
||||||
this.idMapper = idMapper;
|
this.idMapper = idMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,8 +72,10 @@ public class SsoSessionCacheListener {
|
|||||||
@CacheEntryRemoved
|
@CacheEntryRemoved
|
||||||
@CacheEntryModified
|
@CacheEntryModified
|
||||||
public void addEvent(TransactionalEvent event) {
|
public void addEvent(TransactionalEvent event) {
|
||||||
if (event.isPre() == false) {
|
if (event.getGlobalTransaction() != null) {
|
||||||
map.get(event.getGlobalTransaction()).add(event);
|
map.get(event.getGlobalTransaction()).add(event);
|
||||||
|
} else {
|
||||||
|
processEvent(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,40 +93,53 @@ public class SsoSessionCacheListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (final Event e : events) {
|
for (final Event e : events) {
|
||||||
switch (e.getType()) {
|
processEvent(e);
|
||||||
case CACHE_ENTRY_CREATED:
|
}
|
||||||
this.executor.submit(new Runnable() {
|
}
|
||||||
@Override public void run() {
|
|
||||||
cacheEntryCreated((CacheEntryCreatedEvent) e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CACHE_ENTRY_MODIFIED:
|
private void processEvent(final Event e) {
|
||||||
this.executor.submit(new Runnable() {
|
switch (e.getType()) {
|
||||||
@Override public void run() {
|
case CACHE_ENTRY_CREATED:
|
||||||
cacheEntryModified((CacheEntryModifiedEvent) e);
|
this.executor.submit(new Runnable() {
|
||||||
}
|
@Override public void run() {
|
||||||
});
|
cacheEntryCreated((CacheEntryCreatedEvent) e);
|
||||||
break;
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
case CACHE_ENTRY_REMOVED:
|
case CACHE_ENTRY_MODIFIED:
|
||||||
this.executor.submit(new Runnable() {
|
this.executor.submit(new Runnable() {
|
||||||
@Override public void run() {
|
@Override public void run() {
|
||||||
cacheEntryRemoved((CacheEntryRemovedEvent) e);
|
cacheEntryModified((CacheEntryModifiedEvent) e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
case CACHE_ENTRY_REMOVED:
|
||||||
|
this.executor.submit(new Runnable() {
|
||||||
|
@Override public void run() {
|
||||||
|
cacheEntryRemoved((CacheEntryRemovedEvent) e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cacheEntryCreated(CacheEntryCreatedEvent event) {
|
private void cacheEntryCreated(CacheEntryCreatedEvent event) {
|
||||||
if (! (event.getKey() instanceof String) || ! (event.getValue() instanceof String[])) {
|
if (! (event.getKey() instanceof String)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String httpSessionId = (String) event.getKey();
|
String httpSessionId = (String) event.getKey();
|
||||||
String[] value = (String[]) event.getValue();
|
|
||||||
|
if (idMapper.hasSession(httpSessionId)) {
|
||||||
|
// Ignore local events generated by remote store
|
||||||
|
LOG.tracev("IGNORING cacheEntryCreated {0}", httpSessionId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] value = ssoCache.get((String) httpSessionId);
|
||||||
|
|
||||||
String ssoId = value[0];
|
String ssoId = value[0];
|
||||||
String principal = value[1];
|
String principal = value[1];
|
||||||
|
|
||||||
|
|||||||
@@ -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.Final</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.Final</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|||||||
+157
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 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.saml.as7;
|
||||||
|
|
||||||
|
import org.keycloak.adapters.saml.AdapterConstants;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.jboss.as.server.deployment.DeploymentPhaseContext;
|
||||||
|
import org.jboss.as.server.deployment.DeploymentUnit;
|
||||||
|
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
|
||||||
|
import org.jboss.as.server.deployment.DeploymentUnitProcessor;
|
||||||
|
import org.jboss.as.web.deployment.WarMetaData;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.jboss.metadata.javaee.spec.ParamValueMetaData;
|
||||||
|
import org.jboss.metadata.web.jboss.JBossWebMetaData;
|
||||||
|
import org.jboss.metadata.web.spec.LoginConfigMetaData;
|
||||||
|
import org.jboss.msc.service.ServiceName;
|
||||||
|
import org.jboss.msc.service.ServiceTarget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author hmlnarik
|
||||||
|
*/
|
||||||
|
public class KeycloakClusteredSsoDeploymentProcessor implements DeploymentUnitProcessor {
|
||||||
|
|
||||||
|
private static final Logger LOG = Logger.getLogger(KeycloakClusteredSsoDeploymentProcessor.class);
|
||||||
|
|
||||||
|
private static final String DEFAULT_CACHE_CONTAINER = "web";
|
||||||
|
private static final String SSO_CACHE_CONTAINER_NAME_PARAM_NAME = "keycloak.sessionIdMapperUpdater.infinispan.containerName";
|
||||||
|
private static final String SSO_CACHE_NAME_PARAM_NAME = "keycloak.sessionIdMapperUpdater.infinispan.cacheName";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
|
||||||
|
final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
|
||||||
|
|
||||||
|
if (isKeycloakSamlAuthMethod(deploymentUnit) && isDistributable(deploymentUnit)) {
|
||||||
|
addSamlReplicationConfiguration(deploymentUnit, phaseContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDistributable(final DeploymentUnit deploymentUnit) {
|
||||||
|
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
|
||||||
|
if (warMetaData == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
|
||||||
|
if (webMetaData == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return webMetaData.getDistributable() != null || webMetaData.getReplicationConfig() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isKeycloakSamlAuthMethod(final DeploymentUnit deploymentUnit) {
|
||||||
|
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
|
||||||
|
if (warMetaData == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
|
||||||
|
if (webMetaData == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Configuration.INSTANCE.isSecureDeployment(deploymentUnit)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoginConfigMetaData loginConfig = webMetaData.getLoginConfig();
|
||||||
|
|
||||||
|
return loginConfig != null && Objects.equals(loginConfig.getAuthMethod(), "KEYCLOAK-SAML");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undeploy(DeploymentUnit du) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSamlReplicationConfiguration(DeploymentUnit deploymentUnit, DeploymentPhaseContext context) {
|
||||||
|
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
|
||||||
|
if (warMetaData == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
|
||||||
|
if (webMetaData == null) {
|
||||||
|
webMetaData = new JBossWebMetaData();
|
||||||
|
warMetaData.setMergedJBossWebMetaData(webMetaData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find out default names of cache container and cache
|
||||||
|
String cacheContainer = DEFAULT_CACHE_CONTAINER;
|
||||||
|
String deploymentSessionCacheName =
|
||||||
|
(deploymentUnit.getParent() == null
|
||||||
|
? ""
|
||||||
|
: deploymentUnit.getParent().getName() + ".")
|
||||||
|
+ deploymentUnit.getName();
|
||||||
|
|
||||||
|
// Update names from jboss-web.xml's <replicationConfig>
|
||||||
|
if (webMetaData.getReplicationConfig() != null && webMetaData.getReplicationConfig().getCacheName() != null) {
|
||||||
|
ServiceName sn = ServiceName.parse(webMetaData.getReplicationConfig().getCacheName());
|
||||||
|
cacheContainer = sn.getParent().getSimpleName();
|
||||||
|
deploymentSessionCacheName = sn.getSimpleName();
|
||||||
|
}
|
||||||
|
String ssoCacheName = deploymentSessionCacheName + ".ssoCache";
|
||||||
|
|
||||||
|
// Override if they were set in the context parameters
|
||||||
|
List<ParamValueMetaData> contextParams = webMetaData.getContextParams();
|
||||||
|
if (contextParams == null) {
|
||||||
|
contextParams = new ArrayList<>();
|
||||||
|
}
|
||||||
|
for (ParamValueMetaData contextParam : contextParams) {
|
||||||
|
if (Objects.equals(contextParam.getParamName(), SSO_CACHE_CONTAINER_NAME_PARAM_NAME)) {
|
||||||
|
cacheContainer = contextParam.getParamValue();
|
||||||
|
} else if (Objects.equals(contextParam.getParamName(), SSO_CACHE_NAME_PARAM_NAME)) {
|
||||||
|
ssoCacheName = contextParam.getParamValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.debugv("Determined SSO cache container configuration: container: {0}, cache: {1}", cacheContainer, ssoCacheName);
|
||||||
|
// addCacheDependency(context, deploymentUnit, cacheContainer, cacheName);
|
||||||
|
|
||||||
|
// Set context parameters for SSO cache container/name
|
||||||
|
ParamValueMetaData paramContainer = new ParamValueMetaData();
|
||||||
|
paramContainer.setParamName(AdapterConstants.REPLICATION_CONFIG_CONTAINER_PARAM_NAME);
|
||||||
|
paramContainer.setParamValue(cacheContainer);
|
||||||
|
contextParams.add(paramContainer);
|
||||||
|
|
||||||
|
ParamValueMetaData paramSsoCache = new ParamValueMetaData();
|
||||||
|
paramSsoCache.setParamName(AdapterConstants.REPLICATION_CONFIG_SSO_CACHE_PARAM_NAME);
|
||||||
|
paramSsoCache.setParamValue(ssoCacheName);
|
||||||
|
contextParams.add(paramSsoCache);
|
||||||
|
|
||||||
|
webMetaData.setContextParams(contextParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCacheDependency(DeploymentPhaseContext context, DeploymentUnit deploymentUnit, String cacheContainer, String cacheName) {
|
||||||
|
ServiceName jbossAsCacheContainerService = ServiceName.of("jboss", "infinispan", cacheContainer);
|
||||||
|
ServiceTarget st = context.getServiceTarget();
|
||||||
|
st.addDependency(jbossAsCacheContainerService.append(cacheName));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+8
@@ -48,6 +48,10 @@ class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
|
|||||||
Phase.POST_MODULE, // PHASE
|
Phase.POST_MODULE, // PHASE
|
||||||
Phase.POST_MODULE_VALIDATOR_FACTORY - 1, // PRIORITY
|
Phase.POST_MODULE_VALIDATOR_FACTORY - 1, // PRIORITY
|
||||||
chooseConfigDeploymentProcessor());
|
chooseConfigDeploymentProcessor());
|
||||||
|
processorTarget.addDeploymentProcessor(KeycloakSamlExtension.SUBSYSTEM_NAME,
|
||||||
|
Phase.POST_MODULE, // PHASE
|
||||||
|
Phase.POST_MODULE_VALIDATOR_FACTORY - 1, // PRIORITY
|
||||||
|
chooseClusteredSsoDeploymentProcessor());
|
||||||
}
|
}
|
||||||
}, OperationContext.Stage.RUNTIME);
|
}, OperationContext.Stage.RUNTIME);
|
||||||
}
|
}
|
||||||
@@ -60,6 +64,10 @@ class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
|
|||||||
return new KeycloakAdapterConfigDeploymentProcessor();
|
return new KeycloakAdapterConfigDeploymentProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DeploymentUnitProcessor chooseClusteredSsoDeploymentProcessor() {
|
||||||
|
return new KeycloakClusteredSsoDeploymentProcessor();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
|
protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -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.Final</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
+16
-1
@@ -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.Final</version>
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
@@ -23,4 +23,6 @@ package org.keycloak.adapters.saml;
|
|||||||
*/
|
*/
|
||||||
public class AdapterConstants {
|
public class AdapterConstants {
|
||||||
public static final String AUTH_DATA_PARAM_NAME="org.keycloak.saml.xml.adapterConfig";
|
public static final String AUTH_DATA_PARAM_NAME="org.keycloak.saml.xml.adapterConfig";
|
||||||
|
public static final String REPLICATION_CONFIG_CONTAINER_PARAM_NAME = "org.keycloak.saml.replication.container";
|
||||||
|
public static final String REPLICATION_CONFIG_SSO_CACHE_PARAM_NAME = "org.keycloak.saml.replication.cache.sso";
|
||||||
}
|
}
|
||||||
+3
-3
@@ -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.Final</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.Final</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.Final</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.Final</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.Final</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.Final</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.Final</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.Final</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.Final</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.Final</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.Final</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
Loaded 100 of 1005 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user