summaryrefslogtreecommitdiff
path: root/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java
diff options
context:
space:
mode:
Diffstat (limited to 'eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java')
-rw-r--r--eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/services/ProtocolAuthenticationService.java458
1 files changed, 458 insertions, 0 deletions
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);
+
+ }
+
+ }
+
+
+}