/******************************************************************************* * Copyright (c) 2010-2014 SAP AG and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * SAP AG - initial API and implementation *******************************************************************************/ package org.eclipse.skalli.view.internal.filter; import java.io.IOException; import java.security.AccessControlException; import java.text.MessageFormat; import java.util.UUID; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; import org.eclipse.skalli.commons.Statistics; import org.eclipse.skalli.commons.UUIDUtils; import org.eclipse.skalli.model.Project; import org.eclipse.skalli.model.User; import org.eclipse.skalli.services.Services; import org.eclipse.skalli.services.entity.EntityServices; import org.eclipse.skalli.services.group.GroupUtils; import org.eclipse.skalli.services.permit.Permit; import org.eclipse.skalli.services.permit.PermitService; import org.eclipse.skalli.services.permit.Permits; import org.eclipse.skalli.services.project.ProjectService; import org.eclipse.skalli.services.project.ProjectUtils; import org.eclipse.skalli.services.user.UserServices; import org.eclipse.skalli.view.Consts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This filter determines the user and requested project from the servlet request * and performs a login of the user with the {@link PermitService permit service}. * Furthermore, the filter is the basis for {@link Statistics statistics} tracking. * * This filter sets the following boolean attributes: * <ul> * <li>{@link Consts#ATTRIBUTE_ANONYMOUS_USER} - <code>true</code>, if the user is not the anonymous * user, <code>false</code> otherwise.</li> * <li>{@link Consts#ATTRIBUTE_PROJECTADMIN} - <code>true</code>, if the user is not anonymous, the * request specified a project and the user is an administrator of this project, i.e. has the permit * <tt>PUT /projects/<projectId> ALLOW</tt>, <code>false</code> otherwise.</li> * <li>{@link Consts#ATTRIBUTE_PARENTPROJECTADMIN} - <code>true</code>, if the user is administrator * of one of the projects in the parent chain of the requested project, <code>false</code> otherwise.</li> * </ul> * <p> * This filter sets the following attributes if user is not anonymous: * <ul> * <li>{@link Consts#ATTRIBUTE_USERID} - the unique identifier of the logged in user.</li> * <li>{@link Consts#ATTRIBUTE_USER} - the {@link User} instance of the logged in user; undefined if the user * service in charge does not know the logged in user.</li> * </ul> * <p> * This filter sets the following attributes if the request specifies a project: * <ul> * <li>{@link Consts#ATTRIBUTE_PROJECT} - the {@link Project} instance.</li> * <li>{@link Consts#ATTRIBUTE_PROJECTID} - the {@link Project#getName() symbolic identifier} of the project.</li> * <li>{@link Consts#ATTRIBUTE_PROJECTUUID} - the {@link Project#getUuid() unique identifier} of the project.</li> * </ul> * Otherwise the filter retrieves the {@link HttpServletRequest#getPathInfo() path info} from the request and * sets the attribute {@link Consts#ATTRIBUTE_WINDOWNAME}. * <p> * For convenience the filter sets the following attributes that are derived from the request URL: * <ul> * <li>{@link Consts#ATTRIBUTE_WEBLOCATOR} - <tt>schema://host:port</tt></li> * <li>{@link Consts#ATTRIBUTE_BASE_URL} - <tt>schema://host:port/contextPath</tt></li> * <li>{@link Consts#ATTRIBUTE_SERVLET_URL} - <tt>schema://host:port/contextPath/servletPath</tt></li> * </ul> */ public class LoginFilter implements Filter { private static final Logger LOG = LoggerFactory.getLogger(LoginFilter.class); private boolean rejectAnonymousUsers; @Override public void init(FilterConfig config) throws ServletException { this.rejectAnonymousUsers = BooleanUtils.toBoolean( config.getInitParameter("rejectAnonymousUsers")); //$NON-NLS-1$ } @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { long timeBeginnProcessing = System.currentTimeMillis(); HttpServletRequest httpRequest = (HttpServletRequest) request; String pathInfo = httpRequest.getPathInfo(); String requestURL = httpRequest.getRequestURL().toString(); // servletUrl = schema://host:port/contextPath/servletPath String servletURL = StringUtils.removeEnd(requestURL, pathInfo); request.setAttribute(Consts.ATTRIBUTE_SERVLET_URL, servletURL); // baseUrl = schema://host:port/contextPath String baseURL = StringUtils.removeEnd(servletURL, httpRequest.getServletPath()); request.setAttribute(Consts.ATTRIBUTE_BASE_URL, baseURL); // webLocator = schema://host:port String webLocator = StringUtils.removeEnd(requestURL, httpRequest.getRequestURI()); request.setAttribute(Consts.ATTRIBUTE_WEBLOCATOR, webLocator); String paramProjectId = request.getParameter(Consts.PARAM_ID); // determine the project from the URL Project project = null; ProjectService projectService = ((ProjectService)EntityServices.getByEntityClass(Project.class)); // first check if project can be deduced from pathInfo if (StringUtils.isNotBlank(pathInfo)) { if (pathInfo.startsWith(FilterUtil.PATH_SEPARATOR)) { pathInfo = pathInfo.replaceFirst(FilterUtil.PATH_SEPARATOR, StringUtils.EMPTY); } if (pathInfo.contains(FilterUtil.PATH_SEPARATOR)) { pathInfo = pathInfo.substring(0, pathInfo.indexOf(FilterUtil.PATH_SEPARATOR)); } project = projectService.getProjectByProjectId(pathInfo); // project not found by name, search by UUID if (project == null && UUIDUtils.isUUID(pathInfo)) { UUID uuid = UUIDUtils.asUUID(pathInfo); project = projectService.getByUUID(uuid); // project not found by UUID, search for deleted project by UUID if (project == null) { project = projectService.getDeletedProject(uuid); } } if (project == null) { request.setAttribute(Consts.ATTRIBUTE_WINDOWNAME, httpRequest.getPathInfo()); } } // project not found by pathInfo, check if project is provided via URL parameter if (project == null && StringUtils.isNotBlank(paramProjectId)) { project = projectService.getProjectByProjectId(paramProjectId); if (project == null) { // currently we don't support a scenario where projects are passed via UUID FilterUtil.handleException(request, response, new FilterException(String.format("Invalid project identifier '%s' specified in query '%s'", paramProjectId, Consts.PARAM_ID))); return; } } if (project != null) { request.setAttribute(Consts.ATTRIBUTE_PROJECT, project); request.setAttribute(Consts.ATTRIBUTE_PROJECTID, project.getProjectId()); request.setAttribute(Consts.ATTRIBUTE_PROJECTUUID, project.getUuid().toString()); } else { // do nothing if project is null since this filter runs during // creation of projects and displaying of search results, too } // login and ensure that the user is allowed to access PermitService permitService = Services.getRequiredService(PermitService.class); String userId = permitService.login(httpRequest, project); User user = null; boolean isAnonymousUser = StringUtils.isBlank(userId); if (isAnonymousUser && rejectAnonymousUsers) { FilterUtil.handleACException(httpRequest, response, new AccessControlException("Forbidden for anonymous users")); return; } if (!isAnonymousUser) { request.setAttribute(Consts.ATTRIBUTE_USERID, userId); String userDisplayName = userId; user = UserServices.getUser(userId); if (user != null) { userDisplayName = user.getDisplayName(); request.setAttribute(Consts.ATTRIBUTE_USER, user); } request.setAttribute(Consts.ATTRIBUTE_USER_DISPLAY_NAME, userDisplayName); } boolean isProjectAdmin = !isAnonymousUser && project != null && (GroupUtils.isAdministrator(userId) || Permits.isAllowed(Permit.ACTION_PUT, project)); boolean isProjectAdminInParentChain = !isAnonymousUser && project != null && ProjectUtils.isProjectAdminInParentChain(userId, project); request.setAttribute(Consts.ATTRIBUTE_ANONYMOUS_USER, isAnonymousUser); request.setAttribute(Consts.ATTRIBUTE_PROJECTADMIN, isProjectAdmin); request.setAttribute(Consts.ATTRIBUTE_PARENTPROJECTADMIN, isProjectAdminInParentChain); // track the access Statistics statistics = Statistics.getDefault(); if (user != null) { statistics.trackUser(userId, user.getDepartment(), user.getLocation()); } else if (StringUtils.isNotBlank(userId)) { statistics.trackUser(userId, null, null); } String referer = httpRequest.getHeader("Referer"); //$NON-NLS-1$ if (StringUtils.isBlank(referer)) { referer = request.getParameter("referer"); //$NON-NLS-1$ } if (StringUtils.isNotBlank(referer)) { statistics.trackReferer(userId, referer); } String requestLine = MessageFormat.format("{0} {1}", //$NON-NLS-1$ httpRequest.getMethod(), httpRequest.getRequestURI()); if (project != null) { requestLine = MessageFormat.format("{0} /projects/{1}", //$NON-NLS-1$ httpRequest.getMethod(), project.getProjectId()); } statistics.trackUsage(userId, requestLine, referer); String browser = httpRequest.getHeader("User-Agent"); //$NON-NLS-1$ if (StringUtils.isNotBlank(browser)) { statistics.trackBrowser(userId, browser); } // proceed along the chain chain.doFilter(request, response); // track the overall response time long responseTime = System.currentTimeMillis() - timeBeginnProcessing; statistics.trackResponseTime(userId, requestLine, responseTime); LOG.info(MessageFormat.format("{0}: responseTime={1} milliseconds)", requestLine, Long.toString(responseTime))); } }