/* * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in * compliance with the Licence. You may obtain a copy of the Licence at: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * * Unless required by applicable law or agreed to in writing, software distributed under the Licence * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the Licence for the specific language governing permissions and limitations under * the Licence. * * This product combines work with different licenses. See the "NOTICE" text file for details on the * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative * works that you distribute must include a readable copy of the "NOTICE" text file. */ package at.gv.egiz.eaaf.modules.pvp2.sp.impl.utils; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.opensaml.core.xml.XMLObject; import org.opensaml.saml.saml2.core.Assertion; import org.opensaml.saml.saml2.core.Attribute; import org.opensaml.saml.saml2.core.AttributeStatement; import org.opensaml.saml.saml2.core.AuthnContextClassRef; import org.opensaml.saml.saml2.core.AuthnStatement; import org.opensaml.saml.saml2.core.Response; import org.opensaml.saml.saml2.core.StatusResponseType; import org.opensaml.saml.saml2.core.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import at.gv.egiz.eaaf.core.api.data.ExtendedPvpAttributeDefinitions; import at.gv.egiz.eaaf.modules.pvp2.PvpConstants; import at.gv.egiz.eaaf.modules.pvp2.sp.exception.AssertionAttributeExtractorExeption; public class AssertionAttributeExtractor { private static final Logger log = LoggerFactory.getLogger(AssertionAttributeExtractor.class); private Assertion assertion = null; private final Map> attributs = new HashMap<>(); // private PersonalAttributeList storkAttributes = new PersonalAttributeList(); private final List minimalMdsAttributeNamesList = Arrays.asList(PvpConstants.PRINCIPAL_NAME_NAME, PvpConstants.GIVEN_NAME_NAME, PvpConstants.BIRTHDATE_NAME, PvpConstants.BPK_NAME); @Deprecated private final List minimalIdlAttributeNamesList = Arrays.asList(PvpConstants.EID_IDENTITY_LINK_NAME, PvpConstants.EID_SOURCE_PIN_NAME, PvpConstants.EID_SOURCE_PIN_TYPE_NAME); private final List minimalEidAttributeNamesList = Arrays.asList(ExtendedPvpAttributeDefinitions.EID_EIDBIND_NAME); /** * Parse the SAML2 Response element and extracts included information.
*
* INFO: Actually, only the first SAML2 Assertion of the SAML2 Response * is used! * * @param samlResponse SAML2 Response * @throws AssertionAttributeExtractorExeption In case of an error */ public AssertionAttributeExtractor(final StatusResponseType samlResponse) throws AssertionAttributeExtractorExeption { if (samlResponse != null && samlResponse instanceof Response) { final List assertions = ((Response) samlResponse).getAssertions(); if (assertions.size() == 0) { throw new AssertionAttributeExtractorExeption("Assertion"); } else if (assertions.size() > 1) { log.warn("Found more then ONE PVP2.1 assertions. Only the First is used."); } assertion = assertions.get(0); internalInitialize(); } else { throw new AssertionAttributeExtractorExeption(); } } /** * Parse the SAML2 Assertion element and extracts included information.
*
* * @param assertion SAML2 Assertion * @throws AssertionAttributeExtractorExeption In case of an error */ public AssertionAttributeExtractor(final Assertion assertion) throws AssertionAttributeExtractorExeption { this.assertion = assertion; internalInitialize(); } /** * Get all SAML2 attributes from first SAML2 AttributeStatement element. * * @return List of SAML2 Attributes */ public List getAllResponseAttributesFromFirstAttributeStatement() { return assertion.getAttributeStatements().get(0).getAttributes(); } /** * Get all SAML2 attributes of specific SAML2 AttributeStatement element. * * @param attrStatementID List ID of the AttributeStatement element * @return List of SAML2 Attributes */ public List getAllResponseAttributes(final int attrStatementID) { return assertion.getAttributeStatements().get(attrStatementID).getAttributes(); } /** * check attributes from assertion with minimal required attribute list. * * @return */ public boolean containsAllRequiredAttributes() { return containsAllRequiredAttributes(minimalEidAttributeNamesList) || containsAllRequiredAttributes(minimalIdlAttributeNamesList) || containsAllRequiredAttributes(minimalMdsAttributeNamesList); } /** * check attributes from assertion with attributeNameList bPK or enc_bPK are * always needed. * * @param attributeNameList List of attributes which are required * * @return */ public boolean containsAllRequiredAttributes(final Collection attributeNameList) { // first check if a bPK or an encrypted bPK is available boolean flag = true; for (final String attr : attributeNameList) { if (!attributs.containsKey(attr)) { flag = false; log.debug("Assertion contains no Attribute " + attr); } } if (flag) { return flag; } else { log.debug( "Assertion contains no all minimum attributes from: " + attributeNameList.toString()); return false; } } public boolean containsAttribute(final String attributeName) { return attributs.containsKey(attributeName); } /** * Get single attribute with name. * * @param attributeName attribute Name * @return Attribute value */ public String getSingleAttributeValue(final String attributeName) { if (attributs.containsKey(attributeName) && attributs.get(attributeName).size() > 0) { return attributs.get(attributeName).get(0); } else { return null; } } public List getAttributeValues(final String attributeName) { return attributs.get(attributeName); } /** * Return all include PVP attribute names. * * @return */ public Set getAllIncludeAttributeNames() { return attributs.keySet(); } /** * Get User's nameId. * * @return nameId * @throws AssertionAttributeExtractorExeption In case of an error */ public String getNameID() throws AssertionAttributeExtractorExeption { if (assertion.getSubject() != null) { final Subject subject = assertion.getSubject(); if (subject.getNameID() != null) { if (StringUtils.isNotEmpty(subject.getNameID().getValue())) { return subject.getNameID().getValue(); } else { log.error("SAML2 NameID Element is empty."); } } } throw new AssertionAttributeExtractorExeption("nameID"); } /** * Get the Id attribute from SAML2 assertion. * * @return */ public String getAssertionID() { return assertion.getID(); } /** * Get SessionIndex from assertion. * * @return sessionIndex * @throws AssertionAttributeExtractorExeption In case of an error */ public String getSessionIndex() throws AssertionAttributeExtractorExeption { final AuthnStatement authn = getAuthnStatement(); if (StringUtils.isNotEmpty(authn.getSessionIndex())) { return authn.getSessionIndex(); } else { throw new AssertionAttributeExtractorExeption("SessionIndex"); } } /** * Get LoA from Assertion. * * @return LoA * @throws AssertionAttributeExtractorExeption In case of an error */ public String getQaaLevel() throws AssertionAttributeExtractorExeption { final AuthnStatement authn = getAuthnStatement(); if (authn.getAuthnContext() != null && authn.getAuthnContext().getAuthnContextClassRef() != null) { final AuthnContextClassRef qaaClass = authn.getAuthnContext().getAuthnContextClassRef(); if (StringUtils.isNotEmpty(qaaClass.getURI())) { return qaaClass.getURI(); } else { throw new AssertionAttributeExtractorExeption("AuthnContextClassRef (QAALevel)"); } } throw new AssertionAttributeExtractorExeption("AuthnContextClassRef"); } public Assertion getFullAssertion() { return assertion; } /** * Get the Assertion validTo period. * *

* Primarily, the 'SessionNotOnOrAfter' attribute in the SAML2 'AuthnStatment' * element is used. If this is empty, this method returns value of SAML * 'Conditions' element. *

* * @return Date, until this SAML2 assertion is valid */ public Date getAssertionNotOnOrAfter() { if (getFullAssertion().getAuthnStatements() != null && getFullAssertion().getAuthnStatements().size() > 0) { for (final AuthnStatement el : getFullAssertion().getAuthnStatements()) { if (el.getSessionNotOnOrAfter() != null) { return Date.from(el.getSessionNotOnOrAfter()); } } } return Date.from(getFullAssertion().getConditions().getNotOnOrAfter()); } /** * Get the Assertion issuing date. * *

* This method returns value of SAML 'Conditions' element. *

* * @return Date, when the SAML2 assertion was issued, otherwise null */ public Instant getAssertionIssuingDate() { try { return getFullAssertion().getIssueInstant(); } catch (final NullPointerException e) { return null; } } /** * Get the Assertion validFrom period. * *

* This method returns value of SAML 'Conditions' element. *

* * @return Date, after this SAML2 assertion is valid, otherwise null */ public Date getAssertionNotBefore() { try { return Date.from(getFullAssertion().getConditions().getNotBefore()); } catch (final NullPointerException e) { return null; } } private AuthnStatement getAuthnStatement() throws AssertionAttributeExtractorExeption { final List authnList = assertion.getAuthnStatements(); if (authnList.size() == 0) { throw new AssertionAttributeExtractorExeption("AuthnStatement"); } else if (authnList.size() > 1) { log.warn("Found more then ONE AuthnStatements in PVP2.1 assertions. Only the First is used."); } return authnList.get(0); } private void internalInitialize() { if (assertion.getAttributeStatements() != null && assertion.getAttributeStatements().size() > 0) { final AttributeStatement attrStat = assertion.getAttributeStatements().get(0); for (final Attribute attr : attrStat.getAttributes()) { final List attrList = new ArrayList<>(); for (final XMLObject el : attr.getAttributeValues()) { attrList.add(el.getDOM().getTextContent()); attributs.put(attr.getName(), attrList); } } } } }