--- /dev/null
+/**\r
+ * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.\r
+ *\r
+ * This library is free software; you can redistribute it and/or modify it under\r
+ * the terms of the GNU Lesser General Public License as published by the Free\r
+ * Software Foundation; either version 2.1 of the License, or (at your option)\r
+ * any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful, but WITHOUT\r
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\r
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\r
+ * details.\r
+ */\r
+\r
+package com.liferay.portal.servlet.filters.secure;\r
+\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+\r
+import javax.servlet.FilterChain;\r
+import javax.servlet.FilterConfig;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+import javax.servlet.http.HttpSession;\r
+\r
+import com.liferay.portal.kernel.log.Log;\r
+import com.liferay.portal.kernel.log.LogFactoryUtil;\r
+import com.liferay.portal.kernel.servlet.HttpHeaders;\r
+import com.liferay.portal.kernel.servlet.ProtectedServletRequest;\r
+import com.liferay.portal.kernel.util.GetterUtil;\r
+import com.liferay.portal.kernel.util.Http;\r
+import com.liferay.portal.kernel.util.HttpUtil;\r
+import com.liferay.portal.kernel.util.StringBundler;\r
+import com.liferay.portal.kernel.util.StringPool;\r
+import com.liferay.portal.kernel.util.StringUtil;\r
+import com.liferay.portal.kernel.util.Validator;\r
+import com.liferay.portal.model.Group;\r
+import com.liferay.portal.model.User;\r
+import com.liferay.portal.security.auth.PrincipalThreadLocal;\r
+import com.liferay.portal.security.permission.PermissionChecker;\r
+import com.liferay.portal.security.permission.PermissionCheckerFactoryUtil;\r
+import com.liferay.portal.security.permission.PermissionThreadLocal;\r
+import com.liferay.portal.service.GroupLocalServiceUtil;\r
+import com.liferay.portal.service.UserLocalServiceUtil;\r
+import com.liferay.portal.servlet.filters.BasePortalFilter;\r
+import com.liferay.portal.util.Portal;\r
+import com.liferay.portal.util.PortalInstances;\r
+import com.liferay.portal.util.PortalUtil;\r
+import com.liferay.portal.util.PropsUtil;\r
+import com.liferay.portal.util.PropsValues;\r
+import com.liferay.portal.util.WebKeys;\r
+\r
+/**\r
+ * @author Brian Wing Shun Chan\r
+ * @author Raymond Augé\r
+ * @author Alexander Chow\r
+ */\r
+public class SecureFilter extends BasePortalFilter {\r
+\r
+ @Override\r
+ public void init(FilterConfig filterConfig) {\r
+ super.init(filterConfig);\r
+\r
+ _basicAuthEnabled = GetterUtil.getBoolean(\r
+ filterConfig.getInitParameter("basic_auth"));\r
+ _digestAuthEnabled = GetterUtil.getBoolean(\r
+ filterConfig.getInitParameter("digest_auth"));\r
+ _usePermissionChecker = GetterUtil.getBoolean(\r
+ filterConfig.getInitParameter("use_permission_checker"));\r
+\r
+ String propertyPrefix = filterConfig.getInitParameter(\r
+ "portal_property_prefix");\r
+\r
+ String[] hostsAllowedArray = null;\r
+\r
+ if (Validator.isNull(propertyPrefix)) {\r
+ hostsAllowedArray = StringUtil.split(\r
+ filterConfig.getInitParameter("hosts.allowed"));\r
+ _httpsRequired = GetterUtil.getBoolean(\r
+ filterConfig.getInitParameter("https.required"));\r
+ }\r
+ else {\r
+ hostsAllowedArray = PropsUtil.getArray(\r
+ propertyPrefix + "hosts.allowed");\r
+ _httpsRequired = GetterUtil.getBoolean(\r
+ PropsUtil.get(propertyPrefix + "https.required"));\r
+ }\r
+\r
+ for (int i = 0; i < hostsAllowedArray.length; i++) {\r
+ _hostsAllowed.add(hostsAllowedArray[i]);\r
+ }\r
+ }\r
+\r
+ protected HttpServletRequest basicAuth(\r
+ HttpServletRequest request, HttpServletResponse response)\r
+ throws Exception {\r
+\r
+ HttpSession session = request.getSession();\r
+\r
+ session.setAttribute(WebKeys.BASIC_AUTH_ENABLED, Boolean.TRUE);\r
+\r
+ long userId = GetterUtil.getLong(\r
+ (String)session.getAttribute(_AUTHENTICATED_USER));\r
+\r
+ if (userId > 0) {\r
+ request = new ProtectedServletRequest(request, String.valueOf(userId), HttpServletRequest.BASIC_AUTH);\r
+ }\r
+ else {\r
+ try {\r
+ userId = PortalUtil.getBasicAuthUserId(request);\r
+ }\r
+ catch (Exception e) {\r
+ _log.error(e, e);\r
+ }\r
+\r
+ if (userId > 0) {\r
+ request = setCredentials(\r
+ request, session, userId, HttpServletRequest.BASIC_AUTH);\r
+ request = checkWebdavAccessPermission(request, response, userId);\r
+ }\r
+ else {\r
+ response.setHeader(HttpHeaders.WWW_AUTHENTICATE, _BASIC_REALM);\r
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\r
+\r
+ return null;\r
+ }\r
+ }\r
+\r
+ return request;\r
+ }\r
+\r
+ protected HttpServletRequest digestAuth(\r
+ HttpServletRequest request, HttpServletResponse response)\r
+ throws Exception {\r
+\r
+ HttpSession session = request.getSession();\r
+\r
+ long userId = GetterUtil.getLong(\r
+ (String)session.getAttribute(_AUTHENTICATED_USER));\r
+\r
+ if (userId > 0) {\r
+ request = new ProtectedServletRequest(\r
+ request, String.valueOf(userId),\r
+ HttpServletRequest.DIGEST_AUTH);\r
+ }\r
+ else {\r
+ try {\r
+ userId = PortalUtil.getDigestAuthUserId(request);\r
+ }\r
+ catch (Exception e) {\r
+ _log.error(e, e);\r
+ }\r
+\r
+ if (userId > 0) {\r
+ request = setCredentials(\r
+ request, session, userId, HttpServletRequest.DIGEST_AUTH);\r
+ request = checkWebdavAccessPermission(request, response, userId);\r
+ }\r
+ else {\r
+\r
+ // Must generate a new nonce for each 401 (RFC2617, 3.2.1)\r
+\r
+ long companyId = PortalInstances.getCompanyId(request);\r
+\r
+ String remoteAddress = request.getRemoteAddr();\r
+\r
+ String nonce = NonceUtil.generate(companyId, remoteAddress);\r
+\r
+ StringBundler sb = new StringBundler(4);\r
+\r
+ sb.append(_DIGEST_REALM);\r
+ sb.append(", nonce=\"");\r
+ sb.append(nonce);\r
+ sb.append("\"");\r
+\r
+ response.setHeader(HttpHeaders.WWW_AUTHENTICATE, sb.toString());\r
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\r
+\r
+ return null;\r
+ }\r
+ }\r
+\r
+ return request;\r
+ }\r
+\r
+ protected boolean isAccessAllowed(HttpServletRequest request) {\r
+ if (_hostsAllowed.isEmpty()) {\r
+ return true;\r
+ }\r
+\r
+ String remoteAddr = request.getRemoteAddr();\r
+\r
+ if (_hostsAllowed.contains(remoteAddr)) {\r
+ return true;\r
+ }\r
+\r
+ String computerAddress = PortalUtil.getComputerAddress();\r
+\r
+ if (computerAddress.equals(remoteAddr) &&\r
+ _hostsAllowed.contains(_SERVER_IP)) {\r
+\r
+ return true;\r
+ }\r
+\r
+ return false;\r
+ }\r
+\r
+ @Override\r
+ protected void processFilter(\r
+ HttpServletRequest request, HttpServletResponse response,\r
+ FilterChain filterChain)\r
+ throws Exception {\r
+\r
+ String remoteAddr = request.getRemoteAddr();\r
+\r
+ if (isAccessAllowed(request)) {\r
+ if (_log.isDebugEnabled()) {\r
+ _log.debug("Access allowed for " + remoteAddr);\r
+ }\r
+ }\r
+ else {\r
+ if (_log.isWarnEnabled()) {\r
+ _log.warn("Access denied for " + remoteAddr);\r
+ }\r
+\r
+ response.sendError(\r
+ HttpServletResponse.SC_FORBIDDEN,\r
+ "Access denied for " + remoteAddr);\r
+\r
+ return;\r
+ }\r
+\r
+ if (_log.isDebugEnabled()) {\r
+ if (_httpsRequired) {\r
+ _log.debug("https is required");\r
+ }\r
+ else {\r
+ _log.debug("https is not required");\r
+ }\r
+ }\r
+\r
+ if (_httpsRequired && !request.isSecure()) {\r
+ if (_log.isDebugEnabled()) {\r
+ String completeURL = HttpUtil.getCompleteURL(request);\r
+\r
+ _log.debug("Securing " + completeURL);\r
+ }\r
+\r
+ StringBundler redirectURL = new StringBundler(5);\r
+\r
+ redirectURL.append(Http.HTTPS_WITH_SLASH);\r
+ redirectURL.append(request.getServerName());\r
+ redirectURL.append(request.getServletPath());\r
+\r
+ String queryString = request.getQueryString();\r
+\r
+ if (Validator.isNotNull(queryString)) {\r
+ redirectURL.append(StringPool.QUESTION);\r
+ redirectURL.append(request.getQueryString());\r
+ }\r
+\r
+ if (_log.isDebugEnabled()) {\r
+ _log.debug("Redirect to " + redirectURL);\r
+ }\r
+\r
+ response.sendRedirect(redirectURL.toString());\r
+ }\r
+ else {\r
+ if (_log.isDebugEnabled()) {\r
+ String completeURL = HttpUtil.getCompleteURL(request);\r
+\r
+ _log.debug("Not securing " + completeURL);\r
+ }\r
+\r
+ // This authentication should only be run if specified by web.xml\r
+ // and JAAS is disabled. Make sure to run this once per session and\r
+ // wrap the request if necessary.\r
+\r
+ if (!PropsValues.PORTAL_JAAS_ENABLE) {\r
+ User user = PortalUtil.getUser(request);\r
+\r
+ if ((user != null) && !user.isDefaultUser()) {\r
+ request = setCredentials(\r
+ request, request.getSession(), user.getUserId(), null);\r
+ }\r
+ else {\r
+ if (_digestAuthEnabled) {\r
+ request = digestAuth(request, response);\r
+ }\r
+ else if (_basicAuthEnabled) {\r
+ request = basicAuth(request, response);\r
+ }\r
+ }\r
+ }\r
+\r
+ if (request != null) {\r
+ processFilter(getClass(), request, response, filterChain);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Check that the authenticated user is the one contained in the WebDAV URL in case of personnal storage access\r
+ * @param request\r
+ * @param response\r
+ * @param userId\r
+ * @return\r
+ */\r
+ protected HttpServletRequest checkWebdavAccessPermission(HttpServletRequest request, HttpServletResponse response, long userId) {\r
+ \r
+ String completeURL = HttpUtil.getCompleteURL(request);\r
+ \r
+ if (completeURL.contains("api/secure/webdav")) {\r
+ \r
+ User user = null;\r
+ \r
+ try {\r
+ user = UserLocalServiceUtil.getUser(userId);\r
+ \r
+ } catch (Exception e) {\r
+ _log.error("Could not find user attached to webdav request", e);\r
+ response.setHeader(HttpHeaders.WWW_AUTHENTICATE, _BASIC_REALM);\r
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\r
+ return null;\r
+ }\r
+ \r
+ // The WebDAV URL has the following format:\r
+ // <host>/api/secure/webdav/<user screenName or group friendly URL>/document_library/...\r
+ \r
+ String[] urlTab = completeURL.split("/");\r
+ \r
+ if (urlTab != null && urlTab.length > 0) {\r
+ \r
+ // Extract the user screen name or group friendlyUrl from the webdav URL\r
+ String urlPart = "";\r
+ int idx = 0;\r
+ while (!urlPart.equals("webdav")) {\r
+ idx++;\r
+ urlPart = urlTab[idx];\r
+ }\r
+ String userOrGroupName = urlTab[idx + 1];\r
+ \r
+ // Determine if the userOrGroupName is either a screenName or a group friendlyURL\r
+ User urlUser = null;\r
+ try {\r
+ urlUser = UserLocalServiceUtil.getUserByScreenName(user.getCompanyId(), userOrGroupName);\r
+ \r
+ // This is a personal storage area\r
+ // We must ensure that the screenname contained in the webdav URL is the screenname of the authenticated user\r
+ if (urlUser != null && urlUser.getUserId() != user.getUserId()) {\r
+ _log.info("Do not authorize webdav connection to url "+completeURL+" for user with screen name "+urlUser.getScreenName());\r
+ response.setHeader(HttpHeaders.WWW_AUTHENTICATE, _BASIC_REALM);\r
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);\r
+ return null;\r
+ }\r
+ \r
+ } catch (Exception e) {\r
+ // This is not a user but a group\r
+ }\r
+\r
+ }\r
+ }\r
+\r
+ return request;\r
+ }\r
+\r
+ \r
+ protected HttpServletRequest setCredentials(\r
+ HttpServletRequest request, HttpSession session, long userId,\r
+ String authType)\r
+ throws Exception {\r
+\r
+ User user = UserLocalServiceUtil.getUser(userId);\r
+\r
+ String userIdString = String.valueOf(userId);\r
+\r
+ request = new ProtectedServletRequest(request, userIdString, authType);\r
+\r
+ session.setAttribute(WebKeys.USER, user);\r
+ session.setAttribute(_AUTHENTICATED_USER, userIdString);\r
+\r
+ if (_usePermissionChecker) {\r
+ PrincipalThreadLocal.setName(userId);\r
+ PrincipalThreadLocal.setPassword(\r
+ PortalUtil.getUserPassword(request));\r
+\r
+ PermissionChecker permissionChecker =\r
+ PermissionCheckerFactoryUtil.create(user);\r
+\r
+ PermissionThreadLocal.setPermissionChecker(permissionChecker);\r
+ }\r
+\r
+ return request;\r
+ }\r
+\r
+ protected void setUsePermissionChecker(boolean usePermissionChecker) {\r
+ _usePermissionChecker = usePermissionChecker;\r
+ }\r
+\r
+ private static final String _AUTHENTICATED_USER =\r
+ SecureFilter.class + "_AUTHENTICATED_USER";\r
+\r
+ private static final String _BASIC_REALM =\r
+ "Basic realm=\"" + Portal.PORTAL_REALM + "\"";\r
+\r
+ private static final String _DIGEST_REALM =\r
+ "Digest realm=\"" + Portal.PORTAL_REALM + "\"";\r
+\r
+ private static final String _SERVER_IP = "SERVER_IP";\r
+\r
+ private static Log _log = LogFactoryUtil.getLog(SecureFilter.class);\r
+\r
+ private boolean _basicAuthEnabled;\r
+ private boolean _digestAuthEnabled;\r
+ private Set<String> _hostsAllowed = new HashSet<String>();\r
+ private boolean _httpsRequired;\r
+ private boolean _usePermissionChecker;\r
+\r
+}
\ No newline at end of file