refactor adapters
This commit is contained in:
parent
f23f1cc813
commit
333ad0efac
92 changed files with 923 additions and 761 deletions
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
8
distribution/feature-packs/adapter-feature-pack/pom.xml
Normal file → Executable file
8
distribution/feature-packs/adapter-feature-pack/pom.xml
Normal file → Executable 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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
5
examples/multi-tenant/src/main/java/org/keycloak/example/multitenant/control/PathBasedKeycloakConfigResolver.java
Normal file → Executable file
5
examples/multi-tenant/src/main/java/org/keycloak/example/multitenant/control/PathBasedKeycloakConfigResolver.java
Normal file → Executable 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) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
8
integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsHttpFacade.java
Normal file → Executable file
8
integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsHttpFacade.java
Normal file → Executable 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);
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
115
integration/jetty/jetty-adapter-spi/pom.xml
Executable file
115
integration/jetty/jetty-adapter-spi/pom.xml
Executable 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>
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
4
integration/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootConfigResolver.java
Normal file → Executable file
4
integration/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootConfigResolver.java
Normal file → Executable 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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
8
integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/SimpleHttpFacade.java
Normal file → Executable file
8
integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/SimpleHttpFacade.java
Normal file → Executable 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;
|
||||
|
|
2
integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/package-info.java
Normal file → Executable file
2
integration/spring-security/src/main/java/org/keycloak/adapters/springsecurity/facade/package-info.java
Normal file → Executable 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;
|
|
@ -14,6 +14,7 @@
|
|||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>tomcat-adapter-spi</module>
|
||||
<module>tomcat-core</module>
|
||||
<module>tomcat6</module>
|
||||
<module>tomcat7</module>
|
||||
|
|
69
integration/tomcat/tomcat-adapter-spi/pom.xml
Executable file
69
integration/tomcat/tomcat-adapter-spi/pom.xml
Executable 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>
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
65
integration/undertow-adapter-spi/pom.xml
Executable file
65
integration/undertow-adapter-spi/pom.xml
Executable 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>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
20
pom.xml
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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,26 +618,19 @@ public class SamlProtocol implements LoginProtocol {
|
|||
.issuer(getResponseIssuer(realm))
|
||||
.userPrincipal(clientSession.getNote(SAML_NAME_ID), clientSession.getNote(SAML_NAME_ID_FORMAT))
|
||||
.destination(logoutUrl);
|
||||
if (requiresRealmSignature(client)) {
|
||||
logoutBuilder.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;
|
||||
}
|
||||
|
||||
private JaxrsSAML2BindingBuilder createBindingBuilder(ClientModel client) {
|
||||
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder();
|
||||
if (requiresRealmSignature(client)) {
|
||||
binding.signatureAlgorithm(getSignatureAlgorithm(client))
|
||||
.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
|
||||
.signDocument();
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue