/******************************************************************************* * 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. *******************************************************************************/ package at.gv.egovernment.moa.id.entrypoints; import java.io.IOException; import java.util.Iterator; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import at.gv.egovernment.moa.id.advancedlogging.MOAIDEventConstants; import at.gv.egovernment.moa.id.advancedlogging.MOAReversionLogger; import at.gv.egovernment.moa.id.advancedlogging.StatisticLogger; import at.gv.egovernment.moa.id.advancedlogging.TransactionIDUtils; import at.gv.egovernment.moa.id.auth.MOAIDAuthConstants; import at.gv.egovernment.moa.id.auth.builder.AuthenticationDataBuilder; import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; import at.gv.egovernment.moa.id.auth.exception.AuthenticationException; import at.gv.egovernment.moa.id.auth.exception.InvalidProtocolRequestException; import at.gv.egovernment.moa.id.auth.exception.MOAIDException; import at.gv.egovernment.moa.id.auth.exception.ProtocolNotActiveException; import at.gv.egovernment.moa.id.auth.exception.WrongParametersException; import at.gv.egovernment.moa.id.auth.servlet.AuthServlet; import at.gv.egovernment.moa.id.config.ConfigurationException; import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; import at.gv.egovernment.moa.id.data.IAuthData; import at.gv.egovernment.moa.id.data.SLOInformationInterface; import at.gv.egovernment.moa.id.moduls.AuthenticationManager; import at.gv.egovernment.moa.id.moduls.IAction; import at.gv.egovernment.moa.id.moduls.IModulInfo; import at.gv.egovernment.moa.id.moduls.IRequest; import at.gv.egovernment.moa.id.moduls.ModulStorage; import at.gv.egovernment.moa.id.moduls.NoPassivAuthenticationException; import at.gv.egovernment.moa.id.moduls.RequestStorage; import at.gv.egovernment.moa.id.moduls.SSOManager; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.AuthnRequestValidatorException; import at.gv.egovernment.moa.id.storage.AuthenticationSessionStoreage; import at.gv.egovernment.moa.id.storage.DBExceptionStoreImpl; import at.gv.egovernment.moa.id.util.ErrorResponseUtils; import at.gv.egovernment.moa.id.util.MOAIDMessageProvider; import at.gv.egovernment.moa.id.util.Random; import at.gv.egovernment.moa.id.util.legacy.LegacyHelper; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.MiscUtil; public class DispatcherServlet extends AuthServlet{ /** * */ private static final long serialVersionUID = 1L; public static final String PARAM_TARGET_MODULE = "mod"; public static final String PARAM_TARGET_ACTION = "action"; public static final String PARAM_TARGET_PENDINGREQUESTID = "pendingid"; // @Override // public void init(ServletConfig config) throws ServletException { // try { // super.init(config); // MOAIDAuthInitializer.initialize(); // Logger.info(MOAIDMessageProvider.getInstance().getMessage( // "init.00", null)); // // Logger.info("Dispatcher Servlet initialization finished."); // // } catch (Exception ex) { // Logger.fatal( // MOAIDMessageProvider.getInstance().getMessage("init.02", // null), ex); // // //throw new ServletException(ex); // // } // // } protected void processRequest(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { boolean isValidSSOSession = false; boolean useSSOOA = false; String protocolRequestID = null; try { Logger.debug("REQUEST: " + req.getRequestURI()); Logger.debug("QUERY : " + req.getQueryString()); // *** start of error handling *** String errorid = req.getParameter(ERROR_CODE_PARAM); if (errorid != null) { Throwable throwable = DBExceptionStoreImpl.getStore() .fetchException(errorid); DBExceptionStoreImpl.getStore().removeException(errorid); Object idObject = req.getParameter(PARAM_TARGET_PENDINGREQUESTID); //Map errorRequests = RequestStorage.getPendingRequest(req.getSession()); String pendingRequestID = null; if (idObject != null && (idObject instanceof String)) { pendingRequestID = (String) idObject; } if (throwable != null) { IRequest errorRequest = null; if (pendingRequestID != null) { errorRequest = RequestStorage.getPendingRequest(pendingRequestID); } if (errorRequest != null) { RequestStorage.removePendingRequest(pendingRequestID); MOAReversionLogger.getInstance().logEvent(errorRequest, MOAIDEventConstants.TRANSACTION_ERROR); try { IModulInfo handlingModule = ModulStorage .getModuleByPath(errorRequest .requestedModule()); if (handlingModule != null) { if (handlingModule.generateErrorMessage( throwable, req, resp, errorRequest)) { //log Error Message StatisticLogger logger = StatisticLogger.getInstance(); logger.logErrorOperation(throwable, errorRequest); //remove MOASession AuthenticationSession moaSession = AuthenticationSessionStoreage.getSessionWithPendingRequestID(pendingRequestID); if (moaSession != null) AuthenticationManager.getInstance().performOnlyIDPLogOut(req, resp, moaSession.getSessionID()); return; } else { handleErrorNoRedirect(throwable.getMessage(), throwable, req, resp); } } } catch (Throwable e) { Logger.error(e); handleErrorNoRedirect(throwable.getMessage(), throwable, req, resp); } } else { handleErrorNoRedirect(throwable.getMessage(), throwable, req, resp); } } else handleErrorNoRedirect(MOAIDMessageProvider.getInstance().getMessage("auth.26", null), null, req, resp); return; } // *** end of error handling *** // *** start of protocol specific stuff *** Object moduleObject = req.getParameter(PARAM_TARGET_MODULE); String module = null; if (moduleObject != null && (moduleObject instanceof String)) { module = (String) moduleObject; } if (module == null) { module = (String) req.getAttribute(PARAM_TARGET_MODULE); } Object actionObject = req.getParameter(PARAM_TARGET_ACTION); String action = null; if (actionObject != null && (actionObject instanceof String)) { action = (String) actionObject; } if (action == null) { action = req.getParameter(PARAM_TARGET_ACTION); } Logger.debug("dispatching to " + module + " protocol " + action); IModulInfo info = ModulStorage.getModuleByPath(module); IAction moduleAction = null; if (info == null) { Iterator modules = ModulStorage.getAllModules() .iterator(); while (modules.hasNext()) { info = modules.next(); moduleAction = info.canHandleRequest(req, resp); if (moduleAction != null) { action = moduleAction.getDefaultActionName(); module = info.getPath(); break; } info = null; } if (moduleAction == null) { resp.sendError(HttpServletResponse.SC_NOT_FOUND); Logger.error("Protocol " + module + " has no module registered"); return; } } if (moduleAction == null) { moduleAction = info.getAction(action); if (moduleAction == null) { resp.sendError(HttpServletResponse.SC_NOT_FOUND); Logger.error("Action " + action + " is not available!"); return; } } //get SSO Cookie for Request SSOManager ssomanager = SSOManager.getInstance(); String ssoId = ssomanager.getSSOSessionID(req); IRequest protocolRequest = null; String uniqueSessionIdentifier = null; try { Object idObject = req.getParameter(PARAM_TARGET_PENDINGREQUESTID); if (idObject != null && (idObject instanceof String)) { protocolRequestID = (String) idObject; protocolRequest = RequestStorage.getPendingRequest(protocolRequestID); //get IRequest if it exits if (protocolRequest != null) { Logger.debug(DispatcherServlet.class.getName()+": Found PendingRequest with ID " + protocolRequestID); } else { Logger.error("No PendingRequest with ID " + protocolRequestID + " found.!"); handleErrorNoRedirect("Während des Anmeldevorgangs ist ein Fehler aufgetreten. Bitte versuchen Sie es noch einmal.", null, req, resp); return; } } else { try { //load unique session identifier with SSO-sessionID uniqueSessionIdentifier = ssomanager.getUniqueSessionIdentifier(ssoId); if (MiscUtil.isEmpty(uniqueSessionIdentifier)) uniqueSessionIdentifier = Random.nextRandom(); TransactionIDUtils.setSessionId(uniqueSessionIdentifier); //set transactionID to Logger protocolRequestID = Random.nextRandom(); TransactionIDUtils.setTransactionId(protocolRequestID); //log information for security and process reversion MOAReversionLogger.getInstance().logEvent(MOAIDEventConstants.SESSION_CREATED, uniqueSessionIdentifier); MOAReversionLogger.getInstance().logEvent(MOAIDEventConstants.TRANSACTION_CREATED, protocolRequestID); MOAReversionLogger.getInstance().logEvent(uniqueSessionIdentifier, protocolRequestID, MOAIDEventConstants.TRANSACTION_IP, req.getRemoteAddr()); protocolRequest = info.preProcess(req, resp, action, uniqueSessionIdentifier, protocolRequestID); //request is a valid interfederation response if (protocolRequest != null && protocolRequest.getInterfederationResponse() != null ) { Logger.debug("Create new interfederated MOA-Session and add to HTTPRequest"); //reload SP protocol implementation info = ModulStorage.getModuleByPath(protocolRequest.requestedModule()); moduleAction = info.getAction(protocolRequest.requestedAction()); //create interfederated MOASession String sessionID = AuthenticationSessionStoreage.createInterfederatedSession(protocolRequest, true, ssoId); req.getParameterMap().put(MOAIDAuthConstants.PARAM_SESSIONID, new String[]{ sessionID }); Logger.info("PreProcessing of SSO interfederation response complete. "); //request is a not valid interfederation response } else if (protocolRequest != null && MiscUtil.isNotEmpty(protocolRequest.getRequestID())) { OAAuthParameter oaParams = AuthConfigurationProviderFactory.getInstance().getOnlineApplicationParameter(protocolRequest.getOAURL()); if (!oaParams.isPerformLocalAuthenticationOnInterfederationError()) { // -> send end error to service provider Logger.info("Federated authentication for entity " + protocolRequest.getOAURL() + " FAILED. Sending error message to service provider."); MOAIDException e = new MOAIDException("auth.27", new Object[]{}); IModulInfo requestedModul = ModulStorage.getModuleByPath(protocolRequest.requestedModule()); if (!requestedModul.generateErrorMessage(e, req, resp, protocolRequest)) handleErrorNoRedirect(e.getMessage(), e, req, resp); return; } else //-> Restart local authentication Logger.info("Restart authentication with stored " + protocolRequest.requestedModule() + " AuthnRequest for OnlineApplication " + protocolRequest.getOAURL()); //request is a new authentication request } else if (protocolRequest != null && MiscUtil.isEmpty(protocolRequest.getRequestID())) { //Start new Authentication protocolRequest.setModule(module); //if preProcessing has not set a specific action from decoded request // then set the default action if (MiscUtil.isEmpty(protocolRequest.requestedAction())) protocolRequest.setAction(action); else moduleAction = info.getAction(protocolRequest.requestedAction()); protocolRequest.setRequestID(protocolRequestID); protocolRequest.setSessionIdentifier(uniqueSessionIdentifier); RequestStorage.setPendingRequest(protocolRequest); Logger.debug(DispatcherServlet.class.getName()+": Create PendingRequest with ID " + protocolRequestID + "."); } else { Logger.error("Failed to generate a valid protocol request!"); resp.setContentType("text/html;charset=UTF-8"); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "NO valid protocol request received!"); return; } } catch (ProtocolNotActiveException e) { resp.getWriter().write(e.getMessage()); resp.setContentType("text/html;charset=UTF-8"); resp.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); return; } catch (AuthnRequestValidatorException e) { //log Error Message StatisticLogger logger = StatisticLogger.getInstance(); logger.logErrorOperation(e, e.getErrorRequest()); //TODO: maybe add some error message handling??? return; }catch (InvalidProtocolRequestException e) { ErrorResponseUtils utils = ErrorResponseUtils.getInstance(); String code = utils.mapInternalErrorToExternalError(e.getMessageId()); String descr = e.getMessage(); Logger.error("Protocol validation FAILED!"); resp.setContentType("text/html;charset=UTF-8"); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Protocol validation FAILED!" + "(Errorcode=" + code + " | Description=" + descr + ")"); return; } catch (ConfigurationException e) { resp.setContentType("text/html;charset=UTF-8"); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "NO valid protocol request received!" + "(Errorcode=9199" +" | Description="+ e.getMessage() + ")"); return; } catch (MOAIDException e) { Logger.error("Failed to generate a valid protocol request!"); resp.setContentType("text/html;charset=UTF-8"); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "NO valid protocol request received!" + "(Errorcode=6000" +" | Description=Das Authentifizierungsprotokoll wurde nicht erkannt oder wird nicht unterst\u00FCzt" + ")"); return; } } // *** end of protocol specific stuff *** if (protocolRequest != null) MOAReversionLogger.getInstance().logEvent(protocolRequest.getOnlineApplicationConfiguration(), protocolRequest, MOAIDEventConstants.AUTHPROTOCOL_TYPE, protocolRequest.requestedModule()); // *** start handling authentication *** AuthenticationManager authmanager = AuthenticationManager.getInstance(); String moasessionID = null; String newSSOSessionId = null; AuthenticationSession moasession = null; IAuthData authData = null; boolean needAuthentication = moduleAction.needAuthentication(protocolRequest, req, resp); if (needAuthentication) { //check if interfederation IDP is requested ssomanager.checkInterfederationIsRequested(req, resp, protocolRequest); //check SSO session if (ssoId != null) { String correspondingMOASession = ssomanager.existsOldSSOSession(ssoId); if (correspondingMOASession != null) { Logger.warn("Request sends an old SSO Session ID("+ssoId+")! " + "Invalidate the corresponding MOASession with ID="+ correspondingMOASession); MOAReversionLogger.getInstance().logEvent(protocolRequest.getOnlineApplicationConfiguration(), protocolRequest, MOAIDEventConstants.AUTHPROCESS_SSO_INVALID); AuthenticationSessionStoreage.destroySession(correspondingMOASession); ssomanager.deleteSSOSessionID(req, resp); } } //load Parameters from OnlineApplicationConfiguration OAAuthParameter oaParam = AuthConfigurationProviderFactory.getInstance() .getOnlineApplicationParameter(protocolRequest.getOAURL()); if (oaParam == null) { throw new AuthenticationException("auth.00", new Object[] { protocolRequest.getOAURL() }); } isValidSSOSession = ssomanager.isValidSSOSession(ssoId, protocolRequest); useSSOOA = oaParam.useSSO() || oaParam.isInderfederationIDP(); //if a legacy request is used SSO should not be allowed, actually boolean isUseMandateRequested = LegacyHelper.isUseMandateRequested(req); if (protocolRequest.isPassiv() && protocolRequest.forceAuth()) { // conflict! throw new NoPassivAuthenticationException(); } boolean tryperform = authmanager.tryPerformAuthentication( req, resp); if (tryperform) MOAReversionLogger.getInstance().logEvent(protocolRequest.getOnlineApplicationConfiguration(), protocolRequest, MOAIDEventConstants.AUTHPROCESS_FINISHED); else MOAReversionLogger.getInstance().logEvent(protocolRequest.getOnlineApplicationConfiguration(), protocolRequest, MOAIDEventConstants.AUTHPROCESS_SERVICEPROVIDER, protocolRequest.getOAURL()); if (protocolRequest.forceAuth()) { if (!tryperform) { authmanager.doAuthentication(req, resp, protocolRequest); return; } } else if (protocolRequest.isPassiv()) { if (tryperform || (isValidSSOSession && useSSOOA && !isUseMandateRequested) ) { // Passive authentication ok! } else { throw new NoPassivAuthenticationException(); } } else { if (tryperform || (isValidSSOSession && useSSOOA && !isUseMandateRequested) ) { // Is authenticated .. proceed } else { // Start authentication! authmanager.doAuthentication(req, resp, protocolRequest); return; } } if ((useSSOOA || isValidSSOSession)) //TODO: SSO with mandates requires an OVS extension { if (useSSOOA && isValidSSOSession) { MOAReversionLogger.getInstance().logEvent(protocolRequest.getOnlineApplicationConfiguration(), protocolRequest, MOAIDEventConstants.AUTHPROCESS_SSO); moasessionID = ssomanager.getMOASession(ssoId); moasession = AuthenticationSessionStoreage.getSession(moasessionID); //use new OAParameter if (oaParam.useSSOQuestion() && !AuthenticationSessionStoreage.isAuthenticated(moasessionID)) { authmanager.sendTransmitAssertionQuestion(req, resp, protocolRequest, oaParam); return; } } else { moasessionID = (String) req.getParameter(MOAIDAuthConstants.PARAM_SESSIONID); moasession = AuthenticationSessionStoreage.getSession(moasessionID); } //save SSO session usage in Database if (useSSOOA) { newSSOSessionId = ssomanager.createSSOSessionInformations(moasessionID, protocolRequest.getOAURL()); if (MiscUtil.isNotEmpty(newSSOSessionId)) { ssomanager.setSSOSessionID(req, resp, newSSOSessionId); } else { ssomanager.deleteSSOSessionID(req, resp); } } } else { moasessionID = (String) req.getParameter(MOAIDAuthConstants.PARAM_SESSIONID); moasession = AuthenticationSessionStoreage.getSession(moasessionID); moasessionID = AuthenticationSessionStoreage.changeSessionID(moasession); } //build authenticationdata from session information and OA configuration authData = AuthenticationDataBuilder.buildAuthenticationData(protocolRequest, moasession); } // *** end handling authentication *** // *** start finalizing authentication (SSO, final redirects, statistic logging etc) *** SLOInformationInterface assertionID = moduleAction.processRequest(protocolRequest, req, resp, authData); RequestStorage.removePendingRequest(protocolRequestID); if (needAuthentication) { boolean isSSOSession = MiscUtil.isNotEmpty(newSSOSessionId) && useSSOOA; if ((useSSOOA || isSSOSession) //TODO: SSO with mandates requires an OVS extension && !moasession.getUseMandate()) { try { //Store OA specific SSO session information AuthenticationSessionStoreage.addSSOInformation(moasessionID, newSSOSessionId, assertionID, protocolRequest); } catch (AuthenticationException e) { Logger.warn("SSO Session information can not be stored -> SSO is not enabled!"); authmanager.performOnlyIDPLogOut(req, resp, moasessionID); isSSOSession = false; } } else { authmanager.performOnlyIDPLogOut(req, resp, moasessionID); } //Advanced statistic logging StatisticLogger logger = StatisticLogger.getInstance(); logger.logSuccessOperation(protocolRequest, authData, isSSOSession); } // *** end finalizing authentication *** } catch (Throwable e) { Logger.warn("An authentication error occured: ", e);; // Try handle module specific, if not possible rethrow if (!info.generateErrorMessage(e, req, resp, protocolRequest)) handleErrorNoRedirect(e.getMessage(), e, req, resp); } //log transaction_destroy to reversionslog MOAReversionLogger.getInstance().logEvent(MOAIDEventConstants.TRANSACTION_DESTROYED, protocolRequestID); } catch (WrongParametersException ex) { handleWrongParameters(ex, req, resp); } catch (MOAIDException ex) { handleError(null, ex, req, resp, protocolRequestID); } catch (Throwable e) { handleErrorNoRedirect(e.getMessage(), e, req, resp); } finally { TransactionIDUtils.removeTransactionId(); TransactionIDUtils.removeSessionId(); } Logger.debug("Clossing Dispatcher processing loop"); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { processRequest(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { processRequest(req, resp); } }