more complete
This commit is contained in:
parent
cc9d6d0cf7
commit
1e9c09d23a
31 changed files with 481 additions and 156 deletions
|
@ -11,7 +11,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class InMemorySessionIdMapper implements SessionIdMaper {
|
public class InMemorySessionIdMapper implements SessionIdMapper {
|
||||||
ConcurrentHashMap<String, String> ssoToSession = new ConcurrentHashMap<>();
|
ConcurrentHashMap<String, String> ssoToSession = new ConcurrentHashMap<>();
|
||||||
ConcurrentHashMap<String, String> sessionToSso = new ConcurrentHashMap<>();
|
ConcurrentHashMap<String, String> sessionToSso = new ConcurrentHashMap<>();
|
||||||
ConcurrentHashMap<String, Set<String>> principalToSession = new ConcurrentHashMap<>();
|
ConcurrentHashMap<String, Set<String>> principalToSession = new ConcurrentHashMap<>();
|
||||||
|
@ -33,8 +33,10 @@ public class InMemorySessionIdMapper implements SessionIdMaper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void map(String sso, String principal, String session) {
|
public void map(String sso, String principal, String session) {
|
||||||
ssoToSession.put(sso, session);
|
if (sso != null) {
|
||||||
sessionToSso.put(session, sso);
|
ssoToSession.put(sso, session);
|
||||||
|
sessionToSso.put(session, sso);
|
||||||
|
}
|
||||||
Set<String> userSessions = principalToSession.get(principal);
|
Set<String> userSessions = principalToSession.get(principal);
|
||||||
if (userSessions == null) {
|
if (userSessions == null) {
|
||||||
final Set<String> tmp = Collections.synchronizedSet(new HashSet<String>());
|
final Set<String> tmp = Collections.synchronizedSet(new HashSet<String>());
|
||||||
|
|
|
@ -6,7 +6,7 @@ import java.util.Set;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public interface SessionIdMaper {
|
public interface SessionIdMapper {
|
||||||
Set<String> getUserSessions(String principal);
|
Set<String> getUserSessions(String principal);
|
||||||
|
|
||||||
String getSessionFromSSO(String sso);
|
String getSessionFromSSO(String sso);
|
|
@ -3,6 +3,7 @@ package org.keycloak.adapters.saml;
|
||||||
import org.keycloak.adapters.saml.SamlDeployment;
|
import org.keycloak.adapters.saml.SamlDeployment;
|
||||||
import org.keycloak.adapters.saml.config.IDP;
|
import org.keycloak.adapters.saml.config.IDP;
|
||||||
import org.keycloak.enums.SslRequired;
|
import org.keycloak.enums.SslRequired;
|
||||||
|
import org.keycloak.saml.SignatureAlgorithm;
|
||||||
|
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
|
@ -18,7 +19,6 @@ public class DefaultSamlDeployment implements SamlDeployment {
|
||||||
public static class DefaultSingleSignOnService implements IDP.SingleSignOnService {
|
public static class DefaultSingleSignOnService implements IDP.SingleSignOnService {
|
||||||
private boolean signRequest;
|
private boolean signRequest;
|
||||||
private boolean validateResponseSignature;
|
private boolean validateResponseSignature;
|
||||||
private String signatureCanonicalizationMethod;
|
|
||||||
private Binding requestBinding;
|
private Binding requestBinding;
|
||||||
private Binding responseBinding;
|
private Binding responseBinding;
|
||||||
private String requestBindingUrl;
|
private String requestBindingUrl;
|
||||||
|
@ -33,12 +33,7 @@ public class DefaultSamlDeployment implements SamlDeployment {
|
||||||
return validateResponseSignature;
|
return validateResponseSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getSignatureCanonicalizationMethod() {
|
|
||||||
return signatureCanonicalizationMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Binding getRequestBinding() {
|
public Binding getRequestBinding() {
|
||||||
return requestBinding;
|
return requestBinding;
|
||||||
}
|
}
|
||||||
|
@ -61,10 +56,6 @@ public class DefaultSamlDeployment implements SamlDeployment {
|
||||||
this.validateResponseSignature = validateResponseSignature;
|
this.validateResponseSignature = validateResponseSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
|
|
||||||
this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRequestBinding(Binding requestBinding) {
|
public void setRequestBinding(Binding requestBinding) {
|
||||||
this.requestBinding = requestBinding;
|
this.requestBinding = requestBinding;
|
||||||
}
|
}
|
||||||
|
@ -83,7 +74,6 @@ public class DefaultSamlDeployment implements SamlDeployment {
|
||||||
private boolean validateResponseSignature;
|
private boolean validateResponseSignature;
|
||||||
private boolean signRequest;
|
private boolean signRequest;
|
||||||
private boolean signResponse;
|
private boolean signResponse;
|
||||||
private String signatureCanonicalizationMethod;
|
|
||||||
private Binding requestBinding;
|
private Binding requestBinding;
|
||||||
private Binding responseBinding;
|
private Binding responseBinding;
|
||||||
private String requestBindingUrl;
|
private String requestBindingUrl;
|
||||||
|
@ -109,12 +99,7 @@ public class DefaultSamlDeployment implements SamlDeployment {
|
||||||
return signResponse;
|
return signResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getSignatureCanonicalizationMethod() {
|
|
||||||
return signatureCanonicalizationMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Binding getRequestBinding() {
|
public Binding getRequestBinding() {
|
||||||
return requestBinding;
|
return requestBinding;
|
||||||
}
|
}
|
||||||
|
@ -150,10 +135,6 @@ public class DefaultSamlDeployment implements SamlDeployment {
|
||||||
this.signResponse = signResponse;
|
this.signResponse = signResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
|
|
||||||
this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRequestBinding(Binding requestBinding) {
|
public void setRequestBinding(Binding requestBinding) {
|
||||||
this.requestBinding = requestBinding;
|
this.requestBinding = requestBinding;
|
||||||
}
|
}
|
||||||
|
@ -233,6 +214,8 @@ public class DefaultSamlDeployment implements SamlDeployment {
|
||||||
private PrincipalNamePolicy principalNamePolicy = PrincipalNamePolicy.FROM_NAME_ID;
|
private PrincipalNamePolicy principalNamePolicy = PrincipalNamePolicy.FROM_NAME_ID;
|
||||||
private String principalAttributeName;
|
private String principalAttributeName;
|
||||||
private String logoutPage;
|
private String logoutPage;
|
||||||
|
private SignatureAlgorithm signatureAlgorithm;
|
||||||
|
private String signatureCanonicalizationMethod;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -360,4 +343,22 @@ public class DefaultSamlDeployment implements SamlDeployment {
|
||||||
public void setLogoutPage(String logoutPage) {
|
public void setLogoutPage(String logoutPage) {
|
||||||
this.logoutPage = logoutPage;
|
this.logoutPage = logoutPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSignatureCanonicalizationMethod() {
|
||||||
|
return signatureCanonicalizationMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
|
||||||
|
this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SignatureAlgorithm getSignatureAlgorithm() {
|
||||||
|
return signatureAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignatureAlgorithm(SignatureAlgorithm signatureAlgorithm) {
|
||||||
|
this.signatureAlgorithm = signatureAlgorithm;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,18 +43,21 @@ public class InitiateLogin implements AuthChallenge {
|
||||||
nameIDPolicyFormat = JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get();
|
nameIDPolicyFormat = JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
String protocolBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get();
|
|
||||||
|
|
||||||
if (deployment.getIDP().getSingleSignOnService().getResponseBinding() == SamlDeployment.Binding.POST) {
|
|
||||||
protocolBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
SAML2AuthnRequestBuilder authnRequestBuilder = new SAML2AuthnRequestBuilder()
|
SAML2AuthnRequestBuilder authnRequestBuilder = new SAML2AuthnRequestBuilder()
|
||||||
.destination(destinationUrl)
|
.destination(destinationUrl)
|
||||||
.issuer(issuerURL)
|
.issuer(issuerURL)
|
||||||
.forceAuthn(deployment.isForceAuthentication())
|
.forceAuthn(deployment.isForceAuthentication())
|
||||||
.protocolBinding(protocolBinding)
|
|
||||||
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
|
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
|
||||||
|
if (deployment.getIDP().getSingleSignOnService().getResponseBinding() != null) {
|
||||||
|
String protocolBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get();
|
||||||
|
if (deployment.getIDP().getSingleSignOnService().getResponseBinding() == SamlDeployment.Binding.POST) {
|
||||||
|
protocolBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
|
||||||
|
}
|
||||||
|
authnRequestBuilder.protocolBinding(protocolBinding);
|
||||||
|
|
||||||
|
}
|
||||||
if (deployment.getAssertionConsumerServiceUrl() != null) {
|
if (deployment.getAssertionConsumerServiceUrl() != null) {
|
||||||
authnRequestBuilder.assertionConsumerUrl(deployment.getAssertionConsumerServiceUrl());
|
authnRequestBuilder.assertionConsumerUrl(deployment.getAssertionConsumerServiceUrl());
|
||||||
}
|
}
|
||||||
|
@ -67,8 +70,8 @@ public class InitiateLogin implements AuthChallenge {
|
||||||
if (keypair == null) {
|
if (keypair == null) {
|
||||||
throw new RuntimeException("Signing keys not configured");
|
throw new RuntimeException("Signing keys not configured");
|
||||||
}
|
}
|
||||||
if (deployment.getIDP().getSingleSignOnService().getSignatureCanonicalizationMethod() != null) {
|
if (deployment.getSignatureCanonicalizationMethod() != null) {
|
||||||
binding.canonicalizationMethod(deployment.getIDP().getSingleSignOnService().getSignatureCanonicalizationMethod());
|
binding.canonicalizationMethod(deployment.getSignatureCanonicalizationMethod());
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.signWith(keypair);
|
binding.signWith(keypair);
|
||||||
|
@ -78,7 +81,7 @@ public class InitiateLogin implements AuthChallenge {
|
||||||
|
|
||||||
Document document = authnRequestBuilder.toDocument();
|
Document document = authnRequestBuilder.toDocument();
|
||||||
SamlDeployment.Binding samlBinding = deployment.getIDP().getSingleSignOnService().getRequestBinding();
|
SamlDeployment.Binding samlBinding = deployment.getIDP().getSingleSignOnService().getRequestBinding();
|
||||||
SamlUtil.sendSaml(httpFacade, actionUrl, binding, document, samlBinding);
|
SamlUtil.sendSaml(true, httpFacade, actionUrl, binding, document, samlBinding);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Could not create authentication request.", e);
|
throw new RuntimeException("Could not create authentication request.", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,11 +25,11 @@ import org.keycloak.saml.common.constants.GeneralConstants;
|
||||||
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
||||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||||
import org.keycloak.saml.common.exceptions.ProcessingException;
|
import org.keycloak.saml.common.exceptions.ProcessingException;
|
||||||
|
import org.keycloak.saml.common.util.Base64;
|
||||||
import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
|
import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
|
||||||
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
||||||
import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;
|
import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;
|
||||||
import org.keycloak.saml.processing.web.util.PostBindingUtil;
|
import org.keycloak.saml.processing.web.util.PostBindingUtil;
|
||||||
import org.keycloak.saml.processing.web.util.RedirectBindingUtil;
|
|
||||||
import org.keycloak.util.KeycloakUriBuilder;
|
import org.keycloak.util.KeycloakUriBuilder;
|
||||||
import org.keycloak.util.MultivaluedHashMap;
|
import org.keycloak.util.MultivaluedHashMap;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
|
@ -103,7 +103,7 @@ public abstract class SamlAuthenticator {
|
||||||
binding.relayState("logout");
|
binding.relayState("logout");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SamlUtil.sendSaml(facade, deployment.getIDP().getSingleLogoutService().getRequestBindingUrl(), binding, logoutBuilder.buildDocument(), deployment.getIDP().getSingleLogoutService().getRequestBinding());
|
SamlUtil.sendSaml(true, facade, deployment.getIDP().getSingleLogoutService().getRequestBindingUrl(), binding, logoutBuilder.buildDocument(), deployment.getIDP().getSingleLogoutService().getRequestBinding());
|
||||||
} catch (ProcessingException e) {
|
} catch (ProcessingException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
|
@ -119,15 +119,22 @@ public abstract class SamlAuthenticator {
|
||||||
protected AuthOutcome handleSamlRequest(String samlRequest, String relayState) {
|
protected AuthOutcome handleSamlRequest(String samlRequest, String relayState) {
|
||||||
SAMLDocumentHolder holder = null;
|
SAMLDocumentHolder holder = null;
|
||||||
boolean postBinding = false;
|
boolean postBinding = false;
|
||||||
|
String requestUri = facade.getRequest().getURI();
|
||||||
if (facade.getRequest().getMethod().equalsIgnoreCase("GET")) {
|
if (facade.getRequest().getMethod().equalsIgnoreCase("GET")) {
|
||||||
|
// strip out query params
|
||||||
|
int index = requestUri.indexOf('?');
|
||||||
|
if (index > -1) {
|
||||||
|
requestUri = requestUri.substring(0, index);
|
||||||
|
}
|
||||||
holder = SAMLRequestParser.parseRequestRedirectBinding(samlRequest);
|
holder = SAMLRequestParser.parseRequestRedirectBinding(samlRequest);
|
||||||
} else {
|
} else {
|
||||||
postBinding = true;
|
postBinding = true;
|
||||||
holder = SAMLRequestParser.parseRequestPostBinding(samlRequest);
|
holder = SAMLRequestParser.parseRequestPostBinding(samlRequest);
|
||||||
}
|
}
|
||||||
RequestAbstractType requestAbstractType = (RequestAbstractType) holder.getSamlObject();
|
RequestAbstractType requestAbstractType = (RequestAbstractType) holder.getSamlObject();
|
||||||
if (!facade.getRequest().getURI().toString().equals(requestAbstractType.getDestination())) {
|
if (!requestUri.equals(requestAbstractType.getDestination().toString())) {
|
||||||
throw new RuntimeException("destination not equal to request");
|
log.error("expected destination '" + requestUri + "' got '" + requestAbstractType.getDestination() + "'");
|
||||||
|
throw new RuntimeException("destination not equal to request.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requestAbstractType instanceof LogoutRequestType) {
|
if (requestAbstractType instanceof LogoutRequestType) {
|
||||||
|
@ -156,14 +163,15 @@ public abstract class SamlAuthenticator {
|
||||||
builder.issuer(issuerURL);
|
builder.issuer(issuerURL);
|
||||||
BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder().relayState(relayState);
|
BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder().relayState(relayState);
|
||||||
if (deployment.getIDP().getSingleLogoutService().signResponse()) {
|
if (deployment.getIDP().getSingleLogoutService().signResponse()) {
|
||||||
binding.signWith(deployment.getSigningKeyPair())
|
binding.signatureAlgorithm(deployment.getSignatureAlgorithm())
|
||||||
|
.signWith(deployment.getSigningKeyPair())
|
||||||
.signDocument();
|
.signDocument();
|
||||||
binding.canonicalizationMethod(deployment.getIDP().getSingleLogoutService().getSignatureCanonicalizationMethod());
|
if (deployment.getSignatureCanonicalizationMethod() != null) binding.canonicalizationMethod(deployment.getSignatureCanonicalizationMethod());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SamlUtil.sendSaml(facade, deployment.getIDP().getSingleLogoutService().getResponseBindingUrl(), binding, builder.buildDocument(),
|
SamlUtil.sendSaml(false, facade, deployment.getIDP().getSingleLogoutService().getResponseBindingUrl(), binding, builder.buildDocument(),
|
||||||
deployment.getIDP().getSingleLogoutService().getResponseBinding());
|
deployment.getIDP().getSingleLogoutService().getResponseBinding());
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
@ -180,7 +188,12 @@ public abstract class SamlAuthenticator {
|
||||||
protected AuthOutcome handleSamlResponse(String samlResponse, String relayState) {
|
protected AuthOutcome handleSamlResponse(String samlResponse, String relayState) {
|
||||||
SAMLDocumentHolder holder = null;
|
SAMLDocumentHolder holder = null;
|
||||||
boolean postBinding = false;
|
boolean postBinding = false;
|
||||||
|
String requestUri = facade.getRequest().getURI();
|
||||||
if (facade.getRequest().getMethod().equalsIgnoreCase("GET")) {
|
if (facade.getRequest().getMethod().equalsIgnoreCase("GET")) {
|
||||||
|
int index = requestUri.indexOf('?');
|
||||||
|
if (index > -1) {
|
||||||
|
requestUri = requestUri.substring(0, index);
|
||||||
|
}
|
||||||
holder = extractRedirectBindingResponse(samlResponse);
|
holder = extractRedirectBindingResponse(samlResponse);
|
||||||
} else {
|
} else {
|
||||||
postBinding = true;
|
postBinding = true;
|
||||||
|
@ -188,18 +201,18 @@ public abstract class SamlAuthenticator {
|
||||||
}
|
}
|
||||||
StatusResponseType statusResponse = (StatusResponseType)holder.getSamlObject();
|
StatusResponseType statusResponse = (StatusResponseType)holder.getSamlObject();
|
||||||
// validate destination
|
// validate destination
|
||||||
if (!facade.getRequest().getURI().toString().equals(statusResponse.getDestination())) {
|
if (!requestUri.equals(statusResponse.getDestination())) {
|
||||||
throw new RuntimeException("destination not equal to request");
|
throw new RuntimeException("destination not equal to request");
|
||||||
}
|
}
|
||||||
if (statusResponse instanceof ResponseType) {
|
if (statusResponse instanceof ResponseType) {
|
||||||
if (deployment.getIDP().getSingleSignOnService().validateResponseSignature()) {
|
if (deployment.getIDP().getSingleSignOnService().validateResponseSignature()) {
|
||||||
validateSamlSignature(holder, postBinding, GeneralConstants.SAML_REQUEST_KEY);
|
validateSamlSignature(holder, postBinding, GeneralConstants.SAML_RESPONSE_KEY);
|
||||||
}
|
}
|
||||||
return handleLoginResponse((ResponseType)statusResponse);
|
return handleLoginResponse((ResponseType)statusResponse);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (deployment.getIDP().getSingleLogoutService().validateResponseSignature()) {
|
if (deployment.getIDP().getSingleLogoutService().validateResponseSignature()) {
|
||||||
validateSamlSignature(holder, postBinding, GeneralConstants.SAML_REQUEST_KEY);
|
validateSamlSignature(holder, postBinding, GeneralConstants.SAML_RESPONSE_KEY);
|
||||||
}
|
}
|
||||||
// todo need to check that it is actually a LogoutResponse
|
// todo need to check that it is actually a LogoutResponse
|
||||||
return handleLogoutResponse(holder, statusResponse, relayState);
|
return handleLogoutResponse(holder, statusResponse, relayState);
|
||||||
|
@ -320,10 +333,16 @@ public abstract class SamlAuthenticator {
|
||||||
sessionStore.saveAccount(account);
|
sessionStore.saveAccount(account);
|
||||||
completeAuthentication(account);
|
completeAuthentication(account);
|
||||||
|
|
||||||
|
|
||||||
// redirect to original request, it will be restored
|
// redirect to original request, it will be restored
|
||||||
facade.getResponse().setHeader("Location", sessionStore.getRedirectUri());
|
String redirectUri = sessionStore.getRedirectUri();
|
||||||
facade.getResponse().setStatus(302);
|
if (redirectUri != null) {
|
||||||
facade.getResponse().end();
|
facade.getResponse().setHeader("Location", redirectUri);
|
||||||
|
facade.getResponse().setStatus(302);
|
||||||
|
facade.getResponse().end();
|
||||||
|
} else {
|
||||||
|
log.debug("IDP initiated invocation");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return AuthOutcome.AUTHENTICATED;
|
return AuthOutcome.AUTHENTICATED;
|
||||||
|
@ -400,7 +419,9 @@ public abstract class SamlAuthenticator {
|
||||||
String signature = facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);
|
String signature = facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);
|
||||||
String decodedAlgorithm = facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
|
String decodedAlgorithm = facade.getRequest().getQueryParamValue(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
|
||||||
|
|
||||||
if (request == null) throw new VerificationException("SAM was null");
|
if (request == null) {
|
||||||
|
throw new VerificationException("SAML Request was null");
|
||||||
|
}
|
||||||
if (algorithm == null) throw new VerificationException("SigAlg was null");
|
if (algorithm == null) throw new VerificationException("SigAlg was null");
|
||||||
if (signature == null) throw new VerificationException("Signature was null");
|
if (signature == null) throw new VerificationException("Signature was null");
|
||||||
|
|
||||||
|
@ -417,7 +438,8 @@ public abstract class SamlAuthenticator {
|
||||||
String rawQuery = builder.build().getRawQuery();
|
String rawQuery = builder.build().getRawQuery();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
byte[] decodedSignature = RedirectBindingUtil.urlBase64Decode(signature);
|
//byte[] decodedSignature = RedirectBindingUtil.urlBase64Decode(signature);
|
||||||
|
byte[] decodedSignature = Base64.decode(signature);
|
||||||
|
|
||||||
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.getFromXmlMethod(decodedAlgorithm);
|
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.getFromXmlMethod(decodedAlgorithm);
|
||||||
Signature validator = signatureAlgorithm.createSignature(); // todo plugin signature alg
|
Signature validator = signatureAlgorithm.createSignature(); // todo plugin signature alg
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.keycloak.adapters.saml;
|
package org.keycloak.adapters.saml;
|
||||||
|
|
||||||
import org.keycloak.enums.SslRequired;
|
import org.keycloak.enums.SslRequired;
|
||||||
|
import org.keycloak.saml.SignatureAlgorithm;
|
||||||
|
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
|
@ -32,7 +33,6 @@ public interface SamlDeployment {
|
||||||
public interface SingleSignOnService {
|
public interface SingleSignOnService {
|
||||||
boolean signRequest();
|
boolean signRequest();
|
||||||
boolean validateResponseSignature();
|
boolean validateResponseSignature();
|
||||||
String getSignatureCanonicalizationMethod();
|
|
||||||
Binding getRequestBinding();
|
Binding getRequestBinding();
|
||||||
Binding getResponseBinding();
|
Binding getResponseBinding();
|
||||||
String getRequestBindingUrl();
|
String getRequestBindingUrl();
|
||||||
|
@ -42,7 +42,6 @@ public interface SamlDeployment {
|
||||||
boolean validateResponseSignature();
|
boolean validateResponseSignature();
|
||||||
boolean signRequest();
|
boolean signRequest();
|
||||||
boolean signResponse();
|
boolean signResponse();
|
||||||
String getSignatureCanonicalizationMethod();
|
|
||||||
Binding getRequestBinding();
|
Binding getRequestBinding();
|
||||||
Binding getResponseBinding();
|
Binding getResponseBinding();
|
||||||
String getRequestBindingUrl();
|
String getRequestBindingUrl();
|
||||||
|
@ -59,6 +58,8 @@ public interface SamlDeployment {
|
||||||
boolean isForceAuthentication();
|
boolean isForceAuthentication();
|
||||||
PrivateKey getDecryptionKey();
|
PrivateKey getDecryptionKey();
|
||||||
KeyPair getSigningKeyPair();
|
KeyPair getSigningKeyPair();
|
||||||
|
String getSignatureCanonicalizationMethod();
|
||||||
|
SignatureAlgorithm getSignatureAlgorithm();
|
||||||
String getAssertionConsumerServiceUrl();
|
String getAssertionConsumerServiceUrl();
|
||||||
String getLogoutPage();
|
String getLogoutPage();
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,11 @@ import java.io.IOException;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class SamlUtil {
|
public class SamlUtil {
|
||||||
public static void sendSaml(HttpFacade httpFacade, String actionUrl, BaseSAML2BindingBuilder binding, Document document, SamlDeployment.Binding samlBinding) throws ProcessingException, ConfigurationException, IOException {
|
public static void sendSaml(boolean asRequest, HttpFacade httpFacade, String actionUrl,
|
||||||
|
BaseSAML2BindingBuilder binding, Document document,
|
||||||
|
SamlDeployment.Binding samlBinding) throws ProcessingException, ConfigurationException, IOException {
|
||||||
if (samlBinding == SamlDeployment.Binding.POST) {
|
if (samlBinding == SamlDeployment.Binding.POST) {
|
||||||
String html = binding.postBinding(document).getHtmlRequest(actionUrl);
|
String html = asRequest ? binding.postBinding(document).getHtmlRequest(actionUrl) : binding.postBinding(document).getHtmlResponse(actionUrl) ;
|
||||||
httpFacade.getResponse().setStatus(200);
|
httpFacade.getResponse().setStatus(200);
|
||||||
httpFacade.getResponse().setHeader("Content-Type", "text/html");
|
httpFacade.getResponse().setHeader("Content-Type", "text/html");
|
||||||
httpFacade.getResponse().setHeader("Pragma", "no-cache");
|
httpFacade.getResponse().setHeader("Pragma", "no-cache");
|
||||||
|
@ -23,9 +25,11 @@ public class SamlUtil {
|
||||||
httpFacade.getResponse().getOutputStream().write(html.getBytes());
|
httpFacade.getResponse().getOutputStream().write(html.getBytes());
|
||||||
httpFacade.getResponse().end();
|
httpFacade.getResponse().end();
|
||||||
} else {
|
} else {
|
||||||
String uri = binding.redirectBinding(document).requestURI(actionUrl).toString();
|
String uri = asRequest ? binding.redirectBinding(document).requestURI(actionUrl).toString() : binding.redirectBinding(document).responseURI(actionUrl).toString();
|
||||||
httpFacade.getResponse().setStatus(302);
|
httpFacade.getResponse().setStatus(302);
|
||||||
httpFacade.getResponse().setHeader("Location", uri);
|
httpFacade.getResponse().setHeader("Location", uri);
|
||||||
|
httpFacade.getResponse().end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ public class IDP implements Serializable {
|
||||||
public static class SingleSignOnService implements Serializable {
|
public static class SingleSignOnService implements Serializable {
|
||||||
private boolean signRequest;
|
private boolean signRequest;
|
||||||
private boolean validateResponseSignature;
|
private boolean validateResponseSignature;
|
||||||
private String signatureCanonicalizationMethod;
|
|
||||||
private String requestBinding;
|
private String requestBinding;
|
||||||
private String responseBinding;
|
private String responseBinding;
|
||||||
private String bindingUrl;
|
private String bindingUrl;
|
||||||
|
@ -34,14 +33,6 @@ public class IDP implements Serializable {
|
||||||
this.validateResponseSignature = validateResponseSignature;
|
this.validateResponseSignature = validateResponseSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSignatureCanonicalizationMethod() {
|
|
||||||
return signatureCanonicalizationMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
|
|
||||||
this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRequestBinding() {
|
public String getRequestBinding() {
|
||||||
return requestBinding;
|
return requestBinding;
|
||||||
}
|
}
|
||||||
|
@ -72,7 +63,6 @@ public class IDP implements Serializable {
|
||||||
private boolean signResponse;
|
private boolean signResponse;
|
||||||
private boolean validateRequestSignature;
|
private boolean validateRequestSignature;
|
||||||
private boolean validateResponseSignature;
|
private boolean validateResponseSignature;
|
||||||
private String signatureCanonicalizationMethod;
|
|
||||||
private String requestBinding;
|
private String requestBinding;
|
||||||
private String responseBinding;
|
private String responseBinding;
|
||||||
private String postBindingUrl;
|
private String postBindingUrl;
|
||||||
|
@ -110,14 +100,6 @@ public class IDP implements Serializable {
|
||||||
this.validateResponseSignature = validateResponseSignature;
|
this.validateResponseSignature = validateResponseSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSignatureCanonicalizationMethod() {
|
|
||||||
return signatureCanonicalizationMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
|
|
||||||
this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRequestBinding() {
|
public String getRequestBinding() {
|
||||||
return requestBinding;
|
return requestBinding;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,8 @@ public class SP implements Serializable {
|
||||||
private PrincipalNameMapping principalNameMapping;
|
private PrincipalNameMapping principalNameMapping;
|
||||||
private Set<String> roleAttributes;
|
private Set<String> roleAttributes;
|
||||||
private Set<String> roleFriendlyAttributes;
|
private Set<String> roleFriendlyAttributes;
|
||||||
|
private String signatureAlgorithm;
|
||||||
|
private String signatureCanonicalizationMethod;
|
||||||
private IDP idp;
|
private IDP idp;
|
||||||
|
|
||||||
public String getEntityID() {
|
public String getEntityID() {
|
||||||
|
@ -123,4 +125,20 @@ public class SP implements Serializable {
|
||||||
public void setLogoutPage(String logoutPage) {
|
public void setLogoutPage(String logoutPage) {
|
||||||
this.logoutPage = logoutPage;
|
this.logoutPage = logoutPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSignatureAlgorithm() {
|
||||||
|
return signatureAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignatureAlgorithm(String signatureAlgorithm) {
|
||||||
|
this.signatureAlgorithm = signatureAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSignatureCanonicalizationMethod() {
|
||||||
|
return signatureCanonicalizationMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
|
||||||
|
this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ public class ConfigXmlConstants {
|
||||||
public static final String SSL_POLICY_ATTR = "sslPolicy";
|
public static final String SSL_POLICY_ATTR = "sslPolicy";
|
||||||
public static final String NAME_ID_POLICY_FORMAT_ATTR = "nameIDPolicyFormat";
|
public static final String NAME_ID_POLICY_FORMAT_ATTR = "nameIDPolicyFormat";
|
||||||
public static final String FORCE_AUTHENTICATION_ATTR = "forceAuthentication";
|
public static final String FORCE_AUTHENTICATION_ATTR = "forceAuthentication";
|
||||||
|
public static final String SIGNATURE_ALGORITHM_ATTR = "signatureAlgorithm";
|
||||||
|
public static final String SIGNATURE_CANONICALIZATION_METHOD_ATTR = "signatureCanonicalizationMethod";
|
||||||
public static final String LOGOUT_PAGE_ATTR = "logoutPage";
|
public static final String LOGOUT_PAGE_ATTR = "logoutPage";
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,7 +47,6 @@ public class ConfigXmlConstants {
|
||||||
public static final String SINGLE_LOGOUT_SERVICE_ELEMENT = "SingleLogoutService";
|
public static final String SINGLE_LOGOUT_SERVICE_ELEMENT = "SingleLogoutService";
|
||||||
public static final String SIGN_REQUEST_ATTR = "signRequest";
|
public static final String SIGN_REQUEST_ATTR = "signRequest";
|
||||||
public static final String SIGN_RESPONSE_ATTR = "signResponse";
|
public static final String SIGN_RESPONSE_ATTR = "signResponse";
|
||||||
public static final String SIGNATURE_CANONICALIZATION_METHOD_ATTR = "signatureCanonicalizationMethod";
|
|
||||||
public static final String REQUEST_BINDING_ATTR = "requestBinding";
|
public static final String REQUEST_BINDING_ATTR = "requestBinding";
|
||||||
public static final String RESPONSE_BINDING_ATTR = "responseBinding";
|
public static final String RESPONSE_BINDING_ATTR = "responseBinding";
|
||||||
public static final String BINDING_URL_ATTR = "bindingUrl";
|
public static final String BINDING_URL_ATTR = "bindingUrl";
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.keycloak.adapters.saml.config.Key;
|
||||||
import org.keycloak.adapters.saml.config.KeycloakSamlAdapter;
|
import org.keycloak.adapters.saml.config.KeycloakSamlAdapter;
|
||||||
import org.keycloak.adapters.saml.config.SP;
|
import org.keycloak.adapters.saml.config.SP;
|
||||||
import org.keycloak.enums.SslRequired;
|
import org.keycloak.enums.SslRequired;
|
||||||
|
import org.keycloak.saml.SignatureAlgorithm;
|
||||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||||
import org.keycloak.util.PemUtils;
|
import org.keycloak.util.PemUtils;
|
||||||
|
|
||||||
|
@ -40,6 +41,11 @@ public class DeploymentBuilder {
|
||||||
deployment.setForceAuthentication(sp.isForceAuthentication());
|
deployment.setForceAuthentication(sp.isForceAuthentication());
|
||||||
deployment.setNameIDPolicyFormat(sp.getNameIDPolicyFormat());
|
deployment.setNameIDPolicyFormat(sp.getNameIDPolicyFormat());
|
||||||
deployment.setLogoutPage(sp.getLogoutPage());
|
deployment.setLogoutPage(sp.getLogoutPage());
|
||||||
|
deployment.setSignatureCanonicalizationMethod(sp.getSignatureCanonicalizationMethod());
|
||||||
|
deployment.setSignatureAlgorithm(SignatureAlgorithm.RSA_SHA256);
|
||||||
|
if (sp.getSignatureAlgorithm() != null) {
|
||||||
|
deployment.setSignatureAlgorithm(SignatureAlgorithm.valueOf(sp.getSignatureAlgorithm()));
|
||||||
|
}
|
||||||
if (sp.getPrincipalNameMapping() != null) {
|
if (sp.getPrincipalNameMapping() != null) {
|
||||||
SamlDeployment.PrincipalNamePolicy policy = SamlDeployment.PrincipalNamePolicy.valueOf(sp.getPrincipalNameMapping().getPolicy());
|
SamlDeployment.PrincipalNamePolicy policy = SamlDeployment.PrincipalNamePolicy.valueOf(sp.getPrincipalNameMapping().getPolicy());
|
||||||
deployment.setPrincipalNamePolicy(policy);
|
deployment.setPrincipalNamePolicy(policy);
|
||||||
|
@ -51,45 +57,47 @@ public class DeploymentBuilder {
|
||||||
SslRequired ssl = SslRequired.valueOf(sp.getSslPolicy());
|
SslRequired ssl = SslRequired.valueOf(sp.getSslPolicy());
|
||||||
deployment.setSslRequired(ssl);
|
deployment.setSslRequired(ssl);
|
||||||
}
|
}
|
||||||
for (Key key : sp.getKeys()) {
|
if (sp.getKeys() != null) {
|
||||||
if (key.isSigning()) {
|
for (Key key : sp.getKeys()) {
|
||||||
PrivateKey privateKey = null;
|
if (key.isSigning()) {
|
||||||
PublicKey publicKey = null;
|
PrivateKey privateKey = null;
|
||||||
if (key.getKeystore() != null) {
|
PublicKey publicKey = null;
|
||||||
KeyStore keyStore = loadKeystore(resourceLoader, key);
|
if (key.getKeystore() != null) {
|
||||||
Certificate cert = null;
|
KeyStore keyStore = loadKeystore(resourceLoader, key);
|
||||||
try {
|
Certificate cert = null;
|
||||||
cert = keyStore.getCertificate(key.getKeystore().getCertificateAlias());
|
try {
|
||||||
privateKey = (PrivateKey)keyStore.getKey(key.getKeystore().getPrivateKeyAlias(), key.getKeystore().getPrivateKeyPassword().toCharArray());
|
cert = keyStore.getCertificate(key.getKeystore().getCertificateAlias());
|
||||||
} catch (Exception e) {
|
privateKey = (PrivateKey) keyStore.getKey(key.getKeystore().getPrivateKeyAlias(), key.getKeystore().getPrivateKeyPassword().toCharArray());
|
||||||
throw new RuntimeException(e);
|
} catch (Exception e) {
|
||||||
}
|
throw new RuntimeException(e);
|
||||||
publicKey = cert.getPublicKey();
|
|
||||||
} else {
|
|
||||||
if (key.getPrivateKeyPem() == null) {
|
|
||||||
throw new RuntimeException("SP signing key must have a PrivateKey defined");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
privateKey = PemUtils.decodePrivateKey(key.getPrivateKeyPem().trim());
|
|
||||||
if (key.getPublicKeyPem() == null &&key.getCertificatePem() == null) {
|
|
||||||
throw new RuntimeException("Sp signing key must have a PublicKey or Certificate defined");
|
|
||||||
}
|
}
|
||||||
publicKey = getPublicKeyFromPem(key);
|
publicKey = cert.getPublicKey();
|
||||||
|
} else {
|
||||||
|
if (key.getPrivateKeyPem() == null) {
|
||||||
|
throw new RuntimeException("SP signing key must have a PrivateKey defined");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
privateKey = PemUtils.decodePrivateKey(key.getPrivateKeyPem().trim());
|
||||||
|
if (key.getPublicKeyPem() == null && key.getCertificatePem() == null) {
|
||||||
|
throw new RuntimeException("Sp signing key must have a PublicKey or Certificate defined");
|
||||||
|
}
|
||||||
|
publicKey = getPublicKeyFromPem(key);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyPair keyPair = new KeyPair(publicKey, privateKey);
|
||||||
|
deployment.setSigningKeyPair(keyPair);
|
||||||
|
|
||||||
|
} else if (key.isEncryption()) {
|
||||||
|
KeyStore keyStore = loadKeystore(resourceLoader, key);
|
||||||
|
try {
|
||||||
|
PrivateKey privateKey = (PrivateKey) keyStore.getKey(key.getKeystore().getPrivateKeyAlias(), key.getKeystore().getPrivateKeyPassword().toCharArray());
|
||||||
|
deployment.setDecryptionKey(privateKey);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyPair keyPair = new KeyPair(publicKey, privateKey);
|
|
||||||
deployment.setSigningKeyPair(keyPair);
|
|
||||||
|
|
||||||
} else if (key.isEncryption()) {
|
|
||||||
KeyStore keyStore = loadKeystore(resourceLoader, key);
|
|
||||||
try {
|
|
||||||
PrivateKey privateKey = (PrivateKey)keyStore.getKey(key.getKeystore().getPrivateKeyAlias(), key.getKeystore().getPrivateKeyPassword().toCharArray());
|
|
||||||
deployment.setDecryptionKey(privateKey);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,8 +105,9 @@ public class DeploymentBuilder {
|
||||||
idp.setEntityID(sp.getIdp().getEntityID());
|
idp.setEntityID(sp.getIdp().getEntityID());
|
||||||
sso.setRequestBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleSignOnService().getRequestBinding()));
|
sso.setRequestBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleSignOnService().getRequestBinding()));
|
||||||
sso.setRequestBindingUrl(sp.getIdp().getSingleSignOnService().getBindingUrl());
|
sso.setRequestBindingUrl(sp.getIdp().getSingleSignOnService().getBindingUrl());
|
||||||
sso.setResponseBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleSignOnService().getResponseBinding()));
|
if (sp.getIdp().getSingleSignOnService().getResponseBinding() != null) {
|
||||||
sso.setSignatureCanonicalizationMethod(sp.getIdp().getSingleSignOnService().getSignatureCanonicalizationMethod());
|
sso.setResponseBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleSignOnService().getResponseBinding()));
|
||||||
|
}
|
||||||
sso.setSignRequest(sp.getIdp().getSingleSignOnService().isSignRequest());
|
sso.setSignRequest(sp.getIdp().getSingleSignOnService().isSignRequest());
|
||||||
sso.setValidateResponseSignature(sp.getIdp().getSingleSignOnService().isValidateResponseSignature());
|
sso.setValidateResponseSignature(sp.getIdp().getSingleSignOnService().isValidateResponseSignature());
|
||||||
|
|
||||||
|
@ -106,7 +115,6 @@ public class DeploymentBuilder {
|
||||||
slo.setSignResponse(sp.getIdp().getSingleLogoutService().isSignResponse());
|
slo.setSignResponse(sp.getIdp().getSingleLogoutService().isSignResponse());
|
||||||
slo.setValidateResponseSignature(sp.getIdp().getSingleLogoutService().isValidateResponseSignature());
|
slo.setValidateResponseSignature(sp.getIdp().getSingleLogoutService().isValidateResponseSignature());
|
||||||
slo.setValidateRequestSignature(sp.getIdp().getSingleLogoutService().isValidateRequestSignature());
|
slo.setValidateRequestSignature(sp.getIdp().getSingleLogoutService().isValidateRequestSignature());
|
||||||
slo.setSignatureCanonicalizationMethod(sp.getIdp().getSingleLogoutService().getSignatureCanonicalizationMethod());
|
|
||||||
slo.setRequestBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleLogoutService().getRequestBinding()));
|
slo.setRequestBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleLogoutService().getRequestBinding()));
|
||||||
slo.setResponseBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleLogoutService().getResponseBinding()));
|
slo.setResponseBinding(SamlDeployment.Binding.parseBinding(sp.getIdp().getSingleLogoutService().getResponseBinding()));
|
||||||
if (slo.getRequestBinding() == SamlDeployment.Binding.POST) {
|
if (slo.getRequestBinding() == SamlDeployment.Binding.POST) {
|
||||||
|
@ -119,26 +127,28 @@ public class DeploymentBuilder {
|
||||||
} else {
|
} else {
|
||||||
slo.setResponseBindingUrl(sp.getIdp().getSingleLogoutService().getRedirectBindingUrl());
|
slo.setResponseBindingUrl(sp.getIdp().getSingleLogoutService().getRedirectBindingUrl());
|
||||||
}
|
}
|
||||||
for (Key key : sp.getIdp().getKeys()) {
|
if (sp.getIdp().getKeys() != null) {
|
||||||
if (key.isSigning()) {
|
for (Key key : sp.getIdp().getKeys()) {
|
||||||
if (key.getKeystore() != null) {
|
if (key.isSigning()) {
|
||||||
KeyStore keyStore = loadKeystore(resourceLoader, key);
|
if (key.getKeystore() != null) {
|
||||||
Certificate cert = null;
|
KeyStore keyStore = loadKeystore(resourceLoader, key);
|
||||||
try {
|
Certificate cert = null;
|
||||||
cert = keyStore.getCertificate(key.getKeystore().getCertificateAlias());
|
try {
|
||||||
} catch (KeyStoreException e) {
|
cert = keyStore.getCertificate(key.getKeystore().getCertificateAlias());
|
||||||
throw new RuntimeException(e);
|
} catch (KeyStoreException e) {
|
||||||
}
|
throw new RuntimeException(e);
|
||||||
idp.setSignatureValidationKey(cert.getPublicKey());
|
}
|
||||||
} else {
|
idp.setSignatureValidationKey(cert.getPublicKey());
|
||||||
if (key.getPublicKeyPem() == null && key.getCertificatePem() == null) {
|
} else {
|
||||||
throw new RuntimeException("IDP signing key must have a PublicKey or Certificate defined");
|
if (key.getPublicKeyPem() == null && key.getCertificatePem() == null) {
|
||||||
}
|
throw new RuntimeException("IDP signing key must have a PublicKey or Certificate defined");
|
||||||
try {
|
}
|
||||||
PublicKey publicKey = getPublicKeyFromPem(key);
|
try {
|
||||||
idp.setSignatureValidationKey(publicKey);
|
PublicKey publicKey = getPublicKeyFromPem(key);
|
||||||
} catch (Exception e) {
|
idp.setSignatureValidationKey(publicKey);
|
||||||
throw new RuntimeException(e);
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,6 @@ public class IDPXmlParser extends AbstractParser {
|
||||||
slo.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR));
|
slo.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR));
|
||||||
slo.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR));
|
slo.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR));
|
||||||
slo.setValidateRequestSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR));
|
slo.setValidateRequestSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR));
|
||||||
slo.setSignatureCanonicalizationMethod(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
|
|
||||||
slo.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
|
slo.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
|
||||||
slo.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
|
slo.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
|
||||||
slo.setSignResponse(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR));
|
slo.setSignResponse(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR));
|
||||||
|
@ -86,7 +85,6 @@ public class IDPXmlParser extends AbstractParser {
|
||||||
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
|
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
|
||||||
sso.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR));
|
sso.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR));
|
||||||
sso.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR));
|
sso.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR));
|
||||||
sso.setSignatureCanonicalizationMethod(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
|
|
||||||
sso.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
|
sso.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
|
||||||
sso.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
|
sso.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
|
||||||
sso.setBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
|
sso.setBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
|
||||||
|
|
|
@ -37,6 +37,8 @@ public class SPXmlParser extends AbstractParser {
|
||||||
sp.setSslPolicy(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SSL_POLICY_ATTR));
|
sp.setSslPolicy(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SSL_POLICY_ATTR));
|
||||||
sp.setLogoutPage(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.LOGOUT_PAGE_ATTR));
|
sp.setLogoutPage(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.LOGOUT_PAGE_ATTR));
|
||||||
sp.setNameIDPolicyFormat(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.NAME_ID_POLICY_FORMAT_ATTR));
|
sp.setNameIDPolicyFormat(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.NAME_ID_POLICY_FORMAT_ATTR));
|
||||||
|
sp.setSignatureCanonicalizationMethod(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
|
||||||
|
sp.setSignatureAlgorithm(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_ALGORITHM_ATTR));
|
||||||
sp.setForceAuthentication(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.FORCE_AUTHENTICATION_ATTR));
|
sp.setForceAuthentication(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.FORCE_AUTHENTICATION_ATTR));
|
||||||
while (xmlEventReader.hasNext()) {
|
while (xmlEventReader.hasNext()) {
|
||||||
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
|
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
|
||||||
|
|
|
@ -55,7 +55,6 @@ public class XmlParserTest {
|
||||||
Assert.assertEquals("idp", idp.getEntityID());
|
Assert.assertEquals("idp", idp.getEntityID());
|
||||||
Assert.assertTrue(idp.getSingleSignOnService().isSignRequest());
|
Assert.assertTrue(idp.getSingleSignOnService().isSignRequest());
|
||||||
Assert.assertTrue(idp.getSingleSignOnService().isValidateResponseSignature());
|
Assert.assertTrue(idp.getSingleSignOnService().isValidateResponseSignature());
|
||||||
Assert.assertEquals("canon", idp.getSingleSignOnService().getSignatureCanonicalizationMethod());
|
|
||||||
Assert.assertEquals("post", idp.getSingleSignOnService().getRequestBinding());
|
Assert.assertEquals("post", idp.getSingleSignOnService().getRequestBinding());
|
||||||
Assert.assertEquals("url", idp.getSingleSignOnService().getBindingUrl());
|
Assert.assertEquals("url", idp.getSingleSignOnService().getBindingUrl());
|
||||||
|
|
||||||
|
@ -63,7 +62,6 @@ public class XmlParserTest {
|
||||||
Assert.assertTrue(idp.getSingleLogoutService().isSignResponse());
|
Assert.assertTrue(idp.getSingleLogoutService().isSignResponse());
|
||||||
Assert.assertTrue(idp.getSingleLogoutService().isValidateRequestSignature());
|
Assert.assertTrue(idp.getSingleLogoutService().isValidateRequestSignature());
|
||||||
Assert.assertTrue(idp.getSingleLogoutService().isValidateResponseSignature());
|
Assert.assertTrue(idp.getSingleLogoutService().isValidateResponseSignature());
|
||||||
Assert.assertEquals("canon", idp.getSingleLogoutService().getSignatureCanonicalizationMethod());
|
|
||||||
Assert.assertEquals("redirect", idp.getSingleLogoutService().getRequestBinding());
|
Assert.assertEquals("redirect", idp.getSingleLogoutService().getRequestBinding());
|
||||||
Assert.assertEquals("post", idp.getSingleLogoutService().getResponseBinding());
|
Assert.assertEquals("post", idp.getSingleLogoutService().getResponseBinding());
|
||||||
Assert.assertEquals("posturl", idp.getSingleLogoutService().getPostBindingUrl());
|
Assert.assertEquals("posturl", idp.getSingleLogoutService().getPostBindingUrl());
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
<SP entityID="sp"
|
<SP entityID="sp"
|
||||||
sslPolicy="ssl"
|
sslPolicy="ssl"
|
||||||
nameIDPolicyFormat="format"
|
nameIDPolicyFormat="format"
|
||||||
|
signatureAlgorithm=""
|
||||||
|
sgnatureCanonicalizationMethod=""
|
||||||
forceAuthentication="true">
|
forceAuthentication="true">
|
||||||
<Keys>
|
<Keys>
|
||||||
<Key signing="true" >
|
<Key signing="true" >
|
||||||
|
@ -27,7 +29,6 @@
|
||||||
<IDP entityID="idp">
|
<IDP entityID="idp">
|
||||||
<SingleSignOnService signRequest="true"
|
<SingleSignOnService signRequest="true"
|
||||||
validateResponseSignature="true"
|
validateResponseSignature="true"
|
||||||
signatureCanonicalizationMethod="canon"
|
|
||||||
requestBinding="post"
|
requestBinding="post"
|
||||||
bindingUrl="url"
|
bindingUrl="url"
|
||||||
/>
|
/>
|
||||||
|
@ -37,7 +38,6 @@
|
||||||
validateResponseSignature="true"
|
validateResponseSignature="true"
|
||||||
signRequest="true"
|
signRequest="true"
|
||||||
signResponse="true"
|
signResponse="true"
|
||||||
signatureCanonicalizationMethod="canon"
|
|
||||||
requestBinding="redirect"
|
requestBinding="redirect"
|
||||||
responseBinding="post"
|
responseBinding="post"
|
||||||
postBindingUrl="posturl"
|
postBindingUrl="posturl"
|
||||||
|
@ -51,11 +51,5 @@
|
||||||
</Key>
|
</Key>
|
||||||
</Keys>
|
</Keys>
|
||||||
</IDP>
|
</IDP>
|
||||||
<Keys>
|
|
||||||
<KeyStore>
|
|
||||||
|
|
||||||
</KeyStore>
|
|
||||||
</Keys>
|
|
||||||
|
|
||||||
</SP>
|
</SP>
|
||||||
</keycloak-saml-adapter>
|
</keycloak-saml-adapter>
|
|
@ -5,6 +5,8 @@ import io.undertow.server.HttpServerExchange;
|
||||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
import io.undertow.servlet.handlers.ServletRequestContext;
|
||||||
import io.undertow.util.Headers;
|
import io.undertow.util.Headers;
|
||||||
import org.keycloak.adapters.HttpFacade;
|
import org.keycloak.adapters.HttpFacade;
|
||||||
|
import org.keycloak.adapters.InMemorySessionIdMapper;
|
||||||
|
import org.keycloak.adapters.SessionIdMapper;
|
||||||
import org.keycloak.adapters.saml.SamlDeployment;
|
import org.keycloak.adapters.saml.SamlDeployment;
|
||||||
import org.keycloak.adapters.saml.SamlDeploymentContext;
|
import org.keycloak.adapters.saml.SamlDeploymentContext;
|
||||||
import org.keycloak.adapters.saml.SamlSessionStore;
|
import org.keycloak.adapters.saml.SamlSessionStore;
|
||||||
|
@ -23,13 +25,14 @@ import java.io.IOException;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class ServletSamlAuthMech extends AbstractSamlAuthMech {
|
public class ServletSamlAuthMech extends AbstractSamlAuthMech {
|
||||||
|
private SessionIdMapper idMapper = new InMemorySessionIdMapper();
|
||||||
public ServletSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement, String errorPage) {
|
public ServletSamlAuthMech(SamlDeploymentContext deploymentContext, UndertowUserSessionManagement sessionManagement, String errorPage) {
|
||||||
super(deploymentContext, sessionManagement, errorPage);
|
super(deploymentContext, sessionManagement, errorPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SamlSessionStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, SamlDeployment deployment, SecurityContext securityContext) {
|
protected SamlSessionStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, SamlDeployment deployment, SecurityContext securityContext) {
|
||||||
return new ServletSamlSessionStore(exchange, sessionManagement, securityContext);
|
return new ServletSamlSessionStore(exchange, sessionManagement, securityContext, idMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -4,11 +4,13 @@ import io.undertow.security.api.SecurityContext;
|
||||||
import io.undertow.security.idm.Account;
|
import io.undertow.security.idm.Account;
|
||||||
import io.undertow.server.HttpServerExchange;
|
import io.undertow.server.HttpServerExchange;
|
||||||
import io.undertow.server.session.Session;
|
import io.undertow.server.session.Session;
|
||||||
|
import io.undertow.server.session.SessionManager;
|
||||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
import io.undertow.servlet.handlers.ServletRequestContext;
|
||||||
import io.undertow.servlet.spec.HttpSessionImpl;
|
import io.undertow.servlet.spec.HttpSessionImpl;
|
||||||
import io.undertow.servlet.util.SavedRequest;
|
import io.undertow.servlet.util.SavedRequest;
|
||||||
import io.undertow.util.Sessions;
|
import io.undertow.util.Sessions;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.adapters.SessionIdMapper;
|
||||||
import org.keycloak.adapters.saml.SamlSession;
|
import org.keycloak.adapters.saml.SamlSession;
|
||||||
import org.keycloak.adapters.saml.SamlSessionStore;
|
import org.keycloak.adapters.saml.SamlSessionStore;
|
||||||
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
|
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
|
||||||
|
@ -17,6 +19,8 @@ import org.keycloak.util.KeycloakUriBuilder;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpSession;
|
import javax.servlet.http.HttpSession;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -31,31 +35,66 @@ public class ServletSamlSessionStore implements SamlSessionStore {
|
||||||
private final HttpServerExchange exchange;
|
private final HttpServerExchange exchange;
|
||||||
private final UndertowUserSessionManagement sessionManagement;
|
private final UndertowUserSessionManagement sessionManagement;
|
||||||
private final SecurityContext securityContext;
|
private final SecurityContext securityContext;
|
||||||
|
private final SessionIdMapper idMapper;
|
||||||
|
|
||||||
public ServletSamlSessionStore(HttpServerExchange exchange, UndertowUserSessionManagement sessionManagement,
|
public ServletSamlSessionStore(HttpServerExchange exchange, UndertowUserSessionManagement sessionManagement,
|
||||||
SecurityContext securityContext) {
|
SecurityContext securityContext,
|
||||||
|
SessionIdMapper idMapper) {
|
||||||
this.exchange = exchange;
|
this.exchange = exchange;
|
||||||
this.sessionManagement = sessionManagement;
|
this.sessionManagement = sessionManagement;
|
||||||
this.securityContext = securityContext;
|
this.securityContext = securityContext;
|
||||||
|
this.idMapper = idMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void logoutAccount() {
|
public void logoutAccount() {
|
||||||
HttpSession session = getSession(false);
|
HttpSession session = getSession(false);
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
session.removeAttribute(SamlSession.class.getName());
|
SamlSession samlSession = (SamlSession)session.getAttribute(SamlSession.class.getName());
|
||||||
|
if (samlSession != null) {
|
||||||
|
if (samlSession.getSessionIndex() != null) {
|
||||||
|
idMapper.removeSession(session.getId());
|
||||||
|
}
|
||||||
|
session.removeAttribute(SamlSession.class.getName());
|
||||||
|
}
|
||||||
session.removeAttribute(SAML_REDIRECT_URI);
|
session.removeAttribute(SAML_REDIRECT_URI);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void logoutByPrincipal(String principal) {
|
public void logoutByPrincipal(String principal) {
|
||||||
|
Set<String> sessions = idMapper.getUserSessions(principal);
|
||||||
|
if (sessions != null) {
|
||||||
|
List<String> ids = new LinkedList<>();
|
||||||
|
ids.addAll(sessions);
|
||||||
|
logoutSessionIds(ids);
|
||||||
|
for (String id : ids) {
|
||||||
|
idMapper.removeSession(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void logoutBySsoId(List<String> ssoIds) {
|
public void logoutBySsoId(List<String> ssoIds) {
|
||||||
|
if (ssoIds == null) return;
|
||||||
|
List<String> sessionIds = new LinkedList<>();
|
||||||
|
for (String id : ssoIds) {
|
||||||
|
String sessionId = idMapper.getSessionFromSSO(id);
|
||||||
|
if (sessionId != null) {
|
||||||
|
sessionIds.add(sessionId);
|
||||||
|
idMapper.removeSession(sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
logoutSessionIds(sessionIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void logoutSessionIds(List<String> sessionIds) {
|
||||||
|
if (sessionIds == null || sessionIds.isEmpty()) return;
|
||||||
|
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
||||||
|
SessionManager sessionManager = servletRequestContext.getDeployment().getSessionManager();
|
||||||
|
sessionManagement.logoutHttpSessions(sessionManager, sessionIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -93,6 +132,7 @@ public class ServletSamlSessionStore implements SamlSessionStore {
|
||||||
HttpSession session = getSession(true);
|
HttpSession session = getSession(true);
|
||||||
session.setAttribute(SamlSession.class.getName(), account);
|
session.setAttribute(SamlSession.class.getName(), account);
|
||||||
sessionManagement.login(servletRequestContext.getDeployment().getSessionManager());
|
sessionManagement.login(servletRequestContext.getDeployment().getSessionManager());
|
||||||
|
idMapper.map(account.getSessionIndex(), account.getPrincipal().getSamlSubject(), session.getId());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -625,6 +625,7 @@ public class SamlProtocol implements LoginProtocol {
|
||||||
SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
|
SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
|
||||||
.assertionExpiration(realm.getAccessCodeLifespan())
|
.assertionExpiration(realm.getAccessCodeLifespan())
|
||||||
.issuer(getResponseIssuer(realm))
|
.issuer(getResponseIssuer(realm))
|
||||||
|
.sessionIndex(clientSession.getId())
|
||||||
.userPrincipal(clientSession.getNote(SAML_NAME_ID), clientSession.getNote(SAML_NAME_ID_FORMAT))
|
.userPrincipal(clientSession.getNote(SAML_NAME_ID), clientSession.getNote(SAML_NAME_ID_FORMAT))
|
||||||
.destination(logoutUrl);
|
.destination(logoutUrl);
|
||||||
return logoutBuilder;
|
return logoutBuilder;
|
||||||
|
|
|
@ -68,20 +68,20 @@ public class SamlBindingTest {
|
||||||
public void initWars() {
|
public void initWars() {
|
||||||
ClassLoader classLoader = SamlBindingTest.class.getClassLoader();
|
ClassLoader classLoader = SamlBindingTest.class.getClassLoader();
|
||||||
|
|
||||||
//initializeSamlSecuredWar("/keycloak-saml/simple-post", "/sales-post", "post.war", classLoader);
|
initializeSamlSecuredWar("/keycloak-saml/simple-post", "/sales-post", "post.war", classLoader);
|
||||||
initializeSamlSecuredWar("/keycloak-saml/signed-post", "/sales-post-sig", "post-sig.war", classLoader);
|
initializeSamlSecuredWar("/keycloak-saml/signed-post", "/sales-post-sig", "post-sig.war", classLoader);
|
||||||
//initializeSamlSecuredWar("/keycloak-saml/signed-post-email", "/sales-post-sig-email", "post-sig-email.war", classLoader);
|
initializeSamlSecuredWar("/keycloak-saml/signed-post-email", "/sales-post-sig-email", "post-sig-email.war", classLoader);
|
||||||
//initializeSamlSecuredWar("/keycloak-saml/signed-post-transient", "/sales-post-sig-transient", "post-sig-transient.war", classLoader);
|
initializeSamlSecuredWar("/keycloak-saml/signed-post-transient", "/sales-post-sig-transient", "post-sig-transient.war", classLoader);
|
||||||
//initializeSamlSecuredWar("/keycloak-saml/signed-post-persistent", "/sales-post-sig-persistent", "post-sig-persistent.war", classLoader);
|
initializeSamlSecuredWar("/keycloak-saml/signed-post-persistent", "/sales-post-sig-persistent", "post-sig-persistent.war", classLoader);
|
||||||
//initializeSamlSecuredWar("/keycloak-saml/signed-metadata", "/sales-metadata", "post-metadata.war", classLoader);
|
//initializeSamlSecuredWar("/keycloak-saml/signed-metadata", "/sales-metadata", "post-metadata.war", classLoader);
|
||||||
//initializeSamlSecuredWar("/keycloak-saml/signed-get", "/employee-sig", "employee-sig.war", classLoader);
|
initializeSamlSecuredWar("/keycloak-saml/signed-get", "/employee-sig", "employee-sig.war", classLoader);
|
||||||
//initializeSamlSecuredWar("/saml/simple-get", "/employee", "employee.war", classLoader);
|
//initializeSamlSecuredWar("/saml/simple-get", "/employee", "employee.war", classLoader);
|
||||||
//initializeSamlSecuredWar("/keycloak-saml/signed-front-get", "/employee-sig-front", "employee-sig-front.war", classLoader);
|
initializeSamlSecuredWar("/keycloak-saml/signed-front-get", "/employee-sig-front", "employee-sig-front.war", classLoader);
|
||||||
//initializeSamlSecuredWar("/keycloak-saml/bad-client-signed-post", "/bad-client-sales-post-sig", "bad-client-post-sig.war", classLoader);
|
//initializeSamlSecuredWar("/keycloak-saml/bad-client-signed-post", "/bad-client-sales-post-sig", "bad-client-post-sig.war", classLoader);
|
||||||
//initializeSamlSecuredWar("/keycloak-saml/bad-realm-signed-post", "/bad-realm-sales-post-sig", "bad-realm-post-sig.war", classLoader);
|
//initializeSamlSecuredWar("/keycloak-saml/bad-realm-signed-post", "/bad-realm-sales-post-sig", "bad-realm-post-sig.war", classLoader);
|
||||||
//initializeSamlSecuredWar("/keycloak-saml/encrypted-post", "/sales-post-enc", "post-enc.war", classLoader);
|
//initializeSamlSecuredWar("/keycloak-saml/encrypted-post", "/sales-post-enc", "post-enc.war", classLoader);
|
||||||
//uploadSP();
|
//uploadSP();
|
||||||
//server.getServer().deploy(createDeploymentInfo("employee.war", "/employee", SamlSPFacade.class));
|
server.getServer().deploy(createDeploymentInfo("employee.war", "/employee", SamlSPFacade.class));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -411,7 +411,8 @@ public class SamlBindingTest {
|
||||||
driver.navigate().to("http://localhost:8081/employee-sig?GLO=true");
|
driver.navigate().to("http://localhost:8081/employee-sig?GLO=true");
|
||||||
checkLoggedOut("http://localhost:8081/employee-sig/");
|
checkLoggedOut("http://localhost:8081/employee-sig/");
|
||||||
driver.navigate().to("http://localhost:8081/employee-sig-front/");
|
driver.navigate().to("http://localhost:8081/employee-sig-front/");
|
||||||
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
|
String currentUrl = driver.getCurrentUrl();
|
||||||
|
Assert.assertTrue(currentUrl.startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
|
||||||
driver.navigate().to("http://localhost:8081/sales-post-sig/");
|
driver.navigate().to("http://localhost:8081/sales-post-sig/");
|
||||||
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
|
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/demo/protocol/saml"));
|
||||||
|
|
||||||
|
@ -505,6 +506,4 @@ public class SamlBindingTest {
|
||||||
response.close();
|
response.close();
|
||||||
client.close();
|
client.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<keycloak-saml-adapter>
|
||||||
|
<SP entityID="http://localhost:8081/employee-sig-front/"
|
||||||
|
sslPolicy="EXTERNAL"
|
||||||
|
logoutPage="/logout.jsp"
|
||||||
|
nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
|
||||||
|
forceAuthentication="false">
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true" >
|
||||||
|
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||||
|
<PrivateKey alias="http://localhost:8080/employee-sig/" password="test123"/>
|
||||||
|
<Certificate alias="http://localhost:8080/employee-sig/"/>
|
||||||
|
</KeyStore>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
<PrincipalNameMapping policy="FROM_NAME_ID"/>
|
||||||
|
<RoleMapping>
|
||||||
|
<Attribute name="Role"/>
|
||||||
|
</RoleMapping>
|
||||||
|
<IDP entityID="idp">
|
||||||
|
<SingleSignOnService signRequest="true"
|
||||||
|
validateResponseSignature="true"
|
||||||
|
requestBinding="REDIRECT"
|
||||||
|
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SingleLogoutService
|
||||||
|
validateRequestSignature="true"
|
||||||
|
validateResponseSignature="true"
|
||||||
|
signRequest="true"
|
||||||
|
signResponse="true"
|
||||||
|
requestBinding="REDIRECT"
|
||||||
|
responseBinding="REDIRECT"
|
||||||
|
redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
/>
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true">
|
||||||
|
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||||
|
<Certificate alias="demo"/>
|
||||||
|
</KeyStore>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
</IDP>
|
||||||
|
</SP>
|
||||||
|
</keycloak-saml-adapter>
|
Binary file not shown.
|
@ -0,0 +1,44 @@
|
||||||
|
<keycloak-saml-adapter>
|
||||||
|
<SP entityID="http://localhost:8081/employee-sig/"
|
||||||
|
sslPolicy="EXTERNAL"
|
||||||
|
logoutPage="/logout.jsp"
|
||||||
|
nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
|
||||||
|
forceAuthentication="false">
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true" >
|
||||||
|
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||||
|
<PrivateKey alias="http://localhost:8080/employee-sig/" password="test123"/>
|
||||||
|
<Certificate alias="http://localhost:8080/employee-sig/"/>
|
||||||
|
</KeyStore>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
<PrincipalNameMapping policy="FROM_NAME_ID"/>
|
||||||
|
<RoleMapping>
|
||||||
|
<Attribute name="Role"/>
|
||||||
|
</RoleMapping>
|
||||||
|
<IDP entityID="idp">
|
||||||
|
<SingleSignOnService signRequest="true"
|
||||||
|
validateResponseSignature="true"
|
||||||
|
requestBinding="REDIRECT"
|
||||||
|
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SingleLogoutService
|
||||||
|
validateRequestSignature="true"
|
||||||
|
validateResponseSignature="true"
|
||||||
|
signRequest="true"
|
||||||
|
signResponse="true"
|
||||||
|
requestBinding="REDIRECT"
|
||||||
|
responseBinding="REDIRECT"
|
||||||
|
redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
/>
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true">
|
||||||
|
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||||
|
<Certificate alias="demo"/>
|
||||||
|
</KeyStore>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
</IDP>
|
||||||
|
</SP>
|
||||||
|
</keycloak-saml-adapter>
|
Binary file not shown.
|
@ -0,0 +1,44 @@
|
||||||
|
<keycloak-saml-adapter>
|
||||||
|
<SP entityID="http://localhost:8081/sales-post-sig-email/"
|
||||||
|
sslPolicy="EXTERNAL"
|
||||||
|
logoutPage="/logout.jsp"
|
||||||
|
forceAuthentication="false">
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true" >
|
||||||
|
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||||
|
<PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
|
||||||
|
<Certificate alias="http://localhost:8080/sales-post-sig/"/>
|
||||||
|
</KeyStore>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
<PrincipalNameMapping policy="FROM_NAME_ID"/>
|
||||||
|
<RoleMapping>
|
||||||
|
<Attribute name="Role"/>
|
||||||
|
</RoleMapping>
|
||||||
|
<IDP entityID="idp">
|
||||||
|
<SingleSignOnService signRequest="true"
|
||||||
|
validateResponseSignature="true"
|
||||||
|
requestBinding="POST"
|
||||||
|
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SingleLogoutService
|
||||||
|
validateRequestSignature="true"
|
||||||
|
validateResponseSignature="true"
|
||||||
|
signRequest="true"
|
||||||
|
signResponse="true"
|
||||||
|
requestBinding="POST"
|
||||||
|
responseBinding="POST"
|
||||||
|
postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
/>
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true">
|
||||||
|
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||||
|
<Certificate alias="demo"/>
|
||||||
|
</KeyStore>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
</IDP>
|
||||||
|
</SP>
|
||||||
|
</keycloak-saml-adapter>
|
Binary file not shown.
|
@ -0,0 +1,45 @@
|
||||||
|
<keycloak-saml-adapter>
|
||||||
|
<SP entityID="http://localhost:8081/sales-post-sig-persistent/"
|
||||||
|
sslPolicy="EXTERNAL"
|
||||||
|
nameIDPolicyFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
|
||||||
|
logoutPage="/logout.jsp"
|
||||||
|
forceAuthentication="false">
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true" >
|
||||||
|
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||||
|
<PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
|
||||||
|
<Certificate alias="http://localhost:8080/sales-post-sig/"/>
|
||||||
|
</KeyStore>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
<PrincipalNameMapping policy="FROM_NAME_ID"/>
|
||||||
|
<RoleMapping>
|
||||||
|
<Attribute name="Role"/>
|
||||||
|
</RoleMapping>
|
||||||
|
<IDP entityID="idp">
|
||||||
|
<SingleSignOnService signRequest="true"
|
||||||
|
validateResponseSignature="true"
|
||||||
|
requestBinding="POST"
|
||||||
|
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SingleLogoutService
|
||||||
|
validateRequestSignature="true"
|
||||||
|
validateResponseSignature="true"
|
||||||
|
signRequest="true"
|
||||||
|
signResponse="true"
|
||||||
|
requestBinding="POST"
|
||||||
|
responseBinding="POST"
|
||||||
|
postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
/>
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true">
|
||||||
|
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||||
|
<Certificate alias="demo"/>
|
||||||
|
</KeyStore>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
</IDP>
|
||||||
|
</SP>
|
||||||
|
</keycloak-saml-adapter>
|
Binary file not shown.
|
@ -0,0 +1,45 @@
|
||||||
|
<keycloak-saml-adapter>
|
||||||
|
<SP entityID="http://localhost:8081/sales-post-sig-transient/"
|
||||||
|
sslPolicy="EXTERNAL"
|
||||||
|
nameIDPolicyFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
|
||||||
|
logoutPage="/logout.jsp"
|
||||||
|
forceAuthentication="false">
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true" >
|
||||||
|
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||||
|
<PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
|
||||||
|
<Certificate alias="http://localhost:8080/sales-post-sig/"/>
|
||||||
|
</KeyStore>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
<PrincipalNameMapping policy="FROM_NAME_ID"/>
|
||||||
|
<RoleMapping>
|
||||||
|
<Attribute name="Role"/>
|
||||||
|
</RoleMapping>
|
||||||
|
<IDP entityID="idp">
|
||||||
|
<SingleSignOnService signRequest="true"
|
||||||
|
validateResponseSignature="true"
|
||||||
|
requestBinding="POST"
|
||||||
|
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SingleLogoutService
|
||||||
|
validateRequestSignature="true"
|
||||||
|
validateResponseSignature="true"
|
||||||
|
signRequest="true"
|
||||||
|
signResponse="true"
|
||||||
|
requestBinding="POST"
|
||||||
|
responseBinding="POST"
|
||||||
|
postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
/>
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true">
|
||||||
|
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||||
|
<Certificate alias="demo"/>
|
||||||
|
</KeyStore>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
</IDP>
|
||||||
|
</SP>
|
||||||
|
</keycloak-saml-adapter>
|
Binary file not shown.
|
@ -0,0 +1,24 @@
|
||||||
|
<keycloak-saml-adapter>
|
||||||
|
<SP entityID="http://localhost:8081/sales-post/"
|
||||||
|
sslPolicy="EXTERNAL"
|
||||||
|
nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
|
||||||
|
logoutPage="/logout.jsp"
|
||||||
|
forceAuthentication="false">
|
||||||
|
<PrincipalNameMapping policy="FROM_NAME_ID"/>
|
||||||
|
<RoleMapping>
|
||||||
|
<Attribute name="Role"/>
|
||||||
|
</RoleMapping>
|
||||||
|
<IDP entityID="idp">
|
||||||
|
<SingleSignOnService requestBinding="POST"
|
||||||
|
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SingleLogoutService
|
||||||
|
requestBinding="POST"
|
||||||
|
responseBinding="POST"
|
||||||
|
postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
/>
|
||||||
|
</IDP>
|
||||||
|
</SP>
|
||||||
|
</keycloak-saml-adapter>
|
|
@ -209,7 +209,7 @@
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"saml.server.signature": "true",
|
"saml.server.signature": "true",
|
||||||
"saml.client.signature": "true",
|
"saml.client.signature": "true",
|
||||||
"saml.signature.algorithm": "RSA_SHA1",
|
"saml.signature.algorithm": "RSA_SHA256",
|
||||||
"saml.authnstatement": "true",
|
"saml.authnstatement": "true",
|
||||||
"saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
|
"saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue