adapter for spring boot 2
remove built directory update snapshot version references refactor out core library to remove duplication adapter for spring boot 2 remove built directory update snapshot version references Revert "merge from upstream" This reverts commit 88c39a2f23b8f2d4b25360e2b46e683d11b4972b, reversing changes made to f0811145ceeb8ec609ed66b06067f797e288aa89. setting correct versions updating to latest keycloak arquillian test app for spring boot2 update to 2.0.0.RELEASE added Rest Customizer
This commit is contained in:
parent
fe98c30077
commit
d57fb445eb
34 changed files with 1831 additions and 201 deletions
|
@ -42,6 +42,8 @@
|
||||||
<module>servlet-filter</module>
|
<module>servlet-filter</module>
|
||||||
<module>servlet-oauth-client</module>
|
<module>servlet-oauth-client</module>
|
||||||
<module>spring-boot</module>
|
<module>spring-boot</module>
|
||||||
|
<module>spring-boot2</module>
|
||||||
|
<module>spring-boot-adapter-core</module>
|
||||||
<module>spring-boot-container-bundle</module>
|
<module>spring-boot-container-bundle</module>
|
||||||
<module>spring-security</module>
|
<module>spring-security</module>
|
||||||
<module>tomcat</module>
|
<module>tomcat</module>
|
||||||
|
|
116
adapters/oidc/spring-boot-adapter-core/pom.xml
Executable file
116
adapters/oidc/spring-boot-adapter-core/pom.xml
Executable file
|
@ -0,0 +1,116 @@
|
||||||
|
<?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>4.0.0.CR1-SNAPSHOT</version>
|
||||||
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>keycloak-spring-boot-adapter-core</artifactId>
|
||||||
|
<name>Keycloak Spring Boot Adapter Core</name>
|
||||||
|
<description/>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<spring-boot.version>1.3.0.RELEASE</spring-boot.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.logging</groupId>
|
||||||
|
<artifactId>jboss-logging</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>spring-boot-container-bundle</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<optional>true</optional>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-security-adapter</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.undertow</groupId>
|
||||||
|
<artifactId>undertow-servlet</artifactId>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-server</artifactId>
|
||||||
|
<version>${jetty9.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-security</artifactId>
|
||||||
|
<version>${jetty9.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-webapp</artifactId>
|
||||||
|
<version>${jetty9.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
|
@ -0,0 +1,262 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import io.undertow.servlet.api.DeploymentInfo;
|
||||||
|
import io.undertow.servlet.api.WebResourceCollection;
|
||||||
|
import org.apache.catalina.Context;
|
||||||
|
import org.apache.tomcat.util.descriptor.web.LoginConfig;
|
||||||
|
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
|
||||||
|
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
|
||||||
|
import org.eclipse.jetty.security.ConstraintMapping;
|
||||||
|
import org.eclipse.jetty.security.ConstraintSecurityHandler;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.util.security.Constraint;
|
||||||
|
import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
|
import org.keycloak.adapters.jetty.KeycloakJettyAuthenticator;
|
||||||
|
import org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve;
|
||||||
|
import org.keycloak.adapters.undertow.KeycloakServletExtension;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||||
|
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
|
||||||
|
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
|
||||||
|
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
|
||||||
|
import org.springframework.boot.context.embedded.jetty.JettyServerCustomizer;
|
||||||
|
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
|
||||||
|
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
|
||||||
|
import org.springframework.boot.context.embedded.undertow.UndertowDeploymentInfoCustomizer;
|
||||||
|
import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keycloak authentication base integration for Spring Boot - base to be extended for particular boot versions.
|
||||||
|
*/
|
||||||
|
public class KeycloakBaseSpringBootConfiguration {
|
||||||
|
|
||||||
|
protected KeycloakSpringBootProperties keycloakProperties;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void setKeycloakSpringBootProperties(KeycloakSpringBootProperties keycloakProperties) {
|
||||||
|
this.keycloakProperties = keycloakProperties;
|
||||||
|
KeycloakSpringBootConfigResolver.setAdapterConfig(keycloakProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class KeycloakBaseUndertowDeploymentInfoCustomizer {
|
||||||
|
|
||||||
|
protected final KeycloakSpringBootProperties keycloakProperties;
|
||||||
|
|
||||||
|
public KeycloakBaseUndertowDeploymentInfoCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
||||||
|
this.keycloakProperties = keycloakProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void customize(DeploymentInfo deploymentInfo) {
|
||||||
|
|
||||||
|
io.undertow.servlet.api.LoginConfig loginConfig = new io.undertow.servlet.api.LoginConfig(keycloakProperties.getRealm());
|
||||||
|
loginConfig.addFirstAuthMethod("KEYCLOAK");
|
||||||
|
|
||||||
|
deploymentInfo.setLoginConfig(loginConfig);
|
||||||
|
|
||||||
|
deploymentInfo.addInitParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName());
|
||||||
|
deploymentInfo.addSecurityConstraints(getSecurityConstraints());
|
||||||
|
|
||||||
|
deploymentInfo.addServletExtension(new KeycloakServletExtension());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<io.undertow.servlet.api.SecurityConstraint> getSecurityConstraints() {
|
||||||
|
|
||||||
|
List<io.undertow.servlet.api.SecurityConstraint> undertowSecurityConstraints = new ArrayList<io.undertow.servlet.api.SecurityConstraint>();
|
||||||
|
for (KeycloakSpringBootProperties.SecurityConstraint constraintDefinition : keycloakProperties.getSecurityConstraints()) {
|
||||||
|
|
||||||
|
io.undertow.servlet.api.SecurityConstraint undertowSecurityConstraint = new io.undertow.servlet.api.SecurityConstraint();
|
||||||
|
undertowSecurityConstraint.addRolesAllowed(constraintDefinition.getAuthRoles());
|
||||||
|
|
||||||
|
for (KeycloakSpringBootProperties.SecurityCollection collectionDefinition : constraintDefinition.getSecurityCollections()) {
|
||||||
|
|
||||||
|
WebResourceCollection webResourceCollection = new WebResourceCollection();
|
||||||
|
webResourceCollection.addHttpMethods(collectionDefinition.getMethods());
|
||||||
|
webResourceCollection.addHttpMethodOmissions(collectionDefinition.getOmittedMethods());
|
||||||
|
webResourceCollection.addUrlPatterns(collectionDefinition.getPatterns());
|
||||||
|
|
||||||
|
undertowSecurityConstraint.addWebResourceCollections(webResourceCollection);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
undertowSecurityConstraints.add(undertowSecurityConstraint);
|
||||||
|
}
|
||||||
|
return undertowSecurityConstraints;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class KeycloakBaseJettyServerCustomizer {
|
||||||
|
|
||||||
|
protected final KeycloakSpringBootProperties keycloakProperties;
|
||||||
|
|
||||||
|
public KeycloakBaseJettyServerCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
||||||
|
this.keycloakProperties = keycloakProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void customize(Server server) {
|
||||||
|
|
||||||
|
KeycloakJettyAuthenticator keycloakJettyAuthenticator = new KeycloakJettyAuthenticator();
|
||||||
|
keycloakJettyAuthenticator.setConfigResolver(new KeycloakSpringBootConfigResolver());
|
||||||
|
|
||||||
|
/* see org.eclipse.jetty.webapp.StandardDescriptorProcessor#visitSecurityConstraint for an example
|
||||||
|
on how to map servlet spec to Constraints */
|
||||||
|
|
||||||
|
List<ConstraintMapping> jettyConstraintMappings = new ArrayList<ConstraintMapping>();
|
||||||
|
for (KeycloakSpringBootProperties.SecurityConstraint constraintDefinition : keycloakProperties.getSecurityConstraints()) {
|
||||||
|
|
||||||
|
for (KeycloakSpringBootProperties.SecurityCollection securityCollectionDefinition : constraintDefinition
|
||||||
|
.getSecurityCollections()) {
|
||||||
|
// securityCollection matches servlet spec's web-resource-collection
|
||||||
|
Constraint jettyConstraint = new Constraint();
|
||||||
|
|
||||||
|
if (constraintDefinition.getAuthRoles().size() > 0) {
|
||||||
|
jettyConstraint.setAuthenticate(true);
|
||||||
|
jettyConstraint.setRoles(constraintDefinition.getAuthRoles().toArray(new String[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
jettyConstraint.setName(securityCollectionDefinition.getName());
|
||||||
|
|
||||||
|
// according to the servlet spec each security-constraint has at least one URL pattern
|
||||||
|
for(String pattern : securityCollectionDefinition.getPatterns()) {
|
||||||
|
|
||||||
|
/* the following code is asymmetric as Jetty's ConstraintMapping accepts only one allowed HTTP method,
|
||||||
|
but multiple omitted methods. Therefore we add one ConstraintMapping for each allowed
|
||||||
|
mapping but only one mapping in the cases of omitted methods or no methods.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (securityCollectionDefinition.getMethods().size() > 0) {
|
||||||
|
// according to the servlet spec we have either methods ...
|
||||||
|
for(String method : securityCollectionDefinition.getMethods()) {
|
||||||
|
ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
|
||||||
|
jettyConstraintMappings.add(jettyConstraintMapping);
|
||||||
|
|
||||||
|
jettyConstraintMapping.setConstraint(jettyConstraint);
|
||||||
|
jettyConstraintMapping.setPathSpec(pattern);
|
||||||
|
jettyConstraintMapping.setMethod(method);
|
||||||
|
}
|
||||||
|
} else if (securityCollectionDefinition.getOmittedMethods().size() > 0){
|
||||||
|
// ... omitted methods ...
|
||||||
|
ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
|
||||||
|
jettyConstraintMappings.add(jettyConstraintMapping);
|
||||||
|
|
||||||
|
jettyConstraintMapping.setConstraint(jettyConstraint);
|
||||||
|
jettyConstraintMapping.setPathSpec(pattern);
|
||||||
|
jettyConstraintMapping.setMethodOmissions(
|
||||||
|
securityCollectionDefinition.getOmittedMethods().toArray(new String[0]));
|
||||||
|
} else {
|
||||||
|
// ... or no methods at all
|
||||||
|
ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
|
||||||
|
jettyConstraintMappings.add(jettyConstraintMapping);
|
||||||
|
|
||||||
|
jettyConstraintMapping.setConstraint(jettyConstraint);
|
||||||
|
jettyConstraintMapping.setPathSpec(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WebAppContext webAppContext = server.getBean(WebAppContext.class);
|
||||||
|
//if not found as registered bean let's try the handler
|
||||||
|
if(webAppContext==null){
|
||||||
|
webAppContext = (WebAppContext) server.getHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
|
||||||
|
securityHandler.setConstraintMappings(jettyConstraintMappings);
|
||||||
|
securityHandler.setAuthenticator(keycloakJettyAuthenticator);
|
||||||
|
|
||||||
|
webAppContext.setSecurityHandler(securityHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class KeycloakBaseTomcatContextCustomizer {
|
||||||
|
|
||||||
|
protected final KeycloakSpringBootProperties keycloakProperties;
|
||||||
|
|
||||||
|
public KeycloakBaseTomcatContextCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
||||||
|
this.keycloakProperties = keycloakProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void customize(Context context) {
|
||||||
|
LoginConfig loginConfig = new LoginConfig();
|
||||||
|
loginConfig.setAuthMethod("KEYCLOAK");
|
||||||
|
context.setLoginConfig(loginConfig);
|
||||||
|
|
||||||
|
Set<String> authRoles = new HashSet<String>();
|
||||||
|
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
|
||||||
|
for (String authRole : constraint.getAuthRoles()) {
|
||||||
|
if (!authRoles.contains(authRole)) {
|
||||||
|
context.addSecurityRole(authRole);
|
||||||
|
authRoles.add(authRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
|
||||||
|
SecurityConstraint tomcatConstraint = new SecurityConstraint();
|
||||||
|
|
||||||
|
for (String authRole : constraint.getAuthRoles()) {
|
||||||
|
tomcatConstraint.addAuthRole(authRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) {
|
||||||
|
SecurityCollection tomcatSecCollection = new SecurityCollection();
|
||||||
|
|
||||||
|
if (collection.getName() != null) {
|
||||||
|
tomcatSecCollection.setName(collection.getName());
|
||||||
|
}
|
||||||
|
if (collection.getDescription() != null) {
|
||||||
|
tomcatSecCollection.setDescription(collection.getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String pattern : collection.getPatterns()) {
|
||||||
|
tomcatSecCollection.addPattern(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String method : collection.getMethods()) {
|
||||||
|
tomcatSecCollection.addMethod(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String method : collection.getOmittedMethods()) {
|
||||||
|
tomcatSecCollection.addOmittedMethod(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
tomcatConstraint.addCollection(tomcatSecCollection);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.addConstraint(tomcatConstraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.addParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
|
org.keycloak.adapters.springboot.KeycloakAutoConfiguration
|
|
@ -37,6 +37,12 @@
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-boot-adapter-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jboss.logging</groupId>
|
<groupId>org.jboss.logging</groupId>
|
||||||
<artifactId>jboss-logging</artifactId>
|
<artifactId>jboss-logging</artifactId>
|
||||||
|
|
|
@ -17,20 +17,7 @@
|
||||||
|
|
||||||
package org.keycloak.adapters.springboot;
|
package org.keycloak.adapters.springboot;
|
||||||
|
|
||||||
import io.undertow.servlet.api.DeploymentInfo;
|
|
||||||
import io.undertow.servlet.api.WebResourceCollection;
|
|
||||||
import org.apache.catalina.Context;
|
|
||||||
import org.apache.tomcat.util.descriptor.web.LoginConfig;
|
|
||||||
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
|
|
||||||
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
|
|
||||||
import org.eclipse.jetty.security.ConstraintMapping;
|
|
||||||
import org.eclipse.jetty.security.ConstraintSecurityHandler;
|
|
||||||
import org.eclipse.jetty.server.Server;
|
|
||||||
import org.eclipse.jetty.util.security.Constraint;
|
|
||||||
import org.eclipse.jetty.webapp.WebAppContext;
|
|
||||||
import org.keycloak.adapters.jetty.KeycloakJettyAuthenticator;
|
|
||||||
import org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve;
|
import org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve;
|
||||||
import org.keycloak.adapters.undertow.KeycloakServletExtension;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
@ -47,10 +34,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keycloak authentication integration for Spring Boot
|
* Keycloak authentication integration for Spring Boot
|
||||||
|
@ -62,7 +45,7 @@ import java.util.Set;
|
||||||
@ConditionalOnWebApplication
|
@ConditionalOnWebApplication
|
||||||
@EnableConfigurationProperties(KeycloakSpringBootProperties.class)
|
@EnableConfigurationProperties(KeycloakSpringBootProperties.class)
|
||||||
@ConditionalOnProperty(value = "keycloak.enabled", matchIfMissing = true)
|
@ConditionalOnProperty(value = "keycloak.enabled", matchIfMissing = true)
|
||||||
public class KeycloakAutoConfiguration {
|
public class KeycloakAutoConfiguration extends KeycloakBaseSpringBootConfiguration {
|
||||||
|
|
||||||
private KeycloakSpringBootProperties keycloakProperties;
|
private KeycloakSpringBootProperties keycloakProperties;
|
||||||
|
|
||||||
|
@ -117,202 +100,27 @@ public class KeycloakAutoConfiguration {
|
||||||
return new KeycloakUndertowDeploymentInfoCustomizer(keycloakProperties);
|
return new KeycloakUndertowDeploymentInfoCustomizer(keycloakProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
static class KeycloakUndertowDeploymentInfoCustomizer implements UndertowDeploymentInfoCustomizer {
|
static class KeycloakUndertowDeploymentInfoCustomizer extends KeycloakBaseUndertowDeploymentInfoCustomizer implements UndertowDeploymentInfoCustomizer {
|
||||||
|
|
||||||
private final KeycloakSpringBootProperties keycloakProperties;
|
|
||||||
|
|
||||||
public KeycloakUndertowDeploymentInfoCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
public KeycloakUndertowDeploymentInfoCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
||||||
this.keycloakProperties = keycloakProperties;
|
super(keycloakProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void customize(DeploymentInfo deploymentInfo) {
|
|
||||||
|
|
||||||
io.undertow.servlet.api.LoginConfig loginConfig = new io.undertow.servlet.api.LoginConfig(keycloakProperties.getRealm());
|
|
||||||
loginConfig.addFirstAuthMethod("KEYCLOAK");
|
|
||||||
|
|
||||||
deploymentInfo.setLoginConfig(loginConfig);
|
|
||||||
deploymentInfo.addInitParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName());
|
|
||||||
deploymentInfo.addSecurityConstraints(getSecurityConstraints());
|
|
||||||
deploymentInfo.addServletExtension(new KeycloakServletExtension());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<io.undertow.servlet.api.SecurityConstraint> getSecurityConstraints() {
|
static class KeycloakJettyServerCustomizer extends KeycloakBaseJettyServerCustomizer implements JettyServerCustomizer {
|
||||||
|
|
||||||
List<io.undertow.servlet.api.SecurityConstraint> undertowSecurityConstraints = new ArrayList<io.undertow.servlet.api.SecurityConstraint>();
|
|
||||||
for (KeycloakSpringBootProperties.SecurityConstraint constraintDefinition : keycloakProperties.getSecurityConstraints()) {
|
|
||||||
|
|
||||||
io.undertow.servlet.api.SecurityConstraint undertowSecurityConstraint = new io.undertow.servlet.api.SecurityConstraint();
|
|
||||||
undertowSecurityConstraint.addRolesAllowed(constraintDefinition.getAuthRoles());
|
|
||||||
|
|
||||||
for (KeycloakSpringBootProperties.SecurityCollection collectionDefinition : constraintDefinition.getSecurityCollections()) {
|
|
||||||
|
|
||||||
WebResourceCollection webResourceCollection = new WebResourceCollection();
|
|
||||||
webResourceCollection.addHttpMethods(collectionDefinition.getMethods());
|
|
||||||
webResourceCollection.addHttpMethodOmissions(collectionDefinition.getOmittedMethods());
|
|
||||||
webResourceCollection.addUrlPatterns(collectionDefinition.getPatterns());
|
|
||||||
|
|
||||||
undertowSecurityConstraint.addWebResourceCollections(webResourceCollection);
|
|
||||||
|
|
||||||
}
|
|
||||||
undertowSecurityConstraints.add(undertowSecurityConstraint);
|
|
||||||
}
|
|
||||||
return undertowSecurityConstraints;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class KeycloakJettyServerCustomizer implements JettyServerCustomizer {
|
|
||||||
|
|
||||||
private final KeycloakSpringBootProperties keycloakProperties;
|
|
||||||
|
|
||||||
public KeycloakJettyServerCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
public KeycloakJettyServerCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
||||||
this.keycloakProperties = keycloakProperties;
|
super(keycloakProperties);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void customize(Server server) {
|
|
||||||
|
|
||||||
KeycloakJettyAuthenticator keycloakJettyAuthenticator = new KeycloakJettyAuthenticator();
|
|
||||||
keycloakJettyAuthenticator.setConfigResolver(new KeycloakSpringBootConfigResolver());
|
|
||||||
|
|
||||||
/* see org.eclipse.jetty.webapp.StandardDescriptorProcessor#visitSecurityConstraint for an example
|
|
||||||
on how to map servlet spec to Constraints */
|
|
||||||
|
|
||||||
List<ConstraintMapping> jettyConstraintMappings = new ArrayList<ConstraintMapping>();
|
|
||||||
for (KeycloakSpringBootProperties.SecurityConstraint constraintDefinition : keycloakProperties.getSecurityConstraints()) {
|
|
||||||
|
|
||||||
for (KeycloakSpringBootProperties.SecurityCollection securityCollectionDefinition : constraintDefinition
|
|
||||||
.getSecurityCollections()) {
|
|
||||||
// securityCollection matches servlet spec's web-resource-collection
|
|
||||||
Constraint jettyConstraint = new Constraint();
|
|
||||||
|
|
||||||
if (constraintDefinition.getAuthRoles().size() > 0) {
|
|
||||||
jettyConstraint.setAuthenticate(true);
|
|
||||||
jettyConstraint.setRoles(constraintDefinition.getAuthRoles().toArray(new String[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
jettyConstraint.setName(securityCollectionDefinition.getName());
|
|
||||||
|
|
||||||
// according to the servlet spec each security-constraint has at least one URL pattern
|
|
||||||
for(String pattern : securityCollectionDefinition.getPatterns()) {
|
|
||||||
|
|
||||||
/* the following code is asymmetric as Jetty's ConstraintMapping accepts only one allowed HTTP method,
|
|
||||||
but multiple omitted methods. Therefore we add one ConstraintMapping for each allowed
|
|
||||||
mapping but only one mapping in the cases of omitted methods or no methods.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (securityCollectionDefinition.getMethods().size() > 0) {
|
|
||||||
// according to the servlet spec we have either methods ...
|
|
||||||
for(String method : securityCollectionDefinition.getMethods()) {
|
|
||||||
ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
|
|
||||||
jettyConstraintMappings.add(jettyConstraintMapping);
|
|
||||||
|
|
||||||
jettyConstraintMapping.setConstraint(jettyConstraint);
|
|
||||||
jettyConstraintMapping.setPathSpec(pattern);
|
|
||||||
jettyConstraintMapping.setMethod(method);
|
|
||||||
}
|
|
||||||
} else if (securityCollectionDefinition.getOmittedMethods().size() > 0){
|
|
||||||
// ... omitted methods ...
|
|
||||||
ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
|
|
||||||
jettyConstraintMappings.add(jettyConstraintMapping);
|
|
||||||
|
|
||||||
jettyConstraintMapping.setConstraint(jettyConstraint);
|
|
||||||
jettyConstraintMapping.setPathSpec(pattern);
|
|
||||||
jettyConstraintMapping.setMethodOmissions(
|
|
||||||
securityCollectionDefinition.getOmittedMethods().toArray(new String[0]));
|
|
||||||
} else {
|
|
||||||
// ... or no methods at all
|
|
||||||
ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
|
|
||||||
jettyConstraintMappings.add(jettyConstraintMapping);
|
|
||||||
|
|
||||||
jettyConstraintMapping.setConstraint(jettyConstraint);
|
|
||||||
jettyConstraintMapping.setPathSpec(pattern);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
static class KeycloakTomcatContextCustomizer extends KeycloakBaseTomcatContextCustomizer implements TomcatContextCustomizer {
|
||||||
}
|
|
||||||
|
|
||||||
WebAppContext webAppContext = server.getBean(WebAppContext.class);
|
|
||||||
//if not found as registered bean let's try the handler
|
|
||||||
if(webAppContext==null){
|
|
||||||
webAppContext = (WebAppContext) server.getHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
|
|
||||||
securityHandler.setConstraintMappings(jettyConstraintMappings);
|
|
||||||
securityHandler.setAuthenticator(keycloakJettyAuthenticator);
|
|
||||||
|
|
||||||
webAppContext.setSecurityHandler(securityHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class KeycloakTomcatContextCustomizer implements TomcatContextCustomizer {
|
|
||||||
|
|
||||||
private final KeycloakSpringBootProperties keycloakProperties;
|
|
||||||
|
|
||||||
public KeycloakTomcatContextCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
public KeycloakTomcatContextCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
||||||
this.keycloakProperties = keycloakProperties;
|
super(keycloakProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void customize(Context context) {
|
|
||||||
LoginConfig loginConfig = new LoginConfig();
|
|
||||||
loginConfig.setAuthMethod("KEYCLOAK");
|
|
||||||
context.setLoginConfig(loginConfig);
|
|
||||||
|
|
||||||
Set<String> authRoles = new HashSet<String>();
|
|
||||||
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
|
|
||||||
for (String authRole : constraint.getAuthRoles()) {
|
|
||||||
if (!authRoles.contains(authRole)) {
|
|
||||||
context.addSecurityRole(authRole);
|
|
||||||
authRoles.add(authRole);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
|
|
||||||
SecurityConstraint tomcatConstraint = new SecurityConstraint();
|
|
||||||
|
|
||||||
for (String authRole : constraint.getAuthRoles()) {
|
|
||||||
tomcatConstraint.addAuthRole(authRole);
|
|
||||||
if(authRole.equals("*") || authRole.equals("**")) {
|
|
||||||
// For some reasons embed tomcat don't set the auth constraint on true when wildcard is
|
|
||||||
// used
|
|
||||||
tomcatConstraint.setAuthConstraint(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) {
|
|
||||||
SecurityCollection tomcatSecCollection = new SecurityCollection();
|
|
||||||
|
|
||||||
if (collection.getName() != null) {
|
|
||||||
tomcatSecCollection.setName(collection.getName());
|
|
||||||
}
|
|
||||||
if (collection.getDescription() != null) {
|
|
||||||
tomcatSecCollection.setDescription(collection.getDescription());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String pattern : collection.getPatterns()) {
|
|
||||||
tomcatSecCollection.addPattern(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String method : collection.getMethods()) {
|
|
||||||
tomcatSecCollection.addMethod(method);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String method : collection.getOmittedMethods()) {
|
|
||||||
tomcatSecCollection.addOmittedMethod(method);
|
|
||||||
}
|
|
||||||
|
|
||||||
tomcatConstraint.addCollection(tomcatSecCollection);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.addConstraint(tomcatConstraint);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.addParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
151
adapters/oidc/spring-boot2/pom.xml
Executable file
151
adapters/oidc/spring-boot2/pom.xml
Executable file
|
@ -0,0 +1,151 @@
|
||||||
|
<?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>4.0.0.CR1-SNAPSHOT</version>
|
||||||
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>keycloak-spring-boot-2-adapter</artifactId>
|
||||||
|
<name>Keycloak Spring Boot 2 Integration</name>
|
||||||
|
<description/>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<spring-boot.version>2.0.0.RELEASE</spring-boot.version>
|
||||||
|
<spring.version>5.0.2.RELEASE</spring.version>
|
||||||
|
<mockito.version>1.9.5</mockito.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-boot-adapter-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.logging</groupId>
|
||||||
|
<artifactId>jboss-logging</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>spring-boot-container-bundle</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<optional>true</optional>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-security-adapter</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>2.9.4</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-annotations</artifactId>
|
||||||
|
<version>2.9.4</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
<version>5.0.2.RELEASE</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.undertow</groupId>
|
||||||
|
<artifactId>undertow-servlet</artifactId>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-server</artifactId>
|
||||||
|
<version>${jetty9.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-security</artifactId>
|
||||||
|
<version>${jetty9.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-webapp</artifactId>
|
||||||
|
<version>${jetty9.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</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>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
|
@ -0,0 +1,116 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||||
|
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
|
||||||
|
import org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer;
|
||||||
|
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||||
|
import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer;
|
||||||
|
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
|
||||||
|
import org.springframework.boot.web.embedded.undertow.UndertowDeploymentInfoCustomizer;
|
||||||
|
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keycloak authentication integration for Spring Boot 2
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnWebApplication
|
||||||
|
@EnableConfigurationProperties(KeycloakSpringBootProperties.class)
|
||||||
|
@ConditionalOnProperty(value = "keycloak.enabled", matchIfMissing = true)
|
||||||
|
public class KeycloakAutoConfiguration extends KeycloakBaseSpringBootConfiguration {
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> getKeycloakContainerCustomizer() {
|
||||||
|
return new WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>() {
|
||||||
|
@Override
|
||||||
|
public void customize(ConfigurableServletWebServerFactory configurableServletWebServerFactory) {
|
||||||
|
if(configurableServletWebServerFactory instanceof TomcatServletWebServerFactory){
|
||||||
|
|
||||||
|
TomcatServletWebServerFactory container = (TomcatServletWebServerFactory)configurableServletWebServerFactory;
|
||||||
|
container.addContextValves(new KeycloakAuthenticatorValve());
|
||||||
|
container.addContextCustomizers(tomcatKeycloakContextCustomizer());
|
||||||
|
|
||||||
|
} else if (configurableServletWebServerFactory instanceof UndertowServletWebServerFactory){
|
||||||
|
|
||||||
|
UndertowServletWebServerFactory container = (UndertowServletWebServerFactory)configurableServletWebServerFactory;
|
||||||
|
container.addDeploymentInfoCustomizers(undertowKeycloakContextCustomizer());
|
||||||
|
|
||||||
|
} else if (configurableServletWebServerFactory instanceof JettyServletWebServerFactory){
|
||||||
|
|
||||||
|
JettyServletWebServerFactory container = (JettyServletWebServerFactory)configurableServletWebServerFactory;
|
||||||
|
container.addServerCustomizers(jettyKeycloakServerCustomizer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnClass(name = {"org.eclipse.jetty.webapp.WebAppContext"})
|
||||||
|
public JettyServerCustomizer jettyKeycloakServerCustomizer() {
|
||||||
|
return new KeycloakJettyServerCustomizer(keycloakProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnClass(name = {"org.apache.catalina.startup.Tomcat"})
|
||||||
|
public TomcatContextCustomizer tomcatKeycloakContextCustomizer() {
|
||||||
|
return new KeycloakTomcatContextCustomizer(keycloakProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnClass(name = {"io.undertow.Undertow"})
|
||||||
|
public UndertowDeploymentInfoCustomizer undertowKeycloakContextCustomizer() {
|
||||||
|
return new KeycloakUndertowDeploymentInfoCustomizer(keycloakProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class KeycloakJettyServerCustomizer extends KeycloakBaseJettyServerCustomizer implements JettyServerCustomizer {
|
||||||
|
|
||||||
|
|
||||||
|
public KeycloakJettyServerCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
||||||
|
super(keycloakProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class KeycloakTomcatContextCustomizer extends KeycloakBaseTomcatContextCustomizer implements TomcatContextCustomizer {
|
||||||
|
|
||||||
|
public KeycloakTomcatContextCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
||||||
|
super(keycloakProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class KeycloakUndertowDeploymentInfoCustomizer extends KeycloakBaseUndertowDeploymentInfoCustomizer implements UndertowDeploymentInfoCustomizer {
|
||||||
|
|
||||||
|
public KeycloakUndertowDeploymentInfoCustomizer(KeycloakSpringBootProperties keycloakProperties){
|
||||||
|
super(keycloakProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
|
org.keycloak.adapters.springboot.KeycloakAutoConfiguration
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -109,6 +109,11 @@
|
||||||
<artifactId>keycloak-spring-boot-adapter</artifactId>
|
<artifactId>keycloak-spring-boot-adapter</artifactId>
|
||||||
<version>4.0.0.CR1-SNAPSHOT</version>
|
<version>4.0.0.CR1-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-boot-2-adapter</artifactId>
|
||||||
|
<version>4.0.0.CR1-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>spring-boot-container-bundle</artifactId>
|
<artifactId>spring-boot-container-bundle</artifactId>
|
||||||
|
@ -124,6 +129,16 @@
|
||||||
<artifactId>keycloak-spring-boot-starter</artifactId>
|
<artifactId>keycloak-spring-boot-starter</artifactId>
|
||||||
<version>4.0.0.CR1-SNAPSHOT</version>
|
<version>4.0.0.CR1-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-boot-2-starter</artifactId>
|
||||||
|
<version>4.0.0.CR1-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-boot-2-starter</artifactId>
|
||||||
|
<version>4.0.0.CR1-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-authz-client</artifactId>
|
<artifactId>keycloak-authz-client</artifactId>
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-boot-2-starter-parent</artifactId>
|
||||||
|
<version>4.0.0.CR1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<artifactId>keycloak-spring-boot-2-starter</artifactId>
|
||||||
|
<name>Keycloak :: Spring :: Boot :: 2 :: Default :: Starter</name>
|
||||||
|
<description>Spring Boot 2 Default Starter for Keycloak</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<spring-boot.version>2.0.0.RELEASE</spring-boot.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-boot-2-adapter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-authz-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>spring-boot-container-bundle</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-security-adapter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
30
misc/spring-boot-2-starter/pom.xml
Normal file
30
misc/spring-boot-2-starter/pom.xml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<artifactId>keycloak-misc-parent</artifactId>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<version>4.0.0.CR1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-boot-2-starter-parent</artifactId>
|
||||||
|
<name>Keycloak :: Spring :: Boot ::2</name>
|
||||||
|
<description>Support for using Keycloak in Spring Boot 2 applications.</description>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<modules>
|
||||||
|
<module>keycloak-spring-boot-2-starter</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak.bom</groupId>
|
||||||
|
<artifactId>keycloak-adapter-bom</artifactId>
|
||||||
|
<version>4.0.0.CR1-SNAPSHOT</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
</project>
|
10
pom.xml
10
pom.xml
|
@ -882,11 +882,21 @@
|
||||||
<artifactId>keycloak-servlet-oauth-client</artifactId>
|
<artifactId>keycloak-servlet-oauth-client</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-boot-adapter-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-spring-boot-adapter</artifactId>
|
<artifactId>keycloak-spring-boot-adapter</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-boot-2-adapter</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-tomcat-adapter-spi</artifactId>
|
<artifactId>keycloak-tomcat-adapter-spi</artifactId>
|
||||||
|
|
225
testsuite/integration-arquillian/test-apps/spring-boot-2-adapter/mvnw
vendored
Executable file
225
testsuite/integration-arquillian/test-apps/spring-boot-2-adapter/mvnw
vendored
Executable file
|
@ -0,0 +1,225 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you 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.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Maven2 Start Up Batch script
|
||||||
|
#
|
||||||
|
# Required ENV vars:
|
||||||
|
# ------------------
|
||||||
|
# JAVA_HOME - location of a JDK home dir
|
||||||
|
#
|
||||||
|
# Optional ENV vars
|
||||||
|
# -----------------
|
||||||
|
# M2_HOME - location of maven2's installed home dir
|
||||||
|
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||||
|
# e.g. to debug Maven itself, use
|
||||||
|
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||||
|
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||||
|
|
||||||
|
if [ -f /etc/mavenrc ] ; then
|
||||||
|
. /etc/mavenrc
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$HOME/.mavenrc" ] ; then
|
||||||
|
. "$HOME/.mavenrc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
# OS specific support. $var _must_ be set to either true or false.
|
||||||
|
cygwin=false;
|
||||||
|
darwin=false;
|
||||||
|
mingw=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN*) cygwin=true ;;
|
||||||
|
MINGW*) mingw=true;;
|
||||||
|
Darwin*) darwin=true
|
||||||
|
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||||
|
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
if [ -x "/usr/libexec/java_home" ]; then
|
||||||
|
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||||
|
else
|
||||||
|
export JAVA_HOME="/Library/Java/Home"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
if [ -r /etc/gentoo-release ] ; then
|
||||||
|
JAVA_HOME=`java-config --jre-home`
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$M2_HOME" ] ; then
|
||||||
|
## resolve links - $0 may be a link to maven's home
|
||||||
|
PRG="$0"
|
||||||
|
|
||||||
|
# need this for relative symlinks
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG="`dirname "$PRG"`/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
saveddir=`pwd`
|
||||||
|
|
||||||
|
M2_HOME=`dirname "$PRG"`/..
|
||||||
|
|
||||||
|
# make it fully qualified
|
||||||
|
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||||
|
|
||||||
|
cd "$saveddir"
|
||||||
|
# echo Using m2 at $M2_HOME
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $cygwin ; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||||
|
[ -n "$CLASSPATH" ] &&
|
||||||
|
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Migwn, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $mingw ; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||||
|
# TODO classpath?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
javaExecutable="`which javac`"
|
||||||
|
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||||
|
# readlink(1) is not available as standard on Solaris 10.
|
||||||
|
readLink=`which readlink`
|
||||||
|
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||||
|
if $darwin ; then
|
||||||
|
javaHome="`dirname \"$javaExecutable\"`"
|
||||||
|
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||||
|
else
|
||||||
|
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||||
|
fi
|
||||||
|
javaHome="`dirname \"$javaExecutable\"`"
|
||||||
|
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||||
|
JAVA_HOME="$javaHome"
|
||||||
|
export JAVA_HOME
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVACMD" ] ; then
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="`which java`"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||||
|
echo " We cannot execute $JAVACMD" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
echo "Warning: JAVA_HOME environment variable is not set."
|
||||||
|
fi
|
||||||
|
|
||||||
|
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||||
|
|
||||||
|
# traverses directory structure from process work directory to filesystem root
|
||||||
|
# first directory with .mvn subdirectory is considered project base directory
|
||||||
|
find_maven_basedir() {
|
||||||
|
|
||||||
|
if [ -z "$1" ]
|
||||||
|
then
|
||||||
|
echo "Path not specified to find_maven_basedir"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
basedir="$1"
|
||||||
|
wdir="$1"
|
||||||
|
while [ "$wdir" != '/' ] ; do
|
||||||
|
if [ -d "$wdir"/.mvn ] ; then
|
||||||
|
basedir=$wdir
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||||
|
if [ -d "${wdir}" ]; then
|
||||||
|
wdir=`cd "$wdir/.."; pwd`
|
||||||
|
fi
|
||||||
|
# end of workaround
|
||||||
|
done
|
||||||
|
echo "${basedir}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# concatenates all lines of a file
|
||||||
|
concat_lines() {
|
||||||
|
if [ -f "$1" ]; then
|
||||||
|
echo "$(tr -s '\n' ' ' < "$1")"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||||
|
if [ -z "$BASE_DIR" ]; then
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||||
|
echo $MAVEN_PROJECTBASEDIR
|
||||||
|
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||||
|
[ -n "$CLASSPATH" ] &&
|
||||||
|
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||||
|
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||||
|
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||||
|
|
||||||
|
exec "$JAVACMD" \
|
||||||
|
$MAVEN_OPTS \
|
||||||
|
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||||
|
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||||
|
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
143
testsuite/integration-arquillian/test-apps/spring-boot-2-adapter/mvnw.cmd
vendored
Normal file
143
testsuite/integration-arquillian/test-apps/spring-boot-2-adapter/mvnw.cmd
vendored
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
@REM or more contributor license agreements. See the NOTICE file
|
||||||
|
@REM distributed with this work for additional information
|
||||||
|
@REM regarding copyright ownership. The ASF licenses this file
|
||||||
|
@REM to you under the Apache License, Version 2.0 (the
|
||||||
|
@REM "License"); you may not use this file except in compliance
|
||||||
|
@REM with the License. You may obtain a copy of the License at
|
||||||
|
@REM
|
||||||
|
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@REM
|
||||||
|
@REM Unless required by applicable law or agreed to in writing,
|
||||||
|
@REM software distributed under the License is distributed on an
|
||||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
@REM KIND, either express or implied. See the License for the
|
||||||
|
@REM specific language governing permissions and limitations
|
||||||
|
@REM under the License.
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Maven2 Start Up Batch script
|
||||||
|
@REM
|
||||||
|
@REM Required ENV vars:
|
||||||
|
@REM JAVA_HOME - location of a JDK home dir
|
||||||
|
@REM
|
||||||
|
@REM Optional ENV vars
|
||||||
|
@REM M2_HOME - location of maven2's installed home dir
|
||||||
|
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||||
|
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
|
||||||
|
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||||
|
@REM e.g. to debug Maven itself, use
|
||||||
|
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||||
|
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||||
|
@echo off
|
||||||
|
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
|
||||||
|
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||||
|
|
||||||
|
@REM set %HOME% to equivalent of $HOME
|
||||||
|
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||||
|
|
||||||
|
@REM Execute a user defined script before this one
|
||||||
|
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||||
|
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||||
|
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
|
||||||
|
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
|
||||||
|
:skipRcPre
|
||||||
|
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
set ERROR_CODE=0
|
||||||
|
|
||||||
|
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
@REM ==== START VALIDATION ====
|
||||||
|
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: JAVA_HOME not found in your environment. >&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||||
|
echo location of your Java installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
:OkJHome
|
||||||
|
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||||
|
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||||
|
echo location of your Java installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
@REM ==== END VALIDATION ====
|
||||||
|
|
||||||
|
:init
|
||||||
|
|
||||||
|
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||||
|
@REM Fallback to current working directory if not found.
|
||||||
|
|
||||||
|
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||||
|
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||||
|
|
||||||
|
set EXEC_DIR=%CD%
|
||||||
|
set WDIR=%EXEC_DIR%
|
||||||
|
:findBaseDir
|
||||||
|
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||||
|
cd ..
|
||||||
|
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||||
|
set WDIR=%CD%
|
||||||
|
goto findBaseDir
|
||||||
|
|
||||||
|
:baseDirFound
|
||||||
|
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||||
|
cd "%EXEC_DIR%"
|
||||||
|
goto endDetectBaseDir
|
||||||
|
|
||||||
|
:baseDirNotFound
|
||||||
|
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||||
|
cd "%EXEC_DIR%"
|
||||||
|
|
||||||
|
:endDetectBaseDir
|
||||||
|
|
||||||
|
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||||
|
|
||||||
|
@setlocal EnableExtensions EnableDelayedExpansion
|
||||||
|
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||||
|
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||||
|
|
||||||
|
:endReadAdditionalConfig
|
||||||
|
|
||||||
|
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||||
|
|
||||||
|
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||||
|
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||||
|
|
||||||
|
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||||
|
if ERRORLEVEL 1 goto error
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:error
|
||||||
|
set ERROR_CODE=1
|
||||||
|
|
||||||
|
:end
|
||||||
|
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||||
|
|
||||||
|
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
|
||||||
|
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||||
|
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
|
||||||
|
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
|
||||||
|
:skipRcPost
|
||||||
|
|
||||||
|
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||||
|
if "%MAVEN_BATCH_PAUSE%" == "on" pause
|
||||||
|
|
||||||
|
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
|
||||||
|
|
||||||
|
exit /B %ERROR_CODE%
|
|
@ -0,0 +1,224 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>spring-boot-2-adapter</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>spring-boot-adapter</name>
|
||||||
|
<description>Spring boot adapter test application</description>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>2.0.0.RELEASE</version>
|
||||||
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<java.version>1.8</java.version>
|
||||||
|
|
||||||
|
<keycloak.version>4.0.0.CR1-SNAPSHOT</keycloak.version>
|
||||||
|
|
||||||
|
<repo.url />
|
||||||
|
|
||||||
|
|
||||||
|
<jetty.adapter.version />
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-boot-2-adapter</artifactId>
|
||||||
|
<version>${keycloak.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>spring-boot-adapter-tomcat</id>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-tomcat8-adapter</artifactId>
|
||||||
|
<version>${keycloak.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</profile>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>spring-boot-adapter-jetty</id>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</profile>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>spring-boot-adapter-undertow</id>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-undertow</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-undertow-adapter</artifactId>
|
||||||
|
<version>${keycloak.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</profile>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>repo-url</id>
|
||||||
|
<activation>
|
||||||
|
<property>
|
||||||
|
<name>repo.url</name>
|
||||||
|
</property>
|
||||||
|
</activation>
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>custom-repo</id>
|
||||||
|
<name>custom repo</name>
|
||||||
|
<url>${repo.url}</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
</profile>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>jetty-version-81</id>
|
||||||
|
<activation>
|
||||||
|
<property>
|
||||||
|
<name>jetty.adapter.version</name>
|
||||||
|
<value>81</value>
|
||||||
|
</property>
|
||||||
|
</activation>
|
||||||
|
<properties>
|
||||||
|
<jetty.version>8.1.22.v20160922</jetty.version>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-jetty81-adapter</artifactId>
|
||||||
|
<version>${keycloak.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-jetty</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||||
|
<artifactId>*</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</profile>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>jetty-version-92</id>
|
||||||
|
<activation>
|
||||||
|
<property>
|
||||||
|
<name>jetty.adapter.version</name>
|
||||||
|
<value>92</value>
|
||||||
|
</property>
|
||||||
|
</activation>
|
||||||
|
<properties>
|
||||||
|
<jetty.version>9.2.23.v20171218</jetty.version>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-jetty92-adapter</artifactId>
|
||||||
|
<version>${keycloak.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-jetty</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</profile>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>jetty-version-93</id>
|
||||||
|
<activation>
|
||||||
|
<property>
|
||||||
|
<name>jetty.adapter.version</name>
|
||||||
|
<value>93</value>
|
||||||
|
</property>
|
||||||
|
</activation>
|
||||||
|
<properties>
|
||||||
|
<jetty.version>9.3.22.v20171030</jetty.version>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-jetty93-adapter</artifactId>
|
||||||
|
<version>${keycloak.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-jetty</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</profile>
|
||||||
|
|
||||||
|
</profiles>
|
||||||
|
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,142 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||||
|
import org.keycloak.common.util.Base64Url;
|
||||||
|
import org.keycloak.common.util.KeycloakUriBuilder;
|
||||||
|
import org.keycloak.common.util.Time;
|
||||||
|
import org.keycloak.jose.jws.JWSInput;
|
||||||
|
import org.keycloak.jose.jws.JWSInputException;
|
||||||
|
import org.keycloak.representations.AccessToken;
|
||||||
|
import org.keycloak.representations.RefreshToken;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
import org.springframework.web.context.request.WebRequest;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@RequestMapping(path = "/admin")
|
||||||
|
public class AdminController {
|
||||||
|
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(AdminController.class);
|
||||||
|
|
||||||
|
@RequestMapping(path = "/TokenServlet", method = RequestMethod.GET)
|
||||||
|
public String showTokens(WebRequest req, Model model, @RequestParam Map<String, String> attributes) throws IOException {
|
||||||
|
String timeOffset = attributes.get("timeOffset");
|
||||||
|
if (!StringUtils.isEmpty(timeOffset)) {
|
||||||
|
int offset;
|
||||||
|
try {
|
||||||
|
offset = Integer.parseInt(timeOffset, 10);
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Time.setOffset(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshableKeycloakSecurityContext ctx =
|
||||||
|
(RefreshableKeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName(), WebRequest.SCOPE_REQUEST);
|
||||||
|
String accessTokenPretty = JsonSerialization.writeValueAsPrettyString(ctx.getToken());
|
||||||
|
RefreshToken refreshToken;
|
||||||
|
try {
|
||||||
|
refreshToken = new JWSInput(ctx.getRefreshToken()).readJsonContent(RefreshToken.class);
|
||||||
|
} catch (JWSInputException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
String refreshTokenPretty = JsonSerialization.writeValueAsPrettyString(refreshToken);
|
||||||
|
|
||||||
|
model.addAttribute("accessToken", accessTokenPretty);
|
||||||
|
model.addAttribute("refreshToken", refreshTokenPretty);
|
||||||
|
model.addAttribute("accessTokenString", ctx.getTokenString());
|
||||||
|
|
||||||
|
return "tokens";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(path = "/SessionServlet", method = RequestMethod.GET)
|
||||||
|
public String sessionServlet(WebRequest webRequest, Model model) {
|
||||||
|
String counterString = (String) webRequest.getAttribute("counter", RequestAttributes.SCOPE_SESSION);
|
||||||
|
int counter = 0;
|
||||||
|
try {
|
||||||
|
counter = Integer.parseInt(counterString, 10);
|
||||||
|
}
|
||||||
|
catch (NumberFormatException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
model.addAttribute("counter", counter);
|
||||||
|
|
||||||
|
webRequest.setAttribute("counter", Integer.toString(counter+1), RequestAttributes.SCOPE_SESSION);
|
||||||
|
|
||||||
|
return "session";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(path = "/LinkServlet", method = RequestMethod.GET)
|
||||||
|
public String tokenController(WebRequest webRequest,
|
||||||
|
@RequestParam Map<String, String> attributes,
|
||||||
|
Model model) {
|
||||||
|
|
||||||
|
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
|
||||||
|
HttpSession httpSession = attr.getRequest().getSession(true);
|
||||||
|
|
||||||
|
// response.addHeader("Cache-Control", "no-cache");
|
||||||
|
|
||||||
|
String responseAttr = attributes.get("response");
|
||||||
|
|
||||||
|
if (StringUtils.isEmpty(responseAttr)) {
|
||||||
|
String provider = attributes.get("provider");
|
||||||
|
String realm = attributes.get("realm");
|
||||||
|
KeycloakSecurityContext keycloakSession =
|
||||||
|
(KeycloakSecurityContext) webRequest.getAttribute(
|
||||||
|
KeycloakSecurityContext.class.getName(),
|
||||||
|
RequestAttributes.SCOPE_REQUEST);
|
||||||
|
AccessToken token = keycloakSession.getToken();
|
||||||
|
String clientId = token.getAudience()[0];
|
||||||
|
String nonce = UUID.randomUUID().toString();
|
||||||
|
MessageDigest md;
|
||||||
|
try {
|
||||||
|
md = MessageDigest.getInstance("SHA-256");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
String input = nonce + token.getSessionState() + clientId + provider;
|
||||||
|
byte[] check = md.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||||
|
String hash = Base64Url.encode(check);
|
||||||
|
httpSession.setAttribute("hash", hash);
|
||||||
|
String redirectUri = KeycloakUriBuilder.fromUri("http://localhost:8280/admin/LinkServlet")
|
||||||
|
.queryParam("response", "true").build().toString();
|
||||||
|
String accountLinkUrl = KeycloakUriBuilder.fromUri("http://localhost:8180/")
|
||||||
|
.path("/auth/realms/{realm}/broker/{provider}/link")
|
||||||
|
.queryParam("nonce", nonce)
|
||||||
|
.queryParam("hash", hash)
|
||||||
|
.queryParam("client_id", token.getIssuedFor())
|
||||||
|
.queryParam("redirect_uri", redirectUri).build(realm, provider).toString();
|
||||||
|
|
||||||
|
return "redirect:" + accountLinkUrl;
|
||||||
|
} else {
|
||||||
|
String error = attributes.get("link_error");
|
||||||
|
if (StringUtils.isEmpty(error))
|
||||||
|
model.addAttribute("error", "Account linked");
|
||||||
|
else
|
||||||
|
model.addAttribute("error", error);
|
||||||
|
|
||||||
|
return "linking";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class SpringBootAdapterApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(SpringBootAdapterApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
server.port=8280
|
||||||
|
|
||||||
|
keycloak.realm=test
|
||||||
|
keycloak.auth-server-url=http://localhost:8180/auth
|
||||||
|
keycloak.ssl-required=external
|
||||||
|
keycloak.resource=spring-boot-app
|
||||||
|
keycloak.credentials.secret=e3789ac5-bde6-4957-a7b0-612823dac101
|
||||||
|
keycloak.realm-key=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB
|
||||||
|
|
||||||
|
keycloak.security-constraints[0].authRoles[0]=admin
|
||||||
|
keycloak.security-constraints[0].securityCollections[0].name=Admin zone
|
||||||
|
keycloak.security-constraints[0].securityCollections[0].patterns[0]=/admin/*
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>springboot admin page</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="test">You are now admin</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>springboot test page</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="test">Click <a href="admin/index.html" class="adminlink">here</a> to go admin</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
|
||||||
|
<head>
|
||||||
|
<title>Linking page result</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<span id="error" th:text="${error}"/>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org/">
|
||||||
|
<head>
|
||||||
|
<title>session counter page</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<span id="counter" th:text="${counter}"></span>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org/">
|
||||||
|
<head>
|
||||||
|
<title>Tokens from spring boot</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<span id="accessToken" th:text="${accessToken}"></span>
|
||||||
|
<span id="refreshToken" th:text="${refreshToken}"></span>
|
||||||
|
<span id="accessTokenString" th:text="${accessTokenString}"></span>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.keycloak;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBootTest
|
||||||
|
public class SpringBootAdapterApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -23,7 +23,7 @@
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
|
|
||||||
<keycloak.version>3.3.0.CR1-SNAPSHOT</keycloak.version>
|
<keycloak.version>4.0.0.CR1-SNAPSHOT</keycloak.version>
|
||||||
|
|
||||||
<repo.url />
|
<repo.url />
|
||||||
|
|
||||||
|
|
|
@ -145,6 +145,55 @@
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</profile>
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>test-springboot-2</id>
|
||||||
|
<properties>
|
||||||
|
<exclude.springboot>-</exclude.springboot>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.bazaarvoice.maven.plugins</groupId>
|
||||||
|
<artifactId>process-exec-maven-plugin</artifactId>
|
||||||
|
<version>0.7</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>spring-boot-application-process</id>
|
||||||
|
<phase>generate-test-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>start</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<name>springboot</name>
|
||||||
|
<workingDir>../../../../test-apps/spring-boot-2-adapter</workingDir>
|
||||||
|
<healthcheckUrl>http://localhost:8280/index.html</healthcheckUrl>
|
||||||
|
<waitAfterLaunch>360</waitAfterLaunch>
|
||||||
|
<arguments>
|
||||||
|
<argument>mvn</argument>
|
||||||
|
<argument>spring-boot:run</argument>
|
||||||
|
<argument>-B</argument>
|
||||||
|
<argument>-Dkeycloak.version=${project.version}</argument>
|
||||||
|
<argument>-Pspring-boot-adapter-${adapter.container}</argument>
|
||||||
|
<argument>-Dmaven.repo.local=${settings.localRepository}</argument>
|
||||||
|
<argument>-Djetty.adapter.version=${jetty.adapter.version}</argument>
|
||||||
|
<argument>${repo.argument}</argument>
|
||||||
|
</arguments>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
|
||||||
|
<execution>
|
||||||
|
<id>kill-processes</id>
|
||||||
|
<phase>post-integration-test</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>stop-all</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
|
||||||
<profile>
|
<profile>
|
||||||
<id>turn-on-repo-url</id>
|
<id>turn-on-repo-url</id>
|
||||||
|
|
Loading…
Reference in a new issue