refactor adapters

This commit is contained in:
Bill Burke 2015-09-04 15:56:28 -04:00
parent f23f1cc813
commit 333ad0efac
92 changed files with 923 additions and 761 deletions

View file

@ -24,6 +24,7 @@ import org.keycloak.events.EventType;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
import org.keycloak.protocol.saml.SAML2LogoutResponseBuilder;
import org.keycloak.protocol.saml.SAMLRequestParser;
import org.keycloak.protocol.saml.SamlProtocol;
@ -247,16 +248,17 @@ public class SAMLEndpoint {
builder.logoutRequestID(request.getID());
builder.destination(config.getSingleLogoutServiceUrl());
builder.issuer(issuerURL);
builder.relayState(relayState);
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(relayState);
if (config.isWantAuthnRequestsSigned()) {
builder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
binding.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signDocument();
}
try {
if (config.isPostBindingResponse()) {
return builder.postBinding().response();
return binding.postBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl());
} else {
return builder.redirectBinding().response();
return binding.redirectBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl());
}
} catch (ConfigurationException e) {
throw new RuntimeException(e);

View file

@ -33,6 +33,7 @@ import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder;
import org.keycloak.protocol.saml.SAML2LogoutRequestBuilder;
import org.keycloak.protocol.saml.SAML2NameIDPolicyBuilder;
@ -92,7 +93,8 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
.issuer(issuerURL)
.forceAuthn(getConfig().isForceAuthn())
.protocolBinding(protocolBinding)
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat))
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(request.getState());
if (getConfig().isWantAuthnRequestsSigned()) {
@ -109,14 +111,14 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
KeyPair keypair = new KeyPair(publicKey, privateKey);
authnRequestBuilder.signWith(keypair);
authnRequestBuilder.signDocument();
binding.signWith(keypair);
binding.signDocument();
}
if (getConfig().isPostBindingAuthnRequest()) {
return authnRequestBuilder.postBinding().request();
return binding.postBinding(authnRequestBuilder.toDocument()).request(destinationUrl);
} else {
return authnRequestBuilder.redirectBinding().request();
return binding.redirectBinding(authnRequestBuilder.toDocument()).request(destinationUrl);
}
} catch (Exception e) {
throw new IdentityBrokerException("Could not create authentication request.", e);
@ -153,9 +155,10 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
String singleLogoutServiceUrl = getConfig().getSingleLogoutServiceUrl();
if (singleLogoutServiceUrl == null || singleLogoutServiceUrl.trim().equals("") || !getConfig().isBackchannelSupported()) return;
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
JaxrsSAML2BindingBuilder binding = buildLogoutBinding(userSession, realm);
try {
int status = SimpleHttp.doPost(singleLogoutServiceUrl)
.param(GeneralConstants.SAML_REQUEST_KEY, logoutBuilder.postBinding().encoded())
.param(GeneralConstants.SAML_REQUEST_KEY, binding.postBinding(logoutBuilder.buildDocument()).encoded())
.param(GeneralConstants.RELAY_STATE, userSession.getId()).asStatus();
boolean success = status >=200 && status < 400;
if (!success) {
@ -178,7 +181,8 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
} else {
try {
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
return logoutBuilder.postBinding().request();
JaxrsSAML2BindingBuilder binding = buildLogoutBinding(userSession, realm);
return binding.postBinding(logoutBuilder.buildDocument()).request(singleLogoutServiceUrl);
} catch (Exception e) {
throw new RuntimeException(e);
}
@ -192,13 +196,18 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
.issuer(getEntityId(uriInfo, realm))
.sessionIndex(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SESSION_INDEX))
.userPrincipal(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT), userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT_NAMEFORMAT))
.destination(singleLogoutServiceUrl)
.destination(singleLogoutServiceUrl);
return logoutBuilder;
}
private JaxrsSAML2BindingBuilder buildLogoutBinding(UserSessionModel userSession, RealmModel realm) {
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(userSession.getId());
if (getConfig().isWantAuthnRequestsSigned()) {
logoutBuilder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
binding.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signDocument();
}
return logoutBuilder;
return binding;
}
@Override

View file

