/* * Password Management Servlets (PWM) * http://www.pwm-project.org * * Copyright (c) 2006-2009 Novell, Inc. * Copyright (c) 2009-2017 The PWM Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package password.pwm.http.servlet; import password.pwm.AppProperty; import password.pwm.PwmApplication; import password.pwm.PwmConstants; import password.pwm.config.PwmSetting; import password.pwm.error.PwmUnrecoverableException; import password.pwm.http.HttpMethod; import password.pwm.http.JspUrl; import password.pwm.http.ProcessStatus; import password.pwm.http.PwmRequest; import password.pwm.http.PwmRequestAttribute; import password.pwm.http.PwmSession; import password.pwm.http.PwmURL; import password.pwm.util.logging.PwmLogger; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import java.io.IOException; import java.net.URI; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; @WebServlet( name="LogoutServlet", urlPatterns = { PwmConstants.URL_PREFIX_PUBLIC + "/logout", PwmConstants.URL_PREFIX_PRIVATE+ "/logout", PwmConstants.URL_PREFIX_PUBLIC + "/Logout", PwmConstants.URL_PREFIX_PRIVATE+ "/Logout", } ) public class LogoutServlet extends ControlledPwmServlet { private static final PwmLogger LOGGER = PwmLogger.forClass(LogoutServlet.class); private static final String PARAM_URL = "url"; private static final String PARAM_IDLE = "idle"; private static final String PARAM_PASSWORD_MODIFIED = "passwordModified"; private static final String PARAM_PUBLIC_ONLY = "publicOnly"; private enum LogoutAction implements ControlledPwmServlet.ProcessAction { showLogout, showTimeout, ; @Override public Collection<HttpMethod> permittedMethods() { return Collections.singletonList(HttpMethod.GET); } } @Override public Class<? extends ProcessAction> getProcessActionsClass() { return LogoutAction.class; } @ActionHandler(action = "showLogout") public ProcessStatus processShowLogout( final PwmRequest pwmRequest ) throws ServletException, PwmUnrecoverableException, IOException { final Optional<String> nextUrl = readAndValidateNextUrlParameter(pwmRequest); if (nextUrl.isPresent()) { pwmRequest.setAttribute(PwmRequestAttribute.NextUrl, nextUrl.get()); } pwmRequest.forwardToJsp(JspUrl.LOGOUT); return ProcessStatus.Halt; } @ActionHandler(action = "showTimeout") public ProcessStatus processShowTimeout( final PwmRequest pwmRequest ) throws ServletException, PwmUnrecoverableException, IOException { pwmRequest.forwardToJsp(JspUrl.LOGOUT_PUBLIC); return ProcessStatus.Halt; } public void nextStep( final PwmRequest pwmRequest ) throws PwmUnrecoverableException, IOException { final String nextUrl; if (!pwmRequest.getPwmSession().getSessionStateBean().isPasswordModified()) { nextUrl = readAndValidateNextUrlParameter(pwmRequest).orElse(null); } else { nextUrl = null; } // no process action so this is the first time through this method; final boolean authenticated = pwmRequest.isAuthenticated(); final boolean logoutDueToIdle = Boolean.parseBoolean(pwmRequest.readParameterAsString(PARAM_IDLE)); String debugMsg = "processing " + (authenticated ? "authenticated": "unauthenticated") + " logout request"; if (logoutDueToIdle) { debugMsg += " due to client idle timeout"; } LOGGER.debug(pwmRequest, debugMsg); final PwmSession pwmSession = pwmRequest.getPwmSession(); final PwmApplication pwmApplication = pwmRequest.getPwmApplication(); pwmSession.unauthenticateUser(pwmRequest); { //if there is a session url, then use that to do a redirect. final String sessionLogoutURL = pwmSession.getSessionStateBean().getLogoutURL(); if (sessionLogoutURL != null && sessionLogoutURL.length() > 0) { LOGGER.trace(pwmSession, "redirecting user to session parameter set logout url: " + sessionLogoutURL ); pwmRequest.sendRedirect(sessionLogoutURL); pwmRequest.invalidateSession(); return; } } { // if the logout url hasn't been set then try seeing if one has been configured. final String configuredLogoutURL = pwmApplication.getConfig().readSettingAsString(PwmSetting.URL_LOGOUT); if (configuredLogoutURL != null && configuredLogoutURL.length() > 0) { // construct params final Map<String,String> logoutUrlParameters = new LinkedHashMap<>(); logoutUrlParameters.put(PARAM_IDLE, String.valueOf(logoutDueToIdle)); logoutUrlParameters.put(PARAM_PASSWORD_MODIFIED, String.valueOf(pwmSession.getSessionStateBean().isPasswordModified())); logoutUrlParameters.put(PARAM_PUBLIC_ONLY, String.valueOf(!pwmSession.getSessionStateBean().isPrivateUrlAccessed())); { final String sessionForwardURL = pwmSession.getSessionStateBean().getForwardURL(); if (sessionForwardURL != null && sessionForwardURL.length() > 0) { logoutUrlParameters.put( pwmApplication.getConfig().readAppProperty(AppProperty.HTTP_PARAM_NAME_FORWARD_URL), sessionForwardURL ); } } final String logoutURL = PwmURL.appendAndEncodeUrlParameters(configuredLogoutURL, logoutUrlParameters); LOGGER.trace(pwmSession, "redirecting user to configured logout url:" + logoutURL); pwmRequest.sendRedirect(logoutURL); pwmRequest.invalidateSession(); return; } } // if we didn't go anywhere yet, then show the pwm logout jsp final LogoutAction nextAction = authenticated ? LogoutAction.showLogout : LogoutAction.showTimeout; final Map<String,String> logoutUrlParameters = new LinkedHashMap<>(); logoutUrlParameters.put(PwmConstants.PARAM_ACTION_REQUEST, nextAction.toString()); if (nextUrl != null) { logoutUrlParameters.put(PARAM_URL, nextUrl); } final String logoutURL = PwmURL.appendAndEncodeUrlParameters( pwmRequest.getContextPath() + PwmServletDefinition.Logout.servletUrl(), logoutUrlParameters ); pwmRequest.invalidateSession(); pwmRequest.sendRedirect(logoutURL); } private static Optional<String> readAndValidateNextUrlParameter( final PwmRequest pwmRequest ) { try { if (!pwmRequest.hasParameter(PARAM_URL)) { return Optional.empty(); } if (pwmRequest.getPwmSession().getSessionStateBean().isPasswordModified()) { return Optional.empty(); } final String urlParameter = pwmRequest.readParameterAsString(PARAM_URL); final URI uri = URI.create(urlParameter); String path = uri.getPath(); if (path != null && path.startsWith(pwmRequest.getContextPath())) { path = path.substring(pwmRequest.getContextPath().length(), path.length()); } PwmServletDefinition matchedServlet = null; outerLoop: for (final PwmServletDefinition pwmServletDefinition : PwmServletDefinition.values()) { for (final String urlPattern : pwmServletDefinition.urlPatterns()) { if (urlPattern.equals(path)) { matchedServlet = pwmServletDefinition; break outerLoop; } } } if (matchedServlet == PwmServletDefinition.Logout) { matchedServlet = null; } if (matchedServlet != null) { LOGGER.trace(pwmRequest, "matched next url to servlet definition " + matchedServlet.toString()); return Optional.of(pwmRequest.getContextPath() + matchedServlet.servletUrl()); } else { LOGGER.trace(pwmRequest, "unable to match next url parameter to servlet definition"); } } catch(Exception e) { LOGGER.debug("error parsing client specified url parameter: " + e.getMessage()); } return Optional.empty(); } @Override public ProcessStatus preProcessCheck(final PwmRequest pwmRequest) { // no checks required, this is a public module. return ProcessStatus.Continue; } }