refactor adapters

This commit is contained in:
Bill Burke committed 2015-09-04 15:56:28 -04:00
1 parent f23f1cc813
commit 333ad0efac
92 files changed
+923 -761

No files matched your search

@@ -24,6 +24,7 @@ import org.keycloak.events.EventType;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
import org.keycloak.protocol.saml.SAML2LogoutResponseBuilder; import org.keycloak.protocol.saml.SAML2LogoutResponseBuilder;
import org.keycloak.protocol.saml.SAMLRequestParser; import org.keycloak.protocol.saml.SAMLRequestParser;
import org.keycloak.protocol.saml.SamlProtocol; import org.keycloak.protocol.saml.SamlProtocol;
@@ -247,16 +248,17 @@ public class SAMLEndpoint {
builder.logoutRequestID(request.getID()); builder.logoutRequestID(request.getID());
builder.destination(config.getSingleLogoutServiceUrl()); builder.destination(config.getSingleLogoutServiceUrl());
builder.issuer(issuerURL); builder.issuer(issuerURL);
builder.relayState(relayState); JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(relayState);
if (config.isWantAuthnRequestsSigned()) { if (config.isWantAuthnRequestsSigned()) {
builder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()) binding.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signDocument(); .signDocument();
} }
try { try {
if (config.isPostBindingResponse()) { if (config.isPostBindingResponse()) {
return builder.postBinding().response(); return binding.postBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl());
} else { } else {
return builder.redirectBinding().response(); return binding.redirectBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl());
} }
} catch (ConfigurationException e) { } catch (ConfigurationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@@ -33,6 +33,7 @@ import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.FederatedIdentityModel; import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder; import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder;
import org.keycloak.protocol.saml.SAML2LogoutRequestBuilder; import org.keycloak.protocol.saml.SAML2LogoutRequestBuilder;
import org.keycloak.protocol.saml.SAML2NameIDPolicyBuilder; import org.keycloak.protocol.saml.SAML2NameIDPolicyBuilder;
@@ -92,7 +93,8 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
.issuer(issuerURL) .issuer(issuerURL)
.forceAuthn(getConfig().isForceAuthn()) .forceAuthn(getConfig().isForceAuthn())
.protocolBinding(protocolBinding) .protocolBinding(protocolBinding)
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat)) .nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(request.getState()); .relayState(request.getState());
if (getConfig().isWantAuthnRequestsSigned()) { if (getConfig().isWantAuthnRequestsSigned()) {
@@ -109,14 +111,14 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
KeyPair keypair = new KeyPair(publicKey, privateKey); KeyPair keypair = new KeyPair(publicKey, privateKey);
authnRequestBuilder.signWith(keypair); binding.signWith(keypair);
authnRequestBuilder.signDocument(); binding.signDocument();
} }
if (getConfig().isPostBindingAuthnRequest()) { if (getConfig().isPostBindingAuthnRequest()) {
return authnRequestBuilder.postBinding().request(); return binding.postBinding(authnRequestBuilder.toDocument()).request(destinationUrl);
} else { } else {
return authnRequestBuilder.redirectBinding().request(); return binding.redirectBinding(authnRequestBuilder.toDocument()).request(destinationUrl);
} }
} catch (Exception e) { } catch (Exception e) {
throw new IdentityBrokerException("Could not create authentication request.", e); throw new IdentityBrokerException("Could not create authentication request.", e);
@@ -153,9 +155,10 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
String singleLogoutServiceUrl = getConfig().getSingleLogoutServiceUrl(); String singleLogoutServiceUrl = getConfig().getSingleLogoutServiceUrl();
if (singleLogoutServiceUrl == null || singleLogoutServiceUrl.trim().equals("") || !getConfig().isBackchannelSupported()) return; if (singleLogoutServiceUrl == null || singleLogoutServiceUrl.trim().equals("") || !getConfig().isBackchannelSupported()) return;
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl); SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
JaxrsSAML2BindingBuilder binding = buildLogoutBinding(userSession, realm);
try { try {
int status = SimpleHttp.doPost(singleLogoutServiceUrl) 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(); .param(GeneralConstants.RELAY_STATE, userSession.getId()).asStatus();
boolean success = status >=200 && status < 400; boolean success = status >=200 && status < 400;
if (!success) { if (!success) {
@@ -178,7 +181,8 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
} else { } else {
try { try {
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl); 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) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -192,13 +196,18 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
.issuer(getEntityId(uriInfo, realm)) .issuer(getEntityId(uriInfo, realm))
.sessionIndex(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SESSION_INDEX)) .sessionIndex(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SESSION_INDEX))
.userPrincipal(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT), userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT_NAMEFORMAT)) .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()); .relayState(userSession.getId());
if (getConfig().isWantAuthnRequestsSigned()) { if (getConfig().isWantAuthnRequestsSigned()) {
logoutBuilder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()) binding.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signDocument(); .signDocument();
} }
return logoutBuilder; return binding;
} }
@Override @Override
@@ -14,6 +14,7 @@
<include>org/bouncycastle/**</include> <include>org/bouncycastle/**</include>
<include>net/iharder/base64/**</include> <include>net/iharder/base64/**</include>
<include>org/keycloak/keycloak-core/**</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-adapter-core/**</include>
<include>org/keycloak/keycloak-jboss-adapter-core/**</include> <include>org/keycloak/keycloak-jboss-adapter-core/**</include>
<include>org/keycloak/keycloak-as7-adapter/**</include> <include>org/keycloak/keycloak-as7-adapter/**</include>
@@ -53,6 +53,11 @@
<!-- subsystems --> <!-- 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"> <module-def name="org.keycloak.keycloak-adapter-core">
<maven-resource group="org.keycloak" artifact="keycloak-adapter-core"/> <maven-resource group="org.keycloak" artifact="keycloak-adapter-core"/>
</module-def> </module-def>
@@ -21,6 +21,14 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
</dependency> </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> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId> <artifactId>keycloak-adapter-core</artifactId>
@@ -14,6 +14,7 @@
<module name="org.apache.httpcomponents"/> <module name="org.apache.httpcomponents"/>
<module name="org.jboss.logging"/> <module name="org.jboss.logging"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="net.iharder.base64"/> <module name="net.iharder.base64"/>
</dependencies> </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.security"/>
<module name="org.jboss.as.web"/> <module name="org.jboss.as.web"/>
<module name="org.picketbox"/> <module name="org.picketbox"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/> <module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
</dependencies> </dependencies>
@@ -10,6 +10,7 @@
<module name="javax.api"/> <module name="javax.api"/>
<module name="org.jboss.logging"/> <module name="org.jboss.logging"/>
<module name="org.picketbox"/> <module name="org.picketbox"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/> <module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
</dependencies> </dependencies>
@@ -10,6 +10,7 @@
<module name="org.jboss.logging"/> <module name="org.jboss.logging"/>
<module name="org.picketbox"/> <module name="org.picketbox"/>
<module name="org.apache.httpcomponents"/> <module name="org.apache.httpcomponents"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/> <module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
</dependencies> </dependencies>
@@ -14,6 +14,7 @@
<include>org/bouncycastle/**</include> <include>org/bouncycastle/**</include>
<include>net/iharder/base64/**</include> <include>net/iharder/base64/**</include>
<include>org/keycloak/keycloak-core/**</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-adapter-core/**</include>
<include>org/keycloak/keycloak-jboss-adapter-core/**</include> <include>org/keycloak/keycloak-jboss-adapter-core/**</include>
<include>org/keycloak/keycloak-as7-adapter/**</include> <include>org/keycloak/keycloak-as7-adapter/**</include>
@@ -49,6 +49,11 @@
<!-- subsystems --> <!-- 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"> <module-def name="org.keycloak.keycloak-adapter-core">
<maven-resource group="org.keycloak" artifact="keycloak-adapter-core"/> <maven-resource group="org.keycloak" artifact="keycloak-adapter-core"/>
</module-def> </module-def>
@@ -21,6 +21,14 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
</dependency> </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> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId> <artifactId>keycloak-adapter-core</artifactId>
@@ -13,6 +13,7 @@
<module name="org.codehaus.jackson.jackson-xc"/> <module name="org.codehaus.jackson.jackson-xc"/>
<module name="org.apache.httpcomponents" slot="4.3" /> <module name="org.apache.httpcomponents" slot="4.3" />
<module name="org.jboss.logging"/> <module name="org.jboss.logging"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
<module name="net.iharder.base64"/> <module name="net.iharder.base64"/>
</dependencies> </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="javax.api"/>
<module name="org.jboss.logging"/> <module name="org.jboss.logging"/>
<module name="org.picketbox"/> <module name="org.picketbox"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/> <module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
</dependencies> </dependencies>
@@ -10,6 +10,7 @@
<module name="org.jboss.logging"/> <module name="org.jboss.logging"/>
<module name="org.picketbox"/> <module name="org.picketbox"/>
<module name="org.apache.httpcomponents" slot="4.3"/> <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-adapter-core"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
</dependencies> </dependencies>
@@ -18,6 +18,7 @@
<module name="org.jboss.xnio"/> <module name="org.jboss.xnio"/>
<module name="io.undertow.core"/> <module name="io.undertow.core"/>
<module name="io.undertow.servlet"/> <module name="io.undertow.servlet"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/> <module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
</dependencies> </dependencies>
@@ -19,6 +19,7 @@
<module name="io.undertow.servlet"/> <module name="io.undertow.servlet"/>
<module name="org.picketbox"/> <module name="org.picketbox"/>
<module name="org.keycloak.keycloak-undertow-adapter"/> <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-adapter-core"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
</dependencies> </dependencies>
@@ -49,6 +49,11 @@
<!-- subsystems --> <!-- 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"> <module-def name="org.keycloak.keycloak-adapter-core">
<maven-resource group="org.keycloak" artifact="keycloak-adapter-core"/> <maven-resource group="org.keycloak" artifact="keycloak-adapter-core"/>
</module-def> </module-def>
@@ -21,6 +21,14 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
</dependency> </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> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId> <artifactId>keycloak-adapter-core</artifactId>
@@ -13,6 +13,7 @@
<module name="org.codehaus.jackson.jackson-xc"/> <module name="org.codehaus.jackson.jackson-xc"/>
<module name="org.apache.httpcomponents"/> <module name="org.apache.httpcomponents"/>
<module name="org.jboss.logging"/> <module name="org.jboss.logging"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
<module name="net.iharder.base64"/> <module name="net.iharder.base64"/>
</dependencies> </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="javax.api"/>
<module name="org.jboss.logging"/> <module name="org.jboss.logging"/>
<module name="org.picketbox"/> <module name="org.picketbox"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/> <module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
</dependencies> </dependencies>
@@ -10,6 +10,7 @@
<module name="org.jboss.logging"/> <module name="org.jboss.logging"/>
<module name="org.picketbox"/> <module name="org.picketbox"/>
<module name="org.apache.httpcomponents"/> <module name="org.apache.httpcomponents"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/> <module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
</dependencies> </dependencies>
@@ -18,6 +18,7 @@
<module name="org.jboss.xnio"/> <module name="org.jboss.xnio"/>
<module name="io.undertow.core"/> <module name="io.undertow.core"/>
<module name="io.undertow.servlet"/> <module name="io.undertow.servlet"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/> <module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
</dependencies> </dependencies>
@@ -19,6 +19,7 @@
<module name="io.undertow.servlet"/> <module name="io.undertow.servlet"/>
<module name="org.picketbox"/> <module name="org.picketbox"/>
<module name="org.keycloak.keycloak-undertow-adapter"/> <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-adapter-core"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
</dependencies> </dependencies>
+8
View File
@@ -44,6 +44,14 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-servlet-oauth-client</artifactId> <artifactId>keycloak-servlet-oauth-client</artifactId>
</dependency> </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> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId> <artifactId>keycloak-undertow-adapter</artifactId>
@@ -13,6 +13,7 @@
<module name="org.codehaus.jackson.jackson-xc"/> <module name="org.codehaus.jackson.jackson-xc"/>
<module name="org.apache.httpcomponents" /> <module name="org.apache.httpcomponents" />
<module name="org.jboss.logging"/> <module name="org.jboss.logging"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
<module name="net.iharder.base64"/> <module name="net.iharder.base64"/>
</dependencies> </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="javax.api"/>
<module name="org.jboss.logging"/> <module name="org.jboss.logging"/>
<module name="org.picketbox"/> <module name="org.picketbox"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/> <module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
</dependencies> </dependencies>
@@ -10,6 +10,7 @@
<module name="org.jboss.logging"/> <module name="org.jboss.logging"/>
<module name="org.picketbox"/> <module name="org.picketbox"/>
<module name="org.apache.httpcomponents"/> <module name="org.apache.httpcomponents"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/> <module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
</dependencies> </dependencies>
@@ -18,6 +18,7 @@
<module name="org.jboss.xnio"/> <module name="org.jboss.xnio"/>
<module name="io.undertow.core"/> <module name="io.undertow.core"/>
<module name="io.undertow.servlet"/> <module name="io.undertow.servlet"/>
<module name="org.keycloak.keycloak-adapter-spi"/>
<module name="org.keycloak.keycloak-adapter-core"/> <module name="org.keycloak.keycloak-adapter-core"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
</dependencies> </dependencies>
@@ -19,6 +19,7 @@
<module name="io.undertow.servlet"/> <module name="io.undertow.servlet"/>
<module name="org.picketbox"/> <module name="org.picketbox"/>
<module name="org.keycloak.keycloak-undertow-adapter"/> <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-adapter-core"/>
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
</dependencies> </dependencies>
+4
View File
@@ -39,6 +39,10 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId> <artifactId>keycloak-adapter-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<!-- Contains KeycloakPrincipal --> <!-- Contains KeycloakPrincipal -->
<dependency> <dependency>
@@ -17,14 +17,13 @@
package org.keycloak.example.multitenant.control; package org.keycloak.example.multitenant.control;
import java.io.InputStream; import java.io.InputStream;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakConfigResolver; import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder; 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>(); private final Map<String, KeycloakDeployment> cache = new ConcurrentHashMap<String, KeycloakDeployment>();
@Override @Override
public KeycloakDeployment resolve(HttpFacade.Request request) { public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
String path = request.getURI(); String path = request.getURI();
int multitenantIndex = path.indexOf("multitenant/"); int multitenantIndex = path.indexOf("multitenant/");
if (multitenantIndex == -1) { if (multitenantIndex == -1) {
+5
View File
@@ -38,6 +38,11 @@
<version>${jboss.logging.version}</version> <version>${jboss.logging.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
@@ -23,9 +23,9 @@ import java.util.Set;
public class AuthenticatedActionsHandler { public class AuthenticatedActionsHandler {
private static final Logger log = Logger.getLogger(AuthenticatedActionsHandler.class); private static final Logger log = Logger.getLogger(AuthenticatedActionsHandler.class);
protected KeycloakDeployment deployment; 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.deployment = deployment;
this.facade = facade; this.facade = facade;
} }
@@ -34,7 +34,7 @@ public class CookieTokenStore {
} }
public static KeycloakPrincipal<RefreshableKeycloakSecurityContext> getPrincipalFromCookie(KeycloakDeployment deployment, HttpFacade facade, AdapterTokenStore tokenStore) { 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) { if (cookie == null) {
log.debug("Not found adapter state cookie in current request"); log.debug("Not found adapter state cookie in current request");
return null; return null;
@@ -14,7 +14,6 @@ import org.keycloak.util.KeycloakUriBuilder;
import org.keycloak.util.UriUtils; import org.keycloak.util.UriUtils;
import java.io.IOException; import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@@ -93,12 +92,12 @@ public class OAuthRequestAuthenticator {
return facade.getRequest().isSecure(); return facade.getRequest().isSecure();
} }
protected HttpFacade.Cookie getCookie(String cookieName) { protected OIDCHttpFacade.Cookie getCookie(String cookieName) {
return facade.getRequest().getCookie(cookieName); return facade.getRequest().getCookie(cookieName);
} }
protected String getCookieValue(String cookieName) { protected String getCookieValue(String cookieName) {
HttpFacade.Cookie cookie = getCookie(cookieName); OIDCHttpFacade.Cookie cookie = getCookie(cookieName);
if (cookie == null) return null; if (cookie == null) return null;
return cookie.getValue(); return cookie.getValue();
} }
@@ -198,7 +197,7 @@ public class OAuthRequestAuthenticator {
} }
protected AuthChallenge checkStateCookie() { protected AuthChallenge checkStateCookie() {
HttpFacade.Cookie stateCookie = getCookie(deployment.getStateCookieName()); OIDCHttpFacade.Cookie stateCookie = getCookie(deployment.getStateCookieName());
if (stateCookie == null) { if (stateCookie == null) {
log.warn("No state cookie"); 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; package org.keycloak.adapters;
import org.keycloak.KeycloakSecurityContext;
import javax.security.cert.X509Certificate; import javax.security.cert.X509Certificate;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.List; import java.util.List;
/** /**
* Bridge between core adapter and HTTP Engine
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public interface HttpFacade { public interface HttpFacade {
Request getRequest();
public class Cookie { Response getResponse();
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) { X509Certificate[] getCertificateChain();
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;
}
}
interface Request { interface Request {
@@ -93,8 +58,39 @@ public interface HttpFacade {
void end(); void end();
} }
KeycloakSecurityContext getSecurityContext(); public class Cookie {
Request getRequest(); protected String name;
Response getResponse(); protected String value;
X509Certificate[] getCertificateChain(); 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;
}
}
} }
+4
View File
@@ -18,6 +18,10 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId> <artifactId>keycloak-adapter-core</artifactId>
+4
View File
@@ -18,6 +18,10 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId> <artifactId>keycloak-adapter-core</artifactId>
+4
View File
@@ -31,6 +31,10 @@
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId> <artifactId>keycloak-adapter-core</artifactId>
@@ -11,13 +11,13 @@ import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.SecurityContext;
import org.keycloak.KeycloakSecurityContext; import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.HttpFacade; import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.util.HostUtils; import org.keycloak.util.HostUtils;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @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 ContainerRequestContext requestContext;
protected final SecurityContext securityContext; protected final SecurityContext securityContext;
@@ -31,7 +31,7 @@ public class JaxrsHttpFacade implements HttpFacade {
this.securityContext = securityContext; this.securityContext = securityContext;
} }
protected class RequestFacade implements HttpFacade.Request { protected class RequestFacade implements OIDCHttpFacade.Request {
@Override @Override
public String getMethod() { 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); private javax.ws.rs.core.Response.ResponseBuilder responseBuilder = javax.ws.rs.core.Response.status(204);
+4
View File
@@ -24,6 +24,10 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId> <artifactId>keycloak-adapter-core</artifactId>
+115
View File
@@ -0,0 +1,115 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.5.0.Final-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-jetty-adapter-spi</artifactId>
<name>Keycloak Jetty Adapter SPI</name>
<properties>
<jetty9.version>8.1.16.v20140903</jetty9.version>
<keycloak.osgi.export>
org.keycloak.adapters.jetty.core.*
</keycloak.osgi.export>
<keycloak.osgi.import>
org.eclipse.jetty.*;version="[8.1,10)";resolution:=optional,
javax.servlet.*;version="[2.5,4)";resolution:=optional,
org.keycloak.*;version="${project.version}",
*;resolution:=optional
</keycloak.osgi.import>
</properties>
<description />
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>${jboss.logging.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty9.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${jetty9.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
<version>${jetty9.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-ClassPath>.</Bundle-ClassPath>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -1,6 +1,5 @@
package org.keycloak.adapters.jetty.core; package org.keycloak.adapters.jetty.core;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.HttpFacade; import org.keycloak.adapters.HttpFacade;
import org.keycloak.util.MultivaluedHashMap; import org.keycloak.util.MultivaluedHashMap;
import org.keycloak.util.UriUtils; import org.keycloak.util.UriUtils;
@@ -25,6 +24,30 @@ public class JettyHttpFacade implements HttpFacade {
protected ResponseFacade responseFacade = new ResponseFacade(); protected ResponseFacade responseFacade = new ResponseFacade();
protected MultivaluedHashMap<String, String> queryParameters; 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 { protected class RequestFacade implements Request {
@Override @Override
public String getURI() { public String getURI() {
@@ -159,33 +182,4 @@ public class JettyHttpFacade implements HttpFacade {
return ended; 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();
}
} }
+8
View File
@@ -35,6 +35,14 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
</dependency> </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> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId> <artifactId>keycloak-adapter-core</artifactId>
@@ -92,7 +92,7 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
AdapterDeploymentContext deploymentContext = (AdapterDeploymentContext) request.getAttribute(AdapterDeploymentContext.class.getName()); AdapterDeploymentContext deploymentContext = (AdapterDeploymentContext) request.getAttribute(AdapterDeploymentContext.class.getName());
KeycloakSecurityContext ksc = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName()); KeycloakSecurityContext ksc = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
if (ksc != null) { if (ksc != null) {
JettyHttpFacade facade = new JettyHttpFacade(request, null); JettyHttpFacade facade = new OIDCJettyHttpFacade(request, null);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade); KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (ksc instanceof RefreshableKeycloakSecurityContext) { if (ksc instanceof RefreshableKeycloakSecurityContext) {
((RefreshableKeycloakSecurityContext) ksc).logout(deployment); ((RefreshableKeycloakSecurityContext) ksc).logout(deployment);
@@ -229,7 +229,7 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
log.trace("*** authenticate"); log.trace("*** authenticate");
} }
Request request = resolveRequest(req); Request request = resolveRequest(req);
JettyHttpFacade facade = new JettyHttpFacade(request, (HttpServletResponse) res); OIDCJettyHttpFacade facade = new OIDCJettyHttpFacade(request, (HttpServletResponse) res);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade); KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment == null || !deployment.isConfigured()) { if (deployment == null || !deployment.isConfigured()) {
log.debug("*** deployment isn't configured return false"); 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());
}
}
+1
View File
@@ -14,6 +14,7 @@
<packaging>pom</packaging> <packaging>pom</packaging>
<modules> <modules>
<module>jetty-adapter-spi</module>
<module>jetty-core</module> <module>jetty-core</module>
<module>jetty8.1</module> <module>jetty8.1</module>
<module>jetty9.2</module> <module>jetty9.2</module>
+2
View File
@@ -14,6 +14,7 @@
<packaging>pom</packaging> <packaging>pom</packaging>
<modules> <modules>
<module>adapter-spi</module>
<module>adapter-core</module> <module>adapter-core</module>
<module>jaxrs-oauth-client</module> <module>jaxrs-oauth-client</module>
<module>servlet-oauth-client</module> <module>servlet-oauth-client</module>
@@ -21,6 +22,7 @@
<module>tomcat</module> <module>tomcat</module>
<module>as7-eap6</module> <module>as7-eap6</module>
<module>jetty</module> <module>jetty</module>
<module>undertow-adapter-spi</module>
<module>undertow</module> <module>undertow</module>
<module>wildfly</module> <module>wildfly</module>
<module>js</module> <module>js</module>
+4
View File
@@ -24,6 +24,10 @@
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId> <artifactId>keycloak-adapter-core</artifactId>
@@ -3,15 +3,13 @@ package org.keycloak.servlet;
import org.keycloak.KeycloakSecurityContext; import org.keycloak.KeycloakSecurityContext;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.AdapterDeploymentContext; import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.ServerRequest; import org.keycloak.adapters.ServerRequest;
import org.keycloak.enums.RelativeUrlsUsed;
import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.JWSInput;
import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken; import org.keycloak.representations.IDToken;
import org.keycloak.util.KeycloakUriBuilder; import org.keycloak.util.KeycloakUriBuilder;
import org.keycloak.util.UriUtils;
import javax.security.cert.X509Certificate; import javax.security.cert.X509Certificate;
import javax.servlet.http.Cookie; 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; private final HttpServletRequest servletRequest;
@@ -1,8 +1,8 @@
package org.keycloak.adapters.springboot; package org.keycloak.adapters.springboot;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder; import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.representations.adapters.config.AdapterConfig; import org.keycloak.representations.adapters.config.AdapterConfig;
public class KeycloakSpringBootConfigResolver implements org.keycloak.adapters.KeycloakConfigResolver { public class KeycloakSpringBootConfigResolver implements org.keycloak.adapters.KeycloakConfigResolver {
@@ -12,7 +12,7 @@ public class KeycloakSpringBootConfigResolver implements org.keycloak.adapters.K
private static AdapterConfig adapterConfig; private static AdapterConfig adapterConfig;
@Override @Override
public KeycloakDeployment resolve(HttpFacade.Request request) { public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
if (keycloakDeployment != null) { if (keycloakDeployment != null) {
return keycloakDeployment; return keycloakDeployment;
} }
+4
View File
@@ -26,6 +26,10 @@
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId> <artifactId>keycloak-adapter-core</artifactId>
@@ -1,9 +1,7 @@
package org.keycloak.adapters.springsecurity.facade; package org.keycloak.adapters.springsecurity.facade;
import org.keycloak.KeycloakSecurityContext; import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.HttpFacade; import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.springsecurity.facade.WrappedHttpServletRequest;
import org.keycloak.adapters.springsecurity.facade.WrappedHttpServletResponse;
import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@@ -13,12 +11,12 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; 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> * @author <a href="mailto:srossillo@smartling.com">Scott Rossillo</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class SimpleHttpFacade implements HttpFacade { public class SimpleHttpFacade implements OIDCHttpFacade {
private final HttpServletRequest request; private final HttpServletRequest request;
private final HttpServletResponse response; private final HttpServletResponse response;
@@ -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; package org.keycloak.adapters.springsecurity.facade;
+1
View File
@@ -14,6 +14,7 @@
<packaging>pom</packaging> <packaging>pom</packaging>
<modules> <modules>
<module>tomcat-adapter-spi</module>
<module>tomcat-core</module> <module>tomcat-core</module>
<module>tomcat6</module> <module>tomcat6</module>
<module>tomcat7</module> <module>tomcat7</module>
+69
View File
@@ -0,0 +1,69 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.5.0.Final-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-tomcat-adapter-spi</artifactId>
<name>Keycloak Tomcat Adapter SPI</name>
<properties>
<!-- <tomcat.version>8.0.14</tomcat.version> -->
<!-- <tomcat.version>7.0.52</tomcat.version> -->
<tomcat.version>6.0.41</tomcat.version>
</properties>
<description />
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>${jboss.logging.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<!--
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>${tomcat.version}</version>
<scope>compile</scope>
</dependency>
-->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>catalina</artifactId>
<version>${tomcat.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -1,6 +1,5 @@
package org.keycloak.adapters.tomcat; package org.keycloak.adapters.tomcat;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.HttpFacade; import org.keycloak.adapters.HttpFacade;
import org.keycloak.util.MultivaluedHashMap; import org.keycloak.util.MultivaluedHashMap;
import org.keycloak.util.ServerCookie; import org.keycloak.util.ServerCookie;
@@ -26,6 +25,30 @@ public class CatalinaHttpFacade implements HttpFacade {
protected ResponseFacade responseFacade = new ResponseFacade(); protected ResponseFacade responseFacade = new ResponseFacade();
protected MultivaluedHashMap<String, String> queryParameters; 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 { protected class RequestFacade implements Request {
@Override @Override
public String getURI() { public String getURI() {
@@ -157,33 +180,4 @@ public class CatalinaHttpFacade implements HttpFacade {
return ended; 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();
}
} }
+8
View File
@@ -28,6 +28,14 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
</dependency> </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> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId> <artifactId>keycloak-adapter-core</artifactId>
@@ -5,13 +5,9 @@ import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent; import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener; import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Manager; import org.apache.catalina.Manager;
import org.apache.catalina.authenticator.Constants;
import org.apache.catalina.authenticator.FormAuthenticator; import org.apache.catalina.authenticator.FormAuthenticator;
import org.apache.catalina.authenticator.SavedRequest;
import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response; 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.KeycloakSecurityContext;
import org.keycloak.constants.AdapterConstants; import org.keycloak.constants.AdapterConstants;
import org.keycloak.adapters.AdapterDeploymentContext; import org.keycloak.adapters.AdapterDeploymentContext;
@@ -28,15 +24,12 @@ import org.keycloak.enums.TokenStore;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Enumeration;
import java.util.Locale;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.keycloak.adapters.KeycloakConfigResolver; import org.keycloak.adapters.KeycloakConfigResolver;
@@ -71,7 +64,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
protected void logoutInternal(Request request) { protected void logoutInternal(Request request) {
KeycloakSecurityContext ksc = (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName()); KeycloakSecurityContext ksc = (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
if (ksc != null) { if (ksc != null) {
CatalinaHttpFacade facade = new CatalinaHttpFacade(request, null); CatalinaHttpFacade facade = new OIDCCatalinaHttpFacade(request, null);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade); KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (ksc instanceof RefreshableKeycloakSecurityContext) { if (ksc instanceof RefreshableKeycloakSecurityContext) {
((RefreshableKeycloakSecurityContext) ksc).logout(deployment); ((RefreshableKeycloakSecurityContext) ksc).logout(deployment);
@@ -164,7 +157,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
@Override @Override
public void invoke(Request request, Response response) throws IOException, ServletException { public void invoke(Request request, Response response) throws IOException, ServletException {
try { try {
CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response); CatalinaHttpFacade facade = new OIDCCatalinaHttpFacade(request, response);
Manager sessionManager = request.getContext().getManager(); Manager sessionManager = request.getContext().getManager();
CatalinaUserSessionManagementWrapper sessionManagementWrapper = new CatalinaUserSessionManagementWrapper(userSessionManagement, sessionManager); CatalinaUserSessionManagementWrapper sessionManagementWrapper = new CatalinaUserSessionManagementWrapper(userSessionManagement, sessionManager);
PreAuthActionsHandler handler = new PreAuthActionsHandler(sessionManagementWrapper, deploymentContext, facade); 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 abstract boolean forwardToErrorPageInternal(Request request, HttpServletResponse response, Object loginConfig) throws IOException;
protected boolean authenticateInternal(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); KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment == null || !deployment.isConfigured()) { if (deployment == null || !deployment.isConfigured()) {
return false; return false;
@@ -39,10 +39,10 @@ public class AuthenticatedActionsValve extends ValveBase {
@Override @Override
public void invoke(Request request, Response response) throws IOException, ServletException { public void invoke(Request request, Response response) throws IOException, ServletException {
log.debugv("AuthenticatedActionsValve.invoke {0}", request.getRequestURI()); log.debugv("AuthenticatedActionsValve.invoke {0}", request.getRequestURI());
CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response); CatalinaHttpFacade facade = new OIDCCatalinaHttpFacade(request, response);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade); KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment != null && deployment.isConfigured()) { 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()) { if (handler.handledRequest()) {
return; 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
View File
@@ -0,0 +1,65 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.5.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-undertow-adapter-spi</artifactId>
<name>Keycloak Undertow Integration</name>
<description/>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>${jboss.logging.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -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; package org.keycloak.adapters.undertow;
import io.undertow.server.HttpServerExchange; import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.CookieImpl; import io.undertow.server.handlers.CookieImpl;
import io.undertow.util.AttachmentKey;
import io.undertow.util.Headers; import io.undertow.util.Headers;
import io.undertow.util.HttpString; import io.undertow.util.HttpString;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.HttpFacade; import org.keycloak.adapters.HttpFacade;
import org.keycloak.util.KeycloakUriBuilder; import org.keycloak.util.KeycloakUriBuilder;
@@ -39,13 +21,36 @@ import java.util.Map;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class UndertowHttpFacade implements HttpFacade { public abstract class UndertowHttpFacade implements HttpFacade {
public static final AttachmentKey<KeycloakSecurityContext> KEYCLOAK_SECURITY_CONTEXT_KEY = AttachmentKey.create(KeycloakSecurityContext.class);
protected HttpServerExchange exchange; protected HttpServerExchange exchange;
protected RequestFacade requestFacade = new RequestFacade(); protected RequestFacade requestFacade = new RequestFacade();
protected ResponseFacade responseFacade = new ResponseFacade(); 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 { protected class RequestFacade implements Request {
@Override @Override
public String getURI() { public String getURI() {
@@ -178,34 +183,4 @@ public class UndertowHttpFacade implements HttpFacade {
exchange.endExchange(); 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;
}
} }
+8
View File
@@ -24,6 +24,14 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
</dependency> </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> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId> <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.SecurityContext;
import io.undertow.security.api.SecurityNotification; import io.undertow.security.api.SecurityNotification;
import io.undertow.server.HttpServerExchange; import io.undertow.server.HttpServerExchange;
import io.undertow.server.session.Session;
import io.undertow.util.AttachmentKey; import io.undertow.util.AttachmentKey;
import io.undertow.util.Headers; import io.undertow.util.Headers;
import io.undertow.util.Sessions;
import io.undertow.util.StatusCodes; import io.undertow.util.StatusCodes;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext; import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterDeploymentContext; import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AdapterTokenStore; import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.AuthChallenge; import org.keycloak.adapters.AuthChallenge;
import org.keycloak.adapters.AuthOutcome; import org.keycloak.adapters.AuthOutcome;
import org.keycloak.adapters.CookieTokenStore;
import org.keycloak.adapters.HttpFacade; import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext; import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
@@ -64,7 +60,7 @@ public abstract class AbstractUndertowKeycloakAuthMech implements Authentication
Integer code = servePage(exchange, errorPage); Integer code = servePage(exchange, errorPage);
return new ChallengeResult(true, code); return new ChallengeResult(true, code);
} }
UndertowHttpFacade facade = new UndertowHttpFacade(exchange); UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
if (challenge.challenge(facade)) { if (challenge.challenge(facade)) {
return new ChallengeResult(true, exchange.getResponseCode()); return new ChallengeResult(true, exchange.getResponseCode());
} }
@@ -93,9 +89,9 @@ public abstract class AbstractUndertowKeycloakAuthMech implements Authentication
if (notification.getEventType() != SecurityNotification.EventType.LOGGED_OUT) return; if (notification.getEventType() != SecurityNotification.EventType.LOGGED_OUT) return;
HttpServerExchange exchange = notification.getExchange(); HttpServerExchange exchange = notification.getExchange();
UndertowHttpFacade facade = new UndertowHttpFacade(exchange); UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade); 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) { if (ksc != null && ksc instanceof RefreshableKeycloakSecurityContext) {
((RefreshableKeycloakSecurityContext) ksc).logout(deployment); ((RefreshableKeycloakSecurityContext) ksc).logout(deployment);
} }
@@ -48,7 +48,7 @@ public abstract class AbstractUndertowRequestAuthenticator extends RequestAuthen
} }
protected void propagateKeycloakContext(KeycloakUndertowAccount account) { protected void propagateKeycloakContext(KeycloakUndertowAccount account) {
exchange.putAttachment(UndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY, account.getKeycloakSecurityContext()); exchange.putAttachment(OIDCUndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY, account.getKeycloakSecurityContext());
} }
@Override @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; package org.keycloak.adapters.undertow;
import io.undertow.security.api.AuthenticationMechanism;
import io.undertow.security.api.SecurityContext; import io.undertow.security.api.SecurityContext;
import io.undertow.server.HttpServerExchange; import io.undertow.server.HttpServerExchange;
import io.undertow.servlet.api.ConfidentialPortManager; import io.undertow.servlet.api.ConfidentialPortManager;
@@ -25,7 +24,6 @@ import io.undertow.util.Headers;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.adapters.AdapterDeploymentContext; import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AdapterTokenStore; import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.AuthChallenge;
import org.keycloak.adapters.HttpFacade; import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.NodesRegistrationManagement; import org.keycloak.adapters.NodesRegistrationManagement;
@@ -81,7 +79,7 @@ public class ServletKeycloakAuthMech extends AbstractUndertowKeycloakAuthMech {
@Override @Override
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) { public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
UndertowHttpFacade facade = new UndertowHttpFacade(exchange); UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade); KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (!deployment.isConfigured()) { if (!deployment.isConfigured()) {
return AuthenticationMechanismOutcome.NOT_ATTEMPTED; return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
@@ -61,7 +61,7 @@ public class ServletPreAuthActionsHandler implements HttpHandler {
@Override @Override
public void handleRequest(HttpServerExchange exchange) throws Exception { 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); final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
SessionManagementBridge bridge = new SessionManagementBridge(userSessionManagement, servletRequestContext.getDeployment().getSessionManager()); SessionManagementBridge bridge = new SessionManagementBridge(userSessionManagement, servletRequestContext.getDeployment().getSessionManager());
PreAuthActionsHandler handler = new PreAuthActionsHandler(bridge, deploymentContext, facade); PreAuthActionsHandler handler = new PreAuthActionsHandler(bridge, deploymentContext, facade);
@@ -57,7 +57,7 @@ public class UndertowAuthenticatedActionsHandler implements HttpHandler {
@Override @Override
public void handleRequest(HttpServerExchange exchange) throws Exception { public void handleRequest(HttpServerExchange exchange) throws Exception {
UndertowHttpFacade facade = new UndertowHttpFacade(exchange); OIDCUndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade); KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment != null && deployment.isConfigured()) { if (deployment != null && deployment.isConfigured()) {
AuthenticatedActionsHandler handler = new AuthenticatedActionsHandler(deployment, facade); AuthenticatedActionsHandler handler = new AuthenticatedActionsHandler(deployment, facade);
@@ -25,7 +25,7 @@ public class UndertowAuthenticationMechanism extends AbstractUndertowKeycloakAut
@Override @Override
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) { public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
UndertowHttpFacade facade = new UndertowHttpFacade(exchange); UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade); KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (!deployment.isConfigured()) { if (!deployment.isConfigured()) {
return AuthenticationMechanismOutcome.NOT_ATTEMPTED; return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
@@ -47,7 +47,7 @@ public class UndertowPreAuthActionsHandler implements HttpHandler {
@Override @Override
public void handleRequest(HttpServerExchange exchange) throws Exception { public void handleRequest(HttpServerExchange exchange) throws Exception {
UndertowHttpFacade facade = new UndertowHttpFacade(exchange); UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
SessionManagementBridge bridge = new SessionManagementBridge(userSessionManagement, sessionManager); SessionManagementBridge bridge = new SessionManagementBridge(userSessionManagement, sessionManager);
PreAuthActionsHandler handler = new PreAuthActionsHandler(bridge, deploymentContext, facade); PreAuthActionsHandler handler = new PreAuthActionsHandler(bridge, deploymentContext, facade);
if (handler.handleRequest()) return; if (handler.handleRequest()) return;
@@ -24,10 +24,18 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId> <artifactId>keycloak-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId> <artifactId>keycloak-adapter-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter-spi</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId> <artifactId>keycloak-undertow-adapter</artifactId>
+20
View File
@@ -765,6 +765,11 @@
<artifactId>keycloak-email-freemarker</artifactId> <artifactId>keycloak-email-freemarker</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId> <artifactId>keycloak-adapter-core</artifactId>
@@ -795,6 +800,11 @@
<artifactId>keycloak-jboss-adapter-core</artifactId> <artifactId>keycloak-jboss-adapter-core</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jetty-adapter-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-jetty-core</artifactId> <artifactId>keycloak-jetty-core</artifactId>
@@ -865,6 +875,11 @@
<artifactId>keycloak-spring-boot-adapter</artifactId> <artifactId>keycloak-spring-boot-adapter</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-tomcat-adapter-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-tomcat-core-adapter</artifactId> <artifactId>keycloak-tomcat-core-adapter</artifactId>
@@ -885,6 +900,11 @@
<artifactId>keycloak-tomcat8-adapter</artifactId> <artifactId>keycloak-tomcat8-adapter</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId> <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.core.util.XMLEncryptionUtil;
import org.keycloak.saml.processing.web.util.PostBindingUtil; import org.keycloak.saml.processing.web.util.PostBindingUtil;
import org.keycloak.saml.processing.web.util.RedirectBindingUtil; import org.keycloak.saml.processing.web.util.RedirectBindingUtil;
import org.keycloak.util.KeycloakUriBuilder;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec; 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.crypto.dsig.CanonicalizationMethod;
import javax.xml.namespace.QName; import javax.xml.namespace.QName;
import java.io.IOException; import java.io.IOException;
@@ -31,15 +28,15 @@ import java.security.PublicKey;
import java.security.Signature; import java.security.Signature;
import java.security.cert.X509Certificate; 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.saml.common.util.StringUtil.isNotNull;
import static org.keycloak.util.HtmlUtils.escapeAttribute;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class SAML2BindingBuilder2<T extends SAML2BindingBuilder2> { public class BaseSAML2BindingBuilder<T extends BaseSAML2BindingBuilder> {
protected static final Logger logger = Logger.getLogger(SAML2BindingBuilder2.class); protected static final Logger logger = Logger.getLogger(BaseSAML2BindingBuilder.class);
protected KeyPair signingKeyPair; protected KeyPair signingKeyPair;
protected X509Certificate signingCertificate; protected X509Certificate signingCertificate;
@@ -116,17 +113,19 @@ public class SAML2BindingBuilder2<T extends SAML2BindingBuilder2> {
return (T)this; return (T)this;
} }
public class PostBindingBuilder { public static class BasePostBindingBuilder {
protected Document document; protected Document document;
protected BaseSAML2BindingBuilder builder;
public PostBindingBuilder(Document document) throws ProcessingException { public BasePostBindingBuilder(BaseSAML2BindingBuilder builder, Document document) throws ProcessingException {
if (encrypt) encryptDocument(document); this.builder = builder;
if (builder.encrypt) builder.encryptDocument(document);
this.document = document; this.document = document;
if (signAssertions) { if (builder.signAssertions) {
signAssertion(document); builder.signAssertion(document);
} }
if (sign) { if (builder.sign) {
signDocument(document); builder.signDocument(document);
} }
} }
@@ -137,24 +136,28 @@ public class SAML2BindingBuilder2<T extends SAML2BindingBuilder2> {
public Document getDocument() { public Document getDocument() {
return document; 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 Document document;
protected BaseSAML2BindingBuilder builder;
public RedirectBindingBuilder(Document document) throws ProcessingException { public BaseRedirectBindingBuilder(BaseSAML2BindingBuilder builder, Document document) throws ProcessingException {
if (encrypt) encryptDocument(document); this.builder = builder;
if (builder.encrypt) builder.encryptDocument(document);
this.document = document; this.document = document;
if (signAssertions) { if (builder.signAssertions) {
signAssertion(document); builder.signAssertion(document);
} }
} }
@@ -168,26 +171,8 @@ public class SAML2BindingBuilder2<T extends SAML2BindingBuilder2> {
samlParameterName = GeneralConstants.SAML_REQUEST_KEY; 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 { 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"); byte[] responseBytes = org.keycloak.saml.common.util.DocumentUtil.getDocumentAsString(responseDoc).getBytes("UTF-8");
String samlResponse = PostBindingUtil.base64Encode(new String(responseBytes)); String samlResponse = PostBindingUtil.base64Encode(new String(responseBytes));
@@ -301,7 +276,7 @@ public class SAML2BindingBuilder2<T extends SAML2BindingBuilder2> {
builder.append("<HTML>"); builder.append("<HTML>");
builder.append("<HEAD>"); 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("</HEAD>");
builder.append("<BODY Onload=\"document.forms[0].submit()\">"); 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 { 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) .replaceQuery(null)
.queryParam(samlParameterName, base64Encoded(document)); .queryParam(samlParameterName, base64Encoded(document));
if (relayState != null) { if (relayState != null) {
@@ -358,13 +333,4 @@ public class SAML2BindingBuilder2<T extends SAML2BindingBuilder2> {
return builder.build(); 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 * @author pedroigor
*/ */
public class SAML2AuthnRequestBuilder extends SAML2BindingBuilder<SAML2AuthnRequestBuilder> { public class SAML2AuthnRequestBuilder {
private final AuthnRequestType authnRequestType; 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() { public SAML2AuthnRequestBuilder() {
try { try {
@@ -62,23 +74,7 @@ public class SAML2AuthnRequestBuilder extends SAML2BindingBuilder<SAML2AuthnRequ
return this; return this;
} }
public RedirectBindingBuilder redirectBinding() { public Document toDocument() {
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() {
try { try {
AuthnRequestType authnRequestType = this.authnRequestType; 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> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class SAML2ErrorResponseBuilder extends SAML2BindingBuilder<SAML2ErrorResponseBuilder> { public class SAML2ErrorResponseBuilder {
protected String status; protected String status;
protected String destination;
protected String issuer;
public SAML2ErrorResponseBuilder status(String status) { public SAML2ErrorResponseBuilder status(String status) {
this.status = status; this.status = status;
return this; return this;
} }
public RedirectBindingBuilder redirectBinding() throws ConfigurationException, ProcessingException { public SAML2ErrorResponseBuilder destination(String destination) {
Document samlResponseDocument = buildDocument(); this.destination = destination;
return new RedirectBindingBuilder(samlResponseDocument); return this;
} }
public PostBindingBuilder postBinding() throws ConfigurationException, ProcessingException { public SAML2ErrorResponseBuilder issuer(String issuer) {
Document samlResponseDocument = buildDocument(); this.issuer = issuer;
return new PostBindingBuilder(samlResponseDocument); return this;
} }
@@ -61,7 +61,6 @@ public class SAML2ErrorResponseBuilder extends SAML2BindingBuilder<SAML2ErrorRes
responseType.setStatus(JBossSAMLAuthnResponseFactory.createStatusTypeForResponder(status)); responseType.setStatus(JBossSAMLAuthnResponseFactory.createStatusTypeForResponder(status));
responseType.setDestination(destination); responseType.setDestination(destination);
if (encrypt) encryptDocument(samlResponse);
return samlResponse; return samlResponse;
} }
@@ -15,11 +15,23 @@ import java.net.URI;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class SAML2LogoutRequestBuilder extends SAML2BindingBuilder<SAML2LogoutRequestBuilder> { public class SAML2LogoutRequestBuilder {
protected String userPrincipal; protected String userPrincipal;
protected String userPrincipalFormat; protected String userPrincipalFormat;
protected String sessionIndex; protected String sessionIndex;
protected long assertionExpiration; 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 * Length of time in seconds the assertion is valid for
@@ -45,20 +57,8 @@ public class SAML2LogoutRequestBuilder extends SAML2BindingBuilder<SAML2LogoutRe
return this; 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 { public Document buildDocument() throws ProcessingException, ConfigurationException, ParsingException {
Document document = new SAML2Request().convert(createLogoutRequest()); Document document = new SAML2Request().convert(createLogoutRequest());
if (encrypt) encryptDocument(document);
return document; return document;
} }
@@ -19,25 +19,25 @@ import java.net.URI;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class SAML2LogoutResponseBuilder extends SAML2BindingBuilder<SAML2LogoutResponseBuilder> { public class SAML2LogoutResponseBuilder {
protected String logoutRequestID; protected String logoutRequestID;
protected String destination;
protected String issuer;
public SAML2LogoutResponseBuilder logoutRequestID(String logoutRequestID) { public SAML2LogoutResponseBuilder logoutRequestID(String logoutRequestID) {
this.logoutRequestID = logoutRequestID; this.logoutRequestID = logoutRequestID;
return this; return this;
} }
public RedirectBindingBuilder redirectBinding() throws ConfigurationException, ProcessingException { public SAML2LogoutResponseBuilder destination(String destination) {
Document samlResponseDocument = buildDocument(); this.destination = destination;
return new RedirectBindingBuilder(samlResponseDocument); return this;
} }
public PostBindingBuilder postBinding() throws ConfigurationException, ProcessingException { public SAML2LogoutResponseBuilder issuer(String issuer) {
Document samlResponseDocument = buildDocument(); this.issuer = issuer;
return new PostBindingBuilder(samlResponseDocument); return this;
} }
@@ -67,7 +67,6 @@ public class SAML2LogoutResponseBuilder extends SAML2BindingBuilder<SAML2LogoutR
} catch (ParsingException e) { } catch (ParsingException e) {
throw new ProcessingException(e); throw new ProcessingException(e);
} }
if (encrypt) encryptDocument(samlResponse);
return samlResponse; return samlResponse;
} }
@@ -166,15 +166,17 @@ public class SamlProtocol implements LoginProtocol {
protected Response getErrorResponse(ClientSessionModel clientSession, String status) { protected Response getErrorResponse(ClientSessionModel clientSession, String status) {
SAML2ErrorResponseBuilder builder = new SAML2ErrorResponseBuilder() SAML2ErrorResponseBuilder builder = new SAML2ErrorResponseBuilder()
.relayState(clientSession.getNote(GeneralConstants.RELAY_STATE))
.destination(clientSession.getRedirectUri()) .destination(clientSession.getRedirectUri())
.issuer(getResponseIssuer(realm)) .issuer(getResponseIssuer(realm))
.status(status); .status(status);
try { try {
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(clientSession.getNote(GeneralConstants.RELAY_STATE));
Document document = builder.buildDocument();
if (isPostBinding(clientSession)) { if (isPostBinding(clientSession)) {
return builder.postBinding().response(); return binding.postBinding(document).response(clientSession.getRedirectUri());
} else { } else {
return builder.redirectBinding().response(); return binding.redirectBinding(document).response(clientSession.getRedirectUri());
} }
} catch (Exception e) { } catch (Exception e) {
return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE); 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); return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
} }
SAML2BindingBuilder2 bindingBuilder = new SAML2BindingBuilder2(); JaxrsSAML2BindingBuilder bindingBuilder = new JaxrsSAML2BindingBuilder();
bindingBuilder.relayState(relayState); bindingBuilder.relayState(relayState);
if (requiresRealmSignature(client)) { if (requiresRealmSignature(client)) {
@@ -486,12 +488,14 @@ public class SamlProtocol implements LoginProtocol {
if (isLogoutPostBindingForClient(clientSession)) { if (isLogoutPostBindingForClient(clientSession)) {
String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING); String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING);
SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client); SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client);
return logoutBuilder.postBinding().request(bindingUri); JaxrsSAML2BindingBuilder binding = createBindingBuilder(client);
return binding.postBinding(logoutBuilder.buildDocument()).request(bindingUri);
} else { } else {
logger.debug("frontchannel redirect binding"); logger.debug("frontchannel redirect binding");
String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_REDIRECT_BINDING); String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_REDIRECT_BINDING);
SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client); 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) { } catch (ConfigurationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@@ -519,24 +523,25 @@ public class SamlProtocol implements LoginProtocol {
builder.logoutRequestID(userSession.getNote(SAML_LOGOUT_REQUEST_ID)); builder.logoutRequestID(userSession.getNote(SAML_LOGOUT_REQUEST_ID));
builder.destination(logoutBindingUri); builder.destination(logoutBindingUri);
builder.issuer(getResponseIssuer(realm)); builder.issuer(getResponseIssuer(realm));
builder.relayState(logoutRelayState); JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder();
binding.relayState(logoutRelayState);
String signingAlgorithm = userSession.getNote(SAML_LOGOUT_SIGNATURE_ALGORITHM); String signingAlgorithm = userSession.getNote(SAML_LOGOUT_SIGNATURE_ALGORITHM);
if (signingAlgorithm != null) { if (signingAlgorithm != null) {
SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(signingAlgorithm); SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(signingAlgorithm);
String canonicalization = userSession.getNote(SAML_LOGOUT_CANONICALIZATION); String canonicalization = userSession.getNote(SAML_LOGOUT_CANONICALIZATION);
if (canonicalization != null) { if (canonicalization != null) {
builder.canonicalizationMethod(canonicalization); binding.canonicalizationMethod(canonicalization);
} }
builder.signatureAlgorithm(algorithm) binding.signatureAlgorithm(algorithm)
.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()) .signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signDocument(); .signDocument();
} }
try { try {
if (isLogoutPostBindingForInitiator(userSession)) { if (isLogoutPostBindingForInitiator(userSession)) {
return builder.postBinding().response(logoutBindingUri); return binding.postBinding(builder.buildDocument()).response(logoutBindingUri);
} else { } else {
return builder.redirectBinding().response(logoutBindingUri); return binding.redirectBinding(builder.buildDocument()).response(logoutBindingUri);
} }
} catch (ConfigurationException e) { } catch (ConfigurationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@@ -562,7 +567,8 @@ public class SamlProtocol implements LoginProtocol {
String logoutRequestString = null; String logoutRequestString = null;
try { try {
logoutRequestString = logoutBuilder.postBinding().encoded(); JaxrsSAML2BindingBuilder binding = createBindingBuilder(client);
logoutRequestString = binding.postBinding(logoutBuilder.buildDocument()).encoded();
} catch (Exception e) { } catch (Exception e) {
logger.warn("failed to send saml logout", e); logger.warn("failed to send saml logout", e);
return; return;
@@ -612,26 +618,19 @@ public class SamlProtocol implements LoginProtocol {
.issuer(getResponseIssuer(realm)) .issuer(getResponseIssuer(realm))
.userPrincipal(clientSession.getNote(SAML_NAME_ID), clientSession.getNote(SAML_NAME_ID_FORMAT)) .userPrincipal(clientSession.getNote(SAML_NAME_ID), clientSession.getNote(SAML_NAME_ID_FORMAT))
.destination(logoutUrl); .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; 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 @Override
public void close() { public void close() {
@@ -389,19 +389,20 @@ public class SamlService {
builder.logoutRequestID(logoutRequest.getID()); builder.logoutRequestID(logoutRequest.getID());
builder.destination(logoutBindingUri); builder.destination(logoutBindingUri);
builder.issuer(RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString()); builder.issuer(RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString());
builder.relayState(logoutRelayState); JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(logoutRelayState);
if (SamlProtocol.requiresRealmSignature(client)) { if (SamlProtocol.requiresRealmSignature(client)) {
SignatureAlgorithm algorithm = SamlProtocol.getSignatureAlgorithm(client); SignatureAlgorithm algorithm = SamlProtocol.getSignatureAlgorithm(client);
builder.signatureAlgorithm(algorithm) binding.signatureAlgorithm(algorithm)
.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()) .signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signDocument(); .signDocument();
} }
try { try {
if (SamlProtocol.SAML_POST_BINDING.equals(logoutBinding)) { if (SamlProtocol.SAML_POST_BINDING.equals(logoutBinding)) {
return builder.postBinding().response(logoutBindingUri); return binding.postBinding(builder.buildDocument()).response(logoutBindingUri);
} else { } else {
return builder.redirectBinding().response(logoutBindingUri); return binding.redirectBinding(builder.buildDocument()).response(logoutBindingUri);
} }
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);