broker logout
This commit is contained in:
1 parent
b26277a17c
commit
90c815055d
31 files changed
+823
-126
No files matched your search
+8
-2
@@ -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);
|
||||
}
|
||||
|
||||
+7
-1
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user