Skip to content

enhancement to support overriding SAMLRequest #9209

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,16 @@
import org.opensaml.core.xml.config.XMLObjectProviderRegistry;
import org.opensaml.core.xml.io.MarshallingException;
import org.opensaml.saml.common.xml.SAMLConstants;
import org.opensaml.saml.saml2.core.AuthnContextClassRef;
import org.opensaml.saml.saml2.core.AuthnContextComparisonTypeEnumeration;
import org.opensaml.saml.saml2.core.AuthnRequest;
import org.opensaml.saml.saml2.core.Issuer;
import org.opensaml.saml.saml2.core.RequestedAuthnContext;
import org.opensaml.saml.saml2.core.impl.AuthnContextClassRefBuilder;
import org.opensaml.saml.saml2.core.impl.AuthnRequestBuilder;
import org.opensaml.saml.saml2.core.impl.AuthnRequestMarshaller;
import org.opensaml.saml.saml2.core.impl.IssuerBuilder;
import org.opensaml.saml.saml2.core.impl.RequestedAuthnContextBuilder;
import org.opensaml.saml.security.impl.SAMLMetadataSignatureSigningParametersResolver;
import org.opensaml.security.SecurityException;
import org.opensaml.security.credential.BasicCredential;
Expand Down Expand Up @@ -84,6 +89,8 @@ public class OpenSamlAuthenticationRequestFactory implements Saml2Authentication

private IssuerBuilder issuerBuilder;

private RequestedAuthnContextBuilder reqAuthnContextBuilder;

