/******************************************************************************* * Copyright 2014 Federal Chancellery Austria * MOA-ID has been developed in a cooperation between BRZ, the Federal * Chancellery Austria - ICT staff unit, and Graz University of Technology. * * Licensed under the EUPL, Version 1.1 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: * http://www.osor.eu/eupl/ * * 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. ******************************************************************************/ /* * Copyright 2003 Federal Chancellery Austria * MOA-ID has been developed in a cooperation between BRZ, the Federal * Chancellery Austria - ICT staff unit, and Graz University of Technology. * * Licensed under the EUPL, Version 1.1 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: * http://www.osor.eu/eupl/ * * 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.egovernment.moa.id.auth.servlet; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.RequestDispatcher; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import at.gv.egovernment.moa.id.advancedlogging.StatisticLogger; import at.gv.egovernment.moa.id.auth.MOAIDAuthConstants; import at.gv.egovernment.moa.id.auth.exception.AuthenticationException; import at.gv.egovernment.moa.id.auth.exception.MOAIDException; import at.gv.egovernment.moa.id.auth.exception.WrongParametersException; import at.gv.egovernment.moa.id.auth.modules.TaskExecutionException; import at.gv.egovernment.moa.id.config.ConfigurationException; import at.gv.egovernment.moa.id.entrypoints.DispatcherServlet; import at.gv.egovernment.moa.id.process.ProcessEngine; import at.gv.egovernment.moa.id.process.ProcessExecutionException; import at.gv.egovernment.moa.id.storage.DBExceptionStoreImpl; import at.gv.egovernment.moa.id.storage.IExceptionStore; import at.gv.egovernment.moa.id.util.ServletUtils; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.MiscUtil; import at.gv.egovernment.moa.util.URLDecoder; /** * Base class for MOA-ID Auth Servlets, providing standard error handling and * constant names. * * @author Paul Ivancsics * @version $Id$ */ public class AuthServlet extends HttpServlet { /** * */ private static final long serialVersionUID = -6929905344382283738L; protected static final String ERROR_CODE_PARAM = "errorid"; /** * The process engine. */ private ProcessEngine processEngine; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Logger.debug("GET " + this.getServletName()); this.setNoCachingHeadersInHttpRespone(req, resp); } protected void handleErrorNoRedirect(String errorMessage, Throwable exceptionThrown, HttpServletRequest req, HttpServletResponse resp) { if (null != errorMessage) { Logger.error(errorMessage); req.setAttribute("ErrorMessage", errorMessage); } if (null != exceptionThrown) { if (null == errorMessage) errorMessage = exceptionThrown.getMessage(); Logger.error(errorMessage, exceptionThrown); req.setAttribute("ExceptionThrown", exceptionThrown); } if (Logger.isDebugEnabled()) { req.setAttribute("LogLevel", "debug"); } StatisticLogger logger = StatisticLogger.getInstance(); logger.logErrorOperation(exceptionThrown); // forward this to errorpage-auth.jsp where the HTML error page is // generated ServletContext context = getServletContext(); RequestDispatcher dispatcher = context .getRequestDispatcher("/errorpage-auth.jsp"); try { resp.setHeader(MOAIDAuthConstants.HEADER_EXPIRES, MOAIDAuthConstants.HEADER_VALUE_EXPIRES); resp.setHeader(MOAIDAuthConstants.HEADER_PRAGMA, MOAIDAuthConstants.HEADER_VALUE_PRAGMA); resp.setHeader(MOAIDAuthConstants.HEADER_CACHE_CONTROL, MOAIDAuthConstants.HEADER_VALUE_CACHE_CONTROL); resp.addHeader(MOAIDAuthConstants.HEADER_CACHE_CONTROL, MOAIDAuthConstants.HEADER_VALUE_CACHE_CONTROL_IE); dispatcher.forward(req, resp); } catch (ServletException e) { Logger.error(e); } catch (IOException e) { Logger.error(e); } } /** * Handles an error.
> * * * @param errorMessage * error message * @param exceptionThrown * exception thrown * @param req * servlet request * @param resp * servlet response */ protected void handleError(String errorMessage, Throwable exceptionThrown, HttpServletRequest req, HttpServletResponse resp, String pendingRequestID) { Throwable loggedException = null; if (exceptionThrown != null && exceptionThrown instanceof ProcessExecutionException) { ProcessExecutionException procExc = (ProcessExecutionException) exceptionThrown; if (procExc.getCause() != null && procExc.getCause() instanceof TaskExecutionException) { TaskExecutionException taskExc = (TaskExecutionException) procExc.getCause(); loggedException = taskExc.getOriginalException(); } } if (loggedException == null) loggedException = exceptionThrown; if (!(loggedException instanceof MOAIDException)) { Logger.error("Receive an internal error: Message=" + loggedException.getMessage(), loggedException); } else { if (Logger.isDebugEnabled() || Logger.isTraceEnabled()) { Logger.error(loggedException.getMessage(), loggedException); } else { Logger.error(loggedException.getMessage()); } } IExceptionStore store = DBExceptionStoreImpl.getStore(); String id = store.storeException(loggedException); if (id != null && MiscUtil.isNotEmpty(pendingRequestID)) { String redirectURL = null; redirectURL = ServletUtils.getBaseUrl(req); redirectURL += "/dispatcher?" + ERROR_CODE_PARAM + "=" + id + "&" + DispatcherServlet.PARAM_TARGET_PENDINGREQUESTID + "=" + pendingRequestID; resp.setContentType("text/html"); resp.setStatus(302); resp.addHeader("Location", redirectURL); Logger.debug("REDIRECT TO: " + redirectURL); return; } else { //Exception can not be stored in database handleErrorNoRedirect(errorMessage, loggedException, req, resp); } } /** * Handles a WrongParametersException. * * @param req * servlet request * @param resp * servlet response */ protected void handleWrongParameters(WrongParametersException ex, HttpServletRequest req, HttpServletResponse resp) { Logger.error(ex.toString()); req.setAttribute("WrongParameters", ex.getMessage()); // forward this to errorpage-auth.jsp where the HTML error page is // generated ServletContext context = getServletContext(); RequestDispatcher dispatcher = context .getRequestDispatcher("/errorpage-auth.jsp"); try { resp.setHeader(MOAIDAuthConstants.HEADER_EXPIRES, MOAIDAuthConstants.HEADER_VALUE_EXPIRES); resp.setHeader(MOAIDAuthConstants.HEADER_PRAGMA, MOAIDAuthConstants.HEADER_VALUE_PRAGMA); resp.setHeader(MOAIDAuthConstants.HEADER_CACHE_CONTROL, MOAIDAuthConstants.HEADER_VALUE_CACHE_CONTROL); resp.addHeader(MOAIDAuthConstants.HEADER_CACHE_CONTROL, MOAIDAuthConstants.HEADER_VALUE_CACHE_CONTROL_IE); dispatcher.forward(req, resp); } catch (ServletException e) { Logger.error(e); } catch (IOException e) { Logger.error(e); } } /** * Logs all servlet parameters for debugging purposes. */ protected void logParameters(HttpServletRequest req) { for (Enumeration params = req.getParameterNames(); params .hasMoreElements();) { String parname = (String) params.nextElement(); Logger.debug("Parameter " + parname + req.getParameter(parname)); } } /** * Parses the request input stream for parameters, assuming parameters are * encoded UTF-8 (no standard exists how browsers should encode them). * * @param req * servlet request * * @return mapping parameter name -> value * * @throws IOException * if parsing request parameters fails. * * @throws FileUploadException * if parsing request parameters fails. */ protected Map getParameters(HttpServletRequest req) throws IOException, FileUploadException { Map parameters = new HashMap(); if (ServletFileUpload.isMultipartContent(req)) { // request is encoded as mulitpart/form-data FileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = null; upload = new ServletFileUpload(factory); List items = null; items = upload.parseRequest(req); for (int i = 0; i < items.size(); i++) { FileItem item = (FileItem) items.get(i); if (item.isFormField()) { // Process only form fields - no file upload items String logString = item.getString("UTF-8"); // TODO use RegExp String startS = ""; String endS = "urn:publicid:gv.at:baseid"; String logWithMaskedBaseid = logString; int start = logString.indexOf(startS); if (start > -1) { int end = logString.indexOf(endS); if (end > -1) { logWithMaskedBaseid = logString.substring(0, start); logWithMaskedBaseid += startS; logWithMaskedBaseid += "xxxxxxxxxxxxxxxxxxxxxxxx"; logWithMaskedBaseid += logString.substring(end, logString.length()); } } parameters .put(item.getFieldName(), item.getString("UTF-8")); Logger.debug("Processed multipart/form-data request parameter: \nName: " + item.getFieldName() + "\nValue: " + logWithMaskedBaseid); } } } else { // request is encoded as application/x-www-urlencoded InputStream in = req.getInputStream(); String paramName; String paramValueURLEncoded; do { paramName = new String(readBytesUpTo(in, '=')); if (paramName.length() > 0) { paramValueURLEncoded = readBytesUpTo(in, '&'); String paramValue = URLDecoder.decode(paramValueURLEncoded, "UTF-8"); parameters.put(paramName, paramValue); } } while (paramName.length() > 0); in.close(); } return parameters; } /** * Reads bytes up to a delimiter, consuming the delimiter. * * @param in * input stream * @param delimiter * delimiter character * @return String constructed from the read bytes * @throws IOException */ protected String readBytesUpTo(InputStream in, char delimiter) throws IOException { ByteArrayOutputStream bout = new ByteArrayOutputStream(); boolean done = false; int b; while (!done && (b = in.read()) >= 0) { if (b == delimiter) done = true; else bout.write(b); } return bout.toString(); } /** * Calls the web application initializer. * * @see javax.servlet.Servlet#init(ServletConfig) */ public void init(ServletConfig servletConfig) throws ServletException { super.init(servletConfig); } // public void contextDestroyed(ServletContextEvent arg0) { // Security.removeProvider((new IAIK()).getName()); // Security.removeProvider((new ECCProvider()).getName()); // } /** * Set response headers to avoid caching * * @param request * HttpServletRequest * @param response * HttpServletResponse */ protected void setNoCachingHeadersInHttpRespone(HttpServletRequest request, HttpServletResponse response) { response.setHeader(MOAIDAuthConstants.HEADER_EXPIRES, MOAIDAuthConstants.HEADER_VALUE_EXPIRES); response.setHeader(MOAIDAuthConstants.HEADER_PRAGMA, MOAIDAuthConstants.HEADER_VALUE_PRAGMA); response.setHeader(MOAIDAuthConstants.HEADER_CACHE_CONTROL, MOAIDAuthConstants.HEADER_VALUE_CACHE_CONTROL); response.addHeader(MOAIDAuthConstants.HEADER_CACHE_CONTROL, MOAIDAuthConstants.HEADER_VALUE_CACHE_CONTROL_IE); } /** * Adds a parameter to a URL. * * @param url * the URL * @param paramname * parameter name * @param paramvalue * parameter value * @return the URL with parameter added */ protected static String addURLParameter(String url, String paramname, String paramvalue) { String param = paramname + "=" + paramvalue; if (url.indexOf("?") < 0) return url + "?" + param; else return url + "&" + param; } /** * Checks if HTTP requests are allowed * * @param authURL * requestURL * @throws AuthenticationException * if HTTP requests are not allowed * @throws ConfigurationException */ protected void checkIfHTTPisAllowed(String authURL) throws AuthenticationException, ConfigurationException { // check if HTTP Connection may be allowed (through // FRONTEND_SERVLETS_ENABLE_HTTP_CONNECTION_PROPERTY) //Removed from MOA-ID 2.0 config // String boolStr = AuthConfigurationProvider // .getInstance() // .getGenericConfigurationParameter( // AuthConfigurationProvider.FRONTEND_SERVLETS_ENABLE_HTTP_CONNECTION_PROPERTY); if ((!authURL.startsWith("https:")) //&& (false == BoolUtils.valueOf(boolStr)) ) throw new AuthenticationException("auth.07", new Object[] { authURL + "*" }); } /** * Returns the underlying process engine instance. * * @return The process engine (never {@code null}). * @throws NoSuchBeanDefinitionException * if no {@link ProcessEngine} bean was found. * @throws NoUniqueBeanDefinitionException * if more than one {@link ProcessEngine} bean was found. * @throws BeansException * if a problem getting the {@link ProcessEngine} bean occurred. * @throws IllegalStateException * if the Spring WebApplicationContext was not found, which means that the servlet is used outside a * Spring web environment. */ public synchronized ProcessEngine getProcessEngine() { if (processEngine == null) { WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); if (ctx == null) { throw new IllegalStateException( "Unable to find Spring WebApplicationContext. Servlet needs to be executed within a Spring web environment."); } processEngine = ctx.getBean(ProcessEngine.class); } return processEngine; } }