From 958770eff456f5724e29166123c7e5c32391e3f4 Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Mon, 21 Aug 2023 15:17:36 +0200 Subject: refact(sl20): clean-up SL20 response handler --- .../sl20/tasks/AbstractReceiveQualEidTask.java | 324 +++++++++++---------- 1 file changed, 177 insertions(+), 147 deletions(-) diff --git a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractReceiveQualEidTask.java b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractReceiveQualEidTask.java index f95d4adc..a12fb36f 100644 --- a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractReceiveQualEidTask.java +++ b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/tasks/AbstractReceiveQualEidTask.java @@ -6,11 +6,14 @@ import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.lang3.StringUtils; import org.jose4j.base64url.Base64Url; import org.springframework.beans.factory.annotation.Autowired; import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; @@ -48,155 +51,12 @@ public abstract class AbstractReceiveQualEidTask extends AbstractAuthServletTask try { log.debug("Receiving SL2.0 response process .... "); - JsonNode sl20ReqObj = null; - // A-Trust does not SET http-header 'SL2ClientType' with value 'native' - // If A-trust sends an error, its maybe FrontChannel on DataURL - // boolean aTrustErrorWorkAround = false; + // get SL2.0 command or result from HTTP request + sl20Result = getSl20OperationFromHttp(request); + revisionsLogger.logEvent(pendingReq, EventCodes.AUTHPROCESS_SL20_DATAURL_IP, request.getRemoteAddr()); - try { - // get SL2.0 command or result from HTTP request - final Map reqParams = getParameters(request); - sl20Result = reqParams.get(SL20Constants.PARAM_SL20_REQ_COMMAND_PARAM); - - if (StringUtils.isEmpty(sl20Result)) { - // Workaround for SIC Handy-Signature, because it sends result in InputStream - final String isReqInput = StreamUtils.readStream(request.getInputStream(), "UTF-8"); - if (StringUtils.isNotEmpty(isReqInput)) { - log.info("Use SIC Handy-Signature work-around!"); - sl20Result = isReqInput.substring("slcommand=".length()); - - } else { - log.info("NO SL2.0 commando or result FOUND."); - throw new SL20Exception("sl20.04", null); - } - - } - - log.trace("Received SL2.0 result: " + sl20Result); - revisionsLogger.logEvent(pendingReq, EventCodes.AUTHPROCESS_SL20_DATAURL_IP, request.getRemoteAddr()); - - // parse SL2.0 command/result into JSON - try { - sl20ReqObj = JsonMapper.getMapper().readTree(Base64Url.decodeToUtf8String(sl20Result)); - - } catch (final JsonParseException e) { - log.error("SL2.0 command or result is NOT valid JSON. Received msg: {}", sl20Result, e); - throw new SL20Exception("sl20.02", new Object[] { "SL2.0 command or result is NOT valid JSON." }, - e); - - } - - log.info("Receive response from A-Trust. Starting response-message validation ... "); - - // check on errorMessage - final VerificationResult payLoadContainerErrorCheck = SL20JsonExtractorUtils.extractSL20PayLoad( - sl20ReqObj, - joseTools, false); - if (SL20JsonExtractorUtils - .getStringValue(payLoadContainerErrorCheck.getPayload(), - SL20Constants.SL20_COMMAND_CONTAINER_NAME, true) - .equals(SL20Constants.SL20_COMMAND_IDENTIFIER_ERROR)) { - log.debug("Find " + SL20Constants.SL20_COMMAND_IDENTIFIER_ERROR + " result .... "); - final JsonNode errorResult = SL20JsonExtractorUtils.extractSL20Result(payLoadContainerErrorCheck - .getPayload(), - joseTools, false); - final String errorCode = SL20JsonExtractorUtils.getStringValue(errorResult, - SL20Constants.SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERRORCODE, true); - final String errorMsg = SL20JsonExtractorUtils.getStringValue(errorResult, - SL20Constants.SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERRORMESSAGE, false); - - final SL20VdaResponseException ex = new SL20VdaResponseException("sl20.08", new Object[] { - errorCode, errorMsg }, errorCode); - log.info("Receiving errorcode: {} with msg: {} from VDA! Stopping auth-process ... ", errorCode, - errorMsg); - - final String vdaSessionId = SL20JsonExtractorUtils.getStringValue(errorResult, - SL20Constants.SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERROR_VDASESSIONID, false); - if (StringUtils.isNotEmpty(vdaSessionId)) { - log.debug("VDA provides an optional sessionId. Inject it to internal error-holder "); - ex.setVdaSessionId(vdaSessionId); - - } - throw ex; - - } else { - // Receive no error - To request validation - - // validate reqId with inResponseTo - final String sl20ReqId = pendingReq - .getRawData(Constants.PENDING_REQ_STORAGE_PREFIX + SL20Constants.SL20_REQID, String.class); - final String inRespTo = SL20JsonExtractorUtils.getStringValue(sl20ReqObj, - SL20Constants.SL20_INRESPTO, true); - if (sl20ReqId == null || !sl20ReqId.equals(inRespTo)) { - log.info("SL20 'reqId': " + sl20ReqId + " does NOT match to 'inResponseTo':" + inRespTo); - throw new SL20SecurityException( - "SL20 'reqId': " + sl20ReqId + " does NOT match to 'inResponseTo':" + inRespTo); - } - - // validate signature - final VerificationResult payLoadContainer = SL20JsonExtractorUtils.extractSL20PayLoad(sl20ReqObj, - joseTools, - authConfig.getBasicConfigurationBoolean(Constants.CONFIG_PROP_FORCE_EID_SIGNED_RESULT, true)); - - if (payLoadContainer.isValidSigned() == null || !payLoadContainer.isValidSigned()) { - if (authConfig.getBasicConfigurationBoolean(Constants.CONFIG_PROP_FORCE_EID_SIGNED_RESULT, - true)) { - log.info("SL20 result from VDA was not valid signed"); - throw new SL20SecurityException(new Object[] { "Signature on SL20 result NOT valid." }); - - } else { - log.warn("SL20 result from VDA is NOT valid signed, but signatures-verification " - + "is DISABLED by configuration!"); - - } - } - - // extract payloaf - final JsonNode payLoad = payLoadContainer.getPayload(); - - // handle SL2.0 response payLoad - handleResponsePayLoad(payLoad); - - } - - } catch (final EaafAuthenticationException e) { - if (sl20Result != null) { - log.debug("Received SL2.0 result: " + sl20Result); - } - - pendingReq.setRawDataToTransaction( - Constants.PENDING_REQ_STORAGE_PREFIX + SL20Constants.SL20_COMMAND_IDENTIFIER_ERROR, - new ExceptionContainer(null, - new TaskExecutionException(pendingReq, "SL2.0 Authentication FAILED. Msg: " + e.getMessage(), - e))); - - } catch (final Exception e) { - if (sl20Result != null) { - log.debug("Received SL2.0 result: " + sl20Result); - } - - pendingReq.setRawDataToTransaction( - Constants.PENDING_REQ_STORAGE_PREFIX + SL20Constants.SL20_COMMAND_IDENTIFIER_ERROR, - new ExceptionContainer(null, new TaskExecutionException(pendingReq, e.getMessage(), e))); - - } finally { - // store pending request - requestStoreage.storePendingRequest(pendingReq); - - // write SL2.0 response - if (sl20ReqObj != null) { - final String resumeEndpoint = new DataUrlBuilder().buildDataUrl(pendingReq.getAuthUrl(), - getResumeEndPoint(), pendingReq.getPendingRequestId()); - SL20ResponseUtils.buildResponse(request, response, pendingReq, resumeEndpoint, - SL20JsonExtractorUtils.getStringValue(sl20ReqObj, SL20Constants.SL20_TRANSACTIONID, false), - authConfig); - - } else { - SL20ResponseUtils.buildErrorResponse(response, "2000", "General transport Binding error"); - } - - } + internalExecute(request, response, sl20Result); } catch (final Exception e) { // write internal server errror 500 according to SL2.0 specification, chapter @@ -221,4 +81,174 @@ public abstract class AbstractReceiveQualEidTask extends AbstractAuthServletTask protected abstract String getResumeEndPoint(); + private void internalExecute(HttpServletRequest request, HttpServletResponse response, String sl20Result) + throws Exception { + JsonNode sl20ReqObj = null; + try { + // parse SL2.0 command/result into JSON + sl20ReqObj = decodeSl20Request(sl20Result); + log.info("Receive response from A-Trust. Starting response-message validation ... "); + + // check on errorMessage + final VerificationResult payLoadContainerErrorCheck = + SL20JsonExtractorUtils.extractSL20PayLoad(sl20ReqObj, joseTools, false); + + if (SL20JsonExtractorUtils.getStringValue( + payLoadContainerErrorCheck.getPayload(), SL20Constants.SL20_COMMAND_CONTAINER_NAME, true) + .equals(SL20Constants.SL20_COMMAND_IDENTIFIER_ERROR)) { + handleSl20Error(payLoadContainerErrorCheck); + + } else { + // Receive no error - To request validation + handleSl20Operation(sl20ReqObj); + + } + + } catch (final EaafAuthenticationException e) { + if (sl20Result != null) { + log.debug("Received SL2.0 result: " + sl20Result); + } + + pendingReq.setRawDataToTransaction( + Constants.PENDING_REQ_STORAGE_PREFIX + SL20Constants.SL20_COMMAND_IDENTIFIER_ERROR, + new ExceptionContainer(null, + new TaskExecutionException(pendingReq, "SL2.0 Authentication FAILED. Msg: " + e.getMessage(), + e))); + + } catch (final Exception e) { + if (sl20Result != null) { + log.debug("Received SL2.0 result: " + sl20Result); + } + + pendingReq.setRawDataToTransaction( + Constants.PENDING_REQ_STORAGE_PREFIX + SL20Constants.SL20_COMMAND_IDENTIFIER_ERROR, + new ExceptionContainer(null, new TaskExecutionException(pendingReq, e.getMessage(), e))); + + } finally { + // store pending request + requestStoreage.storePendingRequest(pendingReq); + + // write SL2.0 response + if (sl20ReqObj != null) { + final String resumeEndpoint = new DataUrlBuilder().buildDataUrl(pendingReq.getAuthUrl(), + getResumeEndPoint(), pendingReq.getPendingRequestId()); + SL20ResponseUtils.buildResponse(request, response, pendingReq, resumeEndpoint, + SL20JsonExtractorUtils.getStringValue(sl20ReqObj, SL20Constants.SL20_TRANSACTIONID, false), + authConfig); + + } else { + SL20ResponseUtils.buildErrorResponse(response, "2000", "General transport Binding error"); + + } + } + } + + private String getSl20OperationFromHttp(HttpServletRequest request) + throws SL20Exception, IOException, FileUploadException { + // A-Trust does not SET http-header 'SL2ClientType' with value 'native' + // If A-trust sends an error, its maybe FrontChannel on DataURL + // boolean aTrustErrorWorkAround = false; + String sl20Result = getSl20OperationFromHttp(request); + + // get SL2.0 command or result from HTTP request + final Map reqParams = getParameters(request); + sl20Result = reqParams.get(SL20Constants.PARAM_SL20_REQ_COMMAND_PARAM); + + if (StringUtils.isEmpty(sl20Result)) { + // Workaround for SIC Handy-Signature, because it sends result in InputStream + final String isReqInput = StreamUtils.readStream(request.getInputStream(), "UTF-8"); + if (StringUtils.isNotEmpty(isReqInput)) { + log.info("Use SIC Handy-Signature work-around!"); + sl20Result = isReqInput.substring("slcommand=".length()); + + } else { + log.info("NO SL2.0 commando or result FOUND."); + throw new SL20Exception("sl20.04", null); + } + + } + + log.trace("Received SL2.0 result: {}", sl20Result); + return sl20Result; + + } + + private JsonNode decodeSl20Request(String sl20Result) + throws JsonMappingException, JsonProcessingException, SL20Exception { + try { + return JsonMapper.getMapper().readTree(Base64Url.decodeToUtf8String(sl20Result)); + + } catch (final JsonParseException e) { + log.error("SL2.0 command or result is NOT valid JSON. Received msg: {}", sl20Result, e); + throw new SL20Exception("sl20.02", new Object[] { "SL2.0 command or result is NOT valid JSON." }, + e); + + } + } + + private void handleSl20Error(VerificationResult payLoadContainerErrorCheck) throws SL20Exception { + log.debug("Find " + SL20Constants.SL20_COMMAND_IDENTIFIER_ERROR + " result .... "); + final JsonNode errorResult = SL20JsonExtractorUtils.extractSL20Result(payLoadContainerErrorCheck + .getPayload(), + joseTools, false); + final String errorCode = SL20JsonExtractorUtils.getStringValue(errorResult, + SL20Constants.SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERRORCODE, true); + final String errorMsg = SL20JsonExtractorUtils.getStringValue(errorResult, + SL20Constants.SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERRORMESSAGE, false); + + final SL20VdaResponseException ex = new SL20VdaResponseException("sl20.08", new Object[] { + errorCode, errorMsg }, errorCode); + log.info("Receiving errorcode: {} with msg: {} from VDA! Stopping auth-process ... ", errorCode, + errorMsg); + + final String vdaSessionId = SL20JsonExtractorUtils.getStringValue(errorResult, + SL20Constants.SL20_COMMAND_PARAM_GENERAL_RESPONSE_ERROR_VDASESSIONID, false); + if (StringUtils.isNotEmpty(vdaSessionId)) { + log.debug("VDA provides an optional sessionId. Inject it to internal error-holder "); + ex.setVdaSessionId(vdaSessionId); + + } + throw ex; + + } + + private void handleSl20Operation(JsonNode sl20ReqObj) throws SlCommandoParserException, SL20Exception, + EaafStorageException { + // validate reqId with inResponseTo + final String sl20ReqId = pendingReq + .getRawData(Constants.PENDING_REQ_STORAGE_PREFIX + SL20Constants.SL20_REQID, String.class); + final String inRespTo = SL20JsonExtractorUtils.getStringValue(sl20ReqObj, + SL20Constants.SL20_INRESPTO, true); + if (sl20ReqId == null || !sl20ReqId.equals(inRespTo)) { + log.info("SL20 'reqId': " + sl20ReqId + " does NOT match to 'inResponseTo':" + inRespTo); + throw new SL20SecurityException( + "SL20 'reqId': " + sl20ReqId + " does NOT match to 'inResponseTo':" + inRespTo); + } + + // validate signature + final VerificationResult payLoadContainer = SL20JsonExtractorUtils.extractSL20PayLoad(sl20ReqObj, + joseTools, + authConfig.getBasicConfigurationBoolean(Constants.CONFIG_PROP_FORCE_EID_SIGNED_RESULT, true)); + + if (payLoadContainer.isValidSigned() == null || !payLoadContainer.isValidSigned()) { + if (authConfig.getBasicConfigurationBoolean(Constants.CONFIG_PROP_FORCE_EID_SIGNED_RESULT, + true)) { + log.info("SL20 result from VDA was not valid signed"); + throw new SL20SecurityException(new Object[] { "Signature on SL20 result NOT valid." }); + + } else { + log.warn("SL20 result from VDA is NOT valid signed, but signatures-verification " + + "is DISABLED by configuration!"); + + } + } + + // extract payloaf + final JsonNode payLoad = payLoadContainer.getPayload(); + + // handle SL2.0 response payLoad + handleResponsePayLoad(payLoad); + + } + } -- cgit v1.2.3