org.apache.wss4j.common.saml.builder.SAML2ComponentBuilder Maven / Gradle / Ivy
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.wss4j.common.saml.builder;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.saml.OpenSAMLUtil;
import org.apache.wss4j.common.saml.bean.ActionBean;
import org.apache.wss4j.common.saml.bean.AdviceBean;
import org.apache.wss4j.common.saml.bean.AttributeBean;
import org.apache.wss4j.common.saml.bean.AttributeStatementBean;
import org.apache.wss4j.common.saml.bean.AudienceRestrictionBean;
import org.apache.wss4j.common.saml.bean.AuthDecisionStatementBean;
import org.apache.wss4j.common.saml.bean.AuthenticationStatementBean;
import org.apache.wss4j.common.saml.bean.ConditionsBean;
import org.apache.wss4j.common.saml.bean.DelegateBean;
import org.apache.wss4j.common.saml.bean.KeyInfoBean;
import org.apache.wss4j.common.saml.bean.NameIDBean;
import org.apache.wss4j.common.saml.bean.ProxyRestrictionBean;
import org.apache.wss4j.common.saml.bean.SubjectBean;
import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean;
import org.apache.wss4j.common.saml.bean.SubjectLocalityBean;
import org.apache.xml.security.stax.impl.util.IDGenerator;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.core.xml.XMLObjectBuilderFactory;
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
import org.opensaml.core.xml.schema.XSString;
import org.opensaml.core.xml.schema.impl.XSStringBuilder;
import org.opensaml.saml.common.SAMLObjectBuilder;
import org.opensaml.saml.common.SAMLVersion;
import org.opensaml.saml.ext.saml2delrestrict.Delegate;
import org.opensaml.saml.ext.saml2delrestrict.DelegationRestrictionType;
import org.opensaml.saml.saml2.core.Action;
import org.opensaml.saml.saml2.core.Advice;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.AssertionIDRef;
import org.opensaml.saml.saml2.core.AssertionURIRef;
import org.opensaml.saml.saml2.core.Attribute;
import org.opensaml.saml.saml2.core.AttributeStatement;
import org.opensaml.saml.saml2.core.AttributeValue;
import org.opensaml.saml.saml2.core.Audience;
import org.opensaml.saml.saml2.core.AudienceRestriction;
import org.opensaml.saml.saml2.core.AuthnContext;
import org.opensaml.saml.saml2.core.AuthnContextClassRef;
import org.opensaml.saml.saml2.core.AuthnStatement;
import org.opensaml.saml.saml2.core.AuthzDecisionStatement;
import org.opensaml.saml.saml2.core.Conditions;
import org.opensaml.saml.saml2.core.DecisionTypeEnumeration;
import org.opensaml.saml.saml2.core.Evidence;
import org.opensaml.saml.saml2.core.Issuer;
import org.opensaml.saml.saml2.core.KeyInfoConfirmationDataType;
import org.opensaml.saml.saml2.core.NameID;
import org.opensaml.saml.saml2.core.OneTimeUse;
import org.opensaml.saml.saml2.core.ProxyRestriction;
import org.opensaml.saml.saml2.core.Subject;
import org.opensaml.saml.saml2.core.SubjectConfirmation;
import org.opensaml.saml.saml2.core.SubjectConfirmationData;
import org.opensaml.saml.saml2.core.SubjectLocality;
import org.opensaml.xmlsec.signature.KeyInfo;
import org.w3c.dom.Element;
/**
* Class SAML2ComponentBuilder provides builder methods that can be used
* to construct SAML v2.0 statements using the OpenSaml library.
*/
public final class SAML2ComponentBuilder {
private static final transient org.slf4j.Logger LOG =
org.slf4j.LoggerFactory.getLogger(SAML2ComponentBuilder.class);
private static volatile SAMLObjectBuilder assertionBuilder;
private static volatile SAMLObjectBuilder issuerBuilder;
private static volatile SAMLObjectBuilder subjectBuilder;
private static volatile SAMLObjectBuilder nameIdBuilder;
private static volatile SAMLObjectBuilder subjectConfirmationBuilder;
private static volatile SAMLObjectBuilder oneTimeUseBuilder;
private static volatile SAMLObjectBuilder proxyRestrictionBuilder;
private static volatile SAMLObjectBuilder conditionsBuilder;
private static volatile SAMLObjectBuilder adviceBuilder;
private static volatile SAMLObjectBuilder assertionIDRefBuilder;
private static volatile SAMLObjectBuilder assertionURIRefBuilder;
private static volatile SAMLObjectBuilder subjectConfirmationDataBuilder;
private static volatile SAMLObjectBuilder keyInfoConfirmationDataBuilder;
private static volatile SAMLObjectBuilder authnStatementBuilder;
private static volatile SAMLObjectBuilder authnContextBuilder;
private static volatile SAMLObjectBuilder authnContextClassRefBuilder;
private static volatile SAMLObjectBuilder attributeStatementBuilder;
private static volatile SAMLObjectBuilder attributeBuilder;
private static volatile XSStringBuilder stringBuilder;
private static volatile SAMLObjectBuilder audienceRestrictionBuilder;
private static volatile SAMLObjectBuilder delegationRestrictionBuilder;
private static volatile SAMLObjectBuilder audienceBuilder;
private static volatile SAMLObjectBuilder delegateBuilder;
private static volatile SAMLObjectBuilder authorizationDecisionStatementBuilder;
private static volatile SAMLObjectBuilder actionElementBuilder;
private static volatile XMLObjectBuilderFactory builderFactory =
XMLObjectProviderRegistrySupport.getBuilderFactory();
private static volatile SAMLObjectBuilder subjectLocalityBuilder;
private SAML2ComponentBuilder() {
// Complete
}
/**
* Create a SAML 2 assertion
*
* @return a SAML 2 assertion
*/
@SuppressWarnings("unchecked")
public static Assertion createAssertion() {
if (assertionBuilder == null) {
assertionBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(Assertion.DEFAULT_ELEMENT_NAME);
if (assertionBuilder == null) {
throw new IllegalStateException(
"OpenSaml engine not initialized. Please make sure to initialize the OpenSaml engine "
+ "prior using it"
);
}
}
Assertion assertion =
assertionBuilder.buildObject(Assertion.DEFAULT_ELEMENT_NAME, Assertion.TYPE_NAME);
assertion.setID(IDGenerator.generateID("_"));
assertion.setVersion(SAMLVersion.VERSION_20);
assertion.setIssueInstant(Instant.now());
return assertion;
}
/**
* Create an Issuer object
*
* @param issuerValue of type String
* @param issuerFormat of type String
* @param issuerQualifier of type String
* @return an Issuer object
*/
@SuppressWarnings("unchecked")
public static Issuer createIssuer(String issuerValue, String issuerFormat, String issuerQualifier) {
if (issuerBuilder == null) {
issuerBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
}
Issuer issuer = issuerBuilder.buildObject();
//
// The SAML authority that is making the claim(s) in the assertion. The issuer SHOULD
// be unambiguous to the intended relying parties.
issuer.setValue(issuerValue);
issuer.setFormat(issuerFormat);
issuer.setNameQualifier(issuerQualifier);
return issuer;
}
/**
* Create a Conditions object
*
* @param conditionsBean A ConditionsBean object
* @return a Conditions object
*/
@SuppressWarnings("unchecked")
public static Conditions createConditions(ConditionsBean conditionsBean) {
if (conditionsBuilder == null) {
conditionsBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(Conditions.DEFAULT_ELEMENT_NAME);
}
Conditions conditions = conditionsBuilder.buildObject();
if (conditionsBean == null) {
Instant newNotBefore = Instant.now();
conditions.setNotBefore(newNotBefore);
conditions.setNotOnOrAfter(newNotBefore.plus(Duration.ofMinutes(5)));
return conditions;
}
long tokenPeriodSeconds = conditionsBean.getTokenPeriodSeconds();
Instant notBefore = conditionsBean.getNotBefore();
Instant notAfter = conditionsBean.getNotAfter();
if (notBefore != null && notAfter != null) {
if (notBefore.isAfter(notAfter)) {
throw new IllegalStateException(
"The value of notBefore may not be after the value of notAfter"
);
}
conditions.setNotBefore(notBefore);
conditions.setNotOnOrAfter(notAfter);
} else {
Instant newNotBefore = Instant.now();
conditions.setNotBefore(newNotBefore);
if (tokenPeriodSeconds <= 0) {
tokenPeriodSeconds = 5L * 60L;
}
Instant notOnOrAfter = newNotBefore.plusSeconds(tokenPeriodSeconds);
conditions.setNotOnOrAfter(notOnOrAfter);
}
if (conditionsBean.getAudienceRestrictions() != null
&& !conditionsBean.getAudienceRestrictions().isEmpty()) {
for (AudienceRestrictionBean audienceRestrictionBean
: conditionsBean.getAudienceRestrictions()) {
AudienceRestriction audienceRestriction =
createAudienceRestriction(audienceRestrictionBean);
conditions.getAudienceRestrictions().add(audienceRestriction);
}
}
if (conditionsBean.isOneTimeUse()) {
conditions.getConditions().add(createOneTimeUse());
}
if (conditionsBean.getProxyRestriction() != null) {
conditions.getConditions().add(createProxyRestriction(conditionsBean.getProxyRestriction()));
}
if (conditionsBean.getDelegates() != null && !conditionsBean.getDelegates().isEmpty()) {
DelegationRestrictionType delegationRestriction =
createDelegationRestriction(conditionsBean.getDelegates());
conditions.getConditions().add(delegationRestriction);
}
return conditions;
}
/**
* Create a Advice object
*
* @param adviceBean A AdviceBean object
* @return a Advice object
* @throws WSSecurityException
*/
@SuppressWarnings("unchecked")
public static Advice createAdvice(AdviceBean adviceBean) throws WSSecurityException {
if (adviceBuilder == null) {
adviceBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(Advice.DEFAULT_ELEMENT_NAME);
}
Advice advice = adviceBuilder.buildObject();
if (!adviceBean.getIdReferences().isEmpty()) {
if (assertionIDRefBuilder == null) {
assertionIDRefBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(AssertionIDRef.DEFAULT_ELEMENT_NAME);
}
for (String ref : adviceBean.getIdReferences()) {
AssertionIDRef assertionIdRef = assertionIDRefBuilder.buildObject();
assertionIdRef.setValue(ref);
advice.getAssertionIDReferences().add(assertionIdRef);
}
}
if (!adviceBean.getUriReferences().isEmpty()) {
if (assertionURIRefBuilder == null) {
assertionURIRefBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(AssertionURIRef.DEFAULT_ELEMENT_NAME);
}
for (String ref : adviceBean.getUriReferences()) {
AssertionURIRef assertionURIRef = assertionURIRefBuilder.buildObject();
assertionURIRef.setURI(ref);
advice.getAssertionURIReferences().add(assertionURIRef);
}
}
if (!adviceBean.getAssertions().isEmpty()) {
for (Element assertionElement : adviceBean.getAssertions()) {
XMLObject xmlObject = OpenSAMLUtil.fromDom(assertionElement);
if (xmlObject instanceof Assertion) {
Assertion assertion = (Assertion)xmlObject;
advice.getAssertions().add(assertion);
}
}
}
return advice;
}
/**
* Create an AudienceRestriction object
*
* @param audienceRestrictionBean of type AudienceRestrictionBean
* @return an AudienceRestriction object
*/
@SuppressWarnings("unchecked")
public static AudienceRestriction createAudienceRestriction(
AudienceRestrictionBean audienceRestrictionBean
) {
if (audienceRestrictionBuilder == null) {
audienceRestrictionBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(AudienceRestriction.DEFAULT_ELEMENT_NAME);
}
if (audienceBuilder == null) {
audienceBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(Audience.DEFAULT_ELEMENT_NAME);
}
AudienceRestriction audienceRestriction = audienceRestrictionBuilder.buildObject();
for (String audienceURI : audienceRestrictionBean.getAudienceURIs()) {
Audience audience = audienceBuilder.buildObject();
audience.setURI(audienceURI);
audienceRestriction.getAudiences().add(audience);
}
return audienceRestriction;
}
/**
* Create an DelegationRestrictionType object
*
* @param delegates of type List
* @return a DelegationRestrictionType object
*/
@SuppressWarnings("unchecked")
public static DelegationRestrictionType createDelegationRestriction(
List delegates
) {
if (delegationRestrictionBuilder == null) {
delegationRestrictionBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(DelegationRestrictionType.TYPE_NAME);
}
DelegationRestrictionType delegationRestriction = delegationRestrictionBuilder.buildObject();
if (delegateBuilder == null) {
delegateBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(Delegate.DEFAULT_ELEMENT_NAME);
}
for (DelegateBean delegateBean : delegates) {
Delegate delegate = delegateBuilder.buildObject();
delegate.setConfirmationMethod(delegateBean.getConfirmationMethod());
delegate.setDelegationInstant(delegateBean.getDelegationInstant());
if (delegateBean.getNameIDBean() == null) {
throw new IllegalStateException(
"The value of NameIDBean in DelegateBean may not be null"
);
}
NameID nameID = createNameID(delegateBean.getNameIDBean());
delegate.setNameID(nameID);
delegationRestriction.getDelegates().add(delegate);
}
return delegationRestriction;
}
/**
* Create a OneTimeUse object
*
* @return a OneTimeUse object
*/
@SuppressWarnings("unchecked")
public static OneTimeUse createOneTimeUse() {
if (oneTimeUseBuilder == null) {
oneTimeUseBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(OneTimeUse.DEFAULT_ELEMENT_NAME);
}
return oneTimeUseBuilder.buildObject();
}
/**
* Create a ProxyRestriction object
*
* @return a ProxyRestriction object
*/
@SuppressWarnings("unchecked")
public static ProxyRestriction createProxyRestriction(ProxyRestrictionBean proxyRestrictionBean) {
if (proxyRestrictionBuilder == null) {
proxyRestrictionBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(ProxyRestriction.DEFAULT_ELEMENT_NAME);
}
ProxyRestriction proxyRestriction = proxyRestrictionBuilder.buildObject();
if (proxyRestrictionBean.getCount() > 0) {
proxyRestriction.setProxyCount(proxyRestrictionBean.getCount());
}
if (!proxyRestrictionBean.getAudienceURIs().isEmpty()) {
if (audienceBuilder == null) {
audienceBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(Audience.DEFAULT_ELEMENT_NAME);
}
for (String audienceURI : proxyRestrictionBean.getAudienceURIs()) {
Audience audience = audienceBuilder.buildObject();
audience.setURI(audienceURI);
proxyRestriction.getAudiences().add(audience);
}
}
return proxyRestriction;
}
/**
* Create SAML 2 Authentication Statement(s).
*
* @param authBeans A list of AuthenticationStatementBean instances
* @return SAML 2 Authentication Statement(s).
*/
@SuppressWarnings("unchecked")
public static List createAuthnStatement(
List authBeans
) {
List authnStatements = new ArrayList<>();
if (authnStatementBuilder == null) {
authnStatementBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(AuthnStatement.DEFAULT_ELEMENT_NAME);
}
if (authnContextBuilder == null) {
authnContextBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(AuthnContext.DEFAULT_ELEMENT_NAME);
}
if (authnContextClassRefBuilder == null) {
authnContextClassRefBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(AuthnContextClassRef.DEFAULT_ELEMENT_NAME);
}
if (subjectLocalityBuilder == null) {
subjectLocalityBuilder = (SAMLObjectBuilder)
builderFactory.getBuilder(SubjectLocality.DEFAULT_ELEMENT_NAME);
}
if (authBeans != null && !authBeans.isEmpty()) {
for (AuthenticationStatementBean statementBean : authBeans) {
AuthnStatement authnStatement = authnStatementBuilder.buildObject();
Instant authInstant = statementBean.getAuthenticationInstant();
if (authInstant == null) {
authInstant = Instant.now();
}
authnStatement.setAuthnInstant(authInstant);
Instant sessionNotOnOrAfter = statementBean.getSessionNotOnOrAfter();
if (sessionNotOnOrAfter != null) {
authnStatement.setSessionNotOnOrAfter(sessionNotOnOrAfter);
}
if (statementBean.getSessionIndex() != null) {
authnStatement.setSessionIndex(statementBean.getSessionIndex());
}
AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject();
authnContextClassRef.setURI(
transformAuthenticationMethod(statementBean.getAuthenticationMethod())
);
AuthnContext authnContext = authnContextBuilder.buildObject();
authnContext.setAuthnContextClassRef(authnContextClassRef);
authnStatement.setAuthnContext(authnContext);
SubjectLocalityBean subjectLocalityBean = statementBean.getSubjectLocality();
if (subjectLocalityBean != null) {
SubjectLocality subjectLocality = subjectLocalityBuilder.buildObject();
subjectLocality.setDNSName(subjectLocalityBean.getDnsAddress());
subjectLocality.setAddress(subjectLocalityBean.getIpAddress());
authnStatement.setSubjectLocality(subjectLocality);
}
authnStatements.add(authnStatement);
}
}
return authnStatements;
}
/**
* Transform the user-supplied authentication method value into one of the supported
* specification-compliant values.
*
* @param sourceMethod of type String
* @return String
*/
private static String transformAuthenticationMethod(String sourceMethod) {
String transformedMethod = "";
if ("Password".equalsIgnoreCase(sourceMethod)) {
transformedMethod = SAML2Constants.AUTH_CONTEXT_CLASS_REF_PASSWORD;
} else if (sourceMethod != null && sourceMethod.length() != 0) {
return sourceMethod;
}
return transformedMethod;
}
/**
* Create a SAML2 Attribute
*
* @param friendlyName of type String
* @param name of type String
* @param nameFormat of type String
* @param values of type ArrayList
* @return a SAML2 Attribute
*/
public static Attribute createAttribute(
String friendlyName, String name, String nameFormat, List