aboutsummaryrefslogtreecommitdiff
path: root/modules/authmodule_id-austria/src/main/java/at/asitplus/eidas/specific/modules/auth/idaustria/tasks
diff options
context:
space:
mode:
authorThomas <>2022-03-08 19:06:10 +0100
committerThomas <>2022-03-08 19:06:10 +0100
commit7bf7c3c03fd3a1efeaf3f8e3dd75922e2f5f9921 (patch)
tree6e4ec82475f4f30275d3e0a0305ad3c2d340e0d3 /modules/authmodule_id-austria/src/main/java/at/asitplus/eidas/specific/modules/auth/idaustria/tasks
parent300bd1b44f521a2b33c259be1f8d21eba58c1a31 (diff)
downloadNational_eIDAS_Gateway-7bf7c3c03fd3a1efeaf3f8e3dd75922e2f5f9921.tar.gz
National_eIDAS_Gateway-7bf7c3c03fd3a1efeaf3f8e3dd75922e2f5f9921.tar.bz2
National_eIDAS_Gateway-7bf7c3c03fd3a1efeaf3f8e3dd75922e2f5f9921.zip
refactor(core): move all project libs into sub-project 'modules'
Diffstat (limited to 'modules/authmodule_id-austria/src/main/java/at/asitplus/eidas/specific/modules/auth/idaustria/tasks')
-rw-r--r--modules/authmodule_id-austria/src/main/java/at/asitplus/eidas/specific/modules/auth/idaustria/tasks/ReceiveFromIdAustriaSystemTask.java393
-rw-r--r--modules/authmodule_id-austria/src/main/java/at/asitplus/eidas/specific/modules/auth/idaustria/tasks/RequestIdAustriaSystemTask.java205
2 files changed, 598 insertions, 0 deletions
diff --git a/modules/authmodule_id-austria/src/main/java/at/asitplus/eidas/specific/modules/auth/idaustria/tasks/ReceiveFromIdAustriaSystemTask.java b/modules/authmodule_id-austria/src/main/java/at/asitplus/eidas/specific/modules/auth/idaustria/tasks/ReceiveFromIdAustriaSystemTask.java
new file mode 100644
index 00000000..e486b851
--- /dev/null
+++ b/modules/authmodule_id-austria/src/main/java/at/asitplus/eidas/specific/modules/auth/idaustria/tasks/ReceiveFromIdAustriaSystemTask.java
@@ -0,0 +1,393 @@
+package at.asitplus.eidas.specific.modules.auth.idaustria.tasks;
+
+import java.io.IOException;
+import java.util.Set;
+
+import javax.naming.ConfigurationException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.transform.TransformerException;
+
+import org.apache.commons.lang3.StringUtils;
+import org.opensaml.core.xml.io.MarshallingException;
+import org.opensaml.messaging.decoder.MessageDecodingException;
+import org.opensaml.saml.saml2.core.Response;
+import org.opensaml.saml.saml2.core.StatusCode;
+import org.opensaml.saml.saml2.metadata.IDPSSODescriptor;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import at.asitplus.eidas.specific.core.MsEidasNodeConstants;
+import at.asitplus.eidas.specific.modules.auth.idaustria.IdAustriaAuthConstants;
+import at.asitplus.eidas.specific.modules.auth.idaustria.utils.IdAustriaAuthCredentialProvider;
+import at.asitplus.eidas.specific.modules.auth.idaustria.utils.IdAustriaAuthMetadataProvider;
+import at.asitplus.eidas.specific.modules.auth.idaustria.utils.Utils;
+import at.gv.egiz.eaaf.core.api.data.PvpAttributeDefinitions;
+import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext;
+import at.gv.egiz.eaaf.core.exceptions.EaafAuthenticationException;
+import at.gv.egiz.eaaf.core.exceptions.EaafBuilderException;
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.exceptions.EaafStorageException;
+import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
+import at.gv.egiz.eaaf.core.impl.data.Pair;
+import at.gv.egiz.eaaf.core.impl.idp.auth.data.AuthProcessDataWrapper;
+import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask;
+import at.gv.egiz.eaaf.modules.pvp2.api.binding.IDecoder;
+import at.gv.egiz.eaaf.modules.pvp2.exception.CredentialsNotAvailableException;
+import at.gv.egiz.eaaf.modules.pvp2.exception.SamlAssertionValidationExeption;
+import at.gv.egiz.eaaf.modules.pvp2.exception.SamlSigningException;
+import at.gv.egiz.eaaf.modules.pvp2.impl.binding.PostBinding;
+import at.gv.egiz.eaaf.modules.pvp2.impl.binding.RedirectBinding;
+import at.gv.egiz.eaaf.modules.pvp2.impl.message.InboundMessage;
+import at.gv.egiz.eaaf.modules.pvp2.impl.message.PvpSProfileResponse;
+import at.gv.egiz.eaaf.modules.pvp2.impl.utils.Saml2Utils;
+import at.gv.egiz.eaaf.modules.pvp2.impl.validation.EaafUriCompare;
+import at.gv.egiz.eaaf.modules.pvp2.impl.validation.TrustEngineFactory;
+import at.gv.egiz.eaaf.modules.pvp2.impl.verification.SamlVerificationEngine;
+import at.gv.egiz.eaaf.modules.pvp2.sp.exception.AssertionValidationExeption;
+import at.gv.egiz.eaaf.modules.pvp2.sp.exception.AuthnResponseValidationException;
+import at.gv.egiz.eaaf.modules.pvp2.sp.impl.utils.AssertionAttributeExtractor;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * ID Austria authentication task that receives the SAML2 response from ID
+ * Austria system.
+ *
+ * @author tlenz
+ *
+ */
+@Slf4j
+public class ReceiveFromIdAustriaSystemTask extends AbstractAuthServletTask {
+
+ private static final String ERROR_PVP_03 = "sp.pvp2.03";
+ private static final String ERROR_PVP_05 = "sp.pvp2.05";
+ private static final String ERROR_PVP_06 = "sp.pvp2.06";
+ private static final String ERROR_PVP_08 = "sp.pvp2.08";
+ private static final String ERROR_PVP_10 = "sp.pvp2.10";
+ private static final String ERROR_PVP_11 = "sp.pvp2.11";
+ private static final String ERROR_PVP_12 = "sp.pvp2.12";
+
+ private static final String ERROR_MSG_00 =
+ "Receive INVALID PVP Response from federated IDP";
+ private static final String ERROR_MSG_01 =
+ "Processing PVP response from 'ms-specific eIDAS node' FAILED.";
+ private static final String ERROR_MSG_02 =
+ "PVP response decrytion FAILED. No credential found.";
+ private static final String ERROR_MSG_03 =
+ "PVP response validation FAILED.";
+
+ @Autowired
+ private SamlVerificationEngine samlVerificationEngine;
+ @Autowired
+ private IdAustriaAuthCredentialProvider credentialProvider;
+ @Autowired(required = true)
+ IdAustriaAuthMetadataProvider metadataProvider;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egovernment.moa.id.auth.modules.AbstractAuthServletTask#execute(at.gv.
+ * egovernment.moa.id.process.api.ExecutionContext,
+ * javax.servlet.http.HttpServletRequest,
+ * javax.servlet.http.HttpServletResponse)
+ */
+ @Override
+ public void execute(ExecutionContext executionContext, HttpServletRequest request,
+ HttpServletResponse response)
+ throws TaskExecutionException {
+ InboundMessage msg = null;
+
+ try {
+
+ IDecoder decoder = null;
+ EaafUriCompare comperator = null;
+ // select Response Binding
+ if (request.getMethod().equalsIgnoreCase("POST")) {
+ decoder = new PostBinding();
+ comperator = new EaafUriCompare(pendingReq.getAuthUrl() + IdAustriaAuthConstants.ENDPOINT_POST);
+ log.trace("Receive PVP Response from 'ID Austria', by using POST-Binding.");
+
+ } else if (request.getMethod().equalsIgnoreCase("GET")) {
+ decoder = new RedirectBinding();
+ comperator = new EaafUriCompare(pendingReq.getAuthUrl()
+ + IdAustriaAuthConstants.ENDPOINT_REDIRECT);
+ log.trace("Receive PVP Response from 'ID Austria', by using Redirect-Binding.");
+
+ } else {
+ log.warn("Receive PVP Response, but Binding ("
+ + request.getMethod() + ") is not supported.");
+ throw new AuthnResponseValidationException(ERROR_PVP_03, new Object[] {
+ IdAustriaAuthConstants.MODULE_NAME_FOR_LOGGING });
+
+ }
+
+ // decode PVP response object
+ msg = (InboundMessage) decoder.decode(
+ request, response, metadataProvider, IDPSSODescriptor.DEFAULT_ELEMENT_NAME,
+ comperator);
+
+ // validate response signature
+ if (!msg.isVerified()) {
+ samlVerificationEngine.verify(msg,
+ TrustEngineFactory.getSignatureKnownKeysTrustEngine(metadataProvider));
+ msg.setVerified(true);
+
+ }
+
+ // validate assertion
+ final Pair<PvpSProfileResponse, Boolean> processedMsg =
+ preProcessAuthResponse((PvpSProfileResponse) msg);
+
+ // check if SAML2 response contains user-stop decision
+ if (processedMsg.getSecond()) {
+ stopProcessFromUserDecision(executionContext, request, response);
+
+ } else {
+ // validate entityId of response
+ final String idAustriaEntityID =
+ Utils.getIdAustriaEntityId(pendingReq.getServiceProviderConfiguration(), authConfig);
+ final String respEntityId = msg.getEntityID();
+ if (!idAustriaEntityID.equals(respEntityId)) {
+ log.warn("Response Issuer is not a 'ID Austria System'. Stopping eIDAS authentication ...");
+ throw new AuthnResponseValidationException(ERROR_PVP_08,
+ new Object[] { IdAustriaAuthConstants.MODULE_NAME_FOR_LOGGING,
+ msg.getEntityID() });
+
+ }
+
+ // initialize Attribute extractor
+ final AssertionAttributeExtractor extractor =
+ new AssertionAttributeExtractor(processedMsg.getFirst().getResponse());
+
+ getAuthDataFromInterfederation(extractor);
+
+ // set NeedConsent to false, because user gives consont during authentication
+ pendingReq.setNeedUserConsent(false);
+
+ // store pending-request
+ requestStoreage.storePendingRequest(pendingReq);
+
+ // write log entries
+ // revisionsLogger.logEvent(pendingReq,
+ // EidasAuthEventConstants.AUTHPROCESS_EIDAS_AT_CONNECTOR_MDS_VALID);
+ log.info("Receive a valid assertion from IDP " + msg.getEntityID());
+
+ }
+
+ } catch (final AuthnResponseValidationException e) {
+ throw new TaskExecutionException(pendingReq, ERROR_MSG_03, e);
+
+ } catch (MessageDecodingException | SecurityException | SamlSigningException e) {
+ final String samlRequest = request.getParameter("SAMLRequest");
+ log.debug("Receive INVALID PVP Response from 'ID Austria System': {}",
+ samlRequest, null, e);
+ throw new TaskExecutionException(pendingReq, ERROR_MSG_00,
+ new AuthnResponseValidationException(ERROR_PVP_11,
+ new Object[] { IdAustriaAuthConstants.MODULE_NAME_FOR_LOGGING }, e));
+
+ } catch (IOException | MarshallingException | TransformerException e) {
+ log.debug("Processing PVP response from 'ID Austria System' FAILED.", e);
+ throw new TaskExecutionException(pendingReq, ERROR_MSG_01,
+ new AuthnResponseValidationException(ERROR_PVP_12,
+ new Object[] { IdAustriaAuthConstants.MODULE_NAME_FOR_LOGGING, e.getMessage() },
+ e));
+
+ } catch (final CredentialsNotAvailableException e) {
+ log.debug("PVP response decrytion FAILED. No credential found.", e);
+ throw new TaskExecutionException(pendingReq, ERROR_MSG_02,
+ new AuthnResponseValidationException(ERROR_PVP_10,
+ new Object[] { IdAustriaAuthConstants.MODULE_NAME_FOR_LOGGING }, e));
+
+ } catch (final Exception e) {
+ log.debug("PVP response validation FAILED. Msg:" + e.getMessage(), e);
+ throw new TaskExecutionException(pendingReq, ERROR_MSG_03,
+ new AuthnResponseValidationException(ERROR_PVP_12,
+ new Object[] { IdAustriaAuthConstants.MODULE_NAME_FOR_LOGGING, e.getMessage() }, e));
+
+ }
+
+ }
+
+ private void getAuthDataFromInterfederation(AssertionAttributeExtractor extractor)
+ throws EaafBuilderException, ConfigurationException {
+
+ final Set<String> requiredEidasNodeAttributes =
+ IdAustriaAuthConstants.DEFAULT_REQUIRED_PVP_ATTRIBUTE_NAMES;
+ try {
+ // check if all attributes are include
+ if (!extractor.containsAllRequiredAttributes()
+ || !extractor.containsAllRequiredAttributes(
+ requiredEidasNodeAttributes)) {
+ log.warn("PVP Response from 'ID Austria System' contains not all requested attributes.");
+ throw new AssertionValidationExeption(ERROR_PVP_06, new Object[] {
+ IdAustriaAuthConstants.MODULE_NAME_FOR_LOGGING });
+
+ }
+
+ // copy attributes into MOASession
+ final AuthProcessDataWrapper session = pendingReq.getSessionData(
+ AuthProcessDataWrapper.class);
+
+ // validate response attributes
+ validateResponseAttributes(extractor);
+
+ // inject all attributes into session
+ final Set<String> includedAttrNames = extractor.getAllIncludeAttributeNames();
+ for (final String attrName : includedAttrNames) {
+ injectAuthInfosIntoSession(session, attrName,
+ extractor.getSingleAttributeValue(attrName));
+
+ }
+
+ // set foreigner flag
+ session.setForeigner(false);
+
+ // mark it as ID Austria process, because we have no baseId
+ session.setEidProcess(true);
+
+ // set IssuerInstant from Assertion
+ session.setIssueInstant(extractor.getAssertionIssuingDate());
+
+ // set mandate flag
+ session.setUseMandates(checkIfMandateInformationIsAvailable(extractor));
+
+
+ } catch (final EaafException | IOException e) {
+ throw new EaafBuilderException(ERROR_PVP_06, null, e.getMessage(), e);
+
+ }
+ }
+
+
+ /**
+ * Check if mandate information is available.
+ *
+ * @param extractor Assertion from ID Austria system.
+ * @return <code>true</code> if mandate was used, otherwise <code>false</code>
+ */
+ private boolean checkIfMandateInformationIsAvailable(AssertionAttributeExtractor extractor) {
+ boolean isMandateIncluded = extractor.containsAttribute(PvpAttributeDefinitions.MANDATE_TYPE_NAME);
+ log.debug("Response from ID-Austria system contains mandate information. Switch to mandate-mode ... ");
+ return isMandateIncluded;
+
+ }
+
+ private void validateResponseAttributes(AssertionAttributeExtractor extractor)
+ throws EaafAuthenticationException {
+ final String bpkTarget = extractor.getSingleAttributeValue(
+ PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME);
+ final String spTarget = pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier();
+ if (StringUtils.isNotEmpty(bpkTarget) && bpkTarget.equals(spTarget)) {
+ log.debug("Find attr: {} that matches to requested bPK target. bPK should be valid",
+ PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_FRIENDLY_NAME);
+
+ } else {
+ log.trace("Find not attr: {}. Validation bPK attribute ... ",
+ PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_FRIENDLY_NAME);
+ final String bpk = extractor.getSingleAttributeValue(PvpAttributeDefinitions.BPK_NAME);
+ final String[] split = bpk.split(":", 2);
+ if (split.length == 2) {
+ if (!spTarget.endsWith(split[0])) {
+ log.error("bPK from response: {} does not match to SP target: {}", bpk, spTarget);
+ throw new EaafAuthenticationException(IdAustriaAuthConstants.ERRORTYPE_06, null);
+
+ } else {
+ log.trace("Prefix of PVP bPK attribte matches to SP configuration. bPK looks valid");
+
+ }
+
+ } else {
+ log.warn("Find suspect bPK that has no prefix. Use it as it is ... ");
+
+ }
+ }
+ }
+
+ private void injectAuthInfosIntoSession(AuthProcessDataWrapper session,
+ String attrName, String attrValue) throws EaafStorageException, IOException {
+ log.trace("Inject attribute: {} with value: {} into AuthSession", attrName, attrValue);
+ log.debug("Inject attribute: {} into AuthSession", attrName);
+ if (PvpAttributeDefinitions.BPK_NAME.equals(attrName)) {
+ log.trace("Find bPK attribute. Extract eIDAS identifier ... ");
+ session.setGenericDataToSession(MsEidasNodeConstants.ATTR_EIDAS_PERSONAL_IDENTIFIER,
+ extractBpkFromResponse(attrValue));
+
+ } else {
+ session.setGenericDataToSession(attrName, attrValue);
+
+ }
+
+ }
+
+ private String extractBpkFromResponse(String pvpBpkAttrValue) {
+ final String[] split = pvpBpkAttrValue.split(":", 2);
+ if (split.length == 2) {
+ return split[1];
+
+ } else {
+ log.warn("PVP bPK attribute: {} has wrong format. Use it as it is.", pvpBpkAttrValue);
+ return pvpBpkAttrValue;
+
+ }
+ }
+
+ private Pair<PvpSProfileResponse, Boolean> preProcessAuthResponse(PvpSProfileResponse msg)
+ throws IOException, MarshallingException, TransformerException,
+ CredentialsNotAvailableException, AuthnResponseValidationException, SamlAssertionValidationExeption {
+ log.debug("Start PVP-2x assertion processing... ");
+ final Response samlResp = (Response) msg.getResponse();
+
+ // check SAML2 response status-code
+ if (samlResp.getStatus().getStatusCode().getValue().equals(StatusCode.SUCCESS)) {
+ // validate PVP 2.1 assertion
+ samlVerificationEngine.validateAssertion(samlResp,
+ credentialProvider.getMessageEncryptionCredential(),
+ pendingReq.getAuthUrl() + IdAustriaAuthConstants.ENDPOINT_METADATA,
+ IdAustriaAuthConstants.MODULE_NAME_FOR_LOGGING);
+
+ msg.setSamlMessage(Saml2Utils.asDomDocument(samlResp).getDocumentElement());
+ // revisionsLogger.logEvent(pendingReq,
+ // EidasAuthEventConstants.AUTHPROCESS_EIDAS_AT_CONNECTOR_RECEIVED,
+ // samlResp.getID());
+ return Pair.newInstance(msg, false);
+
+ } else {
+ log.info("Receive StatusCode " + samlResp.getStatus().getStatusCode().getValue()
+ + " from 'ms-specific eIDAS node'.");
+ final StatusCode subStatusCode = getSubStatusCode(samlResp);
+ if (subStatusCode != null
+ && IdAustriaAuthConstants.SAML2_STATUSCODE_USERSTOP.equals(subStatusCode.getValue())) {
+ log.info("Find 'User-Stop operation' in SAML2 response. Stopping authentication process ... ");
+ return Pair.newInstance(msg, true);
+
+ }
+
+ // revisionsLogger.logEvent(pendingReq,
+ // EidasAuthEventConstants.AUTHPROCESS_EIDAS_AT_CONNECTOR_RECEIVED_ERROR);
+ throw new AuthnResponseValidationException(ERROR_PVP_05,
+ new Object[] { IdAustriaAuthConstants.MODULE_NAME_FOR_LOGGING,
+ samlResp.getIssuer().getValue(),
+ samlResp.getStatus().getStatusCode().getValue(),
+ samlResp.getStatus().getStatusMessage().getValue() });
+
+ }
+
+ }
+
+ /**
+ * Get SAML2 Sub-StatusCode if not <code>null</code>.
+ *
+ * @param samlResp SAML2 response
+ * @return Sub-StatusCode or <code>null</code> if it's not set
+ */
+ private StatusCode getSubStatusCode(Response samlResp) {
+ if (samlResp.getStatus().getStatusCode().getStatusCode() != null
+ && StringUtils.isNotEmpty(samlResp.getStatus().getStatusCode().getStatusCode().getValue())) {
+ return samlResp.getStatus().getStatusCode().getStatusCode();
+
+ }
+ return null;
+
+ }
+
+}
diff --git a/modules/authmodule_id-austria/src/main/java/at/asitplus/eidas/specific/modules/auth/idaustria/tasks/RequestIdAustriaSystemTask.java b/modules/authmodule_id-austria/src/main/java/at/asitplus/eidas/specific/modules/auth/idaustria/tasks/RequestIdAustriaSystemTask.java
new file mode 100644
index 00000000..66aadde6
--- /dev/null
+++ b/modules/authmodule_id-austria/src/main/java/at/asitplus/eidas/specific/modules/auth/idaustria/tasks/RequestIdAustriaSystemTask.java
@@ -0,0 +1,205 @@
+package at.asitplus.eidas.specific.modules.auth.idaustria.tasks;
+
+import java.security.NoSuchAlgorithmException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.StringUtils;
+import org.opensaml.messaging.encoder.MessageEncodingException;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import at.asitplus.eidas.specific.core.config.ServiceProviderConfiguration;
+import at.asitplus.eidas.specific.modules.auth.idaustria.IdAustriaAuthConstants;
+import at.asitplus.eidas.specific.modules.auth.idaustria.config.IdAustriaAuthRequestBuilderConfiguration;
+import at.asitplus.eidas.specific.modules.auth.idaustria.utils.IdAustriaAuthCredentialProvider;
+import at.asitplus.eidas.specific.modules.auth.idaustria.utils.IdAustriaAuthMetadataProvider;
+import at.asitplus.eidas.specific.modules.auth.idaustria.utils.Utils;
+import at.asitplus.eidas.specific.modules.msproxyservice.protocol.ProxyServicePendingRequest;
+import at.gv.egiz.eaaf.core.api.IRequest;
+import at.gv.egiz.eaaf.core.api.data.ExtendedPvpAttributeDefinitions;
+import at.gv.egiz.eaaf.core.api.data.PvpAttributeDefinitions;
+import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext;
+import at.gv.egiz.eaaf.core.api.storage.ITransactionStorage;
+import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException;
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException;
+import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask;
+import at.gv.egiz.eaaf.core.impl.utils.Random;
+import at.gv.egiz.eaaf.modules.pvp2.api.reqattr.EaafRequestedAttribute;
+import at.gv.egiz.eaaf.modules.pvp2.impl.builder.PvpAttributeBuilder;
+import at.gv.egiz.eaaf.modules.pvp2.impl.utils.Saml2Utils;
+import at.gv.egiz.eaaf.modules.pvp2.sp.exception.AuthnRequestBuildException;
+import at.gv.egiz.eaaf.modules.pvp2.sp.impl.PvpAuthnRequestBuilder;
+import lombok.extern.slf4j.Slf4j;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+import net.shibboleth.utilities.java.support.security.impl.SecureRandomIdentifierGenerationStrategy;
+
+/**
+ * eIDAS Authentication task that generates PVP2 S-Profile request to central
+ * Austrian MS-Connector.
+ *
+ * @author tlenz
+ *
+ */
+@Slf4j
+public class RequestIdAustriaSystemTask extends AbstractAuthServletTask {
+
+ private static final String ERROR_PVP_02 = "sp.pvp2.02";
+ private static final String ERROR_PVP_13 = "sp.pvp2.13";
+
+ private static final String ERROR_MSG_1 =
+ "Requested 'ms-specific eIDAS node' {0} has no valid metadata or metadata is not found";
+ private static final String ERROR_MSG_4 =
+ "Build PVP2.1 AuthnRequest to connect 'ms-specific eIDAS node' FAILED.";
+
+ @Autowired PvpAuthnRequestBuilder authnReqBuilder;
+ @Autowired IdAustriaAuthCredentialProvider credential;
+ @Autowired IdAustriaAuthMetadataProvider metadataService;
+ @Autowired ITransactionStorage transactionStorage;
+
+ @Override
+ public void execute(ExecutionContext executionContext, HttpServletRequest request,
+ HttpServletResponse response)
+ throws TaskExecutionException {
+ try {
+ //revisionsLogger.logEvent(pendingReq, EidasAuthEventConstants.AUTHPROCESS_EIDAS_AT_CONNECTOR_SELECTED);
+
+ // get entityID for central ID Austria system
+ final String idAustriaEntityID =
+ Utils.getIdAustriaEntityId(pendingReq.getServiceProviderConfiguration(), authConfig);
+ if (StringUtils.isEmpty(idAustriaEntityID)) {
+ log.info("ID Austria authentication not possible -> NO EntityID for central central ID Austria System FOUND!");
+ throw new EaafConfigurationException(IdAustriaAuthConstants.ERRORTYPE_00,
+ new Object[] { IdAustriaAuthConstants.CONFIG_PROPS_IDAUSTRIA_ENTITYID });
+
+ }
+
+ // load IDP SAML2 entitydescriptor
+ final EntityDescriptor entityDesc = metadataService.getEntityDescriptor(idAustriaEntityID);
+ if (entityDesc == null) {
+ throw new EaafConfigurationException(IdAustriaAuthConstants.ERRORTYPE_05,
+ new Object[] { MessageFormat.format(ERROR_MSG_1, idAustriaEntityID) });
+
+ }
+
+ // setup AuthnRequestBuilder configuration
+ final IdAustriaAuthRequestBuilderConfiguration authnReqConfig =
+ new IdAustriaAuthRequestBuilderConfiguration();
+ final SecureRandomIdentifierGenerationStrategy gen =
+ new SecureRandomIdentifierGenerationStrategy();
+
+ // set basic infos
+ authnReqConfig.setRequestId(gen.generateIdentifier());
+ authnReqConfig.setIdpEntity(entityDesc);
+ authnReqConfig.setPassive(false);
+ authnReqConfig.setSignCred(credential.getMessageSigningCredential());
+ authnReqConfig.setSpEntityID(pendingReq.getAuthUrl() + IdAustriaAuthConstants.ENDPOINT_METADATA);
+
+ // set eIDAS Proxy-Service specific information for ID Austria system
+ authnReqConfig.setRequestedAttributes(buildRequestedAttributes(pendingReq));
+
+
+ /*build relayState for session synchronization, because SAML2 only allows RelayState with 80 characters
+ * but encrypted PendingRequestId is much longer.
+ */
+ String relayState = Random.nextProcessReferenceValue();
+ transactionStorage.put(relayState, pendingReq.getPendingRequestId(), -1);
+
+ // build and transmit AuthnRequest
+ authnReqBuilder.buildAuthnRequest(pendingReq, authnReqConfig, relayState, response);
+
+ //revisionsLogger.logEvent(pendingReq,
+ // EidasAuthEventConstants.AUTHPROCESS_EIDAS_AT_CONNECTOR_REQUESTED,
+ // authnReqConfig.getRequestID());
+
+ } catch (final EaafException e) {
+ throw new TaskExecutionException(pendingReq, e.getMessage(), e);
+
+ } catch (final ResolverException e) {
+ throw new TaskExecutionException(pendingReq,
+ ERROR_MSG_4,
+ new AuthnRequestBuildException(ERROR_PVP_02,
+ new Object[] { IdAustriaAuthConstants.MODULE_NAME_FOR_LOGGING }, e));
+
+ } catch (MessageEncodingException | NoSuchAlgorithmException | SecurityException e) {
+ throw new TaskExecutionException(pendingReq,
+ e.getMessage(),
+ new AuthnRequestBuildException(ERROR_PVP_13,
+ new Object[] { IdAustriaAuthConstants.MODULE_NAME_FOR_LOGGING }, e));
+
+ } catch (final Exception e) {
+ throw new TaskExecutionException(pendingReq, e.getMessage(), e);
+
+ }
+ }
+
+ private String selectHighestLoa(List<String> requiredLoA) {
+ //TODO: implement LoA selection
+ return requiredLoA.get(0);
+
+ }
+
+ private List<EaafRequestedAttribute> buildRequestedAttributes(IRequest pendingReq) {
+ final List<EaafRequestedAttribute> attributs = new ArrayList<>();
+
+ //build attribute that contains the unique identifier of the eIDAS-Connector
+ injectAttribute(attributs, ExtendedPvpAttributeDefinitions.EIDAS_CONNECTOR_UNIQUEID_NAME,
+ pendingReq.getServiceProviderConfiguration().getUniqueIdentifier());
+
+ // build EID sector for identification attribute
+ injectAttribute(attributs, PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME,
+ pendingReq.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier());
+
+ // set requested LoA as attribute
+ injectAttribute(attributs, PvpAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_NAME,
+ selectHighestLoa(pendingReq.getServiceProviderConfiguration().getRequiredLoA()));
+
+ //set ProviderName if available
+ String providerName = ((ProxyServicePendingRequest)pendingReq).getEidasRequest().getProviderName();
+ if (StringUtils.isNotEmpty(providerName)) {
+ injectAttribute(attributs, ExtendedPvpAttributeDefinitions.SP_FRIENDLYNAME_NAME, providerName);
+
+ }
+
+ //set ProviderName if available
+ String requesterId = ((ProxyServicePendingRequest)pendingReq).getEidasRequest().getRequesterId();
+ if (StringUtils.isNotEmpty(requesterId)) {
+ injectAttribute(attributs, ExtendedPvpAttributeDefinitions.SP_UNIQUEID_NAME, requesterId);
+
+ }
+
+ //set mandate profiles
+ List<String> mandateProfiles =
+ pendingReq.getServiceProviderConfiguration(ServiceProviderConfiguration.class).getMandateProfiles();
+ if (mandateProfiles != null && !mandateProfiles.isEmpty()) {
+ log.debug("Set mandate-profiles attribute into ID-Austria request");
+ injectAttribute(attributs, ExtendedPvpAttributeDefinitions.SP_USED_MANDATE_PROFILES_NAME,
+ StringUtils.join(mandateProfiles, ","));
+
+ }
+
+ // inject mandate mode attribute
+ injectAttribute(attributs, ExtendedPvpAttributeDefinitions.SP_USED_MANDATE_TYPE_NAME,
+ pendingReq.getServiceProviderConfiguration(ServiceProviderConfiguration.class).getMandateMode().getMode());
+
+
+ return attributs;
+ }
+
+ private void injectAttribute(List<EaafRequestedAttribute> attributs, String attributeName, String attributeValue) {
+ final Attribute requesterIdAttr = PvpAttributeBuilder.buildEmptyAttribute(attributeName);
+ final EaafRequestedAttribute requesterIdReqAttr = Saml2Utils.generateReqAuthnAttributeSimple(
+ requesterIdAttr,
+ true,
+ attributeValue);
+ attributs.add(requesterIdReqAttr);
+
+ }
+
+}