private Converter<Saml2AuthenticationRequestContext, String> protocolBindingResolver = (context) -> {
if (context == null) {
return SAMLConstants.SAML2_POST_BINDING_URI;
Expand All @@ -103,13 +110,16 @@ public OpenSamlAuthenticationRequestFactory() {
this.authnRequestBuilder = (AuthnRequestBuilder) registry.getBuilderFactory()
.getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME);
this.issuerBuilder = (IssuerBuilder) registry.getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
this.reqAuthnContextBuilder = (RequestedAuthnContextBuilder) registry.getBuilderFactory()
.getBuilder(RequestedAuthnContext.DEFAULT_ELEMENT_NAME);
}

@Override
@Deprecated
public String createAuthenticationRequest(Saml2AuthenticationRequest request) {
AuthnRequest authnRequest = createAuthnRequest(request.getIssuer(), request.getDestination(),
request.getAssertionConsumerServiceUrl(), this.protocolBindingResolver.convert(null));
request.getAssertionConsumerServiceUrl(), this.protocolBindingResolver.convert(null), null, false,
false);
for (org.springframework.security.saml2.credentials.Saml2X509Credential credential : request.getCredentials()) {
if (credential.isSigningCredential()) {
X509Certificate certificate = credential.getCertificate();
Expand Down Expand Up @@ -160,17 +170,29 @@ public Saml2RedirectAuthenticationRequest createRedirectAuthenticationRequest(

private AuthnRequest createAuthnRequest(Saml2AuthenticationRequestContext context) {
return createAuthnRequest(context.getIssuer(), context.getDestination(),
context.getAssertionConsumerServiceUrl(), this.protocolBindingResolver.convert(context));
context.getAssertionConsumerServiceUrl(), this.protocolBindingResolver.convert(context),
context.getRequestAuthnContextRefs(), context.getIsPassive(), context.getForceAuthn());
}

private AuthnRequest createAuthnRequest(String issuer, String destination, String assertionConsumerServiceUrl,
String protocolBinding) {
String protocolBinding, List<String> requestAuthnContextRefs, Boolean isPassive, Boolean forceAuth) {
AuthnRequest auth = this.authnRequestBuilder.buildObject();
auth.setID("ARQ" + UUID.randomUUID().toString().substring(1));
auth.setIssueInstant(new DateTime(this.clock.millis()));
auth.setForceAuthn(Boolean.FALSE);
auth.setIsPassive(Boolean.FALSE);
auth.setProtocolBinding(protocolBinding);
auth.setIsPassive(isPassive);
auth.setForceAuthn(forceAuth);
if (requestAuthnContextRefs != null) {
RequestedAuthnContext requestAuthnContext = this.reqAuthnContextBuilder.buildObject();
requestAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT);
requestAuthnContextRefs.forEach((r) -> {
AuthnContextClassRef authnContextClassRef = new AuthnContextClassRefBuilder().buildObject();
authnContextClassRef.setAuthnContextClassRef(r);
requestAuthnContext.getAuthnContextClassRefs().add(authnContextClassRef);
});
auth.setRequestedAuthnContext(requestAuthnContext);
}

Issuer iss = this.issuerBuilder.buildObject();
iss.setValue(issuer);
auth.setIssuer(iss);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

package org.springframework.security.saml2.provider.service.authentication;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.util.Assert;

Expand All @@ -39,6 +43,12 @@ public class Saml2AuthenticationRequestContext {

private final String relayState;

private final List<String> requestAuthnContextRefs;

private final Boolean isPassive;

private final Boolean forceAuthn;

protected Saml2AuthenticationRequestContext(RelyingPartyRegistration relyingPartyRegistration, String issuer,
String assertionConsumerServiceUrl, String relayState) {
Assert.hasText(issuer, "issuer cannot be null or empty");
Expand All @@ -48,6 +58,23 @@ protected Saml2AuthenticationRequestContext(RelyingPartyRegistration relyingPart
this.relyingPartyRegistration = relyingPartyRegistration;
this.assertionConsumerServiceUrl = assertionConsumerServiceUrl;
this.relayState = relayState;
this.isPassive = false;
this.forceAuthn = false;
this.requestAuthnContextRefs = new ArrayList<>();
}

private Saml2AuthenticationRequestContext(Builder builder) {

Assert.hasText(builder.issuer, "issuer cannot be null or empty");
Assert.notNull(builder.relyingPartyRegistration, "relyingPartyRegistration cannot be null");
Assert.hasText(builder.assertionConsumerServiceUrl, "spAssertionConsumerServiceUrl cannot be null or empty");
this.issuer = builder.issuer;
this.relyingPartyRegistration = builder.relyingPartyRegistration;
this.assertionConsumerServiceUrl = builder.assertionConsumerServiceUrl;
this.relayState = builder.relayState;
this.isPassive = builder.isPassive;
this.forceAuthn = builder.forceAuthn;
this.requestAuthnContextRefs = builder.requestAuthnContextRefs;
}

/**
Expand All @@ -70,9 +97,10 @@ public String getIssuer() {
}

/**
* Returns the desired {@code AssertionConsumerServiceUrl} that this SP wishes to
* receive the assertion on. The IDP may or may not honor this request. This property
* populates the {@code AuthNRequest.AssertionConsumerServiceURL} XML attribute.
* Returns the desired {@code AssertionConsumerServiceUrl} that this relying party
* wishes to receive the assertion on. The IDP may or may not honor this request. This
* property populates the {@code AuthNRequest.AssertionConsumerServiceURL} XML
* attribute.
* @return the AssertionConsumerServiceURL value
*/
public String getAssertionConsumerServiceUrl() {
Expand All @@ -87,6 +115,41 @@ public String getRelayState() {
return this.relayState;
}

/**
* Returns the desired ({@code AuthnContextClassRef} that this relying party wishes to
* set in the request.This property populates the
* {@code AuthNRequest.RequestedAuthnContext.AuthnContextClassRef} XML attribute.
* @return the AuthnContextClassRef value
* @since 5.5
*/
public List<String> getRequestAuthnContextRefs() {
return this.requestAuthnContextRefs;
}

/**
* Returns the isPassive value, if present in the parameters
* @return the isPassive value, default is set to false
* @since 5.5
* @see <a href=
* "https://wiki.shibboleth.net/confluence/display/SP3/isPassive">OpenSAML 3
* isPassive</a>
*/
public Boolean getIsPassive() {
return this.isPassive;
}

/**
* Returns the ForceAuthn value, if present in the parameters
* @return this forceAuthn value, default is set to false
* @since 5.5
* @see <a href=
* "https://wiki.shibboleth.net/confluence/display/SP3/ForceAuthn">OpenSAML 3
* ForceAuthn</a>
*/
public Boolean getForceAuthn() {
return this.forceAuthn;
}

/**
* Returns the {@code Destination}, the WEB Single Sign On URI, for this
* authentication request. This property can also populate the
Expand Down Expand Up @@ -116,6 +179,12 @@ public static final class Builder {

private String relayState;

private List<String> requestAuthnContextRefs = new ArrayList<>();

private Boolean isPassive;

private Boolean forceAuthn;

private RelyingPartyRegistration relyingPartyRegistration;

private Builder() {
Expand Down Expand Up @@ -164,14 +233,48 @@ public Builder relayState(String relayState) {
return this;
}

/**
* Sets this {@link Consumer} AuthnContextClassRef parameter that will accompany
* this AuthNRequest.
* @param requestAuthnContextRefsConsumer a {@link Consumer} of the list of
* AuthnContextClassRef
* @return this object
* @since 5.5
*/
public Builder requestAuthnContextRefs(Consumer<List<String>> requestAuthnContextRefsConsumer) {
requestAuthnContextRefsConsumer.accept(this.requestAuthnContextRefs);
return this;
}

/**
* Sets the {@code IsPassive} parameter that will accompany this AuthNRequest
* @param isPassive the isPassive value. if null or empty, defaults to false.
* @return this object
* @since 5.5
*/
public Builder isPassive(Boolean isPassive) {
this.isPassive = isPassive;
return this;
}

/**
* Sets the {@code ForceAuth} parameter that will accompany this AuthNRequest
* @param forceAuth the foceAuth value. if null or empty, defaults to false.
* @return this object
* @since 5.5
*/
public Builder forceAuthn(Boolean forceAuthn) {
this.forceAuthn = forceAuthn;
return this;
}

/**
* Creates a {@link Saml2AuthenticationRequestContext} object.
* @return the Saml2AuthenticationRequest object
* @throws IllegalArgumentException if a required property is not set
*/
public Saml2AuthenticationRequestContext build() {
return new Saml2AuthenticationRequestContext(this.relyingPartyRegistration, this.issuer,
this.assertionConsumerServiceUrl, this.relayState);
return new Saml2AuthenticationRequestContext(this);
}

}
Expand Down