/******************************************************************************* *******************************************************************************/ 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; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; 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.IStatusMessager; 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.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; /** * @author tlenz * */ public abstract class AbstractController { private static final Logger log = LoggerFactory.getLogger(AbstractController.class); @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 IStatusMessager 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); } @ExceptionHandler({Exception.class}) public void GenericExceptionHandler(HttpServletResponse resp, 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!" + "(Errorcode=9199" +" | Description=" + StringEscapeUtils.escapeHtml4(StringEscapeUtils.escapeEcmaScript(exception.getMessage())) + ")"); return; } @ExceptionHandler({IOException.class}) public void IOExceptionHandler(HttpServletResponse resp, Throwable exception) { log.error("Internel Server Error." , exception); resp.setContentType(EAAFConstants.CONTENTTYPE_HTML_UTF8); resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } protected void handleError(String errorMessage, Throwable exceptionThrown, HttpServletRequest req, HttpServletResponse resp, IRequest pendingReq) throws IOException { String pendingRequestID = null; if (pendingReq != null) pendingRequestID = pendingReq.getPendingRequestId(); Throwable loggedException = null; Throwable extractedException = extractOriginalExceptionFromProcessException(exceptionThrown); //extract pendingRequestID and originalException if it was a TaskExecutionException if (extractedException instanceof TaskExecutionException) { //set original exception loggedException = ((TaskExecutionException) extractedException).getOriginalException(); //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; try { //switch to protocol-finalize method to generate a protocol-specific error message //log error directly in debug mode if (log.isDebugEnabled()) log.warn(loggedException.getMessage(), loggedException); //put exception into transaction store for redirect String key = Random.nextLongRandom(); if (pendingReq != null) { revisionsLogger.logEvent(pendingReq, EventConstants.TRANSACTION_ERROR); transactionStorage.put(key, new ExceptionContainer(pendingReq, loggedException), -1); } else { transactionStorage.put(key, new ExceptionContainer(null, loggedException), -1); } //build up redirect URL String redirectURL = null; redirectURL = ServletUtils.getBaseUrl(req); redirectURL += "/"+AbstractAuthProtocolModulController.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); resp.addHeader("Location", redirectURL); log.debug("REDIRECT TO: " + redirectURL); return; } catch (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); } } /** * 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(IStatusMessager.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. * * @param exception * @return Return the latest TaskExecutionExecption if exists, otherwise the latest ProcessExecutionException */ private Throwable extractOriginalExceptionFromProcessException(Throwable exception) { Throwable exholder = exception; TaskExecutionException taskExc = null; while(exholder != null && exholder instanceof ProcessExecutionException) { ProcessExecutionException procExc = (ProcessExecutionException) exholder; if (procExc.getCause() != null && procExc.getCause() instanceof TaskExecutionException) { taskExc = (TaskExecutionException) procExc.getCause(); exholder = taskExc.getOriginalException(); } else break; } if (taskExc == null) return exholder; else 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); } } }