@ -14,6 +14,7 @@
<include>org/bouncycastle/**</include>
<include>net/iharder/base64/**</include>
<include>org/keycloak/keycloak-core/**</include>
<include>org/keycloak/keycloak-adapter-spi/**</include>
<include>org/keycloak/keycloak-adapter-core/**</include>
<include>org/keycloak/keycloak-jboss-adapter-core/**</include>
<include>org/keycloak/keycloak-as7-adapter/**</include>

View file

@ -53,6 +53,11 @@
<!-- subsystems -->
<module-def name="org.keycloak.keycloak-adapter-spi">
<maven-resource group="org.keycloak" artifact="keycloak-adapter-spi"/>
<maven-resource group="org.keycloak" artifact="keycloak-tomcat-adapter-spi"/>
</module-def>
<module-def name="org.keycloak.keycloak-adapter-core">
<maven-resource group="org.keycloak" artifact="keycloak-adapter-core"/>
</module-def>

View file

@ -21,6 +21,14 @@
<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-tomcat-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>

View file

@ -14,6 +14,7 @@
<module name="org.apache.httpcomponents"/>
<module name="org.jboss.logging"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="net.iharder.base64"/>
</dependencies>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-adapter-spi">
<resources>
<!-- Insert resources here -->
</resources>
<dependencies>
<module name="javax.api"/>
<module name="org.jboss.logging"/>
</dependencies>
</module>

View file

@ -18,6 +18,7 @@
<module name="org.jboss.as.security"/>
<module name="org.jboss.as.web"/>
<module name="org.picketbox"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>

View file

@ -10,6 +10,7 @@
<module name="javax.api"/>
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>

View file

@ -10,6 +10,7 @@
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
<module name="org.apache.httpcomponents"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>

View file

@ -14,6 +14,7 @@
<include>org/bouncycastle/**</include>
<include>net/iharder/base64/**</include>
<include>org/keycloak/keycloak-core/**</include>
<include>org/keycloak/keycloak-adapter-spi/**</include>
<include>org/keycloak/keycloak-adapter-core/**</include>
<include>org/keycloak/keycloak-jboss-adapter-core/**</include>
<include>org/keycloak/keycloak-as7-adapter/**</include>

View file

@ -49,6 +49,11 @@
<!-- subsystems -->
<module-def name="org.keycloak.keycloak-adapter-spi">
<maven-resource group="org.keycloak" artifact="keycloak-adapter-spi"/>
<maven-resource group="org.keycloak" artifact="keycloak-undertow-adapter-spi"/>
</module-def>
<module-def name="org.keycloak.keycloak-adapter-core">
<maven-resource group="org.keycloak" artifact="keycloak-adapter-core"/>
</module-def>

View file

@ -21,6 +21,14 @@
<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>

View file

@ -13,6 +13,7 @@
<module name="org.codehaus.jackson.jackson-xc"/>
<module name="org.apache.httpcomponents" slot="4.3" />
<module name="org.jboss.logging"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-core"/>
<module name="net.iharder.base64"/>
</dependencies>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-adapter-spi">
<resources>
<!-- Insert resources here -->
</resources>
<dependencies>
<module name="javax.api"/>
<module name="org.jboss.logging"/>
</dependencies>
</module>

View file

@ -10,6 +10,7 @@
<module name="javax.api"/>
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>

View file

@ -10,6 +10,7 @@
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
<module name="org.apache.httpcomponents" slot="4.3"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>

View file

@ -18,6 +18,7 @@
<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-core"/>
</dependencies>

View file

@ -19,6 +19,7 @@
<module name="io.undertow.servlet"/>
<module name="org.picketbox"/>
<module name="org.keycloak.keycloak-undertow-adapter"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>

View file

@ -49,6 +49,11 @@
<!-- subsystems -->
<module-def name="org.keycloak.keycloak-adapter-spi">
<maven-resource group="org.keycloak" artifact="keycloak-adapter-spi"/>
<maven-resource group="org.keycloak" artifact="keycloak-undertow-adapter-spi"/>
</module-def>
<module-def name="org.keycloak.keycloak-adapter-core">
<maven-resource group="org.keycloak" artifact="keycloak-adapter-core"/>
</module-def>

View file

@ -21,6 +21,14 @@
<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>

View file

@ -13,6 +13,7 @@
<module name="org.codehaus.jackson.jackson-xc"/>
<module name="org.apache.httpcomponents"/>
<module name="org.jboss.logging"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-core"/>
<module name="net.iharder.base64"/>
</dependencies>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-adapter-spi">
<resources>
<!-- Insert resources here -->
</resources>
<dependencies>
<module name="javax.api"/>
<module name="org.jboss.logging"/>
</dependencies>
</module>

View file

@ -10,6 +10,7 @@
<module name="javax.api"/>
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>

View file

@ -10,6 +10,7 @@
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
<module name="org.apache.httpcomponents"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>

View file

@ -18,6 +18,7 @@
<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-core"/>
</dependencies>

View file

@ -19,6 +19,7 @@
<module name="io.undertow.servlet"/>
<module name="org.picketbox"/>
<module name="org.keycloak.keycloak-undertow-adapter"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>

View file

@ -44,6 +44,14 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-servlet-oauth-client</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-undertow-adapter</artifactId>

View file

@ -13,6 +13,7 @@
<module name="org.codehaus.jackson.jackson-xc"/>
<module name="org.apache.httpcomponents" />
<module name="org.jboss.logging"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-core"/>
<module name="net.iharder.base64"/>
</dependencies>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-adapter-spi">
<resources>
<artifact name="${org.keycloak:keycloak-adapter-spi}"/>
<artifact name="${org.keycloak:keycloak-undertow-adapter-spi}"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="org.jboss.logging"/>
</dependencies>
</module>

View file

@ -10,6 +10,7 @@
<module name="javax.api"/>
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>

View file

@ -10,6 +10,7 @@
<module name="org.jboss.logging"/>
<module name="org.picketbox"/>
<module name="org.apache.httpcomponents"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>

View file

@ -18,6 +18,7 @@
<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-core"/>
</dependencies>

View file

@ -19,6 +19,7 @@
<module name="io.undertow.servlet"/>
<module name="org.picketbox"/>
<module name="org.keycloak.keycloak-undertow-adapter"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>

View file

@ -39,6 +39,10 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<!-- Contains KeycloakPrincipal -->
<dependency>

View file

@ -17,14 +17,13 @@
package org.keycloak.example.multitenant.control;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.OIDCHttpFacade;
/**
*
@ -35,7 +34,7 @@ public class PathBasedKeycloakConfigResolver implements KeycloakConfigResolver {
private final Map<String, KeycloakDeployment> cache = new ConcurrentHashMap<String, KeycloakDeployment>();
@Override
public KeycloakDeployment resolve(HttpFacade.Request request) {
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
String path = request.getURI();
int multitenantIndex = path.indexOf("multitenant/");
if (multitenantIndex == -1) {

View file

@ -38,6 +38,11 @@
<version>${jboss.logging.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>

View file

@ -23,9 +23,9 @@ import java.util.Set;
public class AuthenticatedActionsHandler {
private static final Logger log = Logger.getLogger(AuthenticatedActionsHandler.class);
protected KeycloakDeployment deployment;
protected HttpFacade facade;
protected OIDCHttpFacade facade;
public AuthenticatedActionsHandler(KeycloakDeployment deployment, HttpFacade facade) {
public AuthenticatedActionsHandler(KeycloakDeployment deployment, OIDCHttpFacade facade) {
this.deployment = deployment;
this.facade = facade;
}

View file

@ -34,7 +34,7 @@ public class CookieTokenStore {
}
public static KeycloakPrincipal<RefreshableKeycloakSecurityContext> getPrincipalFromCookie(KeycloakDeployment deployment, HttpFacade facade, AdapterTokenStore tokenStore) {
HttpFacade.Cookie cookie = facade.getRequest().getCookie(AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE);
OIDCHttpFacade.Cookie cookie = facade.getRequest().getCookie(AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE);
if (cookie == null) {
log.debug("Not found adapter state cookie in current request");
return null;

View file

@ -14,7 +14,6 @@ import org.keycloak.util.KeycloakUriBuilder;
import org.keycloak.util.UriUtils;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
@ -93,12 +92,12 @@ public class OAuthRequestAuthenticator {
return facade.getRequest().isSecure();
}
protected HttpFacade.Cookie getCookie(String cookieName) {
protected OIDCHttpFacade.Cookie getCookie(String cookieName) {
return facade.getRequest().getCookie(cookieName);
}
protected String getCookieValue(String cookieName) {
HttpFacade.Cookie cookie = getCookie(cookieName);
OIDCHttpFacade.Cookie cookie = getCookie(cookieName);
if (cookie == null) return null;
return cookie.getValue();
}
@ -198,7 +197,7 @@ public class OAuthRequestAuthenticator {
}
protected AuthChallenge checkStateCookie() {
HttpFacade.Cookie stateCookie = getCookie(deployment.getStateCookieName());
OIDCHttpFacade.Cookie stateCookie = getCookie(deployment.getStateCookieName());
if (stateCookie == null) {
log.warn("No state cookie");

View file

@ -0,0 +1,14 @@
package org.keycloak.adapters;
import org.keycloak.KeycloakSecurityContext;
/**
* Bridge between core adapter and HTTP Engine
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface OIDCHttpFacade extends HttpFacade {
KeycloakSecurityContext getSecurityContext();
}

View file

@ -1,55 +1,20 @@
package org.keycloak.adapters;
import org.keycloak.KeycloakSecurityContext;
import javax.security.cert.X509Certificate;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
/**
* Bridge between core adapter and HTTP Engine
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface HttpFacade {
Request getRequest();
public class Cookie {
protected String name;
protected String value;
protected int version;
protected String domain;
protected String path;
Response getResponse();
public Cookie(String name, String value, int version, String domain, String path) {
this.name = name;
this.value = value;
this.version = version;
this.domain = domain;
this.path = path;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
public int getVersion() {
return version;
}
public String getDomain() {
return domain;
}
public String getPath() {
return path;
}
}
X509Certificate[] getCertificateChain();
interface Request {
@ -93,8 +58,39 @@ public interface HttpFacade {
void end();
}
KeycloakSecurityContext getSecurityContext();
Request getRequest();
Response getResponse();
X509Certificate[] getCertificateChain();
public class Cookie {
protected String name;
protected String value;
protected int version;
protected String domain;
protected String path;
public Cookie(String name, String value, int version, String domain, String path) {
this.name = name;
this.value = value;
this.version = version;
this.domain = domain;
this.path = path;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
public int getVersion() {
return version;
}
public String getDomain() {
return domain;
}
public String getPath() {
return path;
}
}
}

View file

@ -18,6 +18,10 @@
<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>

View file

@ -18,6 +18,10 @@
<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>

View file

@ -31,6 +31,10 @@
<artifactId>keycloak-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>

View file

@ -11,13 +11,13 @@ import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.SecurityContext;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.util.HostUtils;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class JaxrsHttpFacade implements HttpFacade {
public class JaxrsHttpFacade implements OIDCHttpFacade {
protected final ContainerRequestContext requestContext;
protected final SecurityContext securityContext;
@ -31,7 +31,7 @@ public class JaxrsHttpFacade implements HttpFacade {
this.securityContext = securityContext;
}
protected class RequestFacade implements HttpFacade.Request {
protected class RequestFacade implements OIDCHttpFacade.Request {
@Override
public String getMethod() {
@ -90,7 +90,7 @@ public class JaxrsHttpFacade implements HttpFacade {
}
}
protected class ResponseFacade implements HttpFacade.Response {
protected class ResponseFacade implements OIDCHttpFacade.Response {
private javax.ws.rs.core.Response.ResponseBuilder responseBuilder = javax.ws.rs.core.Response.status(204);

View file

@ -24,6 +24,10 @@
<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>

View file

@ -0,0 +1,115 @@
<?xml version="1.0"?>
<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>1.5.0.Final-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-jetty-adapter-spi</artifactId>
<name>Keycloak Jetty Adapter SPI</name>
<properties>
<jetty9.version>8.1.16.v20140903</jetty9.version>
<keycloak.osgi.export>
org.keycloak.adapters.jetty.core.*
</keycloak.osgi.export>
<keycloak.osgi.import>
org.eclipse.jetty.*;version="[8.1,10)";resolution:=optional,
javax.servlet.*;version="[2.5,4)";resolution:=optional,
org.keycloak.*;version="${project.version}",
*;resolution:=optional
</keycloak.osgi.import>
</properties>
<description />
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>${jboss.logging.version}</version>
</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.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty9.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${jetty9.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
<version>${jetty9.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<!-- 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>

View file

@ -1,6 +1,5 @@
package org.keycloak.adapters.jetty.core;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.util.MultivaluedHashMap;
import org.keycloak.util.UriUtils;
@ -25,6 +24,30 @@ public class JettyHttpFacade implements HttpFacade {
protected ResponseFacade responseFacade = new ResponseFacade();
protected MultivaluedHashMap<String, String> queryParameters;
public JettyHttpFacade(org.eclipse.jetty.server.Request request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
@Override
public Request getRequest() {
return requestFacade;
}
@Override
public Response getResponse() {
return responseFacade;
}
@Override
public X509Certificate[] getCertificateChain() {
throw new IllegalStateException("Not supported yet");
}
public boolean isEnded() {
return responseFacade.isEnded();
}
protected class RequestFacade implements Request {
@Override
public String getURI() {
@ -159,33 +182,4 @@ public class JettyHttpFacade implements HttpFacade {
return ended;
}
}
public JettyHttpFacade(org.eclipse.jetty.server.Request request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
@Override
public Request getRequest() {
return requestFacade;
}
@Override
public Response getResponse() {
return responseFacade;
}
@Override
public KeycloakSecurityContext getSecurityContext() {
return (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
}
@Override
public X509Certificate[] getCertificateChain() {
throw new IllegalStateException("Not supported yet");
}
public boolean isEnded() {
return responseFacade.isEnded();
}
}

View file

@ -35,6 +35,14 @@
<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-jetty-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>

View file

@ -92,7 +92,7 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
AdapterDeploymentContext deploymentContext = (AdapterDeploymentContext) request.getAttribute(AdapterDeploymentContext.class.getName());
KeycloakSecurityContext ksc = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
if (ksc != null) {
JettyHttpFacade facade = new JettyHttpFacade(request, null);
JettyHttpFacade facade = new OIDCJettyHttpFacade(request, null);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (ksc instanceof RefreshableKeycloakSecurityContext) {
((RefreshableKeycloakSecurityContext) ksc).logout(deployment);
@ -229,7 +229,7 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
log.trace("*** authenticate");
}
Request request = resolveRequest(req);
JettyHttpFacade facade = new JettyHttpFacade(request, (HttpServletResponse) res);
OIDCJettyHttpFacade facade = new OIDCJettyHttpFacade(request, (HttpServletResponse) res);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment == null || !deployment.isConfigured()) {
log.debug("*** deployment isn't configured return false");

View file

@ -0,0 +1,23 @@
package org.keycloak.adapters.jetty.core;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.OIDCHttpFacade;
import javax.servlet.http.HttpServletResponse;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class OIDCJettyHttpFacade extends JettyHttpFacade implements OIDCHttpFacade {
public OIDCJettyHttpFacade(org.eclipse.jetty.server.Request request, HttpServletResponse response) {
super(request, response);
}
@Override
public KeycloakSecurityContext getSecurityContext() {
return (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
}
}

View file

@ -14,6 +14,7 @@
<packaging>pom</packaging>
<modules>
<module>jetty-adapter-spi</module>
<module>jetty-core</module>
<module>jetty8.1</module>
<module>jetty9.2</module>

View file

@ -14,6 +14,7 @@
<packaging>pom</packaging>
<modules>
<module>adapter-spi</module>
<module>adapter-core</module>
<module>jaxrs-oauth-client</module>
<module>servlet-oauth-client</module>
@ -21,6 +22,7 @@
<module>tomcat</module>
<module>as7-eap6</module>
<module>jetty</module>
<module>undertow-adapter-spi</module>
<module>undertow</module>
<module>wildfly</module>
<module>js</module>

View file

@ -24,6 +24,10 @@
<artifactId>keycloak-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>

View file

@ -3,15 +3,13 @@ package org.keycloak.servlet;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.ServerRequest;
import org.keycloak.enums.RelativeUrlsUsed;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken;
import org.keycloak.util.KeycloakUriBuilder;
import org.keycloak.util.UriUtils;
import javax.security.cert.X509Certificate;
import javax.servlet.http.Cookie;
@ -167,7 +165,7 @@ public class ServletOAuthClient extends KeycloakDeploymentDelegateOAuthClient {
}
public static class ServletFacade implements HttpFacade {
public static class ServletFacade implements OIDCHttpFacade {
private final HttpServletRequest servletRequest;

View file

@ -1,8 +1,8 @@
package org.keycloak.adapters.springboot;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.representations.adapters.config.AdapterConfig;
public class KeycloakSpringBootConfigResolver implements org.keycloak.adapters.KeycloakConfigResolver {
@ -12,7 +12,7 @@ public class KeycloakSpringBootConfigResolver implements org.keycloak.adapters.K
private static AdapterConfig adapterConfig;
@Override
public KeycloakDeployment resolve(HttpFacade.Request request) {
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
if (keycloakDeployment != null) {
return keycloakDeployment;
}

View file

@ -26,6 +26,10 @@
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>

View file

@ -1,9 +1,7 @@
package org.keycloak.adapters.springsecurity.facade;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.springsecurity.facade.WrappedHttpServletRequest;
import org.keycloak.adapters.springsecurity.facade.WrappedHttpServletResponse;
import org.keycloak.adapters.OIDCHttpFacade;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
@ -13,12 +11,12 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Simple {@link HttpFacade} wrapping an {@link HttpServletRequest} and {@link HttpServletResponse}.
* Simple {@link org.keycloak.adapters.OIDCHttpFacade} wrapping an {@link HttpServletRequest} and {@link HttpServletResponse}.
*
* @author <a href="mailto:srossillo@smartling.com">Scott Rossillo</a>
* @version $Revision: 1 $
*/
public class SimpleHttpFacade implements HttpFacade {
public class SimpleHttpFacade implements OIDCHttpFacade {
private final HttpServletRequest request;
private final HttpServletResponse response;

View file

@ -1,4 +1,4 @@
/**
* Provides an {@link org.keycloak.adapters.HttpFacade} implementation.
* Provides an {@link org.keycloak.adapters.OIDCHttpFacade} implementation.
*/
package org.keycloak.adapters.springsecurity.facade;

View file

@ -14,6 +14,7 @@
<packaging>pom</packaging>
<modules>
<module>tomcat-adapter-spi</module>
<module>tomcat-core</module>
<module>tomcat6</module>
<module>tomcat7</module>

View file

@ -0,0 +1,69 @@
<?xml version="1.0"?>
<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>1.5.0.Final-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-tomcat-adapter-spi</artifactId>
<name>Keycloak Tomcat Adapter SPI</name>
<properties>
<!-- <tomcat.version>8.0.14</tomcat.version> -->
<!-- <tomcat.version>7.0.52</tomcat.version> -->
<tomcat.version>6.0.41</tomcat.version>
</properties>
<description />
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>${jboss.logging.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<!--
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>${tomcat.version}</version>
<scope>compile</scope>
</dependency>
-->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>catalina</artifactId>
<version>${tomcat.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -1,6 +1,5 @@
package org.keycloak.adapters.tomcat;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.util.MultivaluedHashMap;
import org.keycloak.util.ServerCookie;
@ -26,6 +25,30 @@ public class CatalinaHttpFacade implements HttpFacade {
protected ResponseFacade responseFacade = new ResponseFacade();
protected MultivaluedHashMap<String, String> queryParameters;
public CatalinaHttpFacade(HttpServletResponse response, org.apache.catalina.connector.Request request) {
this.response = response;
this.request = request;
}
@Override
public Request getRequest() {
return requestFacade;
}
@Override
public Response getResponse() {
return responseFacade;
}
@Override
public X509Certificate[] getCertificateChain() {
throw new IllegalStateException("Not supported yet");
}
public boolean isEnded() {
return responseFacade.isEnded();
}
protected class RequestFacade implements Request {
@Override
public String getURI() {
@ -157,33 +180,4 @@ public class CatalinaHttpFacade implements HttpFacade {
return ended;
}
}
public CatalinaHttpFacade(org.apache.catalina.connector.Request request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
@Override
public Request getRequest() {
return requestFacade;
}
@Override
public Response getResponse() {
return responseFacade;
}
@Override
public KeycloakSecurityContext getSecurityContext() {
return (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
}
@Override
public X509Certificate[] getCertificateChain() {
throw new IllegalStateException("Not supported yet");
}
public boolean isEnded() {
return responseFacade.isEnded();
}
}

View file

@ -28,6 +28,14 @@
<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-tomcat-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>

View file

@ -5,13 +5,9 @@ import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Manager;
import org.apache.catalina.authenticator.Constants;
import org.apache.catalina.authenticator.FormAuthenticator;
import org.apache.catalina.authenticator.SavedRequest;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.deploy.LoginConfig;
import org.apache.tomcat.util.buf.ByteChunk;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.adapters.AdapterDeploymentContext;
@ -28,15 +24,12 @@ import org.keycloak.enums.TokenStore;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.keycloak.adapters.KeycloakConfigResolver;
@ -71,7 +64,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
protected void logoutInternal(Request request) {
KeycloakSecurityContext ksc = (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
if (ksc != null) {
CatalinaHttpFacade facade = new CatalinaHttpFacade(request, null);
CatalinaHttpFacade facade = new OIDCCatalinaHttpFacade(request, null);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (ksc instanceof RefreshableKeycloakSecurityContext) {
((RefreshableKeycloakSecurityContext) ksc).logout(deployment);
@ -164,7 +157,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
try {
CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response);
CatalinaHttpFacade facade = new OIDCCatalinaHttpFacade(request, response);
Manager sessionManager = request.getContext().getManager();
CatalinaUserSessionManagementWrapper sessionManagementWrapper = new CatalinaUserSessionManagementWrapper(userSessionManagement, sessionManager);
PreAuthActionsHandler handler = new PreAuthActionsHandler(sessionManagementWrapper, deploymentContext, facade);
@ -181,7 +174,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
protected abstract boolean forwardToErrorPageInternal(Request request, HttpServletResponse response, Object loginConfig) throws IOException;
protected boolean authenticateInternal(Request request, HttpServletResponse response, Object loginConfig) throws IOException {
CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response);
CatalinaHttpFacade facade = new OIDCCatalinaHttpFacade(request, response);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment == null || !deployment.isConfigured()) {
return false;

View file

@ -39,10 +39,10 @@ public class AuthenticatedActionsValve extends ValveBase {
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
log.debugv("AuthenticatedActionsValve.invoke {0}", request.getRequestURI());
CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response);
CatalinaHttpFacade facade = new OIDCCatalinaHttpFacade(request, response);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment != null && deployment.isConfigured()) {
AuthenticatedActionsHandler handler = new AuthenticatedActionsHandler(deployment, new CatalinaHttpFacade(request, response));
AuthenticatedActionsHandler handler = new AuthenticatedActionsHandler(deployment, new OIDCCatalinaHttpFacade(request, response));
if (handler.handledRequest()) {
return;
}

View file

@ -0,0 +1,23 @@
package org.keycloak.adapters.tomcat;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.OIDCHttpFacade;
import javax.servlet.http.HttpServletResponse;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class OIDCCatalinaHttpFacade extends CatalinaHttpFacade implements OIDCHttpFacade{
public OIDCCatalinaHttpFacade(org.apache.catalina.connector.Request request, HttpServletResponse response) {
super(response, request);
}
@Override
public KeycloakSecurityContext getSecurityContext() {
return (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
}
}

View file

@ -0,0 +1,65 @@
<?xml version="1.0"?>
<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>1.5.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-undertow-adapter-spi</artifactId>
<name>Keycloak Undertow Integration</name>
<description/>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>${jboss.logging.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</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>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -1,27 +1,9 @@
/*
* Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.server.handlers.CookieImpl;
import io.undertow.util.AttachmentKey;
import io.undertow.util.Headers;
import io.undertow.util.HttpString;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.util.KeycloakUriBuilder;
@ -39,13 +21,36 @@ import java.util.Map;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UndertowHttpFacade implements HttpFacade {
public static final AttachmentKey<KeycloakSecurityContext> KEYCLOAK_SECURITY_CONTEXT_KEY = AttachmentKey.create(KeycloakSecurityContext.class);
public abstract class UndertowHttpFacade implements HttpFacade {
protected HttpServerExchange exchange;
protected RequestFacade requestFacade = new RequestFacade();
protected ResponseFacade responseFacade = new ResponseFacade();
public UndertowHttpFacade(HttpServerExchange exchange) {
this.exchange = exchange;
}
@Override
public Request getRequest() {
return requestFacade;
}
@Override
public Response getResponse() {
return responseFacade;
}
@Override
public X509Certificate[] getCertificateChain() {
X509Certificate[] chain = new X509Certificate[0];
try {
chain = exchange.getConnection().getSslSessionInfo().getPeerCertificateChain();
} catch (Exception ignore) {
}
return chain;
}
protected class RequestFacade implements Request {
@Override
public String getURI() {
@ -178,34 +183,4 @@ public class UndertowHttpFacade implements HttpFacade {
exchange.endExchange();
}
}
public UndertowHttpFacade(HttpServerExchange exchange) {
this.exchange = exchange;
}
@Override
public Request getRequest() {
return requestFacade;
}
@Override
public Response getResponse() {
return responseFacade;
}
@Override
public KeycloakSecurityContext getSecurityContext() {
return exchange.getAttachment(KEYCLOAK_SECURITY_CONTEXT_KEY);
}
@Override
public X509Certificate[] getCertificateChain() {
X509Certificate[] chain = new X509Certificate[0];
try {
chain = exchange.getConnection().getSslSessionInfo().getPeerCertificateChain();
} catch (Exception ignore) {
}
return chain;
}
}

View file

@ -24,6 +24,14 @@
<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>

View file

@ -21,18 +21,14 @@ 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.server.session.Session;
import io.undertow.util.AttachmentKey;
import io.undertow.util.Headers;
import io.undertow.util.Sessions;
import io.undertow.util.StatusCodes;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.AuthChallenge;
import org.keycloak.adapters.AuthOutcome;
import org.keycloak.adapters.CookieTokenStore;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
@ -64,7 +60,7 @@ public abstract class AbstractUndertowKeycloakAuthMech implements Authentication
Integer code = servePage(exchange, errorPage);
return new ChallengeResult(true, code);
}
UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
if (challenge.challenge(facade)) {
return new ChallengeResult(true, exchange.getResponseCode());
}
@ -93,9 +89,9 @@ public abstract class AbstractUndertowKeycloakAuthMech implements Authentication
if (notification.getEventType() != SecurityNotification.EventType.LOGGED_OUT) return;
HttpServerExchange exchange = notification.getExchange();
UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
KeycloakSecurityContext ksc = exchange.getAttachment(UndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY);
KeycloakSecurityContext ksc = exchange.getAttachment(OIDCUndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY);
if (ksc != null && ksc instanceof RefreshableKeycloakSecurityContext) {
((RefreshableKeycloakSecurityContext) ksc).logout(deployment);
}

View file

@ -48,7 +48,7 @@ public abstract class AbstractUndertowRequestAuthenticator extends RequestAuthen
}
protected void propagateKeycloakContext(KeycloakUndertowAccount account) {
exchange.putAttachment(UndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY, account.getKeycloakSecurityContext());
exchange.putAttachment(OIDCUndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY, account.getKeycloakSecurityContext());
}
@Override

View file

@ -0,0 +1,40 @@
/*
* Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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);
}
}

View file

@ -16,7 +16,6 @@
*/
package org.keycloak.adapters.undertow;
import io.undertow.security.api.AuthenticationMechanism;
import io.undertow.security.api.SecurityContext;
import io.undertow.server.HttpServerExchange;
import io.undertow.servlet.api.ConfidentialPortManager;
@ -25,7 +24,6 @@ import io.undertow.util.Headers;
import org.jboss.logging.Logger;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.AuthChallenge;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.NodesRegistrationManagement;
@ -81,7 +79,7 @@ public class ServletKeycloakAuthMech extends AbstractUndertowKeycloakAuthMech {
@Override
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (!deployment.isConfigured()) {
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;

View file

@ -61,7 +61,7 @@ public class ServletPreAuthActionsHandler implements HttpHandler {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
SessionManagementBridge bridge = new SessionManagementBridge(userSessionManagement, servletRequestContext.getDeployment().getSessionManager());
PreAuthActionsHandler handler = new PreAuthActionsHandler(bridge, deploymentContext, facade);

View file

@ -57,7 +57,7 @@ public class UndertowAuthenticatedActionsHandler implements HttpHandler {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
OIDCUndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment != null && deployment.isConfigured()) {
AuthenticatedActionsHandler handler = new AuthenticatedActionsHandler(deployment, facade);

View file

@ -25,7 +25,7 @@ public class UndertowAuthenticationMechanism extends AbstractUndertowKeycloakAut
@Override
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (!deployment.isConfigured()) {
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;

View file

@ -47,7 +47,7 @@ public class UndertowPreAuthActionsHandler implements HttpHandler {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
SessionManagementBridge bridge = new SessionManagementBridge(userSessionManagement, sessionManager);
PreAuthActionsHandler handler = new PreAuthActionsHandler(bridge, deploymentContext, facade);
if (handler.handleRequest()) return;

View file

@ -24,10 +24,18 @@
<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.keycloak</groupId>
<artifactId>keycloak-undertow-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId>

20
pom.xml
View file

@ -765,6 +765,11 @@
<artifactId>keycloak-email-freemarker</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
@ -795,6 +800,11 @@
<artifactId>keycloak-jboss-adapter-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jetty-adapter-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jetty-core</artifactId>
@ -865,6 +875,11 @@
<artifactId>keycloak-spring-boot-adapter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-tomcat-adapter-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-tomcat-core-adapter</artifactId>
@ -885,6 +900,11 @@
<artifactId>keycloak-tomcat8-adapter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId>

View file

@ -11,16 +11,13 @@ import org.keycloak.saml.processing.core.saml.v2.util.DocumentUtil;
import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
import org.keycloak.saml.processing.web.util.PostBindingUtil;
import org.keycloak.saml.processing.web.util.RedirectBindingUtil;
import org.keycloak.util.KeycloakUriBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.namespace.QName;
import java.io.IOException;
@ -31,15 +28,15 @@ import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import static org.keycloak.util.HtmlUtils.escapeAttribute;
import static org.keycloak.saml.common.util.StringUtil.isNotNull;
import static org.keycloak.util.HtmlUtils.escapeAttribute;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SAML2BindingBuilder2<T extends SAML2BindingBuilder2> {
protected static final Logger logger = Logger.getLogger(SAML2BindingBuilder2.class);
public class BaseSAML2BindingBuilder<T extends BaseSAML2BindingBuilder> {
protected static final Logger logger = Logger.getLogger(BaseSAML2BindingBuilder.class);
protected KeyPair signingKeyPair;
protected X509Certificate signingCertificate;
@ -116,17 +113,19 @@ public class SAML2BindingBuilder2<T extends SAML2BindingBuilder2> {
return (T)this;
}
public class PostBindingBuilder {
public static class BasePostBindingBuilder {
protected Document document;
protected BaseSAML2BindingBuilder builder;
public PostBindingBuilder(Document document) throws ProcessingException {
if (encrypt) encryptDocument(document);
public BasePostBindingBuilder(BaseSAML2BindingBuilder builder, Document document) throws ProcessingException {
this.builder = builder;
if (builder.encrypt) builder.encryptDocument(document);
this.document = document;
if (signAssertions) {
signAssertion(document);
if (builder.signAssertions) {
builder.signAssertion(document);
}
if (sign) {
signDocument(document);
if (builder.sign) {
builder.signDocument(document);
}
}
@ -137,24 +136,28 @@ public class SAML2BindingBuilder2<T extends SAML2BindingBuilder2> {
public Document getDocument() {
return document;
}
public String getHtmlResponse(String actionUrl) throws ProcessingException, ConfigurationException, IOException {
String str = builder.buildHtmlPostResponse(document, actionUrl, false);
return str;
}
public String getHtmlRequest(String actionUrl) throws ProcessingException, ConfigurationException, IOException {
String str = builder.buildHtmlPostResponse(document, actionUrl, true);
return str;
}
public Response request(String actionUrl) throws ConfigurationException, ProcessingException, IOException {
return buildResponse(document, actionUrl, true);
}
public Response response(String actionUrl) throws ConfigurationException, ProcessingException, IOException {
return buildResponse(document, actionUrl, false);
}
}
public class RedirectBindingBuilder {
public static class BaseRedirectBindingBuilder {
protected Document document;
protected BaseSAML2BindingBuilder builder;
public RedirectBindingBuilder(Document document) throws ProcessingException {
if (encrypt) encryptDocument(document);
public BaseRedirectBindingBuilder(BaseSAML2BindingBuilder builder, Document document) throws ProcessingException {
this.builder = builder;
if (builder.encrypt) builder.encryptDocument(document);
this.document = document;
if (signAssertions) {
signAssertion(document);
if (builder.signAssertions) {
builder.signAssertion(document);
}
}
@ -168,26 +171,8 @@ public class SAML2BindingBuilder2<T extends SAML2BindingBuilder2> {
samlParameterName = GeneralConstants.SAML_REQUEST_KEY;
}
return generateRedirectUri(samlParameterName, redirectUri, document);
return builder.generateRedirectUri(samlParameterName, redirectUri, document);
}
public Response response(String redirectUri) throws ProcessingException, ConfigurationException, IOException {
return response(redirectUri, false);
}
public Response request(String redirect) throws ProcessingException, ConfigurationException, IOException {
return response(redirect, true);
}
private Response response(String redirectUri, boolean asRequest) throws ProcessingException, ConfigurationException, IOException {
URI uri = responseUri(redirectUri, asRequest);
if (logger.isDebugEnabled()) logger.trace("redirect-binding uri: " + uri.toString());
CacheControl cacheControl = new CacheControl();
cacheControl.setNoCache(true);
return Response.status(302).location(uri)
.header("Pragma", "no-cache")
.header("Cache-Control", "no-cache, no-store").build();
}
}
@ -272,16 +257,6 @@ public class SAML2BindingBuilder2<T extends SAML2BindingBuilder2> {
}
protected Response buildResponse(Document responseDoc, String actionUrl, boolean asRequest) throws ProcessingException, ConfigurationException, IOException {
String str = buildHtmlPostResponse(responseDoc, actionUrl, asRequest);
CacheControl cacheControl = new CacheControl();
cacheControl.setNoCache(true);
return Response.ok(str, MediaType.TEXT_HTML_TYPE)
.header("Pragma", "no-cache")
.header("Cache-Control", "no-cache, no-store").build();
}
protected String buildHtmlPostResponse(Document responseDoc, String actionUrl, boolean asRequest) throws ProcessingException, ConfigurationException, IOException {
byte[] responseBytes = org.keycloak.saml.common.util.DocumentUtil.getDocumentAsString(responseDoc).getBytes("UTF-8");
String samlResponse = PostBindingUtil.base64Encode(new String(responseBytes));
@ -301,7 +276,7 @@ public class SAML2BindingBuilder2<T extends SAML2BindingBuilder2> {
builder.append("<HTML>");
builder.append("<HEAD>");
builder.append("<TITLE>HTTP Post Binding Response (Response)</TITLE>");
builder.append("<TITLE>SAML HTTP Post Binding</TITLE>");
builder.append("</HEAD>");
builder.append("<BODY Onload=\"document.forms[0].submit()\">");
@ -332,7 +307,7 @@ public class SAML2BindingBuilder2<T extends SAML2BindingBuilder2> {
protected URI generateRedirectUri(String samlParameterName, String redirectUri, Document document) throws ConfigurationException, ProcessingException, IOException {
UriBuilder builder = UriBuilder.fromUri(redirectUri)
KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(redirectUri)
.replaceQuery(null)
.queryParam(samlParameterName, base64Encoded(document));
if (relayState != null) {
@ -358,13 +333,4 @@ public class SAML2BindingBuilder2<T extends SAML2BindingBuilder2> {
return builder.build();
}
public RedirectBindingBuilder redirectBinding(Document document) throws ProcessingException {
return new RedirectBindingBuilder(document);
}
public PostBindingBuilder postBinding(Document document) throws ProcessingException {
return new PostBindingBuilder(document);
}
}

View file

@ -0,0 +1,77 @@
package org.keycloak.protocol.saml;
import org.keycloak.saml.common.exceptions.ConfigurationException;
import org.keycloak.saml.common.exceptions.ProcessingException;
import org.w3c.dom.Document;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.net.URI;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JaxrsSAML2BindingBuilder extends BaseSAML2BindingBuilder<JaxrsSAML2BindingBuilder> {
public static class PostBindingBuilder extends BasePostBindingBuilder {
public PostBindingBuilder(JaxrsSAML2BindingBuilder builder, Document document) throws ProcessingException {
super(builder, document);
}
public Response request(String actionUrl) throws ConfigurationException, ProcessingException, IOException {
return buildResponse(document, actionUrl, true);
}
public Response response(String actionUrl) throws ConfigurationException, ProcessingException, IOException {
return buildResponse(document, actionUrl, false);
}
protected Response buildResponse(Document responseDoc, String actionUrl, boolean asRequest) throws ProcessingException, ConfigurationException, IOException {
String str = builder.buildHtmlPostResponse(responseDoc, actionUrl, asRequest);
CacheControl cacheControl = new CacheControl();
cacheControl.setNoCache(true);
return Response.ok(str, MediaType.TEXT_HTML_TYPE)
.header("Pragma", "no-cache")
.header("Cache-Control", "no-cache, no-store").build();
}
}
public static class RedirectBindingBuilder extends BaseRedirectBindingBuilder {
public RedirectBindingBuilder(JaxrsSAML2BindingBuilder builder, Document document) throws ProcessingException {
super(builder, document);
}
public Response response(String redirectUri) throws ProcessingException, ConfigurationException, IOException {
return response(redirectUri, false);
}
public Response request(String redirect) throws ProcessingException, ConfigurationException, IOException {
return response(redirect, true);
}
private Response response(String redirectUri, boolean asRequest) throws ProcessingException, ConfigurationException, IOException {
URI uri = responseUri(redirectUri, asRequest);
if (logger.isDebugEnabled()) logger.trace("redirect-binding uri: " + uri.toString());
CacheControl cacheControl = new CacheControl();
cacheControl.setNoCache(true);
return Response.status(302).location(uri)
.header("Pragma", "no-cache")
.header("Cache-Control", "no-cache, no-store").build();
}
}
public RedirectBindingBuilder redirectBinding(Document document) throws ProcessingException {
return new RedirectBindingBuilder(this, document);
}
public PostBindingBuilder postBinding(Document document) throws ProcessingException {
return new PostBindingBuilder(this, document);
}
}

View file

@ -30,9 +30,21 @@ import java.net.URI;
/**
* @author pedroigor
*/
public class SAML2AuthnRequestBuilder extends SAML2BindingBuilder<SAML2AuthnRequestBuilder> {
public class SAML2AuthnRequestBuilder {
private final AuthnRequestType authnRequestType;
protected String destination;
protected String issuer;
public SAML2AuthnRequestBuilder destination(String destination) {
this.destination = destination;
return this;
}
public SAML2AuthnRequestBuilder issuer(String issuer) {
this.issuer = issuer;
return this;
}
public SAML2AuthnRequestBuilder() {
try {
@ -62,23 +74,7 @@ public class SAML2AuthnRequestBuilder extends SAML2BindingBuilder<SAML2AuthnRequ
return this;
}
public RedirectBindingBuilder redirectBinding() {
try {
return new RedirectBindingBuilder(toDocument());
} catch (Exception e) {
throw new RuntimeException("Could not build authn request for post binding.", e);
}
}
public PostBindingBuilder postBinding() {
try {
return new PostBindingBuilder(toDocument());
} catch (Exception e) {
throw new RuntimeException("Could not build authn request for post binding.", e);
}
}
private Document toDocument() {
public Document toDocument() {
try {
AuthnRequestType authnRequestType = this.authnRequestType;

View file

@ -1,389 +0,0 @@
package org.keycloak.protocol.saml;
import org.jboss.logging.Logger;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.common.constants.JBossSAMLConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ConfigurationException;
import org.keycloak.saml.common.exceptions.ProcessingException;
import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
import org.keycloak.saml.processing.core.saml.v2.util.DocumentUtil;
import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
import org.keycloak.saml.processing.web.util.PostBindingUtil;
import org.keycloak.saml.processing.web.util.RedirectBindingUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.namespace.QName;
import java.io.IOException;
import java.net.URI;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import static org.keycloak.util.HtmlUtils.escapeAttribute;
import static org.keycloak.saml.common.util.StringUtil.isNotNull;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SAML2BindingBuilder<T extends SAML2BindingBuilder> {
protected static final Logger logger = Logger.getLogger(SAML2BindingBuilder.class);
public static final String RELAY_STATE = "RelayState";
protected KeyPair signingKeyPair;
protected X509Certificate signingCertificate;
protected boolean sign;
protected boolean signAssertions;
protected SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RSA_SHA1;
protected String relayState;
protected String destination;
protected String issuer;
protected int encryptionKeySize = 128;
protected PublicKey encryptionPublicKey;
protected String encryptionAlgorithm = "AES";
protected boolean encrypt;
protected String canonicalizationMethodType = CanonicalizationMethod.EXCLUSIVE;
public T canonicalizationMethod(String method) {
this.canonicalizationMethodType = method;
return (T)this;
}
public T signDocument() {
this.sign = true;
return (T)this;
}
public T signAssertions() {
this.signAssertions = true;
return (T)this;
}
public T signWith(KeyPair keyPair) {
this.signingKeyPair = keyPair;
return (T)this;
}
public T signWith(PrivateKey privateKey, PublicKey publicKey) {
this.signingKeyPair = new KeyPair(publicKey, privateKey);
return (T)this;
}
public T signWith(KeyPair keyPair, X509Certificate cert) {
this.signingKeyPair = keyPair;
this.signingCertificate = cert;
return (T)this;
}
public T signWith(PrivateKey privateKey, PublicKey publicKey, X509Certificate cert) {
this.signingKeyPair = new KeyPair(publicKey, privateKey);
this.signingCertificate = cert;
return (T)this;
}
public T signatureAlgorithm(SignatureAlgorithm alg) {
this.signatureAlgorithm = alg;
return (T)this;
}
public T encrypt(PublicKey publicKey) {
encrypt = true;
encryptionPublicKey = publicKey;
return (T)this;
}
public T encryptionAlgorithm(String alg) {
this.encryptionAlgorithm = alg;
return (T)this;
}
public T encryptionKeySize(int size) {
this.encryptionKeySize = size;
return (T)this;
}
public T destination(String destination) {
this.destination = destination;
return (T)this;
}
public T issuer(String issuer) {
this.issuer = issuer;
return (T)this;
}
public T relayState(String relayState) {
this.relayState = relayState;
return (T)this;
}
public class PostBindingBuilder {
protected Document document;
public PostBindingBuilder(Document document) throws ProcessingException {
this.document = document;
if (signAssertions) {
signAssertion(document);
}
if (sign) {
signDocument(document);
}
}
public String encoded() throws ProcessingException, ConfigurationException, IOException {
byte[] responseBytes = DocumentUtil.getDocumentAsString(document).getBytes("UTF-8");
return PostBindingUtil.base64Encode(new String(responseBytes));
}
public Document getDocument() {
return document;
}
public Response request() throws ConfigurationException, ProcessingException, IOException {
return buildResponse(document, destination, true);
}
public Response request(String actionUrl) throws ConfigurationException, ProcessingException, IOException {
return buildResponse(document, actionUrl, true);
}
public Response response() throws ConfigurationException, ProcessingException, IOException {
return buildResponse(document, destination, false);
}
public Response response(String actionUrl) throws ConfigurationException, ProcessingException, IOException {
return buildResponse(document, actionUrl, false);
}
}
public class RedirectBindingBuilder {
protected Document document;
public RedirectBindingBuilder(Document document) throws ProcessingException {
this.document = document;
if (signAssertions) {
signAssertion(document);
}
}
public Document getDocument() {
return document;
}
public URI responseUri(String redirectUri, boolean asRequest) throws ConfigurationException, ProcessingException, IOException {
String samlParameterName = GeneralConstants.SAML_RESPONSE_KEY;
if (asRequest) {
samlParameterName = GeneralConstants.SAML_REQUEST_KEY;
}
return generateRedirectUri(samlParameterName, redirectUri, document);
}
public Response response() throws ProcessingException, ConfigurationException, IOException {
return response(destination, false);
}
public Response response(String redirectUri) throws ProcessingException, ConfigurationException, IOException {
return response(redirectUri, false);
}
public Response request(String redirect) throws ProcessingException, ConfigurationException, IOException {
return response(redirect, true);
}
public Response request() throws ProcessingException, ConfigurationException, IOException {
return response(destination, true);
}
private Response response(String redirectUri, boolean asRequest) throws ProcessingException, ConfigurationException, IOException {
URI uri = responseUri(redirectUri, asRequest);
if (logger.isDebugEnabled()) logger.trace("redirect-binding uri: " + uri.toString());
CacheControl cacheControl = new CacheControl();
cacheControl.setNoCache(true);
return Response.status(302).location(uri)
.header("Pragma", "no-cache")
.header("Cache-Control", "no-cache, no-store").build();
}
}
private String getSAMLNSPrefix(Document samlResponseDocument) {
Node assertionElement = samlResponseDocument.getDocumentElement()
.getElementsByTagNameNS(JBossSAMLURIConstants.ASSERTION_NSURI.get(), JBossSAMLConstants.ASSERTION.get()).item(0);
if (assertionElement == null) {
throw new IllegalStateException("Unable to find assertion in saml response document");
}
return assertionElement.getPrefix();
}
protected void encryptDocument(Document samlDocument) throws ProcessingException {
String samlNSPrefix = getSAMLNSPrefix(samlDocument);
try {
QName encryptedAssertionElementQName = new QName(JBossSAMLURIConstants.ASSERTION_NSURI.get(),
JBossSAMLConstants.ENCRYPTED_ASSERTION.get(), samlNSPrefix);
byte[] secret = SamlProtocolUtils.createRandomSecret(encryptionKeySize / 8);
SecretKey secretKey = new SecretKeySpec(secret, encryptionAlgorithm);
// encrypt the Assertion element and replace it with a EncryptedAssertion element.
XMLEncryptionUtil.encryptElement(new QName(JBossSAMLURIConstants.ASSERTION_NSURI.get(),
JBossSAMLConstants.ASSERTION.get(), samlNSPrefix), samlDocument, encryptionPublicKey,
secretKey, encryptionKeySize, encryptedAssertionElementQName, true);
} catch (Exception e) {
throw new ProcessingException("failed to encrypt", e);
}
}
protected void signDocument(Document samlDocument) throws ProcessingException {
String signatureMethod = signatureAlgorithm.getXmlSignatureMethod();
String signatureDigestMethod = signatureAlgorithm.getXmlSignatureDigestMethod();
SAML2Signature samlSignature = new SAML2Signature();
if (signatureMethod != null) {
samlSignature.setSignatureMethod(signatureMethod);
}
if (signatureDigestMethod != null) {
samlSignature.setDigestMethod(signatureDigestMethod);
}
Node nextSibling = samlSignature.getNextSiblingOfIssuer(samlDocument);
samlSignature.setNextSibling(nextSibling);
if (signingCertificate != null) {
samlSignature.setX509Certificate(signingCertificate);
}
samlSignature.signSAMLDocument(samlDocument, signingKeyPair, canonicalizationMethodType);
}
protected void signAssertion(Document samlDocument) throws ProcessingException {
Element originalAssertionElement = org.keycloak.saml.common.util.DocumentUtil.getChildElement(samlDocument.getDocumentElement(), new QName(JBossSAMLURIConstants.ASSERTION_NSURI.get(), JBossSAMLConstants.ASSERTION.get()));
if (originalAssertionElement == null) return;
Node clonedAssertionElement = originalAssertionElement.cloneNode(true);
Document temporaryDocument;
try {
temporaryDocument = org.keycloak.saml.common.util.DocumentUtil.createDocument();
} catch (ConfigurationException e) {
throw new ProcessingException(e);
}
temporaryDocument.adoptNode(clonedAssertionElement);
temporaryDocument.appendChild(clonedAssertionElement);
signDocument(temporaryDocument);
samlDocument.adoptNode(clonedAssertionElement);
Element parentNode = (Element) originalAssertionElement.getParentNode();
parentNode.replaceChild(clonedAssertionElement, originalAssertionElement);
}
protected Response buildResponse(Document responseDoc, String actionUrl, boolean asRequest) throws ProcessingException, ConfigurationException, IOException {
String str = buildHtmlPostResponse(responseDoc, actionUrl, asRequest);
CacheControl cacheControl = new CacheControl();
cacheControl.setNoCache(true);
return Response.ok(str, MediaType.TEXT_HTML_TYPE)
.header("Pragma", "no-cache")
.header("Cache-Control", "no-cache, no-store").build();
}
protected String buildHtmlPostResponse(Document responseDoc, String actionUrl, boolean asRequest) throws ProcessingException, ConfigurationException, IOException {
byte[] responseBytes = org.keycloak.saml.common.util.DocumentUtil.getDocumentAsString(responseDoc).getBytes("UTF-8");
String samlResponse = PostBindingUtil.base64Encode(new String(responseBytes));
return buildHtml(samlResponse, actionUrl, asRequest);
}
protected String buildHtml(String samlResponse, String actionUrl, boolean asRequest) {
if (destination == null) {
throw SAML2LoginResponseBuilder.logger.nullValueError("Destination is null");
}
StringBuilder builder = new StringBuilder();
String key = GeneralConstants.SAML_RESPONSE_KEY;
if (asRequest) {
key = GeneralConstants.SAML_REQUEST_KEY;
}
builder.append("<HTML>");
builder.append("<HEAD>");
builder.append("<TITLE>HTTP Post Binding Response (Response)</TITLE>");
builder.append("</HEAD>");
builder.append("<BODY Onload=\"document.forms[0].submit()\">");
builder.append("<FORM METHOD=\"POST\" ACTION=\"" + actionUrl + "\">");
builder.append("<INPUT TYPE=\"HIDDEN\" NAME=\"" + key + "\"" + " VALUE=\"" + samlResponse + "\"/>");
if (isNotNull(relayState)) {
builder.append("<INPUT TYPE=\"HIDDEN\" NAME=\"" + RELAY_STATE + "\" " + "VALUE=\"" + escapeAttribute(relayState) + "\"/>");
}
builder.append("<NOSCRIPT>");
builder.append("<P>JavaScript is disabled. We strongly recommend to enable it. Click the button below to continue.</P>");
builder.append("<INPUT TYPE=\"SUBMIT\" VALUE=\"CONTINUE\" />");
builder.append("</NOSCRIPT>");
builder.append("</FORM></BODY></HTML>");
return builder.toString();
}
protected String base64Encoded(Document document) throws ConfigurationException, ProcessingException, IOException {
String documentAsString = DocumentUtil.getDocumentAsString(document);
logger.debugv("saml docment: {0}", documentAsString);
byte[] responseBytes = documentAsString.getBytes("UTF-8");
return RedirectBindingUtil.deflateBase64URLEncode(responseBytes);
}
protected URI generateRedirectUri(String samlParameterName, String redirectUri, Document document) throws ConfigurationException, ProcessingException, IOException {
UriBuilder builder = UriBuilder.fromUri(redirectUri)
.replaceQuery(null)
.queryParam(samlParameterName, base64Encoded(document));
if (relayState != null) {
builder.queryParam(RELAY_STATE, relayState);
}
if (sign) {
builder.queryParam(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY, signatureAlgorithm.getJavaSignatureAlgorithm());
URI uri = builder.build();
String rawQuery = uri.getRawQuery();
Signature signature = signatureAlgorithm.createSignature();
byte[] sig = new byte[0];
try {
signature.initSign(signingKeyPair.getPrivate());
signature.update(rawQuery.getBytes("UTF-8"));
sig = signature.sign();
} catch (Exception e) {
throw new ProcessingException(e);
}
String encodedSig = RedirectBindingUtil.base64URLEncode(sig);
builder.queryParam(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY, encodedSig);
}
return builder.build();
}
}

View file

@ -16,25 +16,25 @@ import org.w3c.dom.Document;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SAML2ErrorResponseBuilder extends SAML2BindingBuilder<SAML2ErrorResponseBuilder> {
public class SAML2ErrorResponseBuilder {
protected String status;
protected String destination;
protected String issuer;
public SAML2ErrorResponseBuilder status(String status) {
this.status = status;
return this;
}
public RedirectBindingBuilder redirectBinding() throws ConfigurationException, ProcessingException {
Document samlResponseDocument = buildDocument();
return new RedirectBindingBuilder(samlResponseDocument);
public SAML2ErrorResponseBuilder destination(String destination) {
this.destination = destination;
return this;
}
public PostBindingBuilder postBinding() throws ConfigurationException, ProcessingException {
Document samlResponseDocument = buildDocument();
return new PostBindingBuilder(samlResponseDocument);
public SAML2ErrorResponseBuilder issuer(String issuer) {
this.issuer = issuer;
return this;
}
@ -61,7 +61,6 @@ public class SAML2ErrorResponseBuilder extends SAML2BindingBuilder<SAML2ErrorRes
responseType.setStatus(JBossSAMLAuthnResponseFactory.createStatusTypeForResponder(status));
responseType.setDestination(destination);
if (encrypt) encryptDocument(samlResponse);
return samlResponse;
}

View file

@ -15,11 +15,23 @@ import java.net.URI;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SAML2LogoutRequestBuilder extends SAML2BindingBuilder<SAML2LogoutRequestBuilder> {
public class SAML2LogoutRequestBuilder {
protected String userPrincipal;
protected String userPrincipalFormat;
protected String sessionIndex;
protected long assertionExpiration;
protected String destination;
protected String issuer;
public SAML2LogoutRequestBuilder destination(String destination) {
this.destination = destination;
return this;
}
public SAML2LogoutRequestBuilder issuer(String issuer) {
this.issuer = issuer;
return this;
}
/**
* Length of time in seconds the assertion is valid for
@ -45,20 +57,8 @@ public class SAML2LogoutRequestBuilder extends SAML2BindingBuilder<SAML2LogoutRe
return this;
}
public RedirectBindingBuilder redirectBinding() throws ConfigurationException, ProcessingException, ParsingException {
Document samlResponseDocument = buildDocument();
return new RedirectBindingBuilder(samlResponseDocument);
}
public PostBindingBuilder postBinding() throws ConfigurationException, ProcessingException, ParsingException {
Document samlResponseDocument = buildDocument();
return new PostBindingBuilder(samlResponseDocument);
}
public Document buildDocument() throws ProcessingException, ConfigurationException, ParsingException {
Document document = new SAML2Request().convert(createLogoutRequest());
if (encrypt) encryptDocument(document);
return document;
}

View file

@ -19,25 +19,25 @@ import java.net.URI;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SAML2LogoutResponseBuilder extends SAML2BindingBuilder<SAML2LogoutResponseBuilder> {
public class SAML2LogoutResponseBuilder {
protected String logoutRequestID;
protected String destination;
protected String issuer;
public SAML2LogoutResponseBuilder logoutRequestID(String logoutRequestID) {
this.logoutRequestID = logoutRequestID;
return this;
}
public RedirectBindingBuilder redirectBinding() throws ConfigurationException, ProcessingException {
Document samlResponseDocument = buildDocument();
return new RedirectBindingBuilder(samlResponseDocument);
public SAML2LogoutResponseBuilder destination(String destination) {
this.destination = destination;
return this;
}
public PostBindingBuilder postBinding() throws ConfigurationException, ProcessingException {
Document samlResponseDocument = buildDocument();
return new PostBindingBuilder(samlResponseDocument);
public SAML2LogoutResponseBuilder issuer(String issuer) {
this.issuer = issuer;
return this;
}
@ -67,7 +67,6 @@ public class SAML2LogoutResponseBuilder extends SAML2BindingBuilder<SAML2LogoutR
} catch (ParsingException e) {
throw new ProcessingException(e);
}
if (encrypt) encryptDocument(samlResponse);
return samlResponse;
}

View file

@ -166,15 +166,17 @@ public class SamlProtocol implements LoginProtocol {
protected Response getErrorResponse(ClientSessionModel clientSession, String status) {
SAML2ErrorResponseBuilder builder = new SAML2ErrorResponseBuilder()
.relayState(clientSession.getNote(GeneralConstants.RELAY_STATE))
.destination(clientSession.getRedirectUri())
.issuer(getResponseIssuer(realm))
.status(status);
try {
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(clientSession.getNote(GeneralConstants.RELAY_STATE));
Document document = builder.buildDocument();
if (isPostBinding(clientSession)) {
return builder.postBinding().response();
return binding.postBinding(document).response(clientSession.getRedirectUri());
} else {
return builder.redirectBinding().response();
return binding.redirectBinding(document).response(clientSession.getRedirectUri());
}
} catch (Exception e) {
return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
@ -336,7 +338,7 @@ public class SamlProtocol implements LoginProtocol {
return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
}
SAML2BindingBuilder2 bindingBuilder = new SAML2BindingBuilder2();
JaxrsSAML2BindingBuilder bindingBuilder = new JaxrsSAML2BindingBuilder();
bindingBuilder.relayState(relayState);
if (requiresRealmSignature(client)) {
@ -486,12 +488,14 @@ public class SamlProtocol implements LoginProtocol {
if (isLogoutPostBindingForClient(clientSession)) {
String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING);
SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client);
return logoutBuilder.postBinding().request(bindingUri);
JaxrsSAML2BindingBuilder binding = createBindingBuilder(client);
return binding.postBinding(logoutBuilder.buildDocument()).request(bindingUri);
} else {
logger.debug("frontchannel redirect binding");
String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_REDIRECT_BINDING);
SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client);
return logoutBuilder.redirectBinding().request(bindingUri);
JaxrsSAML2BindingBuilder binding = createBindingBuilder(client);
return binding.redirectBinding(logoutBuilder.buildDocument()).request(bindingUri);
}
} catch (ConfigurationException e) {
throw new RuntimeException(e);
@ -519,24 +523,25 @@ public class SamlProtocol implements LoginProtocol {
builder.logoutRequestID(userSession.getNote(SAML_LOGOUT_REQUEST_ID));
builder.destination(logoutBindingUri);
builder.issuer(getResponseIssuer(realm));
builder.relayState(logoutRelayState);
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder();
binding.relayState(logoutRelayState);
String signingAlgorithm = userSession.getNote(SAML_LOGOUT_SIGNATURE_ALGORITHM);
if (signingAlgorithm != null) {
SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(signingAlgorithm);
String canonicalization = userSession.getNote(SAML_LOGOUT_CANONICALIZATION);
if (canonicalization != null) {
builder.canonicalizationMethod(canonicalization);
binding.canonicalizationMethod(canonicalization);
}
builder.signatureAlgorithm(algorithm)
binding.signatureAlgorithm(algorithm)
.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signDocument();
}
try {
if (isLogoutPostBindingForInitiator(userSession)) {
return builder.postBinding().response(logoutBindingUri);
return binding.postBinding(builder.buildDocument()).response(logoutBindingUri);
} else {
return builder.redirectBinding().response(logoutBindingUri);
return binding.redirectBinding(builder.buildDocument()).response(logoutBindingUri);
}
} catch (ConfigurationException e) {
throw new RuntimeException(e);
@ -562,7 +567,8 @@ public class SamlProtocol implements LoginProtocol {
String logoutRequestString = null;
try {
logoutRequestString = logoutBuilder.postBinding().encoded();
JaxrsSAML2BindingBuilder binding = createBindingBuilder(client);
logoutRequestString = binding.postBinding(logoutBuilder.buildDocument()).encoded();
} catch (Exception e) {
logger.warn("failed to send saml logout", e);
return;
@ -612,24 +618,17 @@ public class SamlProtocol implements LoginProtocol {
.issuer(getResponseIssuer(realm))
.userPrincipal(clientSession.getNote(SAML_NAME_ID), clientSession.getNote(SAML_NAME_ID_FORMAT))
.destination(logoutUrl);
return logoutBuilder;
}
private JaxrsSAML2BindingBuilder createBindingBuilder(ClientModel client) {
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder();
if (requiresRealmSignature(client)) {
logoutBuilder.signatureAlgorithm(getSignatureAlgorithm(client))
binding.signatureAlgorithm(getSignatureAlgorithm(client))
.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signDocument();
}
/*
if (requiresEncryption(client)) {
PublicKey publicKey = null;
try {
publicKey = PemUtils.decodePublicKey(client.getAttribute(ClientModel.PUBLIC_KEY));
} catch (Exception e) {
logger.error("failed", e);
return;
}
logoutBuilder.encrypt(publicKey);
}
*/
return logoutBuilder;
return binding;
}
@Override

View file

@ -389,19 +389,20 @@ public class SamlService {
builder.logoutRequestID(logoutRequest.getID());
builder.destination(logoutBindingUri);
builder.issuer(RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString());
builder.relayState(logoutRelayState);
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(logoutRelayState);
if (SamlProtocol.requiresRealmSignature(client)) {
SignatureAlgorithm algorithm = SamlProtocol.getSignatureAlgorithm(client);
builder.signatureAlgorithm(algorithm)
binding.signatureAlgorithm(algorithm)
.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signDocument();
}
try {
if (SamlProtocol.SAML_POST_BINDING.equals(logoutBinding)) {
return builder.postBinding().response(logoutBindingUri);
return binding.postBinding(builder.buildDocument()).response(logoutBindingUri);
} else {
return builder.redirectBinding().response(logoutBindingUri);
return binding.redirectBinding(builder.buildDocument()).response(logoutBindingUri);
}
} catch (Exception e) {
throw new RuntimeException(e);