summaryrefslogtreecommitdiff
path: root/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/PVP2AssertionBuilder.java
diff options
context:
space:
mode:
Diffstat (limited to 'eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/PVP2AssertionBuilder.java')
-rw-r--r--eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/PVP2AssertionBuilder.java448
1 files changed, 448 insertions, 0 deletions
diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/PVP2AssertionBuilder.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/PVP2AssertionBuilder.java
new file mode 100644
index 00000000..7369da15
--- /dev/null
+++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/PVP2AssertionBuilder.java
@@ -0,0 +1,448 @@
+/*******************************************************************************
+ *******************************************************************************/
+package at.gv.egiz.eaaf.modules.pvp2.idp.impl.builder;
+
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.joda.time.DateTime;
+import org.opensaml.common.xml.SAMLConstants;
+import org.opensaml.saml2.core.Assertion;
+import org.opensaml.saml2.core.Attribute;
+import org.opensaml.saml2.core.AttributeQuery;
+import org.opensaml.saml2.core.AttributeStatement;
+import org.opensaml.saml2.core.Audience;
+import org.opensaml.saml2.core.AudienceRestriction;
+import org.opensaml.saml2.core.AuthnContext;
+import org.opensaml.saml2.core.AuthnContextClassRef;
+import org.opensaml.saml2.core.AuthnRequest;
+import org.opensaml.saml2.core.AuthnStatement;
+import org.opensaml.saml2.core.Conditions;
+import org.opensaml.saml2.core.Issuer;
+import org.opensaml.saml2.core.NameID;
+import org.opensaml.saml2.core.RequestedAuthnContext;
+import org.opensaml.saml2.core.Subject;
+import org.opensaml.saml2.core.SubjectConfirmation;
+import org.opensaml.saml2.core.SubjectConfirmationData;
+import org.opensaml.saml2.core.impl.AuthnRequestImpl;
+import org.opensaml.saml2.metadata.AssertionConsumerService;
+import org.opensaml.saml2.metadata.AttributeConsumingService;
+import org.opensaml.saml2.metadata.EntityDescriptor;
+import org.opensaml.saml2.metadata.NameIDFormat;
+import org.opensaml.saml2.metadata.RequestedAttribute;
+import org.opensaml.saml2.metadata.SPSSODescriptor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Base64Utils;
+
+import at.gv.egiz.eaaf.core.api.data.EAAFConstants;
+import at.gv.egiz.eaaf.core.api.data.ILoALevelMapper;
+import at.gv.egiz.eaaf.core.api.idp.IAuthData;
+import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration;
+import at.gv.egiz.eaaf.core.api.idp.slo.SLOInformationInterface;
+import at.gv.egiz.eaaf.core.exceptions.UnavailableAttributeException;
+import at.gv.egiz.eaaf.core.impl.data.Pair;
+import at.gv.egiz.eaaf.core.impl.idp.controller.protocols.RequestImpl;
+import at.gv.egiz.eaaf.core.impl.utils.Random;
+import at.gv.egiz.eaaf.modules.pvp2.PVPConstants;
+import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2Exception;
+import at.gv.egiz.eaaf.modules.pvp2.exception.QAANotSupportedException;
+import at.gv.egiz.eaaf.modules.pvp2.idp.api.builder.ISubjectNameIdGenerator;
+import at.gv.egiz.eaaf.modules.pvp2.idp.exception.ResponderErrorException;
+import at.gv.egiz.eaaf.modules.pvp2.idp.exception.UnprovideableAttributeException;
+import at.gv.egiz.eaaf.modules.pvp2.idp.impl.PVPSProfilePendingRequest;
+import at.gv.egiz.eaaf.modules.pvp2.impl.builder.PVPAttributeBuilder;
+import at.gv.egiz.eaaf.modules.pvp2.impl.utils.QAALevelVerifier;
+import at.gv.egiz.eaaf.modules.pvp2.impl.utils.SAML2Utils;
+
+@Service("PVP2AssertionBuilder")
+public class PVP2AssertionBuilder implements PVPConstants {
+
+ private static final Logger log = LoggerFactory.getLogger(PVP2AssertionBuilder.class);
+ @Autowired private ILoALevelMapper loaLevelMapper;
+ @Autowired private ISubjectNameIdGenerator subjectNameIdGenerator;
+
+
+ /**
+ * Build a PVP assertion as response for a SAML2 AttributeQuery request
+ *
+ * @param issuerEntityID EnitiyID, which should be used for this IDP response
+ * @param attrQuery AttributeQuery request from Service-Provider
+ * @param attrList List of PVP response attributes
+ * @param now Current time
+ * @param validTo ValidTo time of the assertion
+ * @param qaaLevel QAA level of the authentication
+ * @param sessionIndex SAML2 SessionIndex, which should be included *
+ * @return PVP 2.1 Assertion
+ * @throws PVP2Exception
+ */
+ public Assertion buildAssertion(String issuerEntityID, AttributeQuery attrQuery,
+ List<Attribute> attrList, DateTime now, DateTime validTo, String qaaLevel, String sessionIndex) throws PVP2Exception {
+
+ AuthnContextClassRef authnContextClassRef = SAML2Utils.createSAMLObject(AuthnContextClassRef.class);
+ authnContextClassRef.setAuthnContextClassRef(qaaLevel);
+
+ NameID subjectNameID = SAML2Utils.createSAMLObject(NameID.class);
+ subjectNameID.setFormat(attrQuery.getSubject().getNameID().getFormat());
+ subjectNameID.setValue(attrQuery.getSubject().getNameID().getValue());
+
+ SubjectConfirmationData subjectConfirmationData = null;
+
+ return buildGenericAssertion(issuerEntityID, attrQuery.getIssuer().getValue(), now,
+ authnContextClassRef, attrList, subjectNameID, subjectConfirmationData, sessionIndex,
+ validTo);
+ }
+
+
+ /**
+ * Build a PVP 2.1 assertion as response of a SAML2 AuthnRequest
+ *
+ * @param issuerEntityID EnitiyID, which should be used for this IDP response
+ * @param pendingReq Current processed pendingRequest DAO
+ * @param authnRequest Current processed PVP AuthnRequest
+ * @param authData AuthenticationData of the user, which is already authenticated
+ * @param peerEntity SAML2 EntityDescriptor of the service-provider, which receives the response
+ * @param date TimeStamp
+ * @param assertionConsumerService SAML2 endpoint of the service-provider, which should be used
+ * @param sloInformation Single LogOut information DAO
+ * @return
+ * @throws PVP2Exception
+ */
+ public Assertion buildAssertion(String issuerEntityID, PVPSProfilePendingRequest pendingReq, AuthnRequest authnRequest,
+ IAuthData authData, EntityDescriptor peerEntity, DateTime date,
+ AssertionConsumerService assertionConsumerService, SLOInformationInterface sloInformation)
+ throws PVP2Exception {
+
+ RequestedAuthnContext reqAuthnContext = authnRequest
+ .getRequestedAuthnContext();
+
+ AuthnContextClassRef authnContextClassRef = SAML2Utils
+ .createSAMLObject(AuthnContextClassRef.class);
+
+ ISPConfiguration oaParam = pendingReq.getServiceProviderConfiguration();
+
+ if (reqAuthnContext == null) {
+ authnContextClassRef.setAuthnContextClassRef(authData.getEIDASQAALevel());
+
+ } else {
+
+ boolean eIDAS_qaa_found = false;
+
+ List<AuthnContextClassRef> reqAuthnContextClassRefIt = reqAuthnContext
+ .getAuthnContextClassRefs();
+
+ if (reqAuthnContextClassRefIt.size() == 0) {
+ QAALevelVerifier.verifyQAALevel(authData.getEIDASQAALevel(), EAAFConstants.EIDAS_QAA_HIGH);
+
+ eIDAS_qaa_found = true;
+ authnContextClassRef.setAuthnContextClassRef(EAAFConstants.EIDAS_QAA_HIGH);
+
+ } else {
+ for (AuthnContextClassRef authnClassRef : reqAuthnContextClassRefIt) {
+ String qaa_uri = authnClassRef.getAuthnContextClassRef();
+
+ if (!qaa_uri.trim().startsWith(EAAFConstants.EIDAS_QAA_PREFIX)) {
+ if (loaLevelMapper != null) {
+ log.debug("Find no eIDAS LoA. Start mapping process ... " );
+ qaa_uri = loaLevelMapper.mapToeIDASLoA(qaa_uri.trim());
+
+ } else
+ log.debug("AuthnRequest contains no eIDAS LoA. NO LoA mapper FOUND, ignore "
+ + "'" + qaa_uri.trim() + "'");
+
+ }
+
+ if (qaa_uri.trim().equals(EAAFConstants.EIDAS_QAA_HIGH)
+ || qaa_uri.trim().equals(EAAFConstants.EIDAS_QAA_SUBSTANTIAL)
+ || qaa_uri.trim().equals(EAAFConstants.EIDAS_QAA_LOW)) {
+
+ if (authData.isForeigner()) {
+ QAALevelVerifier.verifyQAALevel(authData.getEIDASQAALevel(), oaParam.getMinimumLevelOfAssurence());
+
+ eIDAS_qaa_found = true;
+ authnContextClassRef.setAuthnContextClassRef(authData.getEIDASQAALevel());
+
+ } else {
+
+ QAALevelVerifier.verifyQAALevel(authData.getEIDASQAALevel(),
+ qaa_uri.trim());
+
+ eIDAS_qaa_found = true;
+ authnContextClassRef.setAuthnContextClassRef(authData.getEIDASQAALevel());
+
+ }
+ break;
+ }
+ }
+ }
+
+ if (!eIDAS_qaa_found)
+ throw new QAANotSupportedException(EAAFConstants.EIDAS_QAA_HIGH);
+
+ }
+
+
+
+ SPSSODescriptor spSSODescriptor = peerEntity
+ .getSPSSODescriptor(SAMLConstants.SAML20P_NS);
+
+ //add Attributes to Assertion
+ List<Attribute> attrList = new ArrayList<Attribute>();
+ if (spSSODescriptor.getAttributeConsumingServices() != null &&
+ spSSODescriptor.getAttributeConsumingServices().size() > 0) {
+
+ Integer aIdx = authnRequest.getAttributeConsumingServiceIndex();
+ int idx = 0;
+
+ AttributeConsumingService attributeConsumingService = null;
+ if (aIdx != null) {
+ idx = aIdx.intValue();
+ attributeConsumingService = spSSODescriptor
+ .getAttributeConsumingServices().get(idx);
+
+ } else {
+ List<AttributeConsumingService> attrConsumingServiceList = spSSODescriptor.getAttributeConsumingServices();
+ for (AttributeConsumingService el : attrConsumingServiceList) {
+ if (el.isDefault())
+ attributeConsumingService = el;
+ }
+ }
+
+ /*
+ * TODO: maybe use first AttributeConsumingService if no is selected
+ * in request or on service is marked as default
+ *
+ */
+ if (attributeConsumingService == null ) {
+ List<AttributeConsumingService> attrConsumingServiceList = spSSODescriptor.getAttributeConsumingServices();
+ if (attrConsumingServiceList != null && !attrConsumingServiceList.isEmpty())
+ attributeConsumingService = attrConsumingServiceList.get(0);
+
+ }
+
+
+ if (attributeConsumingService != null) {
+ Iterator<RequestedAttribute> it = attributeConsumingService
+ .getRequestAttributes().iterator();
+ while (it.hasNext()) {
+ RequestedAttribute reqAttribut = it.next();
+ try {
+ Attribute attr = PVPAttributeBuilder.buildAttribute(
+ reqAttribut.getName(), oaParam, authData);
+ if (attr == null) {
+ if (reqAttribut.isRequired()) {
+ throw new UnprovideableAttributeException(
+ reqAttribut.getName());
+ }
+ } else {
+ attrList.add(attr);
+ }
+
+ } catch (UnavailableAttributeException e) {
+ log.info(
+ "Attribute generation for "
+ + reqAttribut.getFriendlyName() + " not possible.");
+ if (reqAttribut.isRequired()) {
+ throw new UnprovideableAttributeException(
+ reqAttribut.getName());
+ }
+
+
+ } catch (PVP2Exception e) {
+ log.info(
+ "Attribute generation failed! for "
+ + reqAttribut.getFriendlyName());
+ if (reqAttribut.isRequired()) {
+ throw new UnprovideableAttributeException(
+ reqAttribut.getName());
+ }
+
+ } catch (Exception e) {
+ log.warn(
+ "General Attribute generation failed! for "
+ + reqAttribut.getFriendlyName(), e);
+ if (reqAttribut.isRequired()) {
+ throw new UnprovideableAttributeException(
+ reqAttribut.getName());
+ }
+
+ }
+ }
+ }
+ }
+
+ //generate subjectNameId
+ NameID subjectNameID = SAML2Utils.createSAMLObject(NameID.class);
+ Pair<String, String> subjectNameIdPair = subjectNameIdGenerator.generateSubjectNameId(authData, oaParam);
+ subjectNameID.setValue(subjectNameIdPair.getFirst());
+ subjectNameID.setNameQualifier(subjectNameIdPair.getSecond());
+
+ //get NameIDFormat from request
+ String nameIDFormat = NameID.TRANSIENT;
+ AuthnRequest authnReq = (AuthnRequestImpl) authnRequest;
+ if (authnReq.getNameIDPolicy() != null &&
+ StringUtils.isNotEmpty(authnReq.getNameIDPolicy().getFormat())) {
+ nameIDFormat = authnReq.getNameIDPolicy().getFormat();
+
+ } else {
+ //get NameIDFormat from metadata
+ List<NameIDFormat> metadataNameIDFormats = spSSODescriptor.getNameIDFormats();
+
+ if (metadataNameIDFormats != null) {
+
+ for (NameIDFormat el : metadataNameIDFormats) {
+ if (NameID.PERSISTENT.equals(el.getFormat())) {
+ nameIDFormat = NameID.PERSISTENT;
+ break;
+
+ } else if (NameID.TRANSIENT.equals(el.getFormat()) ||
+ NameID.UNSPECIFIED.equals(el.getFormat()))
+ break;
+
+ }
+ }
+ }
+
+ if (NameID.TRANSIENT.equals(nameIDFormat) || NameID.UNSPECIFIED.equals(nameIDFormat)) {
+ String random = Random.nextHexRandom32();
+ String nameID = subjectNameID.getValue();
+
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ byte[] hash = md.digest((nameID + random).getBytes("ISO-8859-1"));
+ subjectNameID.setValue(Base64Utils.encodeToString(hash));
+ subjectNameID.setNameQualifier(null);
+ subjectNameID.setFormat(NameID.TRANSIENT);
+
+ } catch (Exception e) {
+ log.warn("PVP2 subjectNameID error", e);
+ throw new ResponderErrorException("pvp2.13", null, e);
+ }
+
+ } else
+ subjectNameID.setFormat(nameIDFormat);
+
+
+ String sessionIndex = null;
+
+ //if request is a reauthentication and NameIDFormat match reuse old session information
+ if (StringUtils.isNotEmpty(authData.getNameID()) &&
+ StringUtils.isNotEmpty(authData.getNameIDFormat()) &&
+ nameIDFormat.equals(authData.getNameIDFormat())) {
+ subjectNameID.setValue(authData.getNameID());
+ sessionIndex = authData.getSessionIndex();
+
+ }
+
+ //
+ if (StringUtils.isEmpty(sessionIndex))
+ sessionIndex = SAML2Utils.getSecureIdentifier();
+
+ SubjectConfirmationData subjectConfirmationData = SAML2Utils
+ .createSAMLObject(SubjectConfirmationData.class);
+ subjectConfirmationData.setInResponseTo(authnRequest.getID());
+ subjectConfirmationData.setNotOnOrAfter(new DateTime(authData.getSsoSessionValidTo().getTime()));
+// subjectConfirmationData.setNotBefore(date);
+
+ //set 'recipient' attribute in subjectConformationData
+ subjectConfirmationData.setRecipient(assertionConsumerService.getLocation());
+
+ //set IP address of the user machine as 'Address' attribute in subjectConformationData
+ String usersIPAddress = pendingReq.getGenericData(
+ RequestImpl.DATAID_REQUESTER_IP_ADDRESS, String.class);
+ if (StringUtils.isNotEmpty(usersIPAddress))
+ subjectConfirmationData.setAddress(usersIPAddress);
+
+ //set SLO information
+ sloInformation.setUserNameIdentifier(subjectNameID.getValue());
+ sloInformation.setNameIDFormat(subjectNameID.getFormat());
+ sloInformation.setSessionIndex(sessionIndex);
+
+ return buildGenericAssertion(issuerEntityID, peerEntity.getEntityID(), date, authnContextClassRef, attrList, subjectNameID, subjectConfirmationData, sessionIndex, subjectConfirmationData.getNotOnOrAfter());
+ }
+
+ /**
+ *
+ * @param issuer IDP EntityID
+ * @param entityID Service Provider EntityID
+ * @param date
+ * @param authnContextClassRef
+ * @param attrList
+ * @param subjectNameID
+ * @param subjectConfirmationData
+ * @param sessionIndex
+ * @param isValidTo
+ * @return
+ * @throws ConfigurationException
+ */
+
+ public Assertion buildGenericAssertion(String issuer, String entityID, DateTime date,
+ AuthnContextClassRef authnContextClassRef, List<Attribute> attrList,
+ NameID subjectNameID, SubjectConfirmationData subjectConfirmationData,
+ String sessionIndex, DateTime isValidTo) throws ResponderErrorException {
+ Assertion assertion = SAML2Utils.createSAMLObject(Assertion.class);
+
+ AuthnContext authnContext = SAML2Utils
+ .createSAMLObject(AuthnContext.class);
+ authnContext.setAuthnContextClassRef(authnContextClassRef);
+
+ AuthnStatement authnStatement = SAML2Utils
+ .createSAMLObject(AuthnStatement.class);
+
+ authnStatement.setAuthnInstant(date);
+ authnStatement.setSessionIndex(sessionIndex);
+ authnStatement.setAuthnContext(authnContext);
+
+ assertion.getAuthnStatements().add(authnStatement);
+
+ AttributeStatement attributeStatement = SAML2Utils
+ .createSAMLObject(AttributeStatement.class);
+ attributeStatement.getAttributes().addAll(attrList);
+ if (attributeStatement.getAttributes().size() > 0) {
+ assertion.getAttributeStatements().add(attributeStatement);
+ }
+
+ Subject subject = SAML2Utils.createSAMLObject(Subject.class);
+ subject.setNameID(subjectNameID);
+
+ SubjectConfirmation subjectConfirmation = SAML2Utils
+ .createSAMLObject(SubjectConfirmation.class);
+ subjectConfirmation.setMethod(SubjectConfirmation.METHOD_BEARER);
+ subjectConfirmation.setSubjectConfirmationData(subjectConfirmationData);
+
+ subject.getSubjectConfirmations().add(subjectConfirmation);
+
+ Conditions conditions = SAML2Utils.createSAMLObject(Conditions.class);
+ AudienceRestriction audienceRestriction = SAML2Utils
+ .createSAMLObject(AudienceRestriction.class);
+ Audience audience = SAML2Utils.createSAMLObject(Audience.class);
+
+ audience.setAudienceURI(entityID);
+ audienceRestriction.getAudiences().add(audience);
+ conditions.setNotBefore(date);
+ conditions.setNotOnOrAfter(isValidTo);
+
+ conditions.getAudienceRestrictions().add(audienceRestriction);
+
+ assertion.setConditions(conditions);
+
+ Issuer issuerObj = SAML2Utils.createSAMLObject(Issuer.class);
+
+ if (issuer.endsWith("/"))
+ issuer = issuer.substring(0, issuer.length()-1);
+ issuerObj.setValue(issuer);
+ issuerObj.setFormat(NameID.ENTITY);
+
+ assertion.setIssuer(issuerObj);
+ assertion.setSubject(subject);
+ assertion.setID(SAML2Utils.getSecureIdentifier());
+ assertion.setIssueInstant(date);
+
+ return assertion;
+ }
+}