Merge pull request #1641 from patriot1burke/master

initial saml sp adapter
This commit is contained in:
Bill Burke 2015-09-24 14:23:05 -04:00
commit c1cb50b39f
192 changed files with 6402 additions and 959 deletions

View file

@ -10,7 +10,6 @@ import org.keycloak.dom.saml.v2.assertion.AssertionType;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.dom.saml.v2.assertion.AttributeType;
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
import org.keycloak.dom.saml.v2.assertion.EncryptedAssertionType;
import org.keycloak.dom.saml.v2.assertion.NameIDType;
import org.keycloak.dom.saml.v2.assertion.SubjectType;
import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
@ -24,31 +23,23 @@ import org.keycloak.events.EventType;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.saml.SAML2LogoutResponseBuilder;
import org.keycloak.protocol.saml.SAMLRequestParser;
import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
import org.keycloak.saml.SAML2LogoutResponseBuilder;
import org.keycloak.saml.SAMLRequestParser;
import org.keycloak.protocol.saml.SamlProtocol;
import org.keycloak.protocol.saml.SamlProtocolUtils;
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.common.util.DocumentUtil;
import org.keycloak.saml.common.util.StaxParserUtil;
import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response;
import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
import org.keycloak.saml.processing.core.util.JAXPValidationUtil;
import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;
import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
import org.keycloak.saml.processing.web.util.PostBindingUtil;
import org.keycloak.services.ErrorPage;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.messages.Messages;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
@ -61,9 +52,7 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.xml.namespace.QName;
import java.io.IOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.List;
@ -155,7 +144,7 @@ public class SAMLEndpoint {
}
protected abstract String getBindingType();
protected abstract void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException;
protected abstract void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException;
protected abstract SAMLDocumentHolder extractRequestDocument(String samlRequest);
protected abstract SAMLDocumentHolder extractResponseDocument(String response);
protected PublicKey getIDPKey() {
@ -188,7 +177,7 @@ public class SAMLEndpoint {
}
if (config.isValidateSignature()) {
try {
verifySignature(holder);
verifySignature(GeneralConstants.SAML_REQUEST_KEY, holder);
} catch (VerificationException e) {
logger.error("validation failed", e);
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
@ -247,17 +236,18 @@ public class SAMLEndpoint {
builder.logoutRequestID(request.getID());
builder.destination(config.getSingleLogoutServiceUrl());
builder.issuer(issuerURL);
builder.relayState(relayState);
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(relayState);
if (config.isWantAuthnRequestsSigned()) {
builder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
binding.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signatureAlgorithm(provider.getSignatureAlgorithm())
.signDocument();
}
try {
if (config.isPostBindingResponse()) {
return builder.postBinding().response();
return binding.postBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl());
} else {
return builder.redirectBinding().response();
return binding.redirectBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl());
}
} catch (ConfigurationException e) {
throw new RuntimeException(e);
@ -275,7 +265,7 @@ public class SAMLEndpoint {
protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder holder, ResponseType responseType, String relayState) {
try {
AssertionType assertion = getAssertion(responseType);
AssertionType assertion = AssertionUtil.getAssertion(responseType, realm.getPrivateKey());
SubjectType subject = assertion.getSubject();
SubjectType.STSubType subType = subject.getSubType();
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
@ -335,22 +325,6 @@ public class SAMLEndpoint {
private AssertionType getAssertion(ResponseType responseType) throws ProcessingException {
List<ResponseType.RTChoiceType> assertions = responseType.getAssertions();
if (assertions.isEmpty()) {
throw new IdentityBrokerException("No assertion from response.");
}
ResponseType.RTChoiceType rtChoiceType = assertions.get(0);
EncryptedAssertionType encryptedAssertion = rtChoiceType.getEncryptedAssertion();
if (encryptedAssertion != null) {
decryptAssertion(responseType, realm.getPrivateKey());
}
return responseType.getAssertions().get(0).getAssertion();
}
public Response handleSamlResponse(String samlResponse, String relayState) {
SAMLDocumentHolder holder = extractResponseDocument(samlResponse);
@ -364,7 +338,7 @@ public class SAMLEndpoint {
}
if (config.isValidateSignature()) {
try {
verifySignature(holder);
verifySignature(GeneralConstants.SAML_RESPONSE_KEY, holder);
} catch (VerificationException e) {
logger.error("validation failed", e);
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
@ -407,43 +381,14 @@ public class SAMLEndpoint {
}
protected ResponseType decryptAssertion(ResponseType responseType, PrivateKey privateKey) throws ProcessingException {
SAML2Response saml2Response = new SAML2Response();
try {
Document doc = saml2Response.convert(responseType);
Element enc = DocumentUtil.getElement(doc, new QName(JBossSAMLConstants.ENCRYPTED_ASSERTION.get()));
if (enc == null) {
throw new IdentityBrokerException("No encrypted assertion found.");
}
String oldID = enc.getAttribute(JBossSAMLConstants.ID.get());
Document newDoc = DocumentUtil.createDocument();
Node importedNode = newDoc.importNode(enc, true);
newDoc.appendChild(importedNode);
Element decryptedDocumentElement = XMLEncryptionUtil.decryptElementInDocument(newDoc, privateKey);
SAMLParser parser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(decryptedDocumentElement);
AssertionType assertion = (AssertionType) parser.parse(StaxParserUtil.getXMLEventReader(DocumentUtil
.getNodeAsStream(decryptedDocumentElement)));
responseType.replaceAssertion(oldID, new ResponseType.RTChoiceType(assertion));
return responseType;
} catch (Exception e) {
throw new IdentityBrokerException("Could not decrypt assertion.", e);
}
}
}
protected class PostBinding extends Binding {
@Override
protected void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException {
protected void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException {
SamlProtocolUtils.verifyDocumentSignature(documentHolder.getSamlDocument(), getIDPKey());
}
@ -466,9 +411,9 @@ public class SAMLEndpoint {
protected class RedirectBinding extends Binding {
@Override
protected void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException {
protected void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException {
PublicKey publicKey = getIDPKey();
SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo);
SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo, key);
}

View file

@ -33,21 +33,18 @@ import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder;
import org.keycloak.protocol.saml.SAML2LogoutRequestBuilder;
import org.keycloak.protocol.saml.SAML2NameIDPolicyBuilder;
import org.keycloak.protocol.saml.SignatureAlgorithm;
import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
import org.keycloak.saml.SAML2AuthnRequestBuilder;
import org.keycloak.saml.SAML2LogoutRequestBuilder;
import org.keycloak.saml.SAML2NameIDPolicyBuilder;
import org.keycloak.saml.SignatureAlgorithm;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ConfigurationException;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.exceptions.ProcessingException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
@ -93,7 +90,8 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
.issuer(issuerURL)
.forceAuthn(getConfig().isForceAuthn())
.protocolBinding(protocolBinding)
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat))
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(request.getState());
if (getConfig().isWantAuthnRequestsSigned()) {
@ -110,15 +108,15 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
KeyPair keypair = new KeyPair(publicKey, privateKey);
authnRequestBuilder.signWith(keypair);
authnRequestBuilder.signatureAlgorithm(getSignatureAlgorithm());
authnRequestBuilder.signDocument();
binding.signWith(keypair);
binding.signatureAlgorithm(getSignatureAlgorithm());
binding.signDocument();
}
if (getConfig().isPostBindingAuthnRequest()) {
return authnRequestBuilder.postBinding().request();
return binding.postBinding(authnRequestBuilder.toDocument()).request(destinationUrl);
} else {
return authnRequestBuilder.redirectBinding().request();
return binding.redirectBinding(authnRequestBuilder.toDocument()).request(destinationUrl);
}
} catch (Exception e) {
throw new IdentityBrokerException("Could not create authentication request.", e);
@ -155,9 +153,10 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
String singleLogoutServiceUrl = getConfig().getSingleLogoutServiceUrl();
if (singleLogoutServiceUrl == null || singleLogoutServiceUrl.trim().equals("") || !getConfig().isBackchannelSupported()) return;
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
JaxrsSAML2BindingBuilder binding = buildLogoutBinding(userSession, realm);
try {
int status = SimpleHttp.doPost(singleLogoutServiceUrl)
.param(GeneralConstants.SAML_REQUEST_KEY, logoutBuilder.postBinding().encoded())
.param(GeneralConstants.SAML_REQUEST_KEY, binding.postBinding(logoutBuilder.buildDocument()).encoded())
.param(GeneralConstants.RELAY_STATE, userSession.getId()).asStatus();
boolean success = status >=200 && status < 400;
if (!success) {
@ -180,7 +179,8 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
} else {
try {
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
return logoutBuilder.postBinding().request();
JaxrsSAML2BindingBuilder binding = buildLogoutBinding(userSession, realm);
return binding.postBinding(logoutBuilder.buildDocument()).request(singleLogoutServiceUrl);
} catch (Exception e) {
throw new RuntimeException(e);
}
@ -194,14 +194,19 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
.issuer(getEntityId(uriInfo, realm))
.sessionIndex(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SESSION_INDEX))
.userPrincipal(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT), userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT_NAMEFORMAT))
.destination(singleLogoutServiceUrl)
.destination(singleLogoutServiceUrl);
return logoutBuilder;
}
private JaxrsSAML2BindingBuilder buildLogoutBinding(UserSessionModel userSession, RealmModel realm) {
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(userSession.getId());
if (getConfig().isWantAuthnRequestsSigned()) {
logoutBuilder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
binding.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
.signatureAlgorithm(getSignatureAlgorithm())
.signDocument();
}
return logoutBuilder;
return binding;
}
@Override
@ -257,4 +262,5 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
}
return SignatureAlgorithm.RSA_SHA256;
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,20 @@
<?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"/>
<module name="org.jboss.as.web"/>
<module name="javax.servlet.api"/>
<module name="org.apache.httpcomponents"/>
<module name="org.bouncycastle" />
<module name="org.keycloak.keycloak-core"/>
<module name="net.iharder.base64"/>
</dependencies>
</module>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -21,6 +21,14 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>

View file

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

View file

@ -0,0 +1,20 @@
<?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.apache.httpcomponents" slot="4.3" />
<module name="javax.servlet.api"/>
<module name="org.jboss.logging"/>
<module name="org.jboss.xnio"/>
<module name="io.undertow.core"/>
<module name="io.undertow.servlet"/>
<module name="org.keycloak.keycloak-core"/>
</dependencies>
</module>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -21,6 +21,14 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>

View file

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

View file

@ -0,0 +1,20 @@
<?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.keycloak.keycloak-core"/>
<module name="org.apache.httpcomponents"/>
<module name="javax.servlet.api"/>
<module name="org.jboss.logging"/>
<module name="org.jboss.xnio"/>
<module name="io.undertow.core"/>
<module name="io.undertow.servlet"/>
</dependencies>
</module>

View file

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

View file

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

View file

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

View file

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

View file

@ -44,6 +44,14 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-servlet-oauth-client</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-undertow-adapter</artifactId>

View file

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

View file

@ -0,0 +1,22 @@
<?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"/>
<module name="org.jboss.xnio"/>
<module name="io.undertow.core"/>
<module name="io.undertow.servlet"/>
<module name="javax.servlet.api"/>
<module name="org.bouncycastle" />
<module name="org.keycloak.keycloak-core"/>
<module name="net.iharder.base64"/>
</dependencies>
</module>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@ package org.keycloak.adapters;
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface AdapterTokenStore {
public interface AdapterTokenStore extends AdapterSessionStore {
/**
* Impl can validate if current token exists and perform refreshing if it exists and is expired
@ -39,6 +39,4 @@ public interface AdapterTokenStore {
*/
void refreshCallback(RefreshableKeycloakSecurityContext securityContext);
void saveRequest();
boolean restoreRequest();
}

View file

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

View file

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

View file

@ -14,7 +14,6 @@ import org.keycloak.util.KeycloakUriBuilder;
import org.keycloak.util.UriUtils;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
@ -27,7 +26,7 @@ public class OAuthRequestAuthenticator {
protected KeycloakDeployment deployment;
protected RequestAuthenticator reqAuthenticator;
protected int sslRedirectPort;
protected AdapterTokenStore tokenStore;
protected AdapterSessionStore tokenStore;
protected String tokenString;
protected String idTokenString;
protected IDToken idToken;
@ -37,7 +36,7 @@ public class OAuthRequestAuthenticator {
protected String refreshToken;
protected String strippedOauthParametersRequestUri;
public OAuthRequestAuthenticator(RequestAuthenticator requestAuthenticator, HttpFacade facade, KeycloakDeployment deployment, int sslRedirectPort, AdapterTokenStore tokenStore) {
public OAuthRequestAuthenticator(RequestAuthenticator requestAuthenticator, HttpFacade facade, KeycloakDeployment deployment, int sslRedirectPort, AdapterSessionStore tokenStore) {
this.reqAuthenticator = requestAuthenticator;
this.facade = facade;
this.deployment = deployment;
@ -93,12 +92,12 @@ public class OAuthRequestAuthenticator {
return facade.getRequest().isSecure();
}
protected HttpFacade.Cookie getCookie(String cookieName) {
protected OIDCHttpFacade.Cookie getCookie(String cookieName) {
return facade.getRequest().getCookie(cookieName);
}
protected String getCookieValue(String cookieName) {
HttpFacade.Cookie cookie = getCookie(cookieName);
OIDCHttpFacade.Cookie cookie = getCookie(cookieName);
if (cookie == null) return null;
return cookie.getValue();
}
@ -204,7 +203,7 @@ public class OAuthRequestAuthenticator {
}
protected AuthChallenge checkStateCookie() {
HttpFacade.Cookie stateCookie = getCookie(deployment.getStateCookieName());
OIDCHttpFacade.Cookie stateCookie = getCookie(deployment.getStateCookieName());
if (stateCookie == null) {
log.warn("No state cookie");

View file

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

View file

@ -9,11 +9,11 @@ import org.keycloak.KeycloakPrincipal;
*/
public abstract class RequestAuthenticator {
protected static Logger log = Logger.getLogger(RequestAuthenticator.class);
protected HttpFacade facade;
protected AuthChallenge challenge;
protected KeycloakDeployment deployment;
protected AdapterTokenStore tokenStore;
protected AuthChallenge challenge;
protected int sslRedirectPort;
public RequestAuthenticator(HttpFacade facade, KeycloakDeployment deployment, AdapterTokenStore tokenStore, int sslRedirectPort) {

93
integration/adapter-spi/pom.xml Executable file
View file

@ -0,0 +1,93 @@
<?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.6.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-adapter-spi</artifactId>
<name>Keycloak Adapter SPI</name>
<description/>
<properties>
<keycloak.osgi.export>
org.keycloak.adapters.*
</keycloak.osgi.export>
<keycloak.osgi.import>
org.keycloak.*;version="${project.version}",
org.apache.http.*;version=${apache.httpcomponents.version},
org.apache.karaf.jaas.boot.principal;resolution:=optional,
org.apache.karaf.jaas.modules;resolution:=optional,
*;resolution:=optional
</keycloak.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>${jboss.logging.version}</version>
<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>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-ClassPath>.</Bundle-ClassPath>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,10 @@
package org.keycloak.adapters;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface AdapterSessionStore {
void saveRequest();
boolean restoreRequest();
}

View file

@ -7,5 +7,6 @@ package org.keycloak.adapters;
public enum AuthOutcome {
NOT_ATTEMPTED,
FAILED,
AUTHENTICATED
AUTHENTICATED,
LOGGED_OUT
}

View file

@ -1,19 +1,69 @@
package org.keycloak.adapters;
import org.keycloak.KeycloakSecurityContext;
import javax.security.cert.X509Certificate;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
/**
* Bridge between core adapter and HTTP Engine
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface HttpFacade {
Request getRequest();
Response getResponse();
X509Certificate[] getCertificateChain();
interface Request {
String getMethod();
/**
* Full request URI with query params
*
* @return
*/
String getURI();
/**
* HTTPS?
*
* @return
*/
boolean isSecure();
/**
* Get first query or form param
*
* @param param
* @return
*/
String getFirstParam(String param);
String getQueryParamValue(String param);
Cookie getCookie(String cookieName);
String getHeader(String name);
List<String> getHeaders(String name);
InputStream getInputStream();
String getRemoteAddr();
}
interface Response {
void setStatus(int status);
void addHeader(String name, String value);
void setHeader(String name, String value);
void resetCookie(String name, String path);
void setCookie(String name, String value, String path, String domain, int maxAge, boolean secure, boolean httpOnly);
OutputStream getOutputStream();
void sendError(int code, String message);
/**
* If the response is finished, end it.
*
*/
void end();
}
public class Cookie {
protected String name;
@ -50,51 +100,4 @@ public interface HttpFacade {
return path;
}
}
interface Request {
String getMethod();
/**
* Full request URI with query params
*
* @return
*/
String getURI();
/**
* HTTPS?
*
* @return
*/
boolean isSecure();
String getQueryParamValue(String param);
Cookie getCookie(String cookieName);
String getHeader(String name);
List<String> getHeaders(String name);
InputStream getInputStream();
String getRemoteAddr();
}
interface Response {
void setStatus(int status);
void addHeader(String name, String value);
void setHeader(String name, String value);
void resetCookie(String name, String path);
void setCookie(String name, String value, String path, String domain, int maxAge, boolean secure, boolean httpOnly);
OutputStream getOutputStream();
void sendError(int code, String message);
/**
* If the response is finished, end it.
*
*/
void end();
}
KeycloakSecurityContext getSecurityContext();
Request getRequest();
Response getResponse();
X509Certificate[] getCertificateChain();
}

View file

@ -0,0 +1,69 @@
package org.keycloak.adapters;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Maps external principal and SSO id to internal local http session id
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class InMemorySessionIdMapper implements SessionIdMapper {
ConcurrentHashMap<String, String> ssoToSession = new ConcurrentHashMap<>();
ConcurrentHashMap<String, String> sessionToSso = new ConcurrentHashMap<>();
ConcurrentHashMap<String, Set<String>> principalToSession = new ConcurrentHashMap<>();
ConcurrentHashMap<String, String> sessionToPrincipal = new ConcurrentHashMap<>();
@Override
public Set<String> getUserSessions(String principal) {
Set<String> lookup = principalToSession.get(principal);
if (lookup == null) return null;
Set<String> copy = new HashSet<>();
copy.addAll(lookup);
return copy;
}
@Override
public String getSessionFromSSO(String sso) {
return ssoToSession.get(sso);
}
@Override
public void map(String sso, String principal, String session) {
if (sso != null) {
ssoToSession.put(sso, session);
sessionToSso.put(session, sso);
}
Set<String> userSessions = principalToSession.get(principal);
if (userSessions == null) {
final Set<String> tmp = Collections.synchronizedSet(new HashSet<String>());
userSessions = principalToSession.putIfAbsent(principal, tmp);
if (userSessions == null) {
userSessions = tmp;
}
}
userSessions.add(session);
sessionToPrincipal.put(session, principal);
}
@Override
public void removeSession(String session) {
String sso = sessionToSso.remove(session);
if (sso != null) {
ssoToSession.remove(sso);
}
String principal = sessionToPrincipal.remove(session);
if (principal != null) {
Set<String> sessions = principalToSession.get(principal);
sessions.remove(session);
if (sessions.isEmpty()) {
principalToSession.remove(principal, sessions);
}
}
}
}

View file

@ -0,0 +1,17 @@
package org.keycloak.adapters;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface SessionIdMapper {
Set<String> getUserSessions(String principal);
String getSessionFromSSO(String sso);
void map(String sso, String principal, String session);
void removeSession(String session);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,115 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.6.0.Final-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-jetty-adapter-spi</artifactId>
<name>Keycloak Jetty Adapter SPI</name>
<properties>
<jetty9.version>8.1.16.v20140903</jetty9.version>
<keycloak.osgi.export>
org.keycloak.adapters.jetty.core.*
</keycloak.osgi.export>
<keycloak.osgi.import>
org.eclipse.jetty.*;version="[8.1,10)";resolution:=optional,
javax.servlet.*;version="[2.5,4)";resolution:=optional,
org.keycloak.*;version="${project.version}",
*;resolution:=optional
</keycloak.osgi.import>
</properties>
<description />
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>${jboss.logging.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty9.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${jetty9.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
<version>${jetty9.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-ClassPath>.</Bundle-ClassPath>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<Import-Package>${keycloak.osgi.import}</Import-Package>
<Export-Package>${keycloak.osgi.export}</Export-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -1,6 +1,5 @@
package org.keycloak.adapters.jetty.core;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.util.MultivaluedHashMap;
import org.keycloak.util.UriUtils;
@ -19,12 +18,37 @@ import java.util.List;
* @version $Revision: 1 $
*/
public class JettyHttpFacade implements HttpFacade {
public final static String __J_METHOD = "org.eclipse.jetty.security.HTTP_METHOD";
protected org.eclipse.jetty.server.Request request;
protected HttpServletResponse response;
protected RequestFacade requestFacade = new RequestFacade();
protected ResponseFacade responseFacade = new ResponseFacade();
protected MultivaluedHashMap<String, String> queryParameters;
public JettyHttpFacade(org.eclipse.jetty.server.Request request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
@Override
public Request getRequest() {
return requestFacade;
}
@Override
public Response getResponse() {
return responseFacade;
}
@Override
public X509Certificate[] getCertificateChain() {
throw new IllegalStateException("Not supported yet");
}
public boolean isEnded() {
return responseFacade.isEnded();
}
protected class RequestFacade implements Request {
@Override
public String getURI() {
@ -35,6 +59,11 @@ public class JettyHttpFacade implements HttpFacade {
return buf.toString();
}
@Override
public String getFirstParam(String param) {
return request.getParameter(param);
}
@Override
public boolean isSecure() {
return request.isSecure();
@ -159,33 +188,4 @@ public class JettyHttpFacade implements HttpFacade {
return ended;
}
}
public JettyHttpFacade(org.eclipse.jetty.server.Request request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
@Override
public Request getRequest() {
return requestFacade;
}
@Override
public Response getResponse() {
return responseFacade;
}
@Override
public KeycloakSecurityContext getSecurityContext() {
return (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
}
@Override
public X509Certificate[] getCertificateChain() {
throw new IllegalStateException("Not supported yet");
}
public boolean isEnded() {
return responseFacade.isEnded();
}
}

View file

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

View file

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

View file

@ -4,6 +4,7 @@ import org.eclipse.jetty.server.Request;
import org.jboss.logging.Logger;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.AdapterSessionStore;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.AdapterUtils;
import org.keycloak.adapters.KeycloakAccount;
@ -18,17 +19,18 @@ import javax.servlet.http.HttpSession;
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public abstract class AbstractJettySessionTokenStore implements AdapterTokenStore {
public final static String __J_METHOD = "org.eclipse.jetty.security.HTTP_METHOD";
public class JettySessionTokenStore implements AdapterTokenStore {
private static final Logger log = Logger.getLogger(AbstractJettySessionTokenStore.class);
private static final Logger log = Logger.getLogger(JettySessionTokenStore.class);
private Request request;
protected KeycloakDeployment deployment;
protected AdapterSessionStore sessionStore;
public AbstractJettySessionTokenStore(Request request, KeycloakDeployment deployment) {
public JettySessionTokenStore(Request request, KeycloakDeployment deployment, AdapterSessionStore sessionStore) {
this.request = request;
this.deployment = deployment;
this.sessionStore = sessionStore;
}
@Override
@ -93,4 +95,14 @@ public abstract class AbstractJettySessionTokenStore implements AdapterTokenStor
// no-op
}
@Override
public void saveRequest() {
sessionStore.saveRequest();
}
@Override
public boolean restoreRequest() {
return sessionStore.restoreRequest();
}
}

View file

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

View file

@ -38,6 +38,10 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jetty-core</artifactId>

View file

@ -3,8 +3,8 @@ package org.keycloak.adapters.jetty;
import org.eclipse.jetty.security.authentication.FormAuthenticator;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.MultiMap;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.jetty.core.AbstractJettySessionTokenStore;
import org.keycloak.adapters.AdapterSessionStore;
import org.keycloak.adapters.jetty.core.JettyHttpFacade;
import org.keycloak.util.MultivaluedHashMap;
import javax.servlet.http.HttpSession;
@ -13,12 +13,11 @@ import javax.servlet.http.HttpSession;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
public class JettyAdapterSessionStore implements AdapterSessionStore {
public static final String CACHED_FORM_PARAMETERS = "__CACHED_FORM_PARAMETERS";
protected Request myRequest;
public JettySessionTokenStore(Request request, KeycloakDeployment deployment) {
super(request, deployment);
public JettyAdapterSessionStore(Request request) {
this.myRequest = request; // for IDE/compilation purposes
}
@ -43,7 +42,7 @@ public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
if (myRequest.getQueryString() != null)
buf.append("?").append(myRequest.getQueryString());
if (j_uri.equals(buf.toString())) {
String method = (String)session.getAttribute(__J_METHOD);
String method = (String)session.getAttribute(JettyHttpFacade.__J_METHOD);
myRequest.setMethod(method);
MultivaluedHashMap<String, String> j_post = (MultivaluedHashMap<String, String>) session.getAttribute(CACHED_FORM_PARAMETERS);
if (j_post != null) {
@ -57,7 +56,7 @@ public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
restoreFormParameters(map, myRequest);
}
session.removeAttribute(FormAuthenticator.__J_URI);
session.removeAttribute(__J_METHOD);
session.removeAttribute(JettyHttpFacade.__J_METHOD);
session.removeAttribute(FormAuthenticator.__J_POST);
}
return true;
@ -76,7 +75,7 @@ public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
if (myRequest.getQueryString() != null)
buf.append("?").append(myRequest.getQueryString());
session.setAttribute(FormAuthenticator.__J_URI, buf.toString());
session.setAttribute(__J_METHOD, myRequest.getMethod());
session.setAttribute(JettyHttpFacade.__J_METHOD, myRequest.getMethod());
if ("application/x-www-form-urlencoded".equals(myRequest.getContentType()) && "POST".equalsIgnoreCase(myRequest.getMethod())) {
MultiMap<String> formParameters = extractFormParameters(myRequest);

View file

@ -7,6 +7,7 @@ import org.eclipse.jetty.server.UserIdentity;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.jetty.core.AbstractKeycloakJettyAuthenticator;
import org.keycloak.adapters.jetty.core.JettySessionTokenStore;
import javax.servlet.ServletRequest;
@ -23,7 +24,7 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
@Override
public AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment) {
return new JettySessionTokenStore(request, resolvedDeployment);
return new JettySessionTokenStore(request, resolvedDeployment, new JettyAdapterSessionStore(request));
}
@Override

View file

@ -4,8 +4,8 @@ import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.security.authentication.FormAuthenticator;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.MultiMap;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.jetty.core.AbstractJettySessionTokenStore;
import org.keycloak.adapters.AdapterSessionStore;
import org.keycloak.adapters.jetty.core.JettyHttpFacade;
import org.keycloak.util.MultivaluedHashMap;
import javax.servlet.http.HttpSession;
@ -14,17 +14,15 @@ import javax.servlet.http.HttpSession;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
public class JettyAdapterSessionStore implements AdapterSessionStore {
public static final String CACHED_FORM_PARAMETERS = "__CACHED_FORM_PARAMETERS";
protected Request myRequest;
public JettySessionTokenStore(Request request, KeycloakDeployment deployment) {
super(request, deployment);
public JettyAdapterSessionStore(Request request) {
this.myRequest = request; // for IDE/compilation purposes
}
protected MultiMap<String> extractFormParameters(Request base_request) {
MultiMap<String> formParameters = new MultiMap<String>();
base_request.extractParameters();
return base_request.getParameters();
}
@ -44,7 +42,7 @@ public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
if (myRequest.getQueryString() != null)
buf.append("?").append(myRequest.getQueryString());
if (j_uri.equals(buf.toString())) {
String method = (String)session.getAttribute(__J_METHOD);
String method = (String)session.getAttribute(JettyHttpFacade.__J_METHOD);
myRequest.setMethod(HttpMethod.valueOf(method.toUpperCase()), method);
MultivaluedHashMap<String, String> j_post = (MultivaluedHashMap<String, String>) session.getAttribute(CACHED_FORM_PARAMETERS);
if (j_post != null) {
@ -58,7 +56,7 @@ public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
restoreFormParameters(map, myRequest);
}
session.removeAttribute(FormAuthenticator.__J_URI);
session.removeAttribute(__J_METHOD);
session.removeAttribute(JettyHttpFacade.__J_METHOD);
session.removeAttribute(FormAuthenticator.__J_POST);
}
return true;
@ -77,7 +75,7 @@ public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
if (myRequest.getQueryString() != null)
buf.append("?").append(myRequest.getQueryString());
session.setAttribute(FormAuthenticator.__J_URI, buf.toString());
session.setAttribute(__J_METHOD, myRequest.getMethod());
session.setAttribute(JettyHttpFacade.__J_METHOD, myRequest.getMethod());
if ("application/x-www-form-urlencoded".equals(myRequest.getContentType()) && "POST".equalsIgnoreCase(myRequest.getMethod())) {
MultiMap<String> formParameters = extractFormParameters(myRequest);

View file

@ -7,6 +7,7 @@ import org.eclipse.jetty.server.UserIdentity;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.jetty.core.AbstractKeycloakJettyAuthenticator;
import org.keycloak.adapters.jetty.core.JettySessionTokenStore;
import javax.servlet.ServletRequest;
@ -23,7 +24,7 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
@Override
public AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment) {
return new JettySessionTokenStore(request, resolvedDeployment);
return new JettySessionTokenStore(request, resolvedDeployment, new JettyAdapterSessionStore(request));
}
@Override

View file

@ -4,8 +4,8 @@ import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.security.authentication.FormAuthenticator;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.MultiMap;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.jetty.core.AbstractJettySessionTokenStore;
import org.keycloak.adapters.AdapterSessionStore;
import org.keycloak.adapters.jetty.core.JettyHttpFacade;
import org.keycloak.util.MultivaluedHashMap;
import javax.servlet.http.HttpSession;
@ -14,12 +14,11 @@ import javax.servlet.http.HttpSession;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
public class JettyAdapterSessionStore implements AdapterSessionStore {
public static final String CACHED_FORM_PARAMETERS = "__CACHED_FORM_PARAMETERS";
protected Request myRequest;
public JettySessionTokenStore(Request request, KeycloakDeployment deployment) {
super(request, deployment);
public JettyAdapterSessionStore(Request request) {
this.myRequest = request; // for IDE/compilation purposes
}
@ -44,7 +43,7 @@ public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
if (myRequest.getQueryString() != null)
buf.append("?").append(myRequest.getQueryString());
if (j_uri.equals(buf.toString())) {
String method = (String)session.getAttribute(__J_METHOD);
String method = (String)session.getAttribute(JettyHttpFacade.__J_METHOD);
myRequest.setMethod(HttpMethod.valueOf(method.toUpperCase()), method);
MultivaluedHashMap<String, String> j_post = (MultivaluedHashMap<String, String>) session.getAttribute(CACHED_FORM_PARAMETERS);
if (j_post != null) {
@ -58,7 +57,7 @@ public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
restoreFormParameters(map, myRequest);
}
session.removeAttribute(FormAuthenticator.__J_URI);
session.removeAttribute(__J_METHOD);
session.removeAttribute(JettyHttpFacade.__J_METHOD);
session.removeAttribute(FormAuthenticator.__J_POST);
}
return true;
@ -77,7 +76,7 @@ public class JettySessionTokenStore extends AbstractJettySessionTokenStore {
if (myRequest.getQueryString() != null)
buf.append("?").append(myRequest.getQueryString());
session.setAttribute(FormAuthenticator.__J_URI, buf.toString());
session.setAttribute(__J_METHOD, myRequest.getMethod());
session.setAttribute(JettyHttpFacade.__J_METHOD, myRequest.getMethod());
if ("application/x-www-form-urlencoded".equals(myRequest.getContentType()) && "POST".equalsIgnoreCase(myRequest.getMethod())) {
MultiMap<String> formParameters = extractFormParameters(myRequest);

View file

@ -7,6 +7,7 @@ import org.eclipse.jetty.server.UserIdentity;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.jetty.core.AbstractKeycloakJettyAuthenticator;
import org.keycloak.adapters.jetty.core.JettySessionTokenStore;
import javax.servlet.ServletRequest;
@ -38,6 +39,6 @@ public class KeycloakJettyAuthenticator extends AbstractKeycloakJettyAuthenticat
@Override
public AdapterTokenStore createSessionTokenStore(Request request, KeycloakDeployment resolvedDeployment) {
return new JettySessionTokenStore(request, resolvedDeployment);
return new JettySessionTokenStore(request, resolvedDeployment, new JettyAdapterSessionStore(request));
}
}

View file

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

View file

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

View file

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

View file

@ -3,15 +3,13 @@ package org.keycloak.servlet;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.ServerRequest;
import org.keycloak.enums.RelativeUrlsUsed;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken;
import org.keycloak.util.KeycloakUriBuilder;
import org.keycloak.util.UriUtils;
import javax.security.cert.X509Certificate;
import javax.servlet.http.Cookie;
@ -167,7 +165,7 @@ public class ServletOAuthClient extends KeycloakDeploymentDelegateOAuthClient {
}
public static class ServletFacade implements HttpFacade {
public static class ServletFacade implements OIDCHttpFacade {
private final HttpServletRequest servletRequest;
@ -184,6 +182,11 @@ public class ServletOAuthClient extends KeycloakDeploymentDelegateOAuthClient {
public Request getRequest() {
return new Request() {
@Override
public String getFirstParam(String param) {
return servletRequest.getParameter(param);
}
@Override
public String getMethod() {
return servletRequest.getMethod();

View file

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

View file

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

View file

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

View file

@ -32,6 +32,11 @@ class WrappedHttpServletRequest implements Request {
this.request = request;
}
@Override
public String getFirstParam(String param) {
return request.getParameter(param);
}
@Override
public String getMethod() {
return request.getMethod();

View file

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

View file

@ -2,10 +2,9 @@ package org.keycloak.adapters.springsecurity.token;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.AdapterSessionStore;
import org.keycloak.adapters.KeycloakDeployment;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import javax.servlet.http.HttpServletRequest;
@ -32,7 +31,7 @@ public class SpringSecurityAdapterTokenStoreFactoryTest {
@Test
public void testCreateAdapterTokenStore() throws Exception {
AdapterTokenStore store = factory.createAdapterTokenStore(deployment, request);
AdapterSessionStore store = factory.createAdapterTokenStore(deployment, request);
assertNotNull(store);
assertTrue(store instanceof SpringSecurityTokenStore);
}

View file

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

View file

@ -0,0 +1,69 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.6.0.Final-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-tomcat-adapter-spi</artifactId>
<name>Keycloak Tomcat Adapter SPI</name>
<properties>
<!-- <tomcat.version>8.0.14</tomcat.version> -->
<!-- <tomcat.version>7.0.52</tomcat.version> -->
<tomcat.version>6.0.41</tomcat.version>
</properties>
<description />
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>${jboss.logging.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<!--
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>${tomcat.version}</version>
<scope>compile</scope>
</dependency>
-->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>catalina</artifactId>
<version>${tomcat.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,32 @@
package org.keycloak.adapters.tomcat;
import org.apache.catalina.connector.Request;
import org.keycloak.adapters.AdapterSessionStore;
import java.io.IOException;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class CatalinaAdapterSessionStore implements AdapterSessionStore {
protected Request request;
protected AbstractKeycloakAuthenticatorValve valve;
public CatalinaAdapterSessionStore(Request request, AbstractKeycloakAuthenticatorValve valve) {
this.request = request;
this.valve = valve;
}
public void saveRequest() {
try {
valve.keycloakSaveRequest(request);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public boolean restoreRequest() {
return valve.keycloakRestoreRequest(request);
}
}

View file

@ -19,26 +19,23 @@ import java.util.logging.Logger;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class CatalinaSessionTokenStore implements AdapterTokenStore {
public class CatalinaSessionTokenStore extends CatalinaAdapterSessionStore implements AdapterTokenStore {
private static final Logger log = Logger.getLogger("" + CatalinaSessionTokenStore.class);
private Request request;
private KeycloakDeployment deployment;
private CatalinaUserSessionManagement sessionManagement;
protected GenericPrincipalFactory principalFactory;
protected AbstractKeycloakAuthenticatorValve valve;
public CatalinaSessionTokenStore(Request request, KeycloakDeployment deployment,
CatalinaUserSessionManagement sessionManagement,
GenericPrincipalFactory principalFactory,
AbstractKeycloakAuthenticatorValve valve) {
this.request = request;
super(request, valve);
this.deployment = deployment;
this.sessionManagement = sessionManagement;
this.principalFactory = principalFactory;
this.valve = valve;
}
@Override
@ -169,17 +166,4 @@ public class CatalinaSessionTokenStore implements AdapterTokenStore {
// no-op
}
@Override
public void saveRequest() {
try {
valve.keycloakSaveRequest(request);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean restoreRequest() {
return valve.keycloakRestoreRequest(request);
}
}

View file

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

View file

@ -0,0 +1,65 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.6.0.Final-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-undertow-adapter-spi</artifactId>
<name>Keycloak Undertow Integration</name>
<description/>
<dependencies>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>${jboss.logging.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,39 @@
package org.keycloak.adapters.undertow;
import io.undertow.server.HttpServerExchange;
import io.undertow.servlet.handlers.ServletRequestContext;
import org.keycloak.adapters.HttpFacade;
import javax.security.cert.X509Certificate;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ServletHttpFacade extends UndertowHttpFacade {
protected HttpServletRequest request;
protected HttpServletResponse response;
public ServletHttpFacade(HttpServerExchange exchange) {
super(exchange);
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
request = (HttpServletRequest)servletRequestContext.getServletRequest();
}
protected class RequestFacade extends UndertowHttpFacade.RequestFacade {
@Override
public String getFirstParam(String param) {
return request.getParameter(param);
}
}
@Override
public Request getRequest() {
return new RequestFacade();
}
}

View file

@ -1,27 +1,9 @@
/*
* Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.keycloak.adapters.undertow;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.CookieImpl;
import io.undertow.util.AttachmentKey;
import io.undertow.util.Headers;
import io.undertow.util.HttpString;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.HttpFacade;
import org.keycloak.util.KeycloakUriBuilder;
@ -40,12 +22,35 @@ import java.util.Map;
* @version $Revision: 1 $
*/
public class UndertowHttpFacade implements HttpFacade {
public static final AttachmentKey<KeycloakSecurityContext> KEYCLOAK_SECURITY_CONTEXT_KEY = AttachmentKey.create(KeycloakSecurityContext.class);
protected HttpServerExchange exchange;
protected RequestFacade requestFacade = new RequestFacade();
protected ResponseFacade responseFacade = new ResponseFacade();
public UndertowHttpFacade(HttpServerExchange exchange) {
this.exchange = exchange;
}
@Override
public Request getRequest() {
return requestFacade;
}
@Override
public Response getResponse() {
return responseFacade;
}
@Override
public X509Certificate[] getCertificateChain() {
X509Certificate[] chain = new X509Certificate[0];
try {
chain = exchange.getConnection().getSslSessionInfo().getPeerCertificateChain();
} catch (Exception ignore) {
}
return chain;
}
protected class RequestFacade implements Request {
@Override
public String getURI() {
@ -61,6 +66,11 @@ public class UndertowHttpFacade implements HttpFacade {
return protocol.equalsIgnoreCase("https");
}
@Override
public String getFirstParam(String param) {
throw new RuntimeException("Not implemented yet");
}
@Override
public String getQueryParamValue(String param) {
Map<String,Deque<String>> queryParameters = exchange.getQueryParameters();
@ -178,34 +188,4 @@ public class UndertowHttpFacade implements HttpFacade {
exchange.endExchange();
}
}
public UndertowHttpFacade(HttpServerExchange exchange) {
this.exchange = exchange;
}
@Override
public Request getRequest() {
return requestFacade;
}
@Override
public Response getResponse() {
return responseFacade;
}
@Override
public KeycloakSecurityContext getSecurityContext() {
return exchange.getAttachment(KEYCLOAK_SECURITY_CONTEXT_KEY);
}
@Override
public X509Certificate[] getCertificateChain() {
X509Certificate[] chain = new X509Certificate[0];
try {
chain = exchange.getConnection().getSslSessionInfo().getPeerCertificateChain();
} catch (Exception ignore) {
}
return chain;
}
}

Some files were not shown because too many files have changed in this diff Show more