broker logout

This commit is contained in:
Bill Burke committed 2015-03-20 12:13:33 -04:00
1 parent b26277a17c
commit 90c815055d
31 files changed
+823 -126

No files matched your search

@@ -43,8 +43,14 @@ public class SALM2LoginResponseBuilder {
protected String requestID;
protected String authMethod;
protected String requestIssuer;
protected String sessionIndex;
public SALM2LoginResponseBuilder sessionIndex(String sessionIndex) {
this.sessionIndex = sessionIndex;
return this;
}
public SALM2LoginResponseBuilder destination(String destination) {
this.destination = destination;
return this;
@@ -135,8 +141,8 @@ public class SALM2LoginResponseBuilder {
AuthnStatementType authnStatement = StatementUtil.createAuthnStatement(XMLTimeUtil.getIssueInstant(),
authContextRef);
authnStatement.setSessionIndex(assertion.getID());
if (sessionIndex != null) authnStatement.setSessionIndex(sessionIndex);
else authnStatement.setSessionIndex(assertion.getID());
assertion.addStatement(authnStatement);
}
@@ -19,6 +19,7 @@ import java.net.URI;
public class SAML2LogoutRequestBuilder extends SAML2BindingBuilder<SAML2LogoutRequestBuilder> {
protected String userPrincipal;
protected String userPrincipalFormat;
protected String sessionIndex;
public SAML2LogoutRequestBuilder userPrincipal(String nameID, String nameIDformat) {
this.userPrincipal = nameID;
@@ -26,6 +27,11 @@ public class SAML2LogoutRequestBuilder extends SAML2BindingBuilder<SAML2LogoutRe
return this;
}
public SAML2LogoutRequestBuilder sessionIndex(String index) {
this.sessionIndex = index;
return this;
}
public RedirectBindingBuilder redirectBinding() throws ConfigurationException, ProcessingException, ParsingException {
Document samlResponseDocument = buildDocument();
return new RedirectBindingBuilder(samlResponseDocument);
@@ -58,7 +64,7 @@ public class SAML2LogoutRequestBuilder extends SAML2BindingBuilder<SAML2LogoutRe
issuerID.setValue(issuer);
lort.setIssuer(issuerID);
}
if (sessionIndex != null) lort.addSessionIndex(sessionIndex);
long assertionValidity = PicketLinkCoreSTS.instance().getConfiguration().getIssuedTokenTimeout();
@@ -4,6 +4,7 @@ import org.jboss.logging.Logger;
import org.jboss.resteasy.client.ClientRequest;
import org.jboss.resteasy.client.ClientResponse;
import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClaimMask;
import org.keycloak.models.ClientModel;
@@ -97,6 +98,8 @@ public class SamlProtocol implements LoginProtocol {
protected HttpHeaders headers;
protected EventBuilder event;
@Override
public SamlProtocol setSession(KeycloakSession session) {
@@ -122,6 +125,13 @@ public class SamlProtocol implements LoginProtocol {
return this;
}
@Override
public SamlProtocol setEventBuilder(EventBuilder event) {
this.event = event;
return this;
}
@Override
public Response cancelLogin(ClientSessionModel clientSession) {
return getErrorResponse(clientSession, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get());
@@ -265,6 +275,7 @@ public class SamlProtocol implements LoginProtocol {
builder.requestID(requestID)
.destination(redirectUri)
.issuer(responseIssuer)
.sessionIndex(clientSession.getId())
.requestIssuer(clientSession.getClient().getClientId())
.nameIdentifier(nameIdFormat, nameId)
.authMethod(JBossSAMLURIConstants.AC_UNSPECIFIED.get());
@@ -353,10 +364,6 @@ public class SamlProtocol implements LoginProtocol {
return "true".equals(client.getAttribute(SAML_AUTHNSTATEMENT));
}
public static boolean multivaluedRoles(ClientModel client) {
return "true".equals(client.getAttribute(SAML_MULTIVALUED_ROLES));
}
public static SignatureAlgorithm getSignatureAlgorithm(ClientModel client) {
String alg = client.getAttribute(SAML_SIGNATURE_ALGORITHM);
if (alg != null) {
@@ -460,10 +467,13 @@ public class SamlProtocol implements LoginProtocol {
@Override
public Response finishLogout(UserSessionModel userSession) {
logger.debug("finishLogout");
String logoutBindingUri = userSession.getNote(SAML_LOGOUT_BINDING_URI);
String logoutRelayState = userSession.getNote(SAML_LOGOUT_RELAY_STATE);
SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
builder.logoutRequestID(userSession.getNote(SAML_LOGOUT_REQUEST_ID));
builder.destination(userSession.getNote(SAML_LOGOUT_ISSUER));
builder.destination(logoutBindingUri);
builder.issuer(getResponseIssuer(realm));
builder.relayState(logoutRelayState);
String signingAlgorithm = userSession.getNote(SAML_LOGOUT_SIGNATURE_ALGORITHM);
if (signingAlgorithm != null) {
SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(signingAlgorithm);
@@ -474,9 +484,9 @@ public class SamlProtocol implements LoginProtocol {
try {
if (isLogoutPostBindingForInitiator(userSession)) {
return builder.postBinding().response(userSession.getNote(SAML_LOGOUT_BINDING_URI));
return builder.postBinding().response(logoutBindingUri);
} else {
return builder.redirectBinding().response(userSession.getNote(SAML_LOGOUT_BINDING_URI));
return builder.redirectBinding().response(logoutBindingUri);
}
} catch (ConfigurationException e) {
throw new RuntimeException(e);
@@ -3,11 +3,17 @@ package org.keycloak.protocol.saml;
import org.keycloak.VerificationException;
import org.keycloak.models.ClientModel;
import org.keycloak.util.PemUtils;
import org.picketlink.common.constants.GeneralConstants;
import org.picketlink.common.exceptions.ProcessingException;
import org.picketlink.identity.federation.api.saml.v2.sig.SAML2Signature;
import org.picketlink.identity.federation.web.util.RedirectBindingUtil;
import org.w3c.dom.Document;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
/**
@@ -20,8 +26,12 @@ public class SamlProtocolUtils {
if (!"true".equals(client.getAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE))) {
return;
}
SAML2Signature saml2Signature = new SAML2Signature();
PublicKey publicKey = getSignatureValidationKey(client);
verifyDocumentSignature(document, publicKey);
}
public static void verifyDocumentSignature(Document document, PublicKey publicKey) throws VerificationException {
SAML2Signature saml2Signature = new SAML2Signature();
try {
if (!saml2Signature.validate(document, publicKey)) {
throw new VerificationException("Invalid signature on document");
@@ -51,5 +61,43 @@ public class SamlProtocolUtils {
return cert.getPublicKey();
}
public static void verifyRedirectSignature(PublicKey publicKey, UriInfo uriInformation) throws VerificationException {
MultivaluedMap<String, String> encodedParams = uriInformation.getQueryParameters(false);
String request = encodedParams.getFirst(GeneralConstants.SAML_REQUEST_KEY);
String algorithm = encodedParams.getFirst(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
String signature = encodedParams.getFirst(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);
String decodedAlgorithm = uriInformation.getQueryParameters(true).getFirst(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
if (request == null) throw new VerificationException("SAMLRequest as null");
if (algorithm == null) throw new VerificationException("SigAlg as null");
if (signature == null) throw new VerificationException("Signature as null");
// Shibboleth doesn't sign the document for redirect binding.
// todo maybe a flag?
UriBuilder builder = UriBuilder.fromPath("/")
.queryParam(GeneralConstants.SAML_REQUEST_KEY, request);
if (encodedParams.containsKey(GeneralConstants.RELAY_STATE)) {
builder.queryParam(GeneralConstants.RELAY_STATE, encodedParams.getFirst(GeneralConstants.RELAY_STATE));
}
builder.queryParam(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY, algorithm);
String rawQuery = builder.build().getRawQuery();
try {
byte[] decodedSignature = RedirectBindingUtil.urlBase64Decode(signature);
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.getFromXmlMethod(decodedAlgorithm);
Signature validator = signatureAlgorithm.createSignature(); // todo plugin signature alg
validator.initVerify(publicKey);
validator.update(rawQuery.getBytes("UTF-8"));
if (!validator.verify(decodedSignature)) {
throw new VerificationException("Invalid query param signature");
}
} catch (Exception e) {
throw new VerificationException(e);
}
}
}
@@ -358,7 +358,6 @@ public class SamlService {
if (relayState != null) userSession.setNote(SamlProtocol.SAML_LOGOUT_RELAY_STATE, relayState);
userSession.setNote(SamlProtocol.SAML_LOGOUT_REQUEST_ID, logoutRequest.getID());
userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING, logoutBinding);
userSession.setNote(SamlProtocol.SAML_LOGOUT_ISSUER, logoutRequest.getIssuer().getValue());
userSession.setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, SamlProtocol.LOGIN_PROTOCOL);
// remove client from logout requests
for (ClientSessionModel clientSession : userSession.getClientSessions()) {
@@ -446,47 +445,12 @@ public class SamlService {
if (!"true".equals(client.getAttribute("saml.client.signature"))) {
return;
}
MultivaluedMap<String, String> encodedParams = uriInfo.getQueryParameters(false);
String request = encodedParams.getFirst(GeneralConstants.SAML_REQUEST_KEY);
String algorithm = encodedParams.getFirst(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
String signature = encodedParams.getFirst(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);
if (request == null) throw new VerificationException("SAMLRequest as null");
if (algorithm == null) throw new VerificationException("SigAlg as null");
if (signature == null) throw new VerificationException("Signature as null");
// Shibboleth doesn't sign the document for redirect binding.
// todo maybe a flag?
// SamlProtocolUtils.verifyDocumentSignature(client, documentHolder.getSamlDocument());
PublicKey publicKey = SamlProtocolUtils.getSignatureValidationKey(client);
UriBuilder builder = UriBuilder.fromPath("/")
.queryParam(GeneralConstants.SAML_REQUEST_KEY, request);
if (encodedParams.containsKey(GeneralConstants.RELAY_STATE)) {
builder.queryParam(GeneralConstants.RELAY_STATE, encodedParams.getFirst(GeneralConstants.RELAY_STATE));
}
builder.queryParam(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY, algorithm);
String rawQuery = builder.build().getRawQuery();
try {
byte[] decodedSignature = RedirectBindingUtil.urlBase64Decode(signature);
SignatureAlgorithm signatureAlgorithm = SamlProtocol.getSignatureAlgorithm(client);
Signature validator = signatureAlgorithm.createSignature(); // todo plugin signature alg
validator.initVerify(publicKey);
validator.update(rawQuery.getBytes("UTF-8"));
if (!validator.verify(decodedSignature)) {
throw new VerificationException("Invalid query param signature");
}
} catch (Exception e) {
throw new VerificationException(e);
}
SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo);
}
@Override
protected SAMLDocumentHolder extractRequestDocument(String samlRequest) {
return SAMLRequestParser.parseRequestRedirectBinding(samlRequest);
@@ -1,6 +1,8 @@
package org.keycloak.protocol.saml;
import java.security.Signature;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -16,6 +18,29 @@ public enum SignatureAlgorithm {
private final String xmlSignatureDigestMethod;
private final String javaSignatureAlgorithm;
private static final Map<String, SignatureAlgorithm> signatureMethodMap = new HashMap<>();
private static final Map<String, SignatureAlgorithm> signatureDigestMethodMap = new HashMap<>();
static {
signatureMethodMap.put(RSA_SHA1.getXmlSignatureMethod(), RSA_SHA1);
signatureMethodMap.put(RSA_SHA256.getXmlSignatureMethod(), RSA_SHA256);
signatureMethodMap.put(RSA_SHA512.getXmlSignatureMethod(), RSA_SHA512);
signatureMethodMap.put(DSA_SHA1.getXmlSignatureMethod(), DSA_SHA1);
signatureDigestMethodMap.put(RSA_SHA1.getXmlSignatureDigestMethod(), RSA_SHA1);
signatureDigestMethodMap.put(RSA_SHA256.getXmlSignatureDigestMethod(), RSA_SHA256);
signatureDigestMethodMap.put(RSA_SHA512.getXmlSignatureDigestMethod(), RSA_SHA512);
signatureDigestMethodMap.put(DSA_SHA1.getXmlSignatureDigestMethod(), DSA_SHA1);
}
public static SignatureAlgorithm getFromXmlMethod(String xml) {
return signatureMethodMap.get(xml);
}
public static SignatureAlgorithm getFromXmlDigest(String xml) {
return signatureDigestMethodMap.get(xml);
}
SignatureAlgorithm(String xmlSignatureMethod, String xmlSignatureDigestMethod, String javaSignatureAlgorithm) {
this.xmlSignatureMethod = xmlSignatureMethod;
this.xmlSignatureDigestMethod = xmlSignatureDigestMethod;