From 61d276832ebcf1901183dab323126f8ecb6a7370 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Thu, 11 Apr 2019 09:44:11 +0200 Subject: refactor protocol finalization to support protocol response without final redirect --- eaaf_core/pom.xml | 11 +- .../gv/egiz/eaaf/core/api/data/EAAFConstants.java | 4 + .../at/gv/egiz/eaaf/core/api/idp/IAuthData.java | 6 + .../egiz/eaaf/core/api/idp/auth/ISSOManager.java | 4 +- .../idp/auth/data/IAuthProcessDataContainer.java | 3 +- .../services/IProtocolAuthenticationService.java | 83 ++++ .../idp/auth/AbstractAuthenticationManager.java | 6 +- .../idp/auth/modules/AbstractAuthServletTask.java | 4 +- .../services/ProtocolAuthenticationService.java | 458 +++++++++++++++++++++ .../AbstractAuthProtocolModulController.java | 242 ----------- .../impl/idp/controller/AbstractController.java | 227 ++-------- .../AbstractProcessEngineSignalController.java | 6 +- .../controller/ProtocolFinalizationController.java | 78 +--- .../tasks/FinalizeAuthenticationTask.java | 28 +- eaaf_core/src/main/resources/eaaf_core.beans.xml | 7 +- eaaf_modules/eaaf_module_pvp2_core/pom.xml | 20 +- .../pvp2/idp/impl/AbstractPVP2XProtocol.java | 6 +- eaaf_modules/eaaf_module_pvp2_sp/pom.xml | 8 +- eaaf_modules/pom.xml | 3 +- pom.xml | 38 +- 20 files changed, 711 insertions(+), 531 deletions(-) create mode 100644 eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/idp/auth/services/IProtocolAuthenticationService.java create mode 100644 eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java delete mode 100644 eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractAuthProtocolModulController.java diff --git a/eaaf_core/pom.xml b/eaaf_core/pom.xml index 3220dee9..21d6c338 100644 --- a/eaaf_core/pom.xml +++ b/eaaf_core/pom.xml @@ -73,7 +73,7 @@ org.apache.velocity velocity - + jaxen jaxen @@ -86,6 +86,15 @@ xalan + + org.apache.httpcomponents + httpclient + + + org.apache.httpcomponents + httpcore + + junit junit diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/data/EAAFConstants.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/data/EAAFConstants.java index b60d39e1..0a457825 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/data/EAAFConstants.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/data/EAAFConstants.java @@ -66,8 +66,12 @@ public class EAAFConstants { public static final String PROCESS_ENGINE_SERVICE_PROVIDER_ENTITYID = PROCESS_ENGINE_PREFIX + "uniqueSPId"; public static final String PROCESS_ENGINE_SSL_CLIENT_CERTIFICATE = PROCESS_ENGINE_PREFIX + "holderofkey_cert"; public static final String PROCESSCONTEXT_SP_CONFIG = PROCESS_ENGINE_PREFIX + "spConfig"; + public static final String PROCESS_ENGINE_REQUIRES_NO_POSTAUTH_REDIRECT + = PROCESS_ENGINE_PREFIX + "requireNoPostAuthRedirect"; public static final int ALLOWED_TIME_JITTER = 5; //minutes public static final String COUNTRYCODE_AUSTRIA = "AT"; + + public static final String TESTCREDENTIALROOTOID = "1.2.40.0.10.2.4.1"; } diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/idp/IAuthData.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/idp/IAuthData.java index e8e41999..7dcd643d 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/idp/IAuthData.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/idp/IAuthData.java @@ -119,6 +119,7 @@ public interface IAuthData { * * @return */ + @Deprecated String getBPK(); /** @@ -127,6 +128,7 @@ public interface IAuthData { * * @return Sector identifier with prefix */ + @Deprecated String getBPKType(); @@ -134,6 +136,7 @@ public interface IAuthData { * Get List of bPK/bPKType tuples for this service provider * @return List of Pairs */ + @Deprecated List> getAdditionalbPKs(); /** @@ -141,6 +144,7 @@ public interface IAuthData { * * @return */ + @Deprecated String getIdentificationValue(); /** @@ -149,6 +153,7 @@ public interface IAuthData { * * @return */ + @Deprecated String getIdentificationType(); @@ -157,6 +162,7 @@ public interface IAuthData { * * @return IDL, or NULL if no IDL is available */ + @Deprecated IIdentityLink getIdentityLink(); /** diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/idp/auth/ISSOManager.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/idp/auth/ISSOManager.java index cba8fde7..5481fd52 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/idp/auth/ISSOManager.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/idp/auth/ISSOManager.java @@ -60,11 +60,11 @@ public interface ISSOManager { /** * Populate service provider specific SSO settings * - * Check if Single Sign-On is allowed for the current pending request and the requested service provider + * Check if Single Sign-On is allowed for the current pending request and the requested service provider + * Set IRequest.needSingleSignOnFunctionality() to true if SSO is allowed * * @param pendingReq Current incoming pending request * @param httpReq http Servlet request - * @return true if SSO is allowed for this service provider, otherwise false */ public void isSSOAllowedForSP(IRequest pendingReq, HttpServletRequest httpReq); diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/idp/auth/data/IAuthProcessDataContainer.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/idp/auth/data/IAuthProcessDataContainer.java index 76e071c6..46dd3850 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/idp/auth/data/IAuthProcessDataContainer.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/idp/auth/data/IAuthProcessDataContainer.java @@ -67,6 +67,7 @@ public interface IAuthProcessDataContainer { * * @return IdentityLink */ + @Deprecated IIdentityLink getIdentityLink(); /** @@ -75,9 +76,9 @@ public interface IAuthProcessDataContainer { * @param identityLink * The identityLink to set */ + @Deprecated void setIdentityLink(IIdentityLink identityLink); - /** * Indicate that mandates was used in this auth. process * diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/idp/auth/services/IProtocolAuthenticationService.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/idp/auth/services/IProtocolAuthenticationService.java new file mode 100644 index 00000000..f6f8e576 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/api/idp/auth/services/IProtocolAuthenticationService.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * 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.core.api.idp.auth.services; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.logging.IStatisticLogger; +import at.gv.egiz.eaaf.core.exceptions.EAAFException; + +public interface IProtocolAuthenticationService { + + /** + * Initialize an authentication process for this protocol request + * + * @param httpReq HttpServletRequest + * @param httpResp HttpServletResponse + * @param protocolRequest Authentication request which is actually in process + * @throws IOException + * @throws EAAFException + */ + void performAuthentication(HttpServletRequest req, HttpServletResponse resp, IRequest pendingReq) + throws IOException, EAAFException; + + /** + * Finalize the requested protocol operation + * + * @param httpReq HttpServletRequest + * @param httpResp HttpServletResponse + * @param protocolRequest Authentication request which is actually in process + * @throws IOException If response can not be written into {@link HttpServletResponse} + * @throws EAAFException If an internal error occur + */ + void finalizeAuthentication(HttpServletRequest req, HttpServletResponse resp, IRequest pendingReq) throws EAAFException, IOException; + + /** + * @param throwable Exception that should be handled + * @param req Current open http request as {@link HttpServletRequest} + * @param resp Current open http response as {@link HttpServletResponse} + * @param pendingReq Authentication request which is actually in process + * @throws IOException If response can not be written into {@link HttpServletResponse} + * @throws EAAFException If an internal error occur + */ + void buildProtocolSpecificErrorResponse(Throwable throwable, HttpServletRequest req, HttpServletResponse resp, + IRequest pendingReq) throws IOException, EAAFException; + + /** + * Handles all exceptions with no pending request. + * Therefore, the error is written to the users browser + * + * @param throwable Exception that should be handled + * @param req Current open http request as {@link HttpServletRequest} + * @param resp Current open http response as {@link HttpServletResponse} + * @param writeExceptionToStatisticLog if true, the exception get logged into {@link IStatisticLogger} + * @throws IOException If response can not be written into {@link HttpServletResponse} + * @throws EAAFException If an internal error occure + */ + void handleErrorNoRedirect(Throwable throwable, HttpServletRequest req, HttpServletResponse resp, boolean writeExceptionToStatisticLog) throws IOException, EAAFException; + +} \ No newline at end of file diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/AbstractAuthenticationManager.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/AbstractAuthenticationManager.java index 23763cde..0d3eaf18 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/AbstractAuthenticationManager.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/AbstractAuthenticationManager.java @@ -134,8 +134,10 @@ public abstract class AbstractAuthenticationManager implements IAuthenticationMa ssoManager.isSSOAllowedForSP(pendingReq, httpReq); //check if SSO session is active and valid - isValidSSOSession = ssoManager.checkAndValidateSSOSession(pendingReq, httpReq, httpResp); - + isValidSSOSession = ssoManager.checkAndValidateSSOSession(pendingReq, httpReq, httpResp) && + pendingReq.needSingleSignOnFunctionality(); + + } //check if session is already authenticated diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/modules/AbstractAuthServletTask.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/modules/AbstractAuthServletTask.java index 0b334126..eb87e893 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/modules/AbstractAuthServletTask.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/modules/AbstractAuthServletTask.java @@ -55,7 +55,7 @@ import at.gv.egiz.eaaf.core.api.idp.IConfiguration; import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; import at.gv.egiz.eaaf.core.api.logging.IRevisionLogger; import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; -import at.gv.egiz.eaaf.core.impl.idp.controller.AbstractAuthProtocolModulController; +import at.gv.egiz.eaaf.core.impl.idp.controller.ProtocolFinalizationController; import at.gv.egiz.eaaf.core.impl.idp.process.springweb.AbstractTask; import at.gv.egiz.eaaf.core.impl.utils.DataURLBuilder; @@ -101,7 +101,7 @@ public abstract class AbstractAuthServletTask extends AbstractTask { * @param httpResp */ protected void performRedirectToProtocolFinialization(IRequest pendingReq, HttpServletResponse httpResp) { - performRedirectToItself(pendingReq, httpResp, AbstractAuthProtocolModulController.ENDPOINT_FINALIZEPROTOCOL); + performRedirectToItself(pendingReq, httpResp, ProtocolFinalizationController.ENDPOINT_FINALIZEPROTOCOL); } diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java new file mode 100644 index 00000000..85c609e0 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java @@ -0,0 +1,458 @@ +/******************************************************************************* + * Copyright 2019 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.core.impl.idp.auth.services; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.naming.ConfigurationException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringEscapeUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; + +import at.gv.egiz.components.eventlog.api.EventConstants; +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.IRequestStorage; +import at.gv.egiz.eaaf.core.api.IStatusMessenger; +import at.gv.egiz.eaaf.core.api.data.EAAFConstants; +import at.gv.egiz.eaaf.core.api.gui.IGUIBuilderConfiguration; +import at.gv.egiz.eaaf.core.api.gui.IGUIBuilderConfigurationFactory; +import at.gv.egiz.eaaf.core.api.gui.IGUIFormBuilder; +import at.gv.egiz.eaaf.core.api.gui.ModifyableGuiBuilderConfiguration; +import at.gv.egiz.eaaf.core.api.idp.IAction; +import at.gv.egiz.eaaf.core.api.idp.IAuthData; +import at.gv.egiz.eaaf.core.api.idp.IAuthenticationDataBuilder; +import at.gv.egiz.eaaf.core.api.idp.IModulInfo; +import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; +import at.gv.egiz.eaaf.core.api.idp.auth.IAuthenticationManager; +import at.gv.egiz.eaaf.core.api.idp.auth.ISSOManager; +import at.gv.egiz.eaaf.core.api.idp.auth.services.IProtocolAuthenticationService; +import at.gv.egiz.eaaf.core.api.idp.slo.SLOInformationInterface; +import at.gv.egiz.eaaf.core.api.logging.IRevisionLogger; +import at.gv.egiz.eaaf.core.api.logging.IStatisticLogger; +import at.gv.egiz.eaaf.core.api.storage.ITransactionStorage; +import at.gv.egiz.eaaf.core.exceptions.AuthnRequestValidatorException; +import at.gv.egiz.eaaf.core.exceptions.EAAFAuthenticationException; +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.core.exceptions.EAAFSSOException; +import at.gv.egiz.eaaf.core.exceptions.GUIBuildException; +import at.gv.egiz.eaaf.core.exceptions.InvalidProtocolRequestException; +import at.gv.egiz.eaaf.core.exceptions.ProcessExecutionException; +import at.gv.egiz.eaaf.core.exceptions.ProtocolNotActiveException; +import at.gv.egiz.eaaf.core.impl.utils.HTTPUtils; + +@Service +public class ProtocolAuthenticationService implements IProtocolAuthenticationService { + private static final Logger log = LoggerFactory.getLogger(ProtocolAuthenticationService.class); + + @Autowired(required=true) private ApplicationContext applicationContext; + @Autowired(required=true) private ITransactionStorage transactionStorage; + @Autowired(required=true) private IAuthenticationManager authmanager; + @Autowired(required=true) private IAuthenticationDataBuilder authDataBuilder; + @Autowired(required=true) private IGUIFormBuilder guiBuilder; + @Autowired(required=true) private IGUIBuilderConfigurationFactory guiConfigFactory; + @Autowired(required=true) private IStatusMessenger statusMessager; + @Autowired(required=true) private IRequestStorage requestStorage; + + @Autowired private ISSOManager ssoManager; + @Autowired private IStatisticLogger statisticLogger; + @Autowired private IRevisionLogger revisionsLogger; + + /* (non-Javadoc) + * @see at.gv.egiz.eaaf.core.impl.idp.auth.services.IProtocolAuthenticationService#performAuthentication(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, at.gv.egiz.eaaf.core.api.IRequest) + */ + @Override + public void performAuthentication(final HttpServletRequest req, final HttpServletResponse resp, + final IRequest pendingReq) throws IOException, EAAFException { + try { + if (pendingReq.isNeedAuthentication()) { + //request needs authentication --> start authentication process ... + + //load Parameters from OnlineApplicationConfiguration + final ISPConfiguration oaParam = pendingReq.getServiceProviderConfiguration(); + + if (oaParam == null) + throw new EAAFAuthenticationException( + IStatusMessenger.CODES_INTERNAL_ERROR_AUTH_NOSPCONFIG, + new Object[] { pendingReq.getSPEntityId() }); + + if (authmanager.doAuthentication(req, resp, pendingReq)) { + //pending request is already authenticated --> protocol-specific postProcessing can start directly + finalizeAuthentication(req, resp, pendingReq); + + //transaction is finished, log transaction finished event + revisionsLogger.logEvent(EventConstants.TRANSACTION_DESTROYED, pendingReq.getUniqueTransactionIdentifier()); + + } + + } else { + executeProtocolSpecificAction(req, resp, pendingReq, null); + + } + + } catch (final Exception e) { + buildProtocolSpecificErrorResponse(e, req, resp, pendingReq); + authmanager.performOnlyIDPLogOut(req, resp, pendingReq); + + } + } + + /* (non-Javadoc) + * @see at.gv.egiz.eaaf.core.impl.idp.auth.services.IProtocolAuthenticationService#finalizeAuthentication(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, at.gv.egiz.eaaf.core.api.IRequest) + */ + @Override + public void finalizeAuthentication(final HttpServletRequest req, final HttpServletResponse resp, final IRequest pendingReq) throws EAAFException, IOException{ + log.debug("Finalize PendingRequest with ID " + pendingReq.getPendingRequestId()); + try { + + //check if pending-request has 'abortedByUser' flag set + if (pendingReq.isAbortedByUser()) { + //send authentication aborted error to Service Provider + buildProtocolSpecificErrorResponse( + new EAAFAuthenticationException( + IStatusMessenger.CODES_INTERNAL_ERROR_AUTH_USERSTOP, + new Object[] {}), + req, resp, pendingReq); + + //do not remove the full active SSO-Session + // in case of only one Service-Provider authentication request is aborted + if ( !pendingReq.needSingleSignOnFunctionality()) { + transactionStorage.remove(pendingReq.getPendingRequestId()); + + } + + //check if pending-request are authenticated + } else if (pendingReq.isAuthenticated()) { + internalFinalizeAuthenticationProcess(req, resp, pendingReq); + + } else { + //suspect state: pending-request is not aborted but also are not authenticated + log.error("PendingRequest is NOT authenticated --> Abort authentication process!"); + handleErrorNoRedirect( + new EAAFException( + "auth.20", + null), req, resp, true); + + } + + } catch (Exception e) { + log.error("Finalize authentication protocol FAILED." , e); + buildProtocolSpecificErrorResponse(e, req, resp, pendingReq); + + if (pendingReq != null) + transactionStorage.remove(pendingReq.getPendingRequestId()); + + } + + //remove pending-request + if (pendingReq != null) { + requestStorage.removePendingRequest(pendingReq.getPendingRequestId()); + revisionsLogger.logEvent(EventConstants.TRANSACTION_DESTROYED, pendingReq.getUniqueTransactionIdentifier()); + + } + } + + + @Override + public void buildProtocolSpecificErrorResponse(final Throwable throwable, final HttpServletRequest req, + final HttpServletResponse resp, final IRequest protocolRequest) throws EAAFException, IOException { + try { + + final Class clazz = Class.forName(protocolRequest.requestedModule()); + + if (clazz == null || + !IModulInfo.class.isAssignableFrom(clazz)) { + log.error("Requested protocol module Class is NULL or does not implement the IModulInfo interface."); + throw new Exception("Requested protocol module Class is NULL or does not implement the IModulInfo interface."); + + } + + final IModulInfo handlingModule = (IModulInfo) applicationContext.getBean(clazz); + + if (handlingModule.generateErrorMessage( + throwable, req, resp, protocolRequest)) { + + //log Error to technical log + logExceptionToTechnicalLog(throwable); + + //log Error Message + statisticLogger.logErrorOperation(throwable, protocolRequest); + + //write revision log entries + revisionsLogger.logEvent(protocolRequest, EventConstants.TRANSACTION_ERROR, protocolRequest.getUniqueTransactionIdentifier()); + + return; + + } else { + handleErrorNoRedirect(throwable, req, resp, true); + + } + + } catch (final Throwable e) { + handleErrorNoRedirect(throwable, req, resp, true); + + } + + } + + @Override + public void handleErrorNoRedirect(final Throwable throwable, final HttpServletRequest req, + final HttpServletResponse resp, final boolean writeExceptionToStatisticLog) throws IOException, EAAFException { + + //log Exception into statistic database + if (writeExceptionToStatisticLog) + statisticLogger.logErrorOperation(throwable); + + //write errror to console + logExceptionToTechnicalLog(throwable); + + //return error to Web browser + if (throwable instanceof EAAFException || throwable instanceof ProcessExecutionException) + internalMOAIDExceptionHandler(req, resp, (Exception)throwable, false); + + else { + //write generic message for general exceptions + final String msg = statusMessager.getMessage(IStatusMessenger.CODES_INTERNAL_ERROR_GENERIC, null); + writeHTMLErrorResponse(req, resp, msg, "9199", (Exception) throwable); + + } + + } + + /** + * Finalize the requested protocol operation + * + * @param httpReq HttpServletRequest + * @param httpResp HttpServletResponse + * @param protocolRequest Authentication request which is actually in process + * @param moaSession MOASession object, which is used to generate the protocol specific authentication information + * @throws Exception + */ + protected void internalFinalizeAuthenticationProcess(final HttpServletRequest req, final HttpServletResponse resp, + final IRequest pendingReq) throws Exception { + + String newSSOSessionId = null; + + //if Single Sign-On functionality is enabled for this request + if (pendingReq.needSingleSignOnFunctionality()) { + if (ssoManager != null) { + newSSOSessionId = ssoManager.createNewSSOSessionCookie(req, resp, pendingReq); + if (StringUtils.isEmpty(pendingReq.getInternalSSOSessionIdentifier())) + ssoManager.createNewSSOSession(pendingReq, newSSOSessionId); + + } else + log.warn("SSO is requested but there is not SSO Session-Manager available"); + + } + + //build authenticationdata from session information and OA configuration + final IAuthData authData = authDataBuilder.buildAuthenticationData(pendingReq); + + //execute the protocol-specific action + final SLOInformationInterface sloInformation = executeProtocolSpecificAction(req, resp, pendingReq, authData); + + //Store OA specific SSO session information if an SSO cookie is set + if (StringUtils.isNotEmpty(newSSOSessionId)) { + try { + ssoManager.updateSSOSession(pendingReq, newSSOSessionId, sloInformation); + + } catch (final EAAFSSOException e) { + log.warn("SSO Session information can not be stored -> SSO is not enabled!"); + authmanager.performOnlyIDPLogOut(req, resp, pendingReq); + + } + + } else { + //remove MOASession from database + authmanager.performOnlyIDPLogOut(req, resp, pendingReq); + + } + + //Advanced statistic logging + statisticLogger.logSuccessOperation(pendingReq, authData, StringUtils.isNotEmpty(newSSOSessionId)); + + } + + /** + * Executes the requested protocol action + * + * @param httpReq HttpServletRequest + * @param httpResp HttpServletResponse + * @param protocolRequest Authentication request which is actually in process + * @param authData Service-provider specific authentication data + * + * @return Return Single LogOut information or null if protocol supports no SSO + * + * @throws Exception + */ + private SLOInformationInterface executeProtocolSpecificAction(final HttpServletRequest httpReq, final HttpServletResponse httpResp, + final IRequest pendingReq, final IAuthData authData) throws Exception { + try { + // request needs no authentication --> start request processing + final Class clazz = Class.forName(pendingReq.requestedAction()); + if (clazz == null || + !IAction.class.isAssignableFrom(clazz)) { + log.error("Requested protocol-action processing Class is NULL or does not implement the IAction interface."); + throw new Exception("Requested protocol-action processing Class is NULL or does not implement the IAction interface."); + + } + + final IAction protocolAction = (IAction) applicationContext.getBean(clazz); + return protocolAction.processRequest(pendingReq, httpReq, httpResp, authData); + + } catch (final ClassNotFoundException e) { + log.error("Requested Auth. protocol processing Class is NULL or does not implement the IAction interface."); + throw new Exception("Requested Auth. protocol processing Class is NULL or does not implement the IAction interface."); + } + + } + + /** + * Write a Exception to the MOA-ID-Auth internal technical log + * + * @param loggedException Exception to log + */ + protected void logExceptionToTechnicalLog(final Throwable loggedException) { + if (!( loggedException instanceof EAAFException + || loggedException instanceof ProcessExecutionException )) { + log.error("Receive an internal error: Message=" + loggedException.getMessage(), loggedException); + + } else { + if (log.isDebugEnabled() || log.isTraceEnabled()) { + log.warn(loggedException.getMessage(), loggedException); + + } else { + log.warn(loggedException.getMessage()); + + } + } + } + + private void writeBadRequestErrorResponse(final HttpServletRequest req, final HttpServletResponse resp, final EAAFException e) throws IOException { + final String code = statusMessager.mapInternalErrorToExternalError(((InvalidProtocolRequestException)e).getErrorId()); + final String descr = StringEscapeUtils.escapeHtml4(StringEscapeUtils.escapeEcmaScript(e.getMessage())); + resp.setContentType(EAAFConstants.CONTENTTYPE_HTML_UTF8); + resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Protocol validation FAILED!" + + "(Errorcode=" + code + + " | Description=" + descr + ")"); + + } + + private void writeHTMLErrorResponse(final HttpServletRequest req, final HttpServletResponse httpResp, final String msg, final String errorCode, final Exception error) throws IOException, EAAFException { + + try { + final IGUIBuilderConfiguration config + = guiConfigFactory.getDefaultErrorGUI(HTTPUtils.extractAuthURLFromRequest(req)); + +// HTTPUtils.extractAuthURLFromRequest(req), +// DefaultGUIFormBuilderConfiguration.VIEW_ERRORMESSAGE, +// null); + + //add errorcode and errormessage + if (config instanceof ModifyableGuiBuilderConfiguration) { + ((ModifyableGuiBuilderConfiguration)config).putCustomParameter("errorMsg", msg); + ((ModifyableGuiBuilderConfiguration)config).putCustomParameter("errorCode", errorCode); + + //add stacktrace if debug is enabled + if (log.isTraceEnabled()) { + ((ModifyableGuiBuilderConfiguration)config).putCustomParameter("stacktrace", getStacktraceFromException(error)); + + } + + } else + log.info("Can not ADD error message, because 'GUIBuilderConfiguration' is not modifieable "); + + + + guiBuilder.build(httpResp, config, "Error-Message"); + + } catch (final GUIBuildException e) { + log.warn("Can not build error-message GUI.", e); + throw new EAAFException("9199", null, e); + + + } + + } + + private void writeHTMLErrorResponse(final HttpServletRequest req, final HttpServletResponse httpResp, final Exception error) throws IOException, EAAFException { + writeHTMLErrorResponse(req, httpResp, + error.getMessage(), + statusMessager.getResponseErrorCode(error), + error); + } + + + private String getStacktraceFromException(final Exception ex) { + final StringWriter errors = new StringWriter(); + ex.printStackTrace(new PrintWriter(errors)); + return errors.toString(); + + } + + private void internalMOAIDExceptionHandler(final HttpServletRequest req, final HttpServletResponse resp, final Exception e, final boolean writeExceptionToStatisicLog) throws IOException, EAAFException { + if (e instanceof ProtocolNotActiveException) { + resp.getWriter().write(e.getMessage()); + resp.setContentType(EAAFConstants.CONTENTTYPE_HTML_UTF8); + resp.sendError(HttpServletResponse.SC_FORBIDDEN, + StringEscapeUtils.escapeHtml4(StringEscapeUtils.escapeEcmaScript(e.getMessage()))); + + } else if (e instanceof AuthnRequestValidatorException) { + final AuthnRequestValidatorException ex = (AuthnRequestValidatorException)e; + //log Error Message + if (writeExceptionToStatisicLog) + statisticLogger.logErrorOperation(ex, ex.getErrorRequest()); + + //write error message + writeBadRequestErrorResponse(req, resp, (EAAFException) e); + + } else if (e instanceof InvalidProtocolRequestException) { + //send error response + writeBadRequestErrorResponse(req, resp, (EAAFException) e); + + } else if (e instanceof ConfigurationException) { + //send HTML formated error message + writeHTMLErrorResponse(req, resp, e); + + } else if (e instanceof EAAFException) { + //send HTML formated error message + writeHTMLErrorResponse(req, resp, e); + + } else if (e instanceof ProcessExecutionException) { + //send HTML formated error message + writeHTMLErrorResponse(req, resp, e); + + } + + } + + +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractAuthProtocolModulController.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractAuthProtocolModulController.java deleted file mode 100644 index 2f1cc6b4..00000000 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractAuthProtocolModulController.java +++ /dev/null @@ -1,242 +0,0 @@ -/******************************************************************************* - * 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.core.impl.idp.controller; - -import java.io.IOException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; - -import at.gv.egiz.components.eventlog.api.EventConstants; -import at.gv.egiz.eaaf.core.api.IRequest; -import at.gv.egiz.eaaf.core.api.IStatusMessenger; -import at.gv.egiz.eaaf.core.api.idp.IAction; -import at.gv.egiz.eaaf.core.api.idp.IAuthData; -import at.gv.egiz.eaaf.core.api.idp.IAuthenticationDataBuilder; -import at.gv.egiz.eaaf.core.api.idp.IModulInfo; -import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; -import at.gv.egiz.eaaf.core.api.idp.auth.IAuthenticationManager; -import at.gv.egiz.eaaf.core.api.idp.auth.ISSOManager; -import at.gv.egiz.eaaf.core.api.idp.slo.SLOInformationInterface; -import at.gv.egiz.eaaf.core.exceptions.EAAFAuthenticationException; -import at.gv.egiz.eaaf.core.exceptions.EAAFSSOException; - -/** - * @author tlenz - * - */ - -public abstract class AbstractAuthProtocolModulController extends AbstractController { - private static final Logger log = LoggerFactory.getLogger(AbstractAuthProtocolModulController.class); - - public static final String ENDPOINT_FINALIZEPROTOCOL = "finalizeAuthProtocol"; - public static final String ENDPOINT_ERRORHANDLING = "errorHandling"; - - - @Autowired(required=true) private IAuthenticationManager authmanager; - @Autowired(required=true) private IAuthenticationDataBuilder authDataBuilder; - @Autowired(required=false) private ISSOManager ssoManager; - - /** - * Initialize an authentication process for this protocol request - * - * @param httpReq HttpServletRequest - * @param httpResp HttpServletResponse - * @param protocolRequest Authentication request which is actually in process - * @throws IOException - */ - protected void performAuthentication(HttpServletRequest req, HttpServletResponse resp, - IRequest pendingReq) throws IOException { - try { - if (pendingReq.isNeedAuthentication()) { - //request needs authentication --> start authentication process ... - - //load Parameters from OnlineApplicationConfiguration - ISPConfiguration oaParam = pendingReq.getServiceProviderConfiguration(); - - if (oaParam == null) - throw new EAAFAuthenticationException( - IStatusMessenger.CODES_INTERNAL_ERROR_AUTH_NOSPCONFIG, - new Object[] { pendingReq.getSPEntityId() }); - - if (authmanager.doAuthentication(req, resp, pendingReq)) { - //pending request is already authenticated --> protocol-specific postProcessing can start directly - finalizeAuthenticationProcess(req, resp, pendingReq); - - //transaction is finished, log transaction finished event - revisionsLogger.logEvent(EventConstants.TRANSACTION_DESTROYED, pendingReq.getUniqueTransactionIdentifier()); - - } - - } else { - executeProtocolSpecificAction(req, resp, pendingReq, null); - - } - - } catch (Exception e) { - buildProtocolSpecificErrorResponse(e, req, resp, pendingReq); - authmanager.performOnlyIDPLogOut(req, resp, pendingReq); - - } - } - - - /** - * Finalize the requested protocol operation - * - * @param httpReq HttpServletRequest - * @param httpResp HttpServletResponse - * @param protocolRequest Authentication request which is actually in process - * @param moaSession MOASession object, which is used to generate the protocol specific authentication information - * @throws Exception - */ - protected void finalizeAuthenticationProcess(HttpServletRequest req, HttpServletResponse resp, - IRequest pendingReq) throws Exception { - - String newSSOSessionId = null; - - //if Single Sign-On functionality is enabled for this request - if (pendingReq.needSingleSignOnFunctionality()) { - if (ssoManager != null) { - newSSOSessionId = ssoManager.createNewSSOSessionCookie(req, resp, pendingReq); - if (StringUtils.isEmpty(pendingReq.getInternalSSOSessionIdentifier())) - ssoManager.createNewSSOSession(pendingReq, newSSOSessionId); - - } else - log.warn("SSO is requested but there is not SSO Session-Manager available"); - - } - - //build authenticationdata from session information and OA configuration - IAuthData authData = authDataBuilder.buildAuthenticationData(pendingReq); - - //execute the protocol-specific action - SLOInformationInterface sloInformation = executeProtocolSpecificAction(req, resp, pendingReq, authData); - - //Store OA specific SSO session information if an SSO cookie is set - if (StringUtils.isNotEmpty(newSSOSessionId)) { - try { - ssoManager.updateSSOSession(pendingReq, newSSOSessionId, sloInformation); - - } catch (EAAFSSOException e) { - log.warn("SSO Session information can not be stored -> SSO is not enabled!"); - authmanager.performOnlyIDPLogOut(req, resp, pendingReq); - - } - - } else { - //remove MOASession from database - authmanager.performOnlyIDPLogOut(req, resp, pendingReq); - - } - - //Advanced statistic logging - statisticLogger.logSuccessOperation(pendingReq, authData, StringUtils.isNotEmpty(newSSOSessionId)); - - } - - /** - * Executes the requested protocol action - * - * @param httpReq HttpServletRequest - * @param httpResp HttpServletResponse - * @param protocolRequest Authentication request which is actually in process - * @param authData Service-provider specific authentication data - * - * @return Return Single LogOut information or null if protocol supports no SSO - * - * @throws Exception - */ - private SLOInformationInterface executeProtocolSpecificAction(HttpServletRequest httpReq, HttpServletResponse httpResp, - IRequest pendingReq, IAuthData authData) throws Exception { - try { - // request needs no authentication --> start request processing - Class clazz = Class.forName(pendingReq.requestedAction()); - if (clazz == null || - !IAction.class.isAssignableFrom(clazz)) { - log.error("Requested protocol-action processing Class is NULL or does not implement the IAction interface."); - throw new Exception("Requested protocol-action processing Class is NULL or does not implement the IAction interface."); - - } - - IAction protocolAction = (IAction) applicationContext.getBean(clazz); - return protocolAction.processRequest(pendingReq, httpReq, httpResp, authData); - - } catch (ClassNotFoundException e) { - log.error("Requested Auth. protocol processing Class is NULL or does not implement the IAction interface."); - throw new Exception("Requested Auth. protocol processing Class is NULL or does not implement the IAction interface."); - } - - } - - protected void buildProtocolSpecificErrorResponse(Throwable throwable, HttpServletRequest req, - HttpServletResponse resp, IRequest protocolRequest) throws IOException { - try { - - Class clazz = Class.forName(protocolRequest.requestedModule()); - - if (clazz == null || - !IModulInfo.class.isAssignableFrom(clazz)) { - log.error("Requested protocol module Class is NULL or does not implement the IModulInfo interface."); - throw new Exception("Requested protocol module Class is NULL or does not implement the IModulInfo interface."); - - } - - IModulInfo handlingModule = (IModulInfo) applicationContext.getBean(clazz); - - if (handlingModule.generateErrorMessage( - throwable, req, resp, protocolRequest)) { - - //log Error to technical log - logExceptionToTechnicalLog(throwable); - - //log Error Message - statisticLogger.logErrorOperation(throwable, protocolRequest); - - //write revision log entries - revisionsLogger.logEvent(protocolRequest, EventConstants.TRANSACTION_ERROR, protocolRequest.getUniqueTransactionIdentifier()); - - return; - - } else { - handleErrorNoRedirect(throwable, req, resp, true); - - } - - } catch (Throwable e) { - handleErrorNoRedirect(throwable, req, resp, true); - - } - - } - -} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractController.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractController.java index e7f2fe62..4e58868b 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractController.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractController.java @@ -27,14 +27,9 @@ package at.gv.egiz.eaaf.core.impl.idp.controller; import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; - -import javax.naming.ConfigurationException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringEscapeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,26 +39,16 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import at.gv.egiz.components.eventlog.api.EventConstants; import at.gv.egiz.eaaf.core.api.IRequest; -import at.gv.egiz.eaaf.core.api.IRequestStorage; import at.gv.egiz.eaaf.core.api.IStatusMessenger; import at.gv.egiz.eaaf.core.api.data.EAAFConstants; import at.gv.egiz.eaaf.core.api.data.ExceptionContainer; -import at.gv.egiz.eaaf.core.api.gui.IGUIBuilderConfiguration; -import at.gv.egiz.eaaf.core.api.gui.IGUIBuilderConfigurationFactory; -import at.gv.egiz.eaaf.core.api.gui.IGUIFormBuilder; -import at.gv.egiz.eaaf.core.api.gui.ModifyableGuiBuilderConfiguration; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.api.idp.auth.services.IProtocolAuthenticationService; import at.gv.egiz.eaaf.core.api.logging.IRevisionLogger; -import at.gv.egiz.eaaf.core.api.logging.IStatisticLogger; import at.gv.egiz.eaaf.core.api.storage.ITransactionStorage; -import at.gv.egiz.eaaf.core.exceptions.AuthnRequestValidatorException; import at.gv.egiz.eaaf.core.exceptions.EAAFException; -import at.gv.egiz.eaaf.core.exceptions.GUIBuildException; -import at.gv.egiz.eaaf.core.exceptions.InvalidProtocolRequestException; import at.gv.egiz.eaaf.core.exceptions.ProcessExecutionException; -import at.gv.egiz.eaaf.core.exceptions.ProtocolNotActiveException; import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; -import at.gv.egiz.eaaf.core.impl.utils.HTTPUtils; import at.gv.egiz.eaaf.core.impl.utils.Random; import at.gv.egiz.eaaf.core.impl.utils.ServletUtils; @@ -76,30 +61,29 @@ public abstract class AbstractController { private static final Logger log = LoggerFactory.getLogger(AbstractController.class); + @Autowired(required=true) protected IProtocolAuthenticationService protAuthService; @Autowired(required=true) protected ApplicationContext applicationContext; @Autowired(required=true) protected IConfiguration authConfig; @Autowired(required=true) protected ITransactionStorage transactionStorage; - @Autowired(required=true) protected IRequestStorage requestStorage; - @Autowired(required=true) protected IGUIFormBuilder guiBuilder; - @Autowired(required=true) protected IGUIBuilderConfigurationFactory guiConfigFactory; @Autowired(required=true) protected IStatusMessenger statusMessager; - - @Autowired protected IStatisticLogger statisticLogger; + @Autowired protected IRevisionLogger revisionsLogger; - - - - - + @ExceptionHandler({EAAFException.class}) - public void MOAIDExceptionHandler(HttpServletRequest req, HttpServletResponse resp, Exception e) throws IOException { - log.error(e.getMessage() , e); - internalMOAIDExceptionHandler(req, resp, e, true); + public void MOAIDExceptionHandler(final HttpServletRequest req, final HttpServletResponse resp, final Exception e) throws IOException { + try { + protAuthService.handleErrorNoRedirect(e, req, resp, true); + + } catch (final EAAFException e1) { + log.warn("Can NOT handle an 'EAAFException'. Forwarding to generic error ... ", e); + IOExceptionHandler(resp, e); + + } } @ExceptionHandler({Exception.class}) - public void GenericExceptionHandler(HttpServletResponse resp, Exception exception) throws IOException { + public void GenericExceptionHandler(final HttpServletResponse resp, final Exception exception) throws IOException { log.error("Internel Server Error." , exception); resp.setContentType(EAAFConstants.CONTENTTYPE_HTML_UTF8); resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Internal Server Error!" + @@ -112,7 +96,7 @@ public abstract class AbstractController { } @ExceptionHandler({IOException.class}) - public void IOExceptionHandler(HttpServletResponse resp, Throwable exception) { + public void IOExceptionHandler(final HttpServletResponse resp, final Throwable exception) { log.error("Internel Server Error." , exception); resp.setContentType(EAAFConstants.CONTENTTYPE_HTML_UTF8); resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); @@ -120,15 +104,11 @@ public abstract class AbstractController { } - protected void handleError(String errorMessage, Throwable exceptionThrown, - HttpServletRequest req, HttpServletResponse resp, IRequest pendingReq) throws IOException { - - String pendingRequestID = null; - if (pendingReq != null) - pendingRequestID = pendingReq.getPendingRequestId(); + protected void handleError(final String errorMessage, final Throwable exceptionThrown, + final HttpServletRequest req, final HttpServletResponse resp, final IRequest pendingReq) throws IOException, EAAFException { Throwable loggedException = null; - Throwable extractedException = extractOriginalExceptionFromProcessException(exceptionThrown); + final Throwable extractedException = extractOriginalExceptionFromProcessException(exceptionThrown); //extract pendingRequestID and originalException if it was a TaskExecutionException if (extractedException instanceof TaskExecutionException) { @@ -138,12 +118,7 @@ public abstract class AbstractController { //use TaskExecutionException directly, if no Original Exeception is included if (loggedException == null) loggedException = exceptionThrown; - - //set pending-request ID if it is set - String reqID = ((TaskExecutionException) extractedException).getPendingRequestID(); - if (StringUtils.isNotEmpty(reqID)) - pendingRequestID = reqID; - + } else loggedException = exceptionThrown; @@ -156,7 +131,7 @@ public abstract class AbstractController { //put exception into transaction store for redirect - String key = Random.nextLongRandom(); + final String key = Random.nextLongRandom(); if (pendingReq != null) { revisionsLogger.logEvent(pendingReq, EventConstants.TRANSACTION_ERROR); transactionStorage.put(key, @@ -171,13 +146,9 @@ public abstract class AbstractController { //build up redirect URL String redirectURL = null; redirectURL = ServletUtils.getBaseUrl(req); - redirectURL += "/"+AbstractAuthProtocolModulController.ENDPOINT_ERRORHANDLING + redirectURL += "/"+ProtocolFinalizationController.ENDPOINT_ERRORHANDLING + "?" + EAAFConstants.PARAM_HTTP_ERROR_CODE + "=" + key; - -// //only add pending-request Id if it exists -// if (StringUtils.isNotEmpty(pendingRequestID)) -// redirectURL += "&" + EAAFConstants.PARAM_HTTP_TARGET_PENDINGREQUESTID + "=" + pendingRequestID; - + resp.setContentType("text/html"); resp.setStatus(302); @@ -186,128 +157,18 @@ public abstract class AbstractController { return; - } catch (Exception e) { + } catch (final Exception e) { log.warn("Default error-handling FAILED. Exception can not be stored ....", e); log.info("Switch to generic generic backup error-handling ... "); - handleErrorNoRedirect(loggedException, req, resp, true); + protAuthService.handleErrorNoRedirect(loggedException, req, resp, true); } } - /** - * Handles all exceptions with no pending request. - * Therefore, the error is written to the users browser - * - * @param throwable - * @param req - * @param resp - * @throws IOException - */ - protected void handleErrorNoRedirect(Throwable throwable, HttpServletRequest req, - HttpServletResponse resp, boolean writeExceptionToStatisticLog) throws IOException { - - //log Exception into statistic database - if (writeExceptionToStatisticLog) - statisticLogger.logErrorOperation(throwable); - - //write errror to console - logExceptionToTechnicalLog(throwable); - - //return error to Web browser - if (throwable instanceof EAAFException || throwable instanceof ProcessExecutionException) - internalMOAIDExceptionHandler(req, resp, (Exception)throwable, false); - - else { - //write generic message for general exceptions - String msg = statusMessager.getMessage(IStatusMessenger.CODES_INTERNAL_ERROR_GENERIC, null); - writeHTMLErrorResponse(req, resp, msg, "9199", (Exception) throwable); - - } - - } - - /** - * Write a Exception to the MOA-ID-Auth internal technical log - * - * @param loggedException Exception to log - */ - protected void logExceptionToTechnicalLog(Throwable loggedException) { - if (!( loggedException instanceof EAAFException - || loggedException instanceof ProcessExecutionException )) { - log.error("Receive an internal error: Message=" + loggedException.getMessage(), loggedException); - - } else { - if (log.isDebugEnabled() || log.isTraceEnabled()) { - log.warn(loggedException.getMessage(), loggedException); - - } else { - log.warn(loggedException.getMessage()); - - } - } - } - - private void writeBadRequestErrorResponse(HttpServletRequest req, HttpServletResponse resp, EAAFException e) throws IOException { - String code = statusMessager.mapInternalErrorToExternalError(((InvalidProtocolRequestException)e).getErrorId()); - String descr = StringEscapeUtils.escapeHtml4(StringEscapeUtils.escapeEcmaScript(e.getMessage())); - resp.setContentType(EAAFConstants.CONTENTTYPE_HTML_UTF8); - resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Protocol validation FAILED!" + - "(Errorcode=" + code + - " | Description=" + descr + ")"); - - } - - private void writeHTMLErrorResponse(HttpServletRequest req, HttpServletResponse httpResp, String msg, String errorCode, Exception error) throws IOException { - - try { - IGUIBuilderConfiguration config - = guiConfigFactory.getDefaultErrorGUI(HTTPUtils.extractAuthURLFromRequest(req)); -// HTTPUtils.extractAuthURLFromRequest(req), -// DefaultGUIFormBuilderConfiguration.VIEW_ERRORMESSAGE, -// null); - - //add errorcode and errormessage - if (config instanceof ModifyableGuiBuilderConfiguration) { - ((ModifyableGuiBuilderConfiguration)config).putCustomParameter("errorMsg", msg); - ((ModifyableGuiBuilderConfiguration)config).putCustomParameter("errorCode", errorCode); - - //add stacktrace if debug is enabled - if (log.isTraceEnabled()) { - ((ModifyableGuiBuilderConfiguration)config).putCustomParameter("stacktrace", getStacktraceFromException(error)); - - } - - } else - log.info("Can not ADD error message, because 'GUIBuilderConfiguration' is not modifieable "); - - - guiBuilder.build(httpResp, config, "Error-Message"); - - } catch (GUIBuildException e) { - log.warn("Can not build error-message GUI.", e); - GenericExceptionHandler(httpResp, e); - - } - - } - - private void writeHTMLErrorResponse(HttpServletRequest req, HttpServletResponse httpResp, Exception error) throws IOException { - writeHTMLErrorResponse(req, httpResp, - error.getMessage(), - statusMessager.getResponseErrorCode(error), - error); - } - - private String getStacktraceFromException(Exception ex) { - StringWriter errors = new StringWriter(); - ex.printStackTrace(new PrintWriter(errors)); - return errors.toString(); - - } /** * Extracts a TaskExecutionException of a ProcessExecutionExeception Stacktrace. @@ -315,13 +176,13 @@ public abstract class AbstractController { * @param exception * @return Return the latest TaskExecutionExecption if exists, otherwise the latest ProcessExecutionException */ - private Throwable extractOriginalExceptionFromProcessException(Throwable exception) { + private Throwable extractOriginalExceptionFromProcessException(final Throwable exception) { Throwable exholder = exception; TaskExecutionException taskExc = null; while(exholder != null && exholder instanceof ProcessExecutionException) { - ProcessExecutionException procExc = (ProcessExecutionException) exholder; + final ProcessExecutionException procExc = (ProcessExecutionException) exholder; if (procExc.getCause() != null && procExc.getCause() instanceof TaskExecutionException) { taskExc = (TaskExecutionException) procExc.getCause(); @@ -339,40 +200,6 @@ public abstract class AbstractController { return taskExc; } - private void internalMOAIDExceptionHandler(HttpServletRequest req, HttpServletResponse resp, Exception e, boolean writeExceptionToStatisicLog) throws IOException { - if (e instanceof ProtocolNotActiveException) { - resp.getWriter().write(e.getMessage()); - resp.setContentType(EAAFConstants.CONTENTTYPE_HTML_UTF8); - resp.sendError(HttpServletResponse.SC_FORBIDDEN, - StringEscapeUtils.escapeHtml4(StringEscapeUtils.escapeEcmaScript(e.getMessage()))); - - } else if (e instanceof AuthnRequestValidatorException) { - AuthnRequestValidatorException ex = (AuthnRequestValidatorException)e; - //log Error Message - if (writeExceptionToStatisicLog) - statisticLogger.logErrorOperation(ex, ex.getErrorRequest()); - - //write error message - writeBadRequestErrorResponse(req, resp, (EAAFException) e); - - } else if (e instanceof InvalidProtocolRequestException) { - //send error response - writeBadRequestErrorResponse(req, resp, (EAAFException) e); - - } else if (e instanceof ConfigurationException) { - //send HTML formated error message - writeHTMLErrorResponse(req, resp, (EAAFException) e); - - } else if (e instanceof EAAFException) { - //send HTML formated error message - writeHTMLErrorResponse(req, resp, e); - - } else if (e instanceof ProcessExecutionException) { - //send HTML formated error message - writeHTMLErrorResponse(req, resp, e); - - } - - } + } diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractProcessEngineSignalController.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractProcessEngineSignalController.java index d6448c95..6afa4fee 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractProcessEngineSignalController.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/AbstractProcessEngineSignalController.java @@ -37,6 +37,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.IRequestStorage; import at.gv.egiz.eaaf.core.api.IStatusMessenger; import at.gv.egiz.eaaf.core.api.data.EAAFConstants; import at.gv.egiz.eaaf.core.api.idp.process.ProcessEngine; @@ -53,9 +54,10 @@ import at.gv.egiz.eaaf.core.impl.utils.TransactionIDUtils; public abstract class AbstractProcessEngineSignalController extends AbstractController { private static final Logger log = LoggerFactory.getLogger(AbstractProcessEngineSignalController.class); - @Autowired protected ProcessEngine processEngine; + @Autowired(required=true) protected ProcessEngine processEngine; + @Autowired(required=true) IRequestStorage requestStorage; - protected void signalProcessManagement(HttpServletRequest req, HttpServletResponse resp) throws IOException { + protected void signalProcessManagement(HttpServletRequest req, HttpServletResponse resp) throws IOException, EAAFException { String pendingRequestID = StringEscapeUtils.escapeHtml4(getPendingRequestId(req)); IRequest pendingReq = null; try { diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/ProtocolFinalizationController.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/ProtocolFinalizationController.java index e96ea138..b830e240 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/ProtocolFinalizationController.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/ProtocolFinalizationController.java @@ -20,10 +20,6 @@ * 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.core.impl.idp.controller; import java.io.IOException; @@ -34,16 +30,18 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.text.StringEscapeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import at.gv.egiz.components.eventlog.api.EventConstants; import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.IRequestStorage; import at.gv.egiz.eaaf.core.api.IStatusMessenger; import at.gv.egiz.eaaf.core.api.data.EAAFConstants; import at.gv.egiz.eaaf.core.api.data.ExceptionContainer; -import at.gv.egiz.eaaf.core.exceptions.EAAFAuthenticationException; +import at.gv.egiz.eaaf.core.api.idp.auth.services.IProtocolAuthenticationService; import at.gv.egiz.eaaf.core.exceptions.EAAFException; /** @@ -51,8 +49,12 @@ import at.gv.egiz.eaaf.core.exceptions.EAAFException; * */ @Controller -public class ProtocolFinalizationController extends AbstractAuthProtocolModulController { +public class ProtocolFinalizationController extends AbstractController { private static final Logger log = LoggerFactory.getLogger(ProtocolFinalizationController.class); + public static final String ENDPOINT_FINALIZEPROTOCOL = "finalizeAuthProtocol"; + public static final String ENDPOINT_ERRORHANDLING = "errorHandling"; + + @Autowired(required=true) IRequestStorage requestStorage; @RequestMapping(value = ENDPOINT_ERRORHANDLING, method = {RequestMethod.GET}) public void errorHandling(HttpServletRequest req, HttpServletResponse resp) throws EAAFException, IOException { @@ -72,7 +74,7 @@ public class ProtocolFinalizationController extends AbstractAuthProtocolModulCon if (pendingReq != null) { //build protocol-specific error message if possible - buildProtocolSpecificErrorResponse(throwable, req, resp, pendingReq); + protAuthService.buildProtocolSpecificErrorResponse(throwable, req, resp, pendingReq); //remove active user-session transactionStorage.remove(pendingReq.getPendingRequestId()); @@ -80,11 +82,11 @@ public class ProtocolFinalizationController extends AbstractAuthProtocolModulCon return; } else { - handleErrorNoRedirect(throwable, req, resp, true); + protAuthService.handleErrorNoRedirect(throwable, req, resp, true); } } else { - handleErrorNoRedirect( + protAuthService.handleErrorNoRedirect( new EAAFException( IStatusMessenger.CODES_INTERNAL_ERROR_AUTH_NOPENDIGREQID, null), req, resp, false); @@ -93,7 +95,7 @@ public class ProtocolFinalizationController extends AbstractAuthProtocolModulCon } catch (Throwable e) { log.error(e.getMessage(), e); - handleErrorNoRedirect(e, req, resp, false); + protAuthService.handleErrorNoRedirect(e, req, resp, false); } finally { //remove pending-request @@ -107,7 +109,7 @@ public class ProtocolFinalizationController extends AbstractAuthProtocolModulCon } else { log.debug("Request contains NO ErrorId"); - handleErrorNoRedirect( + protAuthService.handleErrorNoRedirect( new EAAFException( IStatusMessenger.CODES_INTERNAL_ERROR_AUTH_NOPENDIGREQID, null), req, resp, false); @@ -132,62 +134,14 @@ public class ProtocolFinalizationController extends AbstractAuthProtocolModulCon if (pendingReq == null) { log.error("No PendingRequest with ID " + pendingRequestID + " found.!"); - handleErrorNoRedirect( + protAuthService.handleErrorNoRedirect( new EAAFException( IStatusMessenger.CODES_INTERNAL_ERROR_AUTH_TIMEOUT, new Object[]{pendingRequestID, }), req, resp, false); - } else { - try { - log.debug("Finalize PendingRequest with ID " + pendingRequestID); - - //check if pending-request has 'abortedByUser' flag set - if (pendingReq.isAbortedByUser()) { - //send authentication aborted error to Service Provider - buildProtocolSpecificErrorResponse( - new EAAFAuthenticationException( - IStatusMessenger.CODES_INTERNAL_ERROR_AUTH_USERSTOP, - new Object[] {}), - req, resp, pendingReq); - - //do not remove the full active SSO-Session - // in case of only one Service-Provider authentication request is aborted - if ( !pendingReq.needSingleSignOnFunctionality()) { - transactionStorage.remove(pendingReq.getPendingRequestId()); - - } - - //check if pending-request are authenticated - } else if (pendingReq.isAuthenticated()) { - finalizeAuthenticationProcess(req, resp, pendingReq); - - } else { - //suspect state: pending-request is not aborted but also are not authenticated - log.error("PendingRequest is NOT authenticated --> Abort authentication process!"); - handleErrorNoRedirect( - new EAAFException( - "auth.20", - null), req, resp, true); - - } - - } catch (Exception e) { - log.error("Finalize authentication protocol FAILED." , e); - buildProtocolSpecificErrorResponse(e, req, resp, pendingReq); - - if (pendingReq != null) - transactionStorage.remove(pendingReq.getPendingRequestId()); - - } - } - - //remove pending-request - if (pendingReq != null) { - requestStorage.removePendingRequest(pendingReq.getPendingRequestId()); - revisionsLogger.logEvent(EventConstants.TRANSACTION_DESTROYED, pendingReq.getUniqueTransactionIdentifier()); - - } + } else + protAuthService.finalizeAuthentication(req, resp, pendingReq); } diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/tasks/FinalizeAuthenticationTask.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/tasks/FinalizeAuthenticationTask.java index 0370c14d..eff6b631 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/tasks/FinalizeAuthenticationTask.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/controller/tasks/FinalizeAuthenticationTask.java @@ -26,15 +26,20 @@ *******************************************************************************/ package at.gv.egiz.eaaf.core.impl.idp.controller.tasks; +import java.io.Serializable; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cglib.proxy.Dispatcher; import org.springframework.stereotype.Component; import at.gv.egiz.eaaf.core.api.data.EAAFConstants; import at.gv.egiz.eaaf.core.api.idp.auth.IAuthenticationManager; +import at.gv.egiz.eaaf.core.api.idp.auth.services.IProtocolAuthenticationService; import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; import at.gv.egiz.eaaf.core.exceptions.EAAFException; import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; @@ -47,7 +52,9 @@ import at.gv.egiz.eaaf.core.impl.idp.auth.modules.AbstractAuthServletTask; @Component("FinalizeAuthenticationTask") public class FinalizeAuthenticationTask extends AbstractAuthServletTask { - private static final Logger log = LoggerFactory.getLogger(FinalizeAuthenticationTask.class); + private static final Logger log = LoggerFactory.getLogger(FinalizeAuthenticationTask.class); + + @Autowired(required=true) IProtocolAuthenticationService protAuchService; /* (non-Javadoc) * @see at.gv.egovernment.moa.id.process.springweb.MoaIdTask#execute(at.gv.egovernment.moa.id.process.api.ExecutionContext, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) @@ -60,13 +67,22 @@ public class FinalizeAuthenticationTask extends AbstractAuthServletTask { try { //set pending request to authenticated pendingReq.setAuthenticated(true); - requestStoreage.storePendingRequest(pendingReq); - - log.info("AuthProcess finished. Redirect to Protocol Dispatcher."); - performRedirectToProtocolFinialization(pendingReq, response); - revisionsLogger.logEvent(pendingReq, IAuthenticationManager.EVENT_AUTHENTICATION_PROCESS_FINISHED); + + Object frontChannelRedirectFlagObj = executionContext.get(EAAFConstants.PROCESS_ENGINE_REQUIRES_NO_POSTAUTH_REDIRECT); + if (frontChannelRedirectFlagObj != null && frontChannelRedirectFlagObj instanceof Boolean && + (Boolean)frontChannelRedirectFlagObj) { + log.info("AuthProcess finished. Forward to Protocol finalization."); + protAuchService.finalizeAuthentication(request, response, pendingReq); + + } else { + log.info("AuthProcess finished. Redirect to Protocol Dispatcher."); + requestStoreage.storePendingRequest(pendingReq); + performRedirectToProtocolFinialization(pendingReq, response); + + } + } catch (EAAFException e) { throw new TaskExecutionException(pendingReq, e.getMessage(), e); diff --git a/eaaf_core/src/main/resources/eaaf_core.beans.xml b/eaaf_core/src/main/resources/eaaf_core.beans.xml index 40d1f2cf..774af6fe 100644 --- a/eaaf_core/src/main/resources/eaaf_core.beans.xml +++ b/eaaf_core/src/main/resources/eaaf_core.beans.xml @@ -12,8 +12,11 @@ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> - + diff --git a/eaaf_modules/eaaf_module_pvp2_core/pom.xml b/eaaf_modules/eaaf_module_pvp2_core/pom.xml index 8b886df6..e5cc555a 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/pom.xml +++ b/eaaf_modules/eaaf_module_pvp2_core/pom.xml @@ -29,15 +29,33 @@ org.opensaml - opensaml + opensaml + + + org.slf4j + log4j-over-slf4j + + org.opensaml xmltooling + + + org.slf4j + log4j-over-slf4j + + org.opensaml openws + + + org.slf4j + log4j-over-slf4j + + org.apache.santuario diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/AbstractPVP2XProtocol.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/AbstractPVP2XProtocol.java index f384cfae..3298559a 100644 --- a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/AbstractPVP2XProtocol.java +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/AbstractPVP2XProtocol.java @@ -63,7 +63,7 @@ import at.gv.egiz.eaaf.core.exceptions.EAAFException; import at.gv.egiz.eaaf.core.exceptions.InvalidProtocolRequestException; import at.gv.egiz.eaaf.core.exceptions.NoPassivAuthenticationException; import at.gv.egiz.eaaf.core.exceptions.SLOException; -import at.gv.egiz.eaaf.core.impl.idp.controller.AbstractAuthProtocolModulController; +import at.gv.egiz.eaaf.core.impl.idp.controller.AbstractController; import at.gv.egiz.eaaf.modules.pvp2.PVPEventConstants; import at.gv.egiz.eaaf.modules.pvp2.api.IPVP2BasicConfiguration; import at.gv.egiz.eaaf.modules.pvp2.api.binding.IEncoder; @@ -85,7 +85,7 @@ 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; -public abstract class AbstractPVP2XProtocol extends AbstractAuthProtocolModulController implements IModulInfo { +public abstract class AbstractPVP2XProtocol extends AbstractController implements IModulInfo { private static final Logger log = LoggerFactory.getLogger(AbstractPVP2XProtocol.class); @Autowired(required=true) protected IPVP2BasicConfiguration pvpBasicConfiguration; @@ -409,7 +409,7 @@ public abstract class AbstractPVP2XProtocol extends AbstractAuthProtocolModulCon //switch to session authentication - performAuthentication(request, response, pendingReq); + protAuthService.performAuthentication(request, response, pendingReq); } diff --git a/eaaf_modules/eaaf_module_pvp2_sp/pom.xml b/eaaf_modules/eaaf_module_pvp2_sp/pom.xml index c6e0b9e5..a4e8d4ca 100644 --- a/eaaf_modules/eaaf_module_pvp2_sp/pom.xml +++ b/eaaf_modules/eaaf_module_pvp2_sp/pom.xml @@ -22,7 +22,13 @@ at.gv.egiz.eaaf eaaf_module_pvp2_core ${egiz.eaaf.version} - + + + org.slf4j + log4j-over-slf4j + + + javax.servlet diff --git a/eaaf_modules/pom.xml b/eaaf_modules/pom.xml index d1b5d6e3..585655b1 100644 --- a/eaaf_modules/pom.xml +++ b/eaaf_modules/pom.xml @@ -21,6 +21,7 @@ eaaf_module_pvp2_core eaaf_module_pvp2_idp eaaf_module_pvp2_sp - + eaaf_module_auth_sl20 + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 6e4da91e..c385dad7 100644 --- a/pom.xml +++ b/pom.xml @@ -12,8 +12,12 @@ EGIZ EAAF components + + UTF-8 + 1.8 + - 1.0.6-snapshot + 1.0.7 @@ -36,7 +40,14 @@ 3.0.1 1.7 1.3.2 - 1.1.6 + + 4.5.7 + 4.4.11 + + 2.9.8 + 0.6.5 + + 1.1.6 2.11.0 2.7.1 @@ -182,7 +193,28 @@ xalan ${xalan.version} - + + + org.apache.httpcomponents + httpclient + ${httpclient.version} + + + org.apache.httpcomponents + httpcore + ${httpcore.version} + + + + com.fasterxml.jackson.core + jackson-databind + ${com.fasterxml.jackson.core.version} + + + org.bitbucket.b_c + jose4j + ${org.bitbucket.b_c.jose4j.version} + -- cgit v1.2.3