KEYCLOAK-7278 KEYCLOAK-7280 CXF/Undertow integration
This commit is contained in:
parent
fea4201f46
commit
b2df872ad4
2 changed files with 176 additions and 1 deletions
|
@ -41,6 +41,7 @@
|
|||
org.ops4j.pax.web.*;version="[3.0,8)",
|
||||
javax.servlet.*;version="[2.5,4)";resolution:=optional,
|
||||
org.apache.cxf.transport.http;resolution:=optional;version="[3,4)",
|
||||
org.apache.cxf.transport.http_undertow;resolution:=optional;version="[3,4)",
|
||||
org.apache.cxf.transport.servlet;resolution:=optional;version="[3,4)",
|
||||
io.undertow.*,
|
||||
*;resolution:=optional
|
||||
|
@ -88,7 +89,7 @@
|
|||
<dependency>
|
||||
<groupId>org.apache.cxf</groupId>
|
||||
<artifactId>cxf-rt-transports-http-undertow</artifactId>
|
||||
<version>3.1.11.fuse-000199-redhat-1</version>
|
||||
<version>${cxf.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Copyright 2018 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.osgi.undertow;
|
||||
|
||||
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.adapters.spi.InMemorySessionIdMapper;
|
||||
import org.keycloak.adapters.spi.SessionIdMapper;
|
||||
import org.keycloak.adapters.undertow.UndertowAuthenticationMechanism;
|
||||
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import io.undertow.security.api.AuthenticationMechanism;
|
||||
import io.undertow.security.api.AuthenticationMode;
|
||||
import io.undertow.security.handlers.AuthenticationCallHandler;
|
||||
import io.undertow.security.handlers.AuthenticationConstraintHandler;
|
||||
import io.undertow.security.handlers.AuthenticationMechanismsHandler;
|
||||
import io.undertow.security.handlers.SecurityInitialHandler;
|
||||
import io.undertow.security.idm.Account;
|
||||
import io.undertow.security.idm.Credential;
|
||||
import io.undertow.security.idm.IdentityManager;
|
||||
import io.undertow.server.HttpHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.cxf.transport.http_undertow.CXFUndertowHttpHandler;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class CxfKeycloakAuthHandler implements CXFUndertowHttpHandler {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(CxfKeycloakAuthHandler.class.getName());
|
||||
|
||||
private static final IdentityManager IDENTITY_MANAGER = 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");
|
||||
}
|
||||
};
|
||||
|
||||
private final UndertowUserSessionManagement userSessionManagement = new UndertowUserSessionManagement();
|
||||
|
||||
protected final NodesRegistrationManagement nodesRegistrationManagement = new NodesRegistrationManagement();
|
||||
|
||||
protected final SessionIdMapper idMapper = new InMemorySessionIdMapper();
|
||||
|
||||
private final AtomicReference<HttpHandler> securityHandler = new AtomicReference<>();
|
||||
|
||||
private Pattern skipPattern;
|
||||
|
||||
private int confidentialPort = 8443;
|
||||
|
||||
private HttpHandler next;
|
||||
|
||||
private KeycloakConfigResolver configResolver;
|
||||
|
||||
private AdapterConfig adapterConfig;
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||
if (shouldSkip(exchange.getRequestPath())) {
|
||||
next.handleRequest(exchange);
|
||||
} else {
|
||||
getSecurityHandler().handleRequest(exchange);
|
||||
}
|
||||
}
|
||||
|
||||
private HttpHandler getSecurityHandler() {
|
||||
if (this.securityHandler.get() == null) {
|
||||
HttpHandler handler = this.next;
|
||||
|
||||
handler = new AuthenticationCallHandler(handler);
|
||||
handler = new AuthenticationConstraintHandler(handler);
|
||||
|
||||
AdapterDeploymentContext deploymentContext = buildDeploymentContext();
|
||||
|
||||
final List<AuthenticationMechanism> mechanisms
|
||||
= Collections.<AuthenticationMechanism>singletonList(
|
||||
new UndertowAuthenticationMechanism(deploymentContext, userSessionManagement, nodesRegistrationManagement, confidentialPort, null));
|
||||
handler = new AuthenticationMechanismsHandler(handler, mechanisms);
|
||||
|
||||
this.securityHandler.compareAndSet(null, new SecurityInitialHandler(AuthenticationMode.PRO_ACTIVE, IDENTITY_MANAGER, "KEYCLOAK", handler));
|
||||
}
|
||||
|
||||
return this.securityHandler.get();
|
||||
}
|
||||
|
||||
private AdapterDeploymentContext buildDeploymentContext() {
|
||||
if (configResolver != null) {
|
||||
LOG.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolver.getClass());
|
||||
return new AdapterDeploymentContext(configResolver);
|
||||
} else if (adapterConfig != null) {
|
||||
KeycloakDeployment kd = KeycloakDeploymentBuilder.build(adapterConfig);
|
||||
return new AdapterDeploymentContext(kd);
|
||||
}
|
||||
|
||||
LOG.warning("Adapter is unconfigured, Keycloak will deny every request");
|
||||
return new AdapterDeploymentContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNext(HttpHandler nextHandler) {
|
||||
this.next = nextHandler;
|
||||
}
|
||||
|
||||
private boolean shouldSkip(String requestPath) {
|
||||
return skipPattern != null && skipPattern.matcher(requestPath).matches();
|
||||
}
|
||||
|
||||
public KeycloakConfigResolver getConfigResolver() {
|
||||
return configResolver;
|
||||
}
|
||||
|
||||
public void setConfigResolver(KeycloakConfigResolver configResolver) {
|
||||
this.configResolver = configResolver;
|
||||
}
|
||||
|
||||
public int getConfidentialPort() {
|
||||
return confidentialPort;
|
||||
}
|
||||
|
||||
public void setConfidentialPort(int confidentialPort) {
|
||||
this.confidentialPort = confidentialPort;
|
||||
}
|
||||
|
||||
public AdapterConfig getAdapterConfig() {
|
||||
return adapterConfig;
|
||||
}
|
||||
|
||||
public void setAdapterConfig(AdapterConfig adapterConfig) {
|
||||
this.adapterConfig = adapterConfig;
|
||||
}
|
||||
|
||||
public String getSkipPattern() {
|
||||
return skipPattern.pattern();
|
||||
}
|
||||
|
||||
public void setSkipPattern(String skipPattern) {
|
||||
this.skipPattern = Pattern.compile(skipPattern, Pattern.DOTALL);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue