From 759ac5f42c6aff901dbeede4fbf1a1d2e08cad0f Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Wed, 4 Dec 2019 19:43:32 +0100 Subject: common EGIZ code-style refactoring --- .../tasks/AbstractCreateQualEidRequestTask.java | 250 +++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractCreateQualEidRequestTask.java (limited to 'eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractCreateQualEidRequestTask.java') diff --git a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractCreateQualEidRequestTask.java b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractCreateQualEidRequestTask.java new file mode 100644 index 00000000..251b516f --- /dev/null +++ b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractCreateQualEidRequestTask.java @@ -0,0 +1,250 @@ +package at.gv.egiz.eaaf.modules.auth.sl20.tasks; + +import java.security.cert.CertificateEncodingException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import at.gv.egiz.eaaf.core.api.idp.IConfigurationWithSP; +import at.gv.egiz.eaaf.core.api.idp.IspConfiguration; +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.TaskExecutionException; +import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask; +import at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory; +import at.gv.egiz.eaaf.core.impl.utils.KeyValueUtils; +import at.gv.egiz.eaaf.core.impl.utils.Random; +import at.gv.egiz.eaaf.core.impl.utils.TransactionIdUtils; +import at.gv.egiz.eaaf.modules.auth.sl20.Constants; +import at.gv.egiz.eaaf.modules.auth.sl20.EventCodes; +import at.gv.egiz.eaaf.modules.auth.sl20.data.VerificationResult; +import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SL20Exception; +import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SlCommandoParserException; +import at.gv.egiz.eaaf.modules.auth.sl20.utils.SL20Constants; +import at.gv.egiz.eaaf.modules.auth.sl20.utils.SL20HttpBindingUtils; +import at.gv.egiz.eaaf.modules.auth.sl20.utils.SL20JsonExtractorUtils; +import at.gv.egiz.eaaf.modules.auth.sl20.utils.SL20JsonBuilderUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.message.BasicNameValuePair; +import org.jose4j.base64url.Base64Url; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +public abstract class AbstractCreateQualEidRequestTask extends AbstractAuthServletTask { + private static final Logger log = LoggerFactory.getLogger(AbstractCreateQualEidRequestTask.class); + + @Autowired(required = true) + private IHttpClientFactory httpClientFactory; + @Autowired(required = true) + protected IConfigurationWithSP authConfigWithSp; + + @Override + public void execute(final ExecutionContext executionContext, final HttpServletRequest request, + final HttpServletResponse response) throws TaskExecutionException { + + log.debug("Starting SL2.0 authentication process .... "); + + revisionsLogger.logEvent(pendingReq, EventCodes.AUTHPROCESS_SL20_SELECTED, "sl20auth"); + + try { + // get service-provider configuration + final IspConfiguration oaConfig = pendingReq.getServiceProviderConfiguration(); + + if (oaConfig == null) { + log.warn("No SP configuration in pendingReq!"); + throw new RuntimeException("Suspect state. NO SP CONFIGURATION IN PendingRequest!"); + + } + + // get basic configuration parameters + final String vdaQualEidDUrl = extractVdaUrlForSpecificOa(oaConfig, executionContext); + if (StringUtils.isEmpty(vdaQualEidDUrl)) { + log.error("NO VDA URL for qualified eID (" + + Constants.CONFIG_PROP_VDA_ENDPOINT_QUALeID_DEFAULT + ")"); + throw new SL20Exception("sl20.03", new Object[] {"NO VDA URL for qualified eID"}); + + } + + log.debug("Use {} as VDA end-point", vdaQualEidDUrl); + pendingReq.setRawDataToTransaction( + Constants.PENDING_REQ_STORAGE_PREFIX + SL20Constants.SL20_COMMAND_PARAM_EID_RESULT_CCSURL, + vdaQualEidDUrl); + revisionsLogger.logEvent(pendingReq, EventCodes.AUTHPROCESS_SL20_ENDPOINT_URL, vdaQualEidDUrl); + + // create SL2.0 command for qualified eID + final String signedQualEidCommand = buildSignedQualifiedEidCommand(); + + // build request container + final String qualEidReqId = Random.nextProcessReferenceValue(); + final ObjectNode sl20Req = + SL20JsonBuilderUtils.createGenericRequest(qualEidReqId, null, null, signedQualEidCommand); + + // build http POST request + final HttpPost httpReq = new HttpPost(new URIBuilder(vdaQualEidDUrl).build()); + final List parameters = new ArrayList<>(); + parameters.add(new BasicNameValuePair(SL20Constants.PARAM_SL20_REQ_COMMAND_PARAM, + Base64Url.encode(sl20Req.toString().getBytes()))); + httpReq.setEntity(new UrlEncodedFormEntity(parameters)); + + // build http GET request + // URIBuilder sl20ReqUri = new URIBuilder(vdaQualeIDUrl); + // sl20ReqUri.addParameter(SL20Constants.PARAM_SL20_REQ_COMMAND_PARAM, + // Base64Url.encode(sl20Req.toString().getBytes())); + // HttpGet httpReq = new HttpGet(sl20ReqUri.build()); + + // set native client header + httpReq.addHeader(SL20Constants.HTTP_HEADER_SL20_CLIENT_TYPE, + SL20Constants.HTTP_HEADER_VALUE_NATIVE); + + log.trace("Request VDA via SL20 with: " + Base64Url.encode(sl20Req.toString().getBytes())); + + // request VDA + final HttpResponse httpResp = httpClientFactory.getHttpClient(false).execute(httpReq); + + // parse response + log.info("Receive response from VDA ... "); + final JsonNode sl20Resp = SL20JsonExtractorUtils.getSL20ContainerFromResponse(httpResp); + final VerificationResult respPayloadContainer = + SL20JsonExtractorUtils.extractSL20PayLoad(sl20Resp, null, false); + + if (respPayloadContainer.isValidSigned() == null) { + log.debug("Receive unsigned payLoad from VDA"); + + } + + final JsonNode respPayload = respPayloadContainer.getPayload(); + if (respPayload.get(SL20Constants.SL20_COMMAND_CONTAINER_NAME).asText() + .equals(SL20Constants.SL20_COMMAND_IDENTIFIER_REDIRECT)) { + log.debug("Find 'redirect' command in VDA response ... "); + final JsonNode params = SL20JsonExtractorUtils.getJsonObjectValue(respPayload, + SL20Constants.SL20_COMMAND_CONTAINER_PARAMS, true); + final String redirectUrl = SL20JsonExtractorUtils.getStringValue(params, + SL20Constants.SL20_COMMAND_PARAM_GENERAL_REDIRECT_URL, true); + final JsonNode command = SL20JsonExtractorUtils.getJsonObjectValue(params, + SL20Constants.SL20_COMMAND_PARAM_GENERAL_REDIRECT_COMMAND, false); + final String signedCommand = SL20JsonExtractorUtils.getStringValue(params, + SL20Constants.SL20_COMMAND_PARAM_GENERAL_REDIRECT_SIGNEDCOMMAND, false); + + // create forward SL2.0 command + final ObjectNode sl20Forward = sl20Resp.deepCopy(); + SL20JsonBuilderUtils.addOnlyOnceOfTwo(sl20Forward, SL20Constants.SL20_PAYLOAD, + SL20Constants.SL20_SIGNEDPAYLOAD, command.deepCopy(), signedCommand); + + // store pending request + pendingReq.setRawDataToTransaction( + Constants.PENDING_REQ_STORAGE_PREFIX + SL20Constants.SL20_REQID, qualEidReqId); + requestStoreage.storePendingRequest(pendingReq); + + // forward SL2.0 command + // TODO: maybe add SL2ClientType Header from execution context + SL20HttpBindingUtils.writeIntoResponse(request, response, sl20Forward, redirectUrl, + Integer + .parseInt(authConfig.getBasicConfiguration(Constants.CONFIG_PROP_HTTP_REDIRECT_CODE, + Constants.CONFIG_PROP_HTTP_REDIRECT_CODE_DEFAULT_VALUE))); + + } else if (respPayload.get(SL20Constants.SL20_COMMAND_CONTAINER_NAME).asText() + .equals(SL20Constants.SL20_COMMAND_IDENTIFIER_ERROR)) { + JsonNode result = SL20JsonExtractorUtils.getJsonObjectValue(respPayload, + SL20Constants.SL20_COMMAND_CONTAINER_RESULT, false); + if (result == null) { + result = SL20JsonExtractorUtils.getJsonObjectValue(respPayload, + SL20Constants.SL20_COMMAND_CONTAINER_PARAMS, false); + } + + final String errorCode = SL20JsonExtractorUtils.getStringValue(result, + SL20Constants.SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERRORCODE, true); + final String errorMsg = SL20JsonExtractorUtils.getStringValue(result, + SL20Constants.SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERRORMESSAGE, true); + + log.info("Receive SL2.0 error. Code:" + errorCode + " Msg:" + errorMsg); + throw new SL20Exception("sl20.08", new Object[] {errorCode, errorMsg}); + + } else { + // TODO: update to add error handling + log.warn("Received an unrecognized command: " + + respPayload.get(SL20Constants.SL20_COMMAND_CONTAINER_NAME).asText()); + throw new SlCommandoParserException( + "Received an unrecognized command: " + + respPayload.get(SL20Constants.SL20_COMMAND_CONTAINER_NAME).toString()); + } + + + } catch (final EaafAuthenticationException e) { + throw new TaskExecutionException(pendingReq, + "SL2.0 Authentication FAILED. Msg: " + e.getMessage(), e); + + } catch (final Exception e) { + log.warn("SL2.0 Authentication FAILED with a generic error.", e); + throw new TaskExecutionException(pendingReq, e.getMessage(), e); + + } finally { + TransactionIdUtils.removeTransactionId(); + TransactionIdUtils.removeSessionId(); + + } + + } + + /** + * Create a implementation specific qualified eID SL2.0 command + * + * @param oaConfig + * + * @return signed JWT token as serialized {@link String} + * @throws CertificateEncodingException In case of certificate parsing error + * @throws SL20Exception In case of a SL2.0 error + */ + protected abstract String buildSignedQualifiedEidCommand() + throws CertificateEncodingException, SL20Exception; + + + private String extractVdaUrlForSpecificOa(final IspConfiguration oaConfig, + final ExecutionContext executionContext) { + + // load SP specific config for development and testing purposes + final String spSpecificVdaEndpoints = + oaConfig.getConfigurationValue(Constants.CONFIG_PROP_SP_SL20_ENDPOINT_LIST); + + // load general configuration + final Map endPointMap = authConfigWithSp + .getBasicConfigurationWithPrefix(Constants.CONFIG_PROP_VDA_ENDPOINT_QUALeID_LIST); + endPointMap.put(Constants.CONFIG_PROP_VDA_ENDPOINT_QUALeID_DEFAULT_ELEMENT, + authConfig.getBasicConfiguration(Constants.CONFIG_PROP_VDA_ENDPOINT_QUALeID_DEFAULT)); + if (StringUtils.isNotEmpty(spSpecificVdaEndpoints)) { + endPointMap.putAll(KeyValueUtils.convertListToMap(KeyValueUtils + .getListOfCsvValues(KeyValueUtils.normalizeCsvValueString(spSpecificVdaEndpoints)))); + log.debug("Find OA specific SL2.0 endpoints. Updating endPoint list ... "); + + } + + log.trace("Find #" + endPointMap.size() + " SL2.0 endpoints ... "); + + // selection based on request Header + final String sl20VdaTypeHeader = + (String) executionContext.get(SL20Constants.HTTP_HEADER_SL20_VDA_TYPE.toLowerCase()); + if (StringUtils.isNotEmpty(sl20VdaTypeHeader)) { + final String vdaUrl = endPointMap.get(sl20VdaTypeHeader); + if (StringUtils.isNotEmpty(vdaUrl)) { + return vdaUrl.trim(); + } else { + log.info("Can NOT find VDA with Id: " + sl20VdaTypeHeader + ". Use default VDA"); + } + + } + + + log.info("NO specific VDA endpoint requested or found. Use default VDA"); + return endPointMap.get(Constants.CONFIG_PROP_VDA_ENDPOINT_QUALeID_DEFAULT_ELEMENT); + + } + +} -- cgit v1.2.3