package org.pac4j.saml.sso.impl;
import org.apache.commons.lang.RandomStringUtils;
import org.joda.time.DateTime;
import org.opensaml.core.xml.XMLObjectBuilderFactory;
import org.opensaml.messaging.context.MessageContext;
import org.opensaml.saml.common.SAMLObjectBuilder;
import org.opensaml.saml.common.SAMLVersion;
import org.opensaml.saml.common.messaging.context.SAMLSelfEntityContext;
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.NameIDPolicy;
import org.opensaml.saml.saml2.core.RequestedAuthnContext;
import org.opensaml.saml.saml2.core.impl.AuthnContextClassRefBuilder;
import org.opensaml.saml.saml2.core.impl.NameIDPolicyBuilder;
import org.opensaml.saml.saml2.core.impl.RequestedAuthnContextBuilder;
import org.opensaml.saml.saml2.metadata.AssertionConsumerService;
import org.opensaml.saml.saml2.metadata.SingleSignOnService;
import org.pac4j.saml.context.SAML2MessageContext;
import org.pac4j.saml.sso.SAML2ObjectBuilder;
import org.pac4j.saml.util.Configuration;
/**
* Build a SAML2 Authn Request from the given {@link MessageContext}.
*
* @author Michael Remond
* @since 1.5.0
*/
public class SAML2AuthnRequestBuilder implements SAML2ObjectBuilder<AuthnRequest> {
private final boolean forceAuth;
private final AuthnContextComparisonTypeEnumeration comparisonType;
private String bindingType = SAMLConstants.SAML2_POST_BINDING_URI;
private String authnContextClassRef = null;
private String nameIdPolicyFormat = null;
private int issueInstantSkewSeconds = 0;
private final XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();
/**
* Instantiates a new Saml 2 authn request builder.
*
* @param forceAuth the force auth
* @param comparisonType the comparison type
* @param bindingType the binding type
* @param authnContextClassRef the authn context class ref
* @param nameIdPolicyFormat the name id policy format
*/
public SAML2AuthnRequestBuilder(final boolean forceAuth, final String comparisonType, final String bindingType,
final String authnContextClassRef, final String nameIdPolicyFormat) {
this.forceAuth = forceAuth;
this.comparisonType = getComparisonTypeEnumFromString(comparisonType);
this.bindingType = bindingType;
this.authnContextClassRef = authnContextClassRef;
this.nameIdPolicyFormat = nameIdPolicyFormat;
}
@Override
public AuthnRequest build(final SAML2MessageContext context) {
final SingleSignOnService ssoService = context.getIDPSingleSignOnService(this.bindingType);
final AssertionConsumerService assertionConsumerService = context.getSPAssertionConsumerService();
return buildAuthnRequest(context, assertionConsumerService, ssoService);
}
@SuppressWarnings("unchecked")
protected final AuthnRequest buildAuthnRequest(final SAML2MessageContext context,
final AssertionConsumerService assertionConsumerService, final SingleSignOnService ssoService) {
final SAMLObjectBuilder<AuthnRequest> builder = (SAMLObjectBuilder<AuthnRequest>) this.builderFactory
.getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME);
final AuthnRequest request = builder.buildObject();
if (comparisonType != null) {
final RequestedAuthnContext authnContext = new RequestedAuthnContextBuilder().buildObject();
authnContext.setComparison(comparisonType);
if (authnContextClassRef != null) {
final AuthnContextClassRef classRef = new AuthnContextClassRefBuilder().buildObject();
classRef.setAuthnContextClassRef(authnContextClassRef);
authnContext.getAuthnContextClassRefs().add(classRef);
}
request.setRequestedAuthnContext(authnContext);
}
final SAMLSelfEntityContext selfContext = context.getSAMLSelfEntityContext();
request.setID(generateID());
request.setIssuer(getIssuer(selfContext.getEntityId()));
request.setIssueInstant(DateTime.now().plusSeconds(this.issueInstantSkewSeconds));
request.setVersion(SAMLVersion.VERSION_20);
request.setIsPassive(false);
request.setForceAuthn(this.forceAuth);
request.setProviderName("pac4j-saml");
if (nameIdPolicyFormat != null) {
final NameIDPolicy nameIdPolicy = new NameIDPolicyBuilder().buildObject();
nameIdPolicy.setAllowCreate(true);
nameIdPolicy.setFormat(nameIdPolicyFormat);
request.setNameIDPolicy(nameIdPolicy);
}
request.setDestination(ssoService.getLocation());
request.setAssertionConsumerServiceURL(assertionConsumerService.getLocation());
request.setProtocolBinding(assertionConsumerService.getBinding());
return request;
}
@SuppressWarnings("unchecked")
protected final Issuer getIssuer(final String spEntityId) {
final SAMLObjectBuilder<Issuer> issuerBuilder = (SAMLObjectBuilder<Issuer>) this.builderFactory
.getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
final Issuer issuer = issuerBuilder.buildObject();
issuer.setValue(spEntityId);
return issuer;
}
protected final String generateID() {
return "_".concat(RandomStringUtils.randomAlphanumeric(39)).toLowerCase();
}
protected final AuthnContextComparisonTypeEnumeration getComparisonTypeEnumFromString(final String comparisonType) {
if ("exact".equalsIgnoreCase(comparisonType)) {
return AuthnContextComparisonTypeEnumeration.EXACT;
}
if ("minimum".equalsIgnoreCase(comparisonType)) {
return AuthnContextComparisonTypeEnumeration.MINIMUM;
}
if ("maximum".equalsIgnoreCase(comparisonType)) {
return AuthnContextComparisonTypeEnumeration.MAXIMUM;
}
if ("better".equalsIgnoreCase(comparisonType)) {
return AuthnContextComparisonTypeEnumeration.BETTER;
}
return null;
}
public void setIssueInstantSkewSeconds(final int issueInstantSkewSeconds) {
this.issueInstantSkewSeconds = issueInstantSkewSeconds;
}
}