Can we remove undertow OIDC adapter?
Closes #28788 Signed-off-by: Douglas Palmer <dpalmer@redhat.com>
This commit is contained in:
parent
6ba8b3faa2
commit
8d628d740e
33 changed files with 0 additions and 2024 deletions
|
@ -33,6 +33,5 @@
|
||||||
<modules>
|
<modules>
|
||||||
<module>adapter-core</module>
|
<module>adapter-core</module>
|
||||||
<module>js</module>
|
<module>js</module>
|
||||||
<module>undertow</module>
|
|
||||||
</modules>
|
</modules>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -1,145 +0,0 @@
|
||||||
<?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>999.0.0-SNAPSHOT</version>
|
|
||||||
<relativePath>../../../pom.xml</relativePath>
|
|
||||||
</parent>
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<artifactId>keycloak-undertow-adapter</artifactId>
|
|
||||||
<name>Keycloak Undertow Integration</name>
|
|
||||||
<description/>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<keycloak.osgi.export>
|
|
||||||
org.keycloak.adapters.undertow.*
|
|
||||||
</keycloak.osgi.export>
|
|
||||||
<keycloak.osgi.import>
|
|
||||||
io.undertow.*;version="[1.4,3)",
|
|
||||||
javax.servlet.*;version="[3.1,5)";resolution:=optional,
|
|
||||||
*;resolution:=optional
|
|
||||||
</keycloak.osgi.import>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.jboss.logging</groupId>
|
|
||||||
<artifactId>jboss-logging</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-core</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-adapter-spi</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter-spi</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-adapter-core</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
|
||||||
<artifactId>httpclient</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.bouncycastle</groupId>
|
|
||||||
<artifactId>bcprov-jdk18on</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
|
||||||
<artifactId>jackson-core</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
|
||||||
<artifactId>jackson-databind</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
|
||||||
<artifactId>jackson-annotations</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.jboss.spec.javax.servlet</groupId>
|
|
||||||
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.undertow</groupId>
|
|
||||||
<artifactId>undertow-servlet</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.undertow</groupId>
|
|
||||||
<artifactId>undertow-core</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<archive>
|
|
||||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.felix</groupId>
|
|
||||||
<artifactId>maven-bundle-plugin</artifactId>
|
|
||||||
<extensions>true</extensions>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>bundle-manifest</id>
|
|
||||||
<phase>process-classes</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>manifest</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
<configuration>
|
|
||||||
<instructions>
|
|
||||||
<Bundle-ClassPath>.</Bundle-ClassPath>
|
|
||||||
<Bundle-Name>${project.name}</Bundle-Name>
|
|
||||||
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
|
||||||
<Import-Package>${keycloak.osgi.import}</Import-Package>
|
|
||||||
<Export-Package>${keycloak.osgi.export}</Export-Package>
|
|
||||||
</instructions>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
</project>
|
|
|
@ -1,134 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.api.AuthenticationMechanism;
|
|
||||||
import io.undertow.security.api.NotificationReceiver;
|
|
||||||
import io.undertow.security.api.SecurityContext;
|
|
||||||
import io.undertow.security.api.SecurityNotification;
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.util.AttachmentKey;
|
|
||||||
import io.undertow.util.Headers;
|
|
||||||
import io.undertow.util.StatusCodes;
|
|
||||||
import org.keycloak.KeycloakSecurityContext;
|
|
||||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
|
||||||
import org.keycloak.adapters.AdapterTokenStore;
|
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
|
||||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
|
||||||
import org.keycloak.adapters.RequestAuthenticator;
|
|
||||||
import org.keycloak.adapters.spi.AuthChallenge;
|
|
||||||
import org.keycloak.adapters.spi.AuthOutcome;
|
|
||||||
import org.keycloak.adapters.spi.HttpFacade;
|
|
||||||
import org.keycloak.enums.TokenStore;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract base class for a Keycloak-enabled Undertow AuthenticationMechanism.
|
|
||||||
*
|
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
|
|
||||||
*/
|
|
||||||
public abstract class AbstractUndertowKeycloakAuthMech implements AuthenticationMechanism {
|
|
||||||
public static final AttachmentKey<AuthChallenge> KEYCLOAK_CHALLENGE_ATTACHMENT_KEY = AttachmentKey.create(AuthChallenge.class);
|
|
||||||
protected AdapterDeploymentContext deploymentContext;
|
|
||||||
protected UndertowUserSessionManagement sessionManagement;
|
|
||||||
protected String errorPage;
|
|
||||||
|
|
||||||
public AbstractUndertowKeycloakAuthMech(AdapterDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement, String errorPage) {
|
|
||||||
this.deploymentContext = deploymentContext;
|
|
||||||
this.sessionManagement = sessionManagement;
|
|
||||||
this.errorPage = errorPage;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChallengeResult sendChallenge(HttpServerExchange exchange, SecurityContext securityContext) {
|
|
||||||
AuthChallenge challenge = exchange.getAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY);
|
|
||||||
if (challenge != null) {
|
|
||||||
UndertowHttpFacade facade = createFacade(exchange);
|
|
||||||
if (challenge.challenge(facade)) {
|
|
||||||
return new ChallengeResult(true, exchange.getResponseCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new ChallengeResult(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UndertowHttpFacade createFacade(HttpServerExchange exchange) {
|
|
||||||
return new OIDCUndertowHttpFacade(exchange);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Integer servePage(final HttpServerExchange exchange, final String location) {
|
|
||||||
sendRedirect(exchange, location);
|
|
||||||
return StatusCodes.TEMPORARY_REDIRECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sendRedirect(final HttpServerExchange exchange, final String location) {
|
|
||||||
// TODO - String concatenation to construct URLS is extremely error prone - switch to a URI which will better handle this.
|
|
||||||
String loc = exchange.getRequestScheme() + "://" + exchange.getHostAndPort() + location;
|
|
||||||
exchange.getResponseHeaders().put(Headers.LOCATION, loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected void registerNotifications(final SecurityContext securityContext) {
|
|
||||||
|
|
||||||
final NotificationReceiver logoutReceiver = new NotificationReceiver() {
|
|
||||||
@Override
|
|
||||||
public void handleNotification(SecurityNotification notification) {
|
|
||||||
if (notification.getEventType() != SecurityNotification.EventType.LOGGED_OUT) return;
|
|
||||||
|
|
||||||
HttpServerExchange exchange = notification.getExchange();
|
|
||||||
UndertowHttpFacade facade = createFacade(exchange);
|
|
||||||
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
|
|
||||||
KeycloakSecurityContext ksc = exchange.getAttachment(OIDCUndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY);
|
|
||||||
if (!deployment.isBearerOnly() && ksc != null && ksc instanceof RefreshableKeycloakSecurityContext) {
|
|
||||||
((RefreshableKeycloakSecurityContext) ksc).logout(deployment);
|
|
||||||
}
|
|
||||||
AdapterTokenStore tokenStore = getTokenStore(exchange, facade, deployment, securityContext);
|
|
||||||
tokenStore.logout();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
securityContext.registerNotificationReceiver(logoutReceiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call this inside your authenticate method.
|
|
||||||
*/
|
|
||||||
protected AuthenticationMechanismOutcome keycloakAuthenticate(HttpServerExchange exchange, SecurityContext securityContext, RequestAuthenticator authenticator) {
|
|
||||||
AuthOutcome outcome = authenticator.authenticate();
|
|
||||||
if (outcome == AuthOutcome.AUTHENTICATED) {
|
|
||||||
registerNotifications(securityContext);
|
|
||||||
return AuthenticationMechanismOutcome.AUTHENTICATED;
|
|
||||||
}
|
|
||||||
AuthChallenge challenge = authenticator.getChallenge();
|
|
||||||
if (challenge != null) {
|
|
||||||
exchange.putAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY, challenge);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outcome == AuthOutcome.FAILED) {
|
|
||||||
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
|
|
||||||
}
|
|
||||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected AdapterTokenStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, KeycloakDeployment deployment, SecurityContext securityContext) {
|
|
||||||
if (deployment.getTokenStore() == TokenStore.SESSION) {
|
|
||||||
return new UndertowSessionTokenStore(exchange, deployment, sessionManagement, securityContext);
|
|
||||||
} else {
|
|
||||||
return new UndertowCookieTokenStore(facade, deployment, securityContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.api.SecurityContext;
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.server.session.Session;
|
|
||||||
import io.undertow.util.Sessions;
|
|
||||||
import org.keycloak.KeycloakPrincipal;
|
|
||||||
import org.keycloak.adapters.AdapterTokenStore;
|
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
|
||||||
import org.keycloak.adapters.OAuthRequestAuthenticator;
|
|
||||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
|
||||||
import org.keycloak.adapters.RequestAuthenticator;
|
|
||||||
import org.keycloak.adapters.spi.HttpFacade;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public abstract class AbstractUndertowRequestAuthenticator extends RequestAuthenticator {
|
|
||||||
protected SecurityContext securityContext;
|
|
||||||
protected HttpServerExchange exchange;
|
|
||||||
|
|
||||||
|
|
||||||
public AbstractUndertowRequestAuthenticator(HttpFacade facade, KeycloakDeployment deployment, int sslRedirectPort,
|
|
||||||
SecurityContext securityContext, HttpServerExchange exchange,
|
|
||||||
AdapterTokenStore tokenStore) {
|
|
||||||
super(facade, deployment, tokenStore, sslRedirectPort);
|
|
||||||
this.securityContext = securityContext;
|
|
||||||
this.exchange = exchange;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void propagateKeycloakContext(KeycloakUndertowAccount account) {
|
|
||||||
exchange.putAttachment(OIDCUndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY, account.getKeycloakSecurityContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected OAuthRequestAuthenticator createOAuthAuthenticator() {
|
|
||||||
return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort, tokenStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void completeOAuthAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
|
|
||||||
KeycloakUndertowAccount account = createAccount(principal);
|
|
||||||
securityContext.authenticationComplete(account, "KEYCLOAK", false);
|
|
||||||
propagateKeycloakContext(account);
|
|
||||||
tokenStore.saveAccountInfo(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal, String method) {
|
|
||||||
KeycloakUndertowAccount account = createAccount(principal);
|
|
||||||
securityContext.authenticationComplete(account, method, false);
|
|
||||||
propagateKeycloakContext(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String changeHttpSessionId(boolean create) {
|
|
||||||
if (create) {
|
|
||||||
Session session = Sessions.getOrCreateSession(exchange);
|
|
||||||
return session.getId();
|
|
||||||
} else {
|
|
||||||
Session session = Sessions.getSession(exchange);
|
|
||||||
return session != null ? session.getId() : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subclasses need to be able to create their own version of the KeycloakUndertowAccount
|
|
||||||
* @return The account
|
|
||||||
*/
|
|
||||||
protected abstract KeycloakUndertowAccount createAccount(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.api.AuthenticationMechanism;
|
|
||||||
import io.undertow.security.api.SecurityContext;
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public interface KeycloakChallenge {
|
|
||||||
public AuthenticationMechanism.ChallengeResult sendChallenge(HttpServerExchange httpServerExchange, SecurityContext securityContext);
|
|
||||||
}
|
|
|
@ -1,226 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.api.AuthenticationMechanism;
|
|
||||||
import io.undertow.security.api.AuthenticationMechanismFactory;
|
|
||||||
import io.undertow.security.idm.Account;
|
|
||||||
import io.undertow.security.idm.Credential;
|
|
||||||
import io.undertow.security.idm.IdentityManager;
|
|
||||||
import io.undertow.server.handlers.form.FormParserFactory;
|
|
||||||
import io.undertow.servlet.ServletExtension;
|
|
||||||
import io.undertow.servlet.api.AuthMethodConfig;
|
|
||||||
import io.undertow.servlet.api.DeploymentInfo;
|
|
||||||
import io.undertow.servlet.api.InstanceFactory;
|
|
||||||
import io.undertow.servlet.api.InstanceHandle;
|
|
||||||
import io.undertow.servlet.api.ListenerInfo;
|
|
||||||
import io.undertow.servlet.api.LoginConfig;
|
|
||||||
import io.undertow.servlet.api.ServletSessionConfig;
|
|
||||||
import io.undertow.servlet.util.ImmediateInstanceHandle;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
|
||||||
import org.keycloak.adapters.KeycloakConfigResolver;
|
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
|
||||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
|
||||||
import org.keycloak.adapters.NodesRegistrationManagement;
|
|
||||||
import org.keycloak.constants.AdapterConstants;
|
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class KeycloakServletExtension implements ServletExtension {
|
|
||||||
|
|
||||||
protected static Logger log = Logger.getLogger(KeycloakServletExtension.class);
|
|
||||||
private final AdapterDeploymentContext deploymentContext;
|
|
||||||
|
|
||||||
public KeycloakServletExtension() {
|
|
||||||
this(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeycloakServletExtension(AdapterDeploymentContext deploymentContext) {
|
|
||||||
this.deploymentContext = deploymentContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo when this DeploymentInfo method of the same name is fixed.
|
|
||||||
public boolean isAuthenticationMechanismPresent(DeploymentInfo deploymentInfo, final String mechanismName) {
|
|
||||||
LoginConfig loginConfig = deploymentInfo.getLoginConfig();
|
|
||||||
if (loginConfig != null) {
|
|
||||||
for (AuthMethodConfig method : loginConfig.getAuthMethods()) {
|
|
||||||
if (method.getName().equalsIgnoreCase(mechanismName)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static InputStream getJSONFromServletContext(ServletContext servletContext) {
|
|
||||||
String json = servletContext.getInitParameter(AdapterConstants.AUTH_DATA_PARAM_NAME);
|
|
||||||
if (json == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new ByteArrayInputStream(json.getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static InputStream getConfigInputStream(ServletContext context) {
|
|
||||||
InputStream is = getJSONFromServletContext(context);
|
|
||||||
if (is == null) {
|
|
||||||
String path = context.getInitParameter("keycloak.config.file");
|
|
||||||
if (path == null) {
|
|
||||||
log.debug("using /WEB-INF/keycloak.json");
|
|
||||||
is = context.getResourceAsStream("/WEB-INF/keycloak.json");
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
is = new FileInputStream(path);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return is;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("UseSpecificCatch")
|
|
||||||
public void handleDeployment(DeploymentInfo deploymentInfo, ServletContext servletContext) {
|
|
||||||
if (!isAuthenticationMechanismPresent(deploymentInfo, "KEYCLOAK") && deploymentContext == null) {
|
|
||||||
log.debug("auth-method is not keycloak!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
log.debug("KeycloakServletException initialization");
|
|
||||||
|
|
||||||
// Possible scenarios:
|
|
||||||
// 1) The deployment has a keycloak.config.resolver specified and it exists:
|
|
||||||
// Outcome: adapter uses the resolver
|
|
||||||
// 2) The deployment has a keycloak.config.resolver and isn't valid (doesn't exist, isn't a resolver, ...) :
|
|
||||||
// Outcome: adapter is left unconfigured
|
|
||||||
// 3) The deployment doesn't have a keycloak.config.resolver , but has a keycloak.json (or equivalent)
|
|
||||||
// Outcome: adapter uses it
|
|
||||||
// 4) The deployment doesn't have a keycloak.config.resolver nor keycloak.json (or equivalent)
|
|
||||||
// Outcome: adapter is left unconfigured
|
|
||||||
AdapterDeploymentContext deploymentContext = this.deploymentContext;
|
|
||||||
|
|
||||||
if (deploymentContext == null) {
|
|
||||||
KeycloakConfigResolver configResolver;
|
|
||||||
String configResolverClass = servletContext.getInitParameter("keycloak.config.resolver");
|
|
||||||
if (configResolverClass != null) {
|
|
||||||
try {
|
|
||||||
configResolver = (KeycloakConfigResolver) deploymentInfo.getClassLoader().loadClass(configResolverClass).newInstance();
|
|
||||||
deploymentContext = new AdapterDeploymentContext(configResolver);
|
|
||||||
log.info("Using " + configResolverClass + " to resolve Keycloak configuration on a per-request basis.");
|
|
||||||
} catch (Exception ex) {
|
|
||||||
log.warn("The specified resolver " + configResolverClass + " could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: " + ex.getMessage());
|
|
||||||
deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
InputStream is = getConfigInputStream(servletContext);
|
|
||||||
final KeycloakDeployment deployment;
|
|
||||||
if (is == null) {
|
|
||||||
log.warn("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
|
|
||||||
deployment = new KeycloakDeployment();
|
|
||||||
} else {
|
|
||||||
deployment = KeycloakDeploymentBuilder.build(is);
|
|
||||||
}
|
|
||||||
deploymentContext = new AdapterDeploymentContext(deployment);
|
|
||||||
log.debug("Keycloak is using a per-deployment configuration.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
deploymentContext = this.deploymentContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
servletContext.setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext);
|
|
||||||
UndertowUserSessionManagement userSessionManagement = new UndertowUserSessionManagement();
|
|
||||||
final NodesRegistrationManagement nodesRegistrationManagement = new NodesRegistrationManagement();
|
|
||||||
final ServletKeycloakAuthMech mech = createAuthenticationMechanism(deploymentInfo, deploymentContext, userSessionManagement, nodesRegistrationManagement);
|
|
||||||
|
|
||||||
UndertowAuthenticatedActionsHandler.Wrapper actions = new UndertowAuthenticatedActionsHandler.Wrapper(deploymentContext);
|
|
||||||
|
|
||||||
// setup handlers
|
|
||||||
|
|
||||||
deploymentInfo.addOuterHandlerChainWrapper(new ServletPreAuthActionsHandler.Wrapper(deploymentContext, userSessionManagement));
|
|
||||||
deploymentInfo.addAuthenticationMechanism("KEYCLOAK", new AuthenticationMechanismFactory() {
|
|
||||||
@Override
|
|
||||||
public AuthenticationMechanism create(String s, FormParserFactory formParserFactory, Map<String, String> stringStringMap) {
|
|
||||||
return mech;
|
|
||||||
}
|
|
||||||
}); // authentication
|
|
||||||
deploymentInfo.addInnerHandlerChainWrapper(actions); // handles authenticated actions and cors.
|
|
||||||
|
|
||||||
deploymentInfo.setIdentityManager(new IdentityManager() {
|
|
||||||
@Override
|
|
||||||
public Account verify(Account account) {
|
|
||||||
return account;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Account verify(String id, Credential credential) {
|
|
||||||
throw new IllegalStateException("Should never be called in Keycloak flow");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Account verify(Credential credential) {
|
|
||||||
throw new IllegalStateException("Should never be called in Keycloak flow");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ServletSessionConfig cookieConfig = deploymentInfo.getServletSessionConfig();
|
|
||||||
if (cookieConfig == null) {
|
|
||||||
cookieConfig = new ServletSessionConfig();
|
|
||||||
}
|
|
||||||
if (cookieConfig.getPath() == null) {
|
|
||||||
log.debug("Setting jsession cookie path to: " + deploymentInfo.getContextPath());
|
|
||||||
cookieConfig.setPath(deploymentInfo.getContextPath());
|
|
||||||
deploymentInfo.setServletSessionConfig(cookieConfig);
|
|
||||||
}
|
|
||||||
ChangeSessionId.turnOffChangeSessionIdOnLogin(deploymentInfo);
|
|
||||||
deploymentInfo.addListener(new ListenerInfo(UndertowNodesRegistrationManagementWrapper.class, new InstanceFactory<UndertowNodesRegistrationManagementWrapper>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InstanceHandle<UndertowNodesRegistrationManagementWrapper> createInstance() throws InstantiationException {
|
|
||||||
UndertowNodesRegistrationManagementWrapper listener = new UndertowNodesRegistrationManagementWrapper(nodesRegistrationManagement);
|
|
||||||
return new ImmediateInstanceHandle<UndertowNodesRegistrationManagementWrapper>(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ServletKeycloakAuthMech createAuthenticationMechanism(DeploymentInfo deploymentInfo, AdapterDeploymentContext deploymentContext, UndertowUserSessionManagement userSessionManagement,
|
|
||||||
NodesRegistrationManagement nodesRegistrationManagement) {
|
|
||||||
log.debug("creating ServletKeycloakAuthMech");
|
|
||||||
String errorPage = getErrorPage(deploymentInfo);
|
|
||||||
return new ServletKeycloakAuthMech(deploymentContext, userSessionManagement, nodesRegistrationManagement, deploymentInfo.getConfidentialPortManager(), errorPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getErrorPage(DeploymentInfo deploymentInfo) {
|
|
||||||
LoginConfig loginConfig = deploymentInfo.getLoginConfig();
|
|
||||||
String errorPage = null;
|
|
||||||
if (loginConfig != null) {
|
|
||||||
errorPage = loginConfig.getErrorPage();
|
|
||||||
}
|
|
||||||
return errorPage;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.idm.Account;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.KeycloakPrincipal;
|
|
||||||
import org.keycloak.adapters.AdapterTokenStore;
|
|
||||||
import org.keycloak.adapters.AdapterUtils;
|
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
|
||||||
import org.keycloak.adapters.OidcKeycloakAccount;
|
|
||||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.security.Principal;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class KeycloakUndertowAccount implements Account, Serializable, OidcKeycloakAccount {
|
|
||||||
protected static Logger log = Logger.getLogger(KeycloakUndertowAccount.class);
|
|
||||||
protected KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal;
|
|
||||||
protected Set<String> accountRoles;
|
|
||||||
|
|
||||||
public KeycloakUndertowAccount(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
|
|
||||||
this.principal = principal;
|
|
||||||
setRoles(principal.getKeycloakSecurityContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setRoles(RefreshableKeycloakSecurityContext session) {
|
|
||||||
Set<String> roles = AdapterUtils.getRolesFromSecurityContext(session);
|
|
||||||
this.accountRoles = roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Principal getPrincipal() {
|
|
||||||
return principal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<String> getRoles() {
|
|
||||||
return accountRoles;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RefreshableKeycloakSecurityContext getKeycloakSecurityContext() {
|
|
||||||
return principal.getKeycloakSecurityContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCurrentRequestInfo(KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
|
|
||||||
principal.getKeycloakSecurityContext().setCurrentRequestInfo(deployment, tokenStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if accessToken is active and try to refresh if it's not
|
|
||||||
public boolean checkActive() {
|
|
||||||
// this object may have been serialized, so we need to reset realm config/metadata
|
|
||||||
RefreshableKeycloakSecurityContext session = getKeycloakSecurityContext();
|
|
||||||
if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) {
|
|
||||||
log.debug("session is active");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug("session is not active or refresh is enforced. Try refresh");
|
|
||||||
boolean success = session.refreshExpiredToken(false);
|
|
||||||
if (!success || !session.isActive()) {
|
|
||||||
log.debug("session is not active return with failure");
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
log.debug("refresh succeeded");
|
|
||||||
|
|
||||||
setRoles(session);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object other) {
|
|
||||||
if (this == other)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!(other instanceof KeycloakUndertowAccount))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
KeycloakUndertowAccount otherAccount = (KeycloakUndertowAccount) other;
|
|
||||||
|
|
||||||
return (this.principal != null ? this.principal.equals(otherAccount.principal) : otherAccount.principal == null) &&
|
|
||||||
(this.accountRoles != null ? this.accountRoles.equals(otherAccount.accountRoles) : otherAccount.accountRoles == null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
final int prime = 31;
|
|
||||||
int result = 1;
|
|
||||||
result = prime * result + (this.principal == null ? 0 : this.principal.hashCode());
|
|
||||||
result = prime * result + (this.accountRoles == null ? 0 : this.accountRoles.hashCode());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import org.keycloak.KeycloakSecurityContext;
|
|
||||||
import org.keycloak.adapters.OIDCHttpFacade;
|
|
||||||
|
|
||||||
import static org.keycloak.adapters.undertow.OIDCUndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class OIDCServletUndertowHttpFacade extends ServletHttpFacade implements OIDCHttpFacade {
|
|
||||||
|
|
||||||
public OIDCServletUndertowHttpFacade(HttpServerExchange exchange) {
|
|
||||||
super(exchange);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KeycloakSecurityContext getSecurityContext() {
|
|
||||||
return exchange.getAttachment(KEYCLOAK_SECURITY_CONTEXT_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.util.AttachmentKey;
|
|
||||||
import org.keycloak.KeycloakSecurityContext;
|
|
||||||
import org.keycloak.adapters.OIDCHttpFacade;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class OIDCUndertowHttpFacade extends UndertowHttpFacade implements OIDCHttpFacade {
|
|
||||||
public static final AttachmentKey<KeycloakSecurityContext> KEYCLOAK_SECURITY_CONTEXT_KEY = AttachmentKey.create(KeycloakSecurityContext.class);
|
|
||||||
|
|
||||||
public OIDCUndertowHttpFacade(HttpServerExchange exchange) {
|
|
||||||
super(exchange);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KeycloakSecurityContext getSecurityContext() {
|
|
||||||
return exchange.getAttachment(KEYCLOAK_SECURITY_CONTEXT_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.api.SecurityContext;
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.servlet.api.ConfidentialPortManager;
|
|
||||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
|
||||||
import io.undertow.util.Headers;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
|
||||||
import org.keycloak.adapters.AdapterTokenStore;
|
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
|
||||||
import org.keycloak.adapters.NodesRegistrationManagement;
|
|
||||||
import org.keycloak.adapters.RequestAuthenticator;
|
|
||||||
import org.keycloak.adapters.spi.HttpFacade;
|
|
||||||
import org.keycloak.enums.TokenStore;
|
|
||||||
|
|
||||||
import javax.servlet.RequestDispatcher;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.ServletRequest;
|
|
||||||
import javax.servlet.ServletResponse;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class ServletKeycloakAuthMech extends AbstractUndertowKeycloakAuthMech {
|
|
||||||
private static final Logger log = Logger.getLogger(ServletKeycloakAuthMech.class);
|
|
||||||
|
|
||||||
protected NodesRegistrationManagement nodesRegistrationManagement;
|
|
||||||
protected ConfidentialPortManager portManager;
|
|
||||||
|
|
||||||
public ServletKeycloakAuthMech(AdapterDeploymentContext deploymentContext, UndertowUserSessionManagement userSessionManagement,
|
|
||||||
NodesRegistrationManagement nodesRegistrationManagement, ConfidentialPortManager portManager,
|
|
||||||
String errorPage) {
|
|
||||||
super(deploymentContext, userSessionManagement, errorPage);
|
|
||||||
this.nodesRegistrationManagement = nodesRegistrationManagement;
|
|
||||||
this.portManager = portManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Integer servePage(HttpServerExchange exchange, String location) {
|
|
||||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
ServletRequest req = servletRequestContext.getServletRequest();
|
|
||||||
ServletResponse resp = servletRequestContext.getServletResponse();
|
|
||||||
RequestDispatcher disp = req.getRequestDispatcher(location);
|
|
||||||
//make sure the login page is never cached
|
|
||||||
exchange.getResponseHeaders().add(Headers.CACHE_CONTROL, "no-cache, no-store, must-revalidate");
|
|
||||||
exchange.getResponseHeaders().add(Headers.PRAGMA, "no-cache");
|
|
||||||
exchange.getResponseHeaders().add(Headers.EXPIRES, "0");
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
disp.forward(req, resp);
|
|
||||||
} catch (ServletException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
|
|
||||||
UndertowHttpFacade facade = createFacade(exchange);
|
|
||||||
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
|
|
||||||
if (!deployment.isConfigured()) {
|
|
||||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
nodesRegistrationManagement.tryRegister(deployment);
|
|
||||||
|
|
||||||
RequestAuthenticator authenticator = createRequestAuthenticator(deployment, exchange, securityContext, facade);
|
|
||||||
|
|
||||||
return keycloakAuthenticate(exchange, securityContext, authenticator);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected RequestAuthenticator createRequestAuthenticator(KeycloakDeployment deployment, HttpServerExchange exchange, SecurityContext securityContext, UndertowHttpFacade facade) {
|
|
||||||
|
|
||||||
int confidentialPort = getConfidentilPort(exchange);
|
|
||||||
AdapterTokenStore tokenStore = getTokenStore(exchange, facade, deployment, securityContext);
|
|
||||||
return new ServletRequestAuthenticator(facade, deployment,
|
|
||||||
confidentialPort, securityContext, exchange, tokenStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int getConfidentilPort(HttpServerExchange exchange) {
|
|
||||||
int confidentialPort = 8443;
|
|
||||||
if (exchange.getRequestScheme().equalsIgnoreCase("HTTPS")) {
|
|
||||||
confidentialPort = exchange.getHostPort();
|
|
||||||
} else if (portManager != null) {
|
|
||||||
confidentialPort = portManager.getConfidentialPort(exchange);
|
|
||||||
}
|
|
||||||
return confidentialPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected AdapterTokenStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, KeycloakDeployment deployment, SecurityContext securityContext) {
|
|
||||||
if (deployment.getTokenStore() == TokenStore.SESSION) {
|
|
||||||
return new ServletSessionTokenStore(exchange, deployment, sessionManagement, securityContext);
|
|
||||||
} else {
|
|
||||||
return new UndertowCookieTokenStore(facade, deployment, securityContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UndertowHttpFacade createFacade(HttpServerExchange exchange) {
|
|
||||||
return new OIDCServletUndertowHttpFacade(exchange);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.server.HandlerWrapper;
|
|
||||||
import io.undertow.server.HttpHandler;
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
|
||||||
import org.keycloak.adapters.PreAuthActionsHandler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class ServletPreAuthActionsHandler implements HttpHandler {
|
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger(ServletPreAuthActionsHandler.class);
|
|
||||||
protected HttpHandler next;
|
|
||||||
protected UndertowUserSessionManagement userSessionManagement;
|
|
||||||
protected AdapterDeploymentContext deploymentContext;
|
|
||||||
|
|
||||||
public static class Wrapper implements HandlerWrapper {
|
|
||||||
protected AdapterDeploymentContext deploymentContext;
|
|
||||||
protected UndertowUserSessionManagement userSessionManagement;
|
|
||||||
|
|
||||||
|
|
||||||
public Wrapper(AdapterDeploymentContext deploymentContext, UndertowUserSessionManagement userSessionManagement) {
|
|
||||||
this.deploymentContext = deploymentContext;
|
|
||||||
this.userSessionManagement = userSessionManagement;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HttpHandler wrap(HttpHandler handler) {
|
|
||||||
return new ServletPreAuthActionsHandler(deploymentContext, userSessionManagement, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ServletPreAuthActionsHandler(AdapterDeploymentContext deploymentContext,
|
|
||||||
UndertowUserSessionManagement userSessionManagement,
|
|
||||||
HttpHandler next) {
|
|
||||||
this.next = next;
|
|
||||||
this.deploymentContext = deploymentContext;
|
|
||||||
this.userSessionManagement = userSessionManagement;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
|
||||||
UndertowHttpFacade facade = new OIDCServletUndertowHttpFacade(exchange);
|
|
||||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
SessionManagementBridge bridge = new SessionManagementBridge(userSessionManagement, servletRequestContext.getDeployment().getSessionManager());
|
|
||||||
PreAuthActionsHandler handler = new PreAuthActionsHandler(bridge, deploymentContext, facade);
|
|
||||||
if (handler.handleRequest()) return;
|
|
||||||
next.handleRequest(exchange);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.api.SecurityContext;
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
|
||||||
import org.keycloak.KeycloakPrincipal;
|
|
||||||
import org.keycloak.KeycloakSecurityContext;
|
|
||||||
import org.keycloak.adapters.AdapterTokenStore;
|
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
|
||||||
import org.keycloak.adapters.OAuthRequestAuthenticator;
|
|
||||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
|
||||||
import org.keycloak.adapters.spi.HttpFacade;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpSession;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class ServletRequestAuthenticator extends AbstractUndertowRequestAuthenticator {
|
|
||||||
|
|
||||||
|
|
||||||
public ServletRequestAuthenticator(HttpFacade facade, KeycloakDeployment deployment, int sslRedirectPort,
|
|
||||||
SecurityContext securityContext, HttpServerExchange exchange,
|
|
||||||
AdapterTokenStore tokenStore) {
|
|
||||||
super(facade, deployment, sslRedirectPort, securityContext, exchange, tokenStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected OAuthRequestAuthenticator createOAuthAuthenticator() {
|
|
||||||
return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort, tokenStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void propagateKeycloakContext(KeycloakUndertowAccount account) {
|
|
||||||
super.propagateKeycloakContext(account);
|
|
||||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
|
|
||||||
req.setAttribute(KeycloakSecurityContext.class.getName(), account.getKeycloakSecurityContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected KeycloakUndertowAccount createAccount(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
|
|
||||||
return new KeycloakUndertowAccount(principal);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String changeHttpSessionId(boolean create) {
|
|
||||||
if (!deployment.isTurnOffChangeSessionIdOnLogin()) return ChangeSessionId.changeSessionId(exchange, create);
|
|
||||||
else return getHttpSessionId(create);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getHttpSessionId(boolean create) {
|
|
||||||
HttpSession session = getSession(create);
|
|
||||||
return session != null ? session.getId() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected HttpSession getSession(boolean create) {
|
|
||||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
|
|
||||||
return req.getSession(create);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,158 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.api.SecurityContext;
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.KeycloakSecurityContext;
|
|
||||||
import org.keycloak.adapters.AdapterTokenStore;
|
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
|
||||||
import org.keycloak.adapters.OidcKeycloakAccount;
|
|
||||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
|
||||||
import org.keycloak.adapters.RequestAuthenticator;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpSession;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Per-request object. Storage of tokens in servlet HTTP session.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
|
||||||
*/
|
|
||||||
public class ServletSessionTokenStore implements AdapterTokenStore {
|
|
||||||
|
|
||||||
protected static Logger log = Logger.getLogger(ServletSessionTokenStore.class);
|
|
||||||
|
|
||||||
private final HttpServerExchange exchange;
|
|
||||||
private final KeycloakDeployment deployment;
|
|
||||||
private final UndertowUserSessionManagement sessionManagement;
|
|
||||||
private final SecurityContext securityContext;
|
|
||||||
|
|
||||||
public ServletSessionTokenStore(HttpServerExchange exchange, KeycloakDeployment deployment, UndertowUserSessionManagement sessionManagement,
|
|
||||||
SecurityContext securityContext) {
|
|
||||||
this.exchange = exchange;
|
|
||||||
this.deployment = deployment;
|
|
||||||
this.sessionManagement = sessionManagement;
|
|
||||||
this.securityContext = securityContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void checkCurrentToken() {
|
|
||||||
// no-op on undertow
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCached(RequestAuthenticator authenticator) {
|
|
||||||
HttpSession session = getSession(false);
|
|
||||||
if (session == null) {
|
|
||||||
log.debug("session was null, returning null");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
KeycloakUndertowAccount account = null;
|
|
||||||
try {
|
|
||||||
account = (KeycloakUndertowAccount)session.getAttribute(KeycloakUndertowAccount.class.getName());
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
log.debug("session was invalidated. Return false.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (account == null) {
|
|
||||||
log.debug("Account was not in session, returning null");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!deployment.getRealm().equals(account.getKeycloakSecurityContext().getRealm())) {
|
|
||||||
log.debug("Account in session belongs to a different realm than for this request.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
account.setCurrentRequestInfo(deployment, this);
|
|
||||||
if (account.checkActive()) {
|
|
||||||
log.debug("Cached account found");
|
|
||||||
securityContext.authenticationComplete(account, "KEYCLOAK", false);
|
|
||||||
((AbstractUndertowRequestAuthenticator)authenticator).propagateKeycloakContext(account);
|
|
||||||
restoreRequest();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
log.debug("Refresh failed. Account was not active. Returning null and invalidating Http session");
|
|
||||||
try {
|
|
||||||
session.removeAttribute(KeycloakUndertowAccount.class.getName());
|
|
||||||
session.removeAttribute(KeycloakSecurityContext.class.getName());
|
|
||||||
session.invalidate();
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.debug("Failed to invalidate session, might already be invalidated");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void saveAccountInfo(OidcKeycloakAccount account) {
|
|
||||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
HttpSession session = getSession(true);
|
|
||||||
session.setAttribute(KeycloakUndertowAccount.class.getName(), account);
|
|
||||||
session.setAttribute(KeycloakSecurityContext.class.getName(), account.getKeycloakSecurityContext());
|
|
||||||
sessionManagement.login(servletRequestContext.getDeployment().getSessionManager());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void logout() {
|
|
||||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
|
|
||||||
req.removeAttribute(KeycloakUndertowAccount.class.getName());
|
|
||||||
req.removeAttribute(KeycloakSecurityContext.class.getName());
|
|
||||||
HttpSession session = req.getSession(false);
|
|
||||||
if (session == null) return;
|
|
||||||
try {
|
|
||||||
KeycloakUndertowAccount account = (KeycloakUndertowAccount) session.getAttribute(KeycloakUndertowAccount.class.getName());
|
|
||||||
if (account == null) return;
|
|
||||||
session.removeAttribute(KeycloakSecurityContext.class.getName());
|
|
||||||
session.removeAttribute(KeycloakUndertowAccount.class.getName());
|
|
||||||
} catch (IllegalStateException ise) {
|
|
||||||
// Session may be already logged-out in case that app has adminUrl
|
|
||||||
log.debugf("Session %s logged-out already", session.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void saveRequest() {
|
|
||||||
SavedRequest.trySaveRequest(exchange);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean restoreRequest() {
|
|
||||||
HttpSession session = getSession(false);
|
|
||||||
if (session == null) return false;
|
|
||||||
SavedRequest.tryRestoreRequest(exchange, session);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected HttpSession getSession(boolean create) {
|
|
||||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
|
||||||
HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
|
|
||||||
return req.getSession(create);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.server.HandlerWrapper;
|
|
||||||
import io.undertow.server.HttpHandler;
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
|
||||||
import org.keycloak.adapters.AuthenticatedActionsHandler;
|
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bridge for authenticated Keycloak adapter actions
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class UndertowAuthenticatedActionsHandler implements HttpHandler {
|
|
||||||
private static final Logger log = Logger.getLogger(UndertowAuthenticatedActionsHandler.class);
|
|
||||||
protected AdapterDeploymentContext deploymentContext;
|
|
||||||
protected HttpHandler next;
|
|
||||||
|
|
||||||
public static class Wrapper implements HandlerWrapper {
|
|
||||||
protected AdapterDeploymentContext deploymentContext;
|
|
||||||
|
|
||||||
public Wrapper(AdapterDeploymentContext deploymentContext) {
|
|
||||||
this.deploymentContext = deploymentContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HttpHandler wrap(HttpHandler handler) {
|
|
||||||
return new UndertowAuthenticatedActionsHandler(deploymentContext, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public UndertowAuthenticatedActionsHandler(AdapterDeploymentContext deploymentContext, HttpHandler next) {
|
|
||||||
this.deploymentContext = deploymentContext;
|
|
||||||
this.next = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
|
||||||
OIDCUndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
|
|
||||||
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
|
|
||||||
if (deployment != null && deployment.isConfigured()) {
|
|
||||||
AuthenticatedActionsHandler handler = new AuthenticatedActionsHandler(deployment, facade);
|
|
||||||
if (handler.handledRequest()) return;
|
|
||||||
}
|
|
||||||
next.handleRequest(exchange);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.api.SecurityContext;
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
|
||||||
import org.keycloak.adapters.AdapterTokenStore;
|
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
|
||||||
import org.keycloak.adapters.NodesRegistrationManagement;
|
|
||||||
import org.keycloak.adapters.RequestAuthenticator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class UndertowAuthenticationMechanism extends AbstractUndertowKeycloakAuthMech {
|
|
||||||
protected NodesRegistrationManagement nodesRegistrationManagement;
|
|
||||||
protected int confidentialPort;
|
|
||||||
|
|
||||||
public UndertowAuthenticationMechanism(AdapterDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement,
|
|
||||||
NodesRegistrationManagement nodesRegistrationManagement, int confidentialPort, String errorPage) {
|
|
||||||
super(deploymentContext, sessionManagement, errorPage);
|
|
||||||
this.nodesRegistrationManagement = nodesRegistrationManagement;
|
|
||||||
this.confidentialPort = confidentialPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
|
|
||||||
UndertowHttpFacade facade = createFacade(exchange);
|
|
||||||
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
|
|
||||||
if (!deployment.isConfigured()) {
|
|
||||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
nodesRegistrationManagement.tryRegister(deployment);
|
|
||||||
|
|
||||||
AdapterTokenStore tokenStore = getTokenStore(exchange, facade, deployment, securityContext);
|
|
||||||
RequestAuthenticator authenticator = new UndertowRequestAuthenticator(facade, deployment, confidentialPort, securityContext, exchange, tokenStore);
|
|
||||||
|
|
||||||
return keycloakAuthenticate(exchange, securityContext, authenticator);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,110 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.api.SecurityContext;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.KeycloakPrincipal;
|
|
||||||
import org.keycloak.adapters.AdapterTokenStore;
|
|
||||||
import org.keycloak.adapters.CookieTokenStore;
|
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
|
||||||
import org.keycloak.adapters.OidcKeycloakAccount;
|
|
||||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
|
||||||
import org.keycloak.adapters.RequestAuthenticator;
|
|
||||||
import org.keycloak.adapters.spi.HttpFacade;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Per-request object. Storage of tokens in cookie
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
|
||||||
*/
|
|
||||||
public class UndertowCookieTokenStore implements AdapterTokenStore {
|
|
||||||
|
|
||||||
protected static Logger log = Logger.getLogger(UndertowCookieTokenStore.class);
|
|
||||||
|
|
||||||
private final HttpFacade facade;
|
|
||||||
private final KeycloakDeployment deployment;
|
|
||||||
private final SecurityContext securityContext;
|
|
||||||
|
|
||||||
public UndertowCookieTokenStore(HttpFacade facade, KeycloakDeployment deployment,
|
|
||||||
SecurityContext securityContext) {
|
|
||||||
this.facade = facade;
|
|
||||||
this.deployment = deployment;
|
|
||||||
this.securityContext = securityContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void checkCurrentToken() {
|
|
||||||
// no-op on undertow
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCached(RequestAuthenticator authenticator) {
|
|
||||||
KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = CookieTokenStore.getPrincipalFromCookie(deployment, facade, this);
|
|
||||||
if (principal == null) {
|
|
||||||
log.debug("Account was not in cookie or was invalid, returning null");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
KeycloakUndertowAccount account = new KeycloakUndertowAccount(principal);
|
|
||||||
|
|
||||||
if (!deployment.getRealm().equals(account.getKeycloakSecurityContext().getRealm())) {
|
|
||||||
log.debug("Account in session belongs to a different realm than for this request.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (account.checkActive()) {
|
|
||||||
log.debug("Cached account found");
|
|
||||||
securityContext.authenticationComplete(account, "KEYCLOAK", false);
|
|
||||||
((AbstractUndertowRequestAuthenticator)authenticator).propagateKeycloakContext(account);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
log.debug("Account was not active, removing cookie and returning false");
|
|
||||||
CookieTokenStore.removeCookie(deployment, facade);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void saveAccountInfo(OidcKeycloakAccount account) {
|
|
||||||
RefreshableKeycloakSecurityContext secContext = (RefreshableKeycloakSecurityContext)account.getKeycloakSecurityContext();
|
|
||||||
CookieTokenStore.setTokenCookie(deployment, facade, secContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void logout() {
|
|
||||||
KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = CookieTokenStore.getPrincipalFromCookie(deployment, facade, this);
|
|
||||||
if (principal == null) return;
|
|
||||||
|
|
||||||
CookieTokenStore.removeCookie(deployment, facade);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
|
|
||||||
CookieTokenStore.setTokenCookie(deployment, facade, securityContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void saveRequest() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean restoreRequest() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import org.keycloak.adapters.NodesRegistrationManagement;
|
|
||||||
|
|
||||||
import javax.servlet.ServletContextEvent;
|
|
||||||
import javax.servlet.ServletContextListener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
|
||||||
*/
|
|
||||||
public class UndertowNodesRegistrationManagementWrapper implements ServletContextListener {
|
|
||||||
|
|
||||||
private final NodesRegistrationManagement delegate;
|
|
||||||
|
|
||||||
public UndertowNodesRegistrationManagementWrapper(NodesRegistrationManagement delegate) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void contextInitialized(ServletContextEvent sce) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void contextDestroyed(ServletContextEvent sce) {
|
|
||||||
delegate.stop();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.server.HttpHandler;
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.server.session.SessionManager;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
|
||||||
import org.keycloak.adapters.PreAuthActionsHandler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class UndertowPreAuthActionsHandler implements HttpHandler {
|
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger(UndertowPreAuthActionsHandler.class);
|
|
||||||
protected HttpHandler next;
|
|
||||||
protected SessionManager sessionManager;
|
|
||||||
protected UndertowUserSessionManagement userSessionManagement;
|
|
||||||
protected AdapterDeploymentContext deploymentContext;
|
|
||||||
|
|
||||||
public UndertowPreAuthActionsHandler(AdapterDeploymentContext deploymentContext,
|
|
||||||
UndertowUserSessionManagement userSessionManagement,
|
|
||||||
SessionManager sessionManager,
|
|
||||||
HttpHandler next) {
|
|
||||||
this.next = next;
|
|
||||||
this.deploymentContext = deploymentContext;
|
|
||||||
this.sessionManager = sessionManager;
|
|
||||||
this.userSessionManagement = userSessionManagement;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
|
||||||
UndertowHttpFacade facade = createFacade(exchange);
|
|
||||||
SessionManagementBridge bridge = new SessionManagementBridge(userSessionManagement, sessionManager);
|
|
||||||
PreAuthActionsHandler handler = new PreAuthActionsHandler(bridge, deploymentContext, facade);
|
|
||||||
if (handler.handleRequest()) return;
|
|
||||||
next.handleRequest(exchange);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UndertowHttpFacade createFacade(HttpServerExchange exchange) {
|
|
||||||
return new OIDCUndertowHttpFacade(exchange);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.api.SecurityContext;
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import org.keycloak.KeycloakPrincipal;
|
|
||||||
import org.keycloak.adapters.AdapterTokenStore;
|
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
|
||||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
|
||||||
import org.keycloak.adapters.spi.HttpFacade;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class UndertowRequestAuthenticator extends AbstractUndertowRequestAuthenticator {
|
|
||||||
public UndertowRequestAuthenticator(HttpFacade facade, KeycloakDeployment deployment, int sslRedirectPort,
|
|
||||||
SecurityContext securityContext, HttpServerExchange exchange, AdapterTokenStore tokenStore) {
|
|
||||||
super(facade, deployment, sslRedirectPort, securityContext, exchange, tokenStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected KeycloakUndertowAccount createAccount(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
|
|
||||||
return new KeycloakUndertowAccount(principal);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,124 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.adapters.undertow;
|
|
||||||
|
|
||||||
import io.undertow.security.api.SecurityContext;
|
|
||||||
import io.undertow.server.HttpServerExchange;
|
|
||||||
import io.undertow.server.session.Session;
|
|
||||||
import io.undertow.util.Sessions;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.KeycloakSecurityContext;
|
|
||||||
import org.keycloak.adapters.AdapterTokenStore;
|
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
|
||||||
import org.keycloak.adapters.OidcKeycloakAccount;
|
|
||||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
|
||||||
import org.keycloak.adapters.RequestAuthenticator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Per-request object. Storage of tokens in undertow session.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
|
||||||
*/
|
|
||||||
public class UndertowSessionTokenStore implements AdapterTokenStore {
|
|
||||||
|
|
||||||
protected static Logger log = Logger.getLogger(UndertowSessionTokenStore.class);
|
|
||||||
|
|
||||||
private final HttpServerExchange exchange;
|
|
||||||
private final KeycloakDeployment deployment;
|
|
||||||
private final UndertowUserSessionManagement sessionManagement;
|
|
||||||
private final SecurityContext securityContext;
|
|
||||||
|
|
||||||
public UndertowSessionTokenStore(HttpServerExchange exchange, KeycloakDeployment deployment, UndertowUserSessionManagement sessionManagement,
|
|
||||||
SecurityContext securityContext) {
|
|
||||||
this.exchange = exchange;
|
|
||||||
this.deployment = deployment;
|
|
||||||
this.sessionManagement = sessionManagement;
|
|
||||||
this.securityContext = securityContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void checkCurrentToken() {
|
|
||||||
// no-op on undertow
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCached(RequestAuthenticator authenticator) {
|
|
||||||
Session session = Sessions.getSession(exchange);
|
|
||||||
if (session == null) {
|
|
||||||
log.debug("session was null, returning null");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
KeycloakUndertowAccount account = (KeycloakUndertowAccount)session.getAttribute(KeycloakUndertowAccount.class.getName());
|
|
||||||
if (account == null) {
|
|
||||||
log.debug("Account was not in session, returning null");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!deployment.getRealm().equals(account.getKeycloakSecurityContext().getRealm())) {
|
|
||||||
log.debug("Account in session belongs to a different realm than for this request.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
account.setCurrentRequestInfo(deployment, this);
|
|
||||||
if (account.checkActive()) {
|
|
||||||
log.debug("Cached account found");
|
|
||||||
securityContext.authenticationComplete(account, "KEYCLOAK", false);
|
|
||||||
((AbstractUndertowRequestAuthenticator)authenticator).propagateKeycloakContext(account);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
log.debug("Account was not active, returning false");
|
|
||||||
session.removeAttribute(KeycloakUndertowAccount.class.getName());
|
|
||||||
session.removeAttribute(KeycloakSecurityContext.class.getName());
|
|
||||||
session.invalidate(exchange);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void saveRequest() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean restoreRequest() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void saveAccountInfo(OidcKeycloakAccount account) {
|
|
||||||
Session session = Sessions.getOrCreateSession(exchange);
|
|
||||||
session.setAttribute(KeycloakUndertowAccount.class.getName(), account);
|
|
||||||
session.setAttribute(KeycloakSecurityContext.class.getName(), account.getKeycloakSecurityContext());
|
|
||||||
sessionManagement.login(session.getSessionManager());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void logout() {
|
|
||||||
Session session = Sessions.getSession(exchange);
|
|
||||||
if (session == null) return;
|
|
||||||
KeycloakUndertowAccount account = (KeycloakUndertowAccount)session.getAttribute(KeycloakUndertowAccount.class.getName());
|
|
||||||
if (account == null) return;
|
|
||||||
session.removeAttribute(KeycloakUndertowAccount.class.getName());
|
|
||||||
session.removeAttribute(KeycloakSecurityContext.class.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
# and other contributors as indicated by the @author tags.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
|
|
||||||
org.keycloak.adapters.undertow.KeycloakServletExtension
|
|
|
@ -64,11 +64,6 @@
|
||||||
<artifactId>keycloak-saml-adapter-api-public</artifactId>
|
<artifactId>keycloak-saml-adapter-api-public</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-authz-client</artifactId>
|
<artifactId>keycloak-authz-client</artifactId>
|
||||||
|
|
|
@ -112,16 +112,6 @@
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter</artifactId>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>*</groupId>
|
|
||||||
<artifactId>*</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Authorization -->
|
<!-- Authorization -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!--
|
|
||||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
~ and other contributors as indicated by the @author tags.
|
|
||||||
~
|
|
||||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
~ you may not use this file except in compliance with the License.
|
|
||||||
~ You may obtain a copy of the License at
|
|
||||||
~
|
|
||||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
~
|
|
||||||
~ Unless required by applicable law or agreed to in writing, software
|
|
||||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
~ See the License for the specific language governing permissions and
|
|
||||||
~ limitations under the License.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-undertow-adapter">
|
|
||||||
<properties>
|
|
||||||
<property name="jboss.api" value="private"/>
|
|
||||||
</properties>
|
|
||||||
<resources>
|
|
||||||
<artifact name="${org.keycloak:keycloak-undertow-adapter}"/>
|
|
||||||
</resources>
|
|
||||||
<dependencies>
|
|
||||||
<module name="javax.api"/>
|
|
||||||
<module name="org.bouncycastle" />
|
|
||||||
<module name="com.fasterxml.jackson.core.jackson-annotations"/>
|
|
||||||
<module name="com.fasterxml.jackson.core.jackson-core"/>
|
|
||||||
<module name="com.fasterxml.jackson.core.jackson-databind"/>
|
|
||||||
<module name="com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider"/>
|
|
||||||
<module name="org.apache.httpcomponents"/>
|
|
||||||
<module name="javax.servlet.api"/>
|
|
||||||
<module name="org.jboss.logging"/>
|
|
||||||
<module name="org.jboss.xnio"/>
|
|
||||||
<module name="io.undertow.core"/>
|
|
||||||
<module name="io.undertow.servlet"/>
|
|
||||||
<module name="org.keycloak.keycloak-adapter-spi"/>
|
|
||||||
<module name="org.keycloak.keycloak-adapter-core"/>
|
|
||||||
<module name="org.keycloak.keycloak-common"/>
|
|
||||||
<module name="org.keycloak.keycloak-core"/>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
</module>
|
|
5
pom.xml
5
pom.xml
|
@ -1061,11 +1061,6 @@
|
||||||
<artifactId>keycloak-undertow-adapter-spi</artifactId>
|
<artifactId>keycloak-undertow-adapter-spi</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-saml-wildfly-elytron-adapter</artifactId>
|
<artifactId>keycloak-saml-wildfly-elytron-adapter</artifactId>
|
||||||
|
|
|
@ -5,7 +5,6 @@ This module is primarily used for custom adapters in the testsuite.
|
||||||
## Undertow
|
## Undertow
|
||||||
Modules related to Undertow:
|
Modules related to Undertow:
|
||||||
* Keycloak Undertow Adapter SPI (`undertow-adapter-spi-jakarta`)
|
* Keycloak Undertow Adapter SPI (`undertow-adapter-spi-jakarta`)
|
||||||
* Keycloak Undertow OIDC adapter (`undertow-adapter-jakarta`)
|
|
||||||
* Keycloak Undertow SAML adapter (`undertow-adapter-saml-jakarta`)
|
* Keycloak Undertow SAML adapter (`undertow-adapter-saml-jakarta`)
|
||||||
|
|
||||||
These modules are automatically generated from the Keycloak adapters module (`/adapters`) and converted to adapters supporting JakartaEE.
|
These modules are automatically generated from the Keycloak adapters module (`/adapters`) and converted to adapters supporting JakartaEE.
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>undertow-adapter-spi-jakarta</module>
|
<module>undertow-adapter-spi-jakarta</module>
|
||||||
<module>undertow-adapter-jakarta</module>
|
|
||||||
<module>undertow-adapter-saml-jakarta</module>
|
<module>undertow-adapter-saml-jakarta</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
src/
|
|
|
@ -1,151 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<parent>
|
|
||||||
<artifactId>integration-arquillian-servers-adapter-spi</artifactId>
|
|
||||||
<groupId>org.keycloak.testsuite</groupId>
|
|
||||||
<version>999.0.0-SNAPSHOT</version>
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
|
||||||
</parent>
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<artifactId>keycloak-undertow-adapter-jakarta</artifactId>
|
|
||||||
<name>Undertow OIDC Adapter (JakartaEE)</name>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<keycloak.osgi.export>
|
|
||||||
org.keycloak.adapters.undertow.*
|
|
||||||
</keycloak.osgi.export>
|
|
||||||
<keycloak.osgi.import>
|
|
||||||
io.undertow.*;version="[1.4,3)",
|
|
||||||
javax.servlet.*;version="[3.1,5)";resolution:=optional,
|
|
||||||
*;resolution:=optional
|
|
||||||
</keycloak.osgi.import>
|
|
||||||
|
|
||||||
<jakarta-transformer-sources>${project.basedir}/../../../../../adapters/oidc/undertow</jakarta-transformer-sources>
|
|
||||||
<jakarta-transformer-target>${project.basedir}</jakarta-transformer-target>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak.testsuite</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter-spi-jakarta</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.jboss.logging</groupId>
|
|
||||||
<artifactId>jboss-logging</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-core</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-adapter-spi</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak</groupId>
|
|
||||||
<artifactId>keycloak-adapter-core</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
|
||||||
<artifactId>httpclient</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.bouncycastle</groupId>
|
|
||||||
<artifactId>bcprov-jdk18on</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
|
||||||
<artifactId>jackson-core</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
|
||||||
<artifactId>jackson-databind</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
|
||||||
<artifactId>jackson-annotations</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>jakarta.servlet</groupId>
|
|
||||||
<artifactId>jakarta.servlet-api</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.undertow</groupId>
|
|
||||||
<artifactId>undertow-servlet</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.undertow</groupId>
|
|
||||||
<artifactId>undertow-core</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-antrun-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>transform</id>
|
|
||||||
<phase>initialize</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>run</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<target>
|
|
||||||
<property name="plugin_classpath" refid="maven.plugin.classpath" />
|
|
||||||
<java classname="org.eclipse.transformer.jakarta.JakartaTransformer" fork="true">
|
|
||||||
<jvmarg value="${ant.jvm.args}"/>
|
|
||||||
<arg value="-o" />
|
|
||||||
<arg value="${jakarta-transformer-sources}" />
|
|
||||||
<arg value="${jakarta-transformer-target}/tmp" />
|
|
||||||
<classpath>
|
|
||||||
<pathelement path="${plugin_classpath}" />
|
|
||||||
</classpath>
|
|
||||||
</java>
|
|
||||||
<touch>
|
|
||||||
<fileset dir="${jakarta-transformer-target}"/>
|
|
||||||
</touch>
|
|
||||||
<copy todir="${jakarta-transformer-target}" overwrite="false">
|
|
||||||
<fileset dir="${jakarta-transformer-target}/tmp"/>
|
|
||||||
</copy>
|
|
||||||
<delete dir="${jakarta-transformer-target}/tmp"/>
|
|
||||||
</target>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.eclipse.transformer</groupId>
|
|
||||||
<artifactId>org.eclipse.transformer.cli</artifactId>
|
|
||||||
<version>0.2.0</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>ant-contrib</groupId>
|
|
||||||
<artifactId>ant-contrib</artifactId>
|
|
||||||
<version>1.0b3</version>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>ant</groupId>
|
|
||||||
<artifactId>ant</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
</project>
|
|
|
@ -50,11 +50,6 @@
|
||||||
<artifactId>keycloak-undertow-adapter-spi-jakarta</artifactId>
|
<artifactId>keycloak-undertow-adapter-spi-jakarta</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak.testsuite</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter-jakarta</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jboss.logging</groupId>
|
<groupId>org.jboss.logging</groupId>
|
||||||
<artifactId>jboss-logging</artifactId>
|
<artifactId>jboss-logging</artifactId>
|
||||||
|
|
|
@ -28,11 +28,6 @@
|
||||||
<name>App Server - Undertow</name>
|
<name>App Server - Undertow</name>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak.testsuite</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter-jakarta</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak.testsuite</groupId>
|
<groupId>org.keycloak.testsuite</groupId>
|
||||||
<artifactId>keycloak-saml-undertow-adapter-jakarta</artifactId>
|
<artifactId>keycloak-saml-undertow-adapter-jakarta</artifactId>
|
||||||
|
|
|
@ -65,11 +65,6 @@
|
||||||
<artifactId>keycloak-dependencies-server-all</artifactId>
|
<artifactId>keycloak-dependencies-server-all</artifactId>
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak.testsuite</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter-jakarta</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak.testsuite</groupId>
|
<groupId>org.keycloak.testsuite</groupId>
|
||||||
<artifactId>integration-arquillian-testsuite-providers</artifactId>
|
<artifactId>integration-arquillian-testsuite-providers</artifactId>
|
||||||
|
|
|
@ -164,11 +164,6 @@
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-kerberos-federation</artifactId>
|
<artifactId>keycloak-kerberos-federation</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.keycloak.testsuite</groupId>
|
|
||||||
<artifactId>keycloak-undertow-adapter-jakarta</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.undertow</groupId>
|
<groupId>io.undertow</groupId>
|
||||||
<artifactId>undertow-servlet</artifactId>
|
<artifactId>undertow-servlet</artifactId>
|
||||||
|
|
Loading…
Reference in a new issue