001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.servlet.filters.secure;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.servlet.HttpHeaders;
020    import com.liferay.portal.kernel.servlet.ProtectedServletRequest;
021    import com.liferay.portal.kernel.util.GetterUtil;
022    import com.liferay.portal.kernel.util.Http;
023    import com.liferay.portal.kernel.util.HttpUtil;
024    import com.liferay.portal.kernel.util.StringBundler;
025    import com.liferay.portal.kernel.util.StringPool;
026    import com.liferay.portal.kernel.util.StringUtil;
027    import com.liferay.portal.kernel.util.Validator;
028    import com.liferay.portal.model.User;
029    import com.liferay.portal.security.auth.AuthSettingsUtil;
030    import com.liferay.portal.security.auth.CompanyThreadLocal;
031    import com.liferay.portal.security.auth.PrincipalThreadLocal;
032    import com.liferay.portal.security.permission.PermissionChecker;
033    import com.liferay.portal.security.permission.PermissionCheckerFactoryUtil;
034    import com.liferay.portal.security.permission.PermissionThreadLocal;
035    import com.liferay.portal.service.UserLocalServiceUtil;
036    import com.liferay.portal.servlet.filters.BasePortalFilter;
037    import com.liferay.portal.util.Portal;
038    import com.liferay.portal.util.PortalInstances;
039    import com.liferay.portal.util.PortalUtil;
040    import com.liferay.portal.util.PropsUtil;
041    import com.liferay.portal.util.PropsValues;
042    import com.liferay.portal.util.WebKeys;
043    
044    import java.util.HashSet;
045    import java.util.Set;
046    
047    import javax.servlet.FilterChain;
048    import javax.servlet.FilterConfig;
049    import javax.servlet.http.HttpServletRequest;
050    import javax.servlet.http.HttpServletResponse;
051    import javax.servlet.http.HttpSession;
052    
053    /**
054     * @author Brian Wing Shun Chan
055     * @author Raymond Augé
056     * @author Alexander Chow
057     */
058    public class SecureFilter extends BasePortalFilter {
059    
060            @Override
061            public void init(FilterConfig filterConfig) {
062                    super.init(filterConfig);
063    
064                    _basicAuthEnabled = GetterUtil.getBoolean(
065                            filterConfig.getInitParameter("basic_auth"));
066                    _digestAuthEnabled = GetterUtil.getBoolean(
067                            filterConfig.getInitParameter("digest_auth"));
068    
069                    String propertyPrefix = filterConfig.getInitParameter(
070                            "portal_property_prefix");
071    
072                    String[] hostsAllowed = null;
073    
074                    if (Validator.isNull(propertyPrefix)) {
075                            hostsAllowed = StringUtil.split(
076                                    filterConfig.getInitParameter("hosts.allowed"));
077                            _httpsRequired = GetterUtil.getBoolean(
078                                    filterConfig.getInitParameter("https.required"));
079                    }
080                    else {
081                            hostsAllowed = PropsUtil.getArray(propertyPrefix + "hosts.allowed");
082                            _httpsRequired = GetterUtil.getBoolean(
083                                    PropsUtil.get(propertyPrefix + "https.required"));
084                    }
085    
086                    for (String hostAllowed : hostsAllowed) {
087                            _hostsAllowed.add(hostAllowed);
088                    }
089    
090                    _usePermissionChecker = GetterUtil.getBoolean(
091                            filterConfig.getInitParameter("use_permission_checker"));
092            }
093    
094            protected HttpServletRequest basicAuth(
095                            HttpServletRequest request, HttpServletResponse response)
096                    throws Exception {
097    
098                    HttpSession session = request.getSession();
099    
100                    session.setAttribute(WebKeys.BASIC_AUTH_ENABLED, Boolean.TRUE);
101    
102                    long userId = GetterUtil.getLong(
103                            (String)session.getAttribute(_AUTHENTICATED_USER));
104    
105                    if (userId > 0) {
106                            request = new ProtectedServletRequest(
107                                    request, String.valueOf(userId), HttpServletRequest.BASIC_AUTH);
108    
109                            initThreadLocals(request);
110                    }
111                    else {
112                            try {
113                                    userId = PortalUtil.getBasicAuthUserId(request);
114                            }
115                            catch (Exception e) {
116                                    _log.error(e, e);
117                            }
118    
119                            if (userId > 0) {
120                                    request = setCredentials(
121                                            request, session, userId, HttpServletRequest.BASIC_AUTH);
122                            }
123                            else {
124                                    response.setHeader(HttpHeaders.WWW_AUTHENTICATE, _BASIC_REALM);
125                                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
126    
127                                    return null;
128                            }
129                    }
130    
131                    return request;
132            }
133    
134            protected HttpServletRequest digestAuth(
135                            HttpServletRequest request, HttpServletResponse response)
136                    throws Exception {
137    
138                    HttpSession session = request.getSession();
139    
140                    long userId = GetterUtil.getLong(
141                            (String)session.getAttribute(_AUTHENTICATED_USER));
142    
143                    if (userId > 0) {
144                            request = new ProtectedServletRequest(
145                                    request, String.valueOf(userId),
146                                    HttpServletRequest.DIGEST_AUTH);
147    
148                            initThreadLocals(request);
149                    }
150                    else {
151                            try {
152                                    userId = PortalUtil.getDigestAuthUserId(request);
153                            }
154                            catch (Exception e) {
155                                    _log.error(e, e);
156                            }
157    
158                            if (userId > 0) {
159                                    request = setCredentials(
160                                            request, session, userId, HttpServletRequest.DIGEST_AUTH);
161                            }
162                            else {
163    
164                                    // Must generate a new nonce for each 401 (RFC2617, 3.2.1)
165    
166                                    long companyId = PortalInstances.getCompanyId(request);
167    
168                                    String remoteAddress = request.getRemoteAddr();
169    
170                                    String nonce = NonceUtil.generate(companyId, remoteAddress);
171    
172                                    StringBundler sb = new StringBundler(4);
173    
174                                    sb.append(_DIGEST_REALM);
175                                    sb.append(", nonce=\"");
176                                    sb.append(nonce);
177                                    sb.append("\"");
178    
179                                    response.setHeader(HttpHeaders.WWW_AUTHENTICATE, sb.toString());
180                                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
181    
182                                    return null;
183                            }
184                    }
185    
186                    return request;
187            }
188    
189            protected void initThreadLocals(HttpServletRequest request)
190                    throws Exception {
191    
192                    HttpSession session = request.getSession();
193    
194                    User user = (User)session.getAttribute(WebKeys.USER);
195    
196                    CompanyThreadLocal.setCompanyId(user.getCompanyId());
197    
198                    PrincipalThreadLocal.setName(user.getUserId());
199                    PrincipalThreadLocal.setPassword(PortalUtil.getUserPassword(request));
200    
201                    if (!_usePermissionChecker) {
202                            return;
203                    }
204    
205                    PermissionChecker permissionChecker =
206                            PermissionCheckerFactoryUtil.create(user);
207    
208                    PermissionThreadLocal.setPermissionChecker(permissionChecker);
209            }
210    
211            @Override
212            protected void processFilter(
213                            HttpServletRequest request, HttpServletResponse response,
214                            FilterChain filterChain)
215                    throws Exception {
216    
217                    String remoteAddr = request.getRemoteAddr();
218    
219                    if (AuthSettingsUtil.isAccessAllowed(request, _hostsAllowed)) {
220                            if (_log.isDebugEnabled()) {
221                                    _log.debug("Access allowed for " + remoteAddr);
222                            }
223                    }
224                    else {
225                            if (_log.isWarnEnabled()) {
226                                    _log.warn("Access denied for " + remoteAddr);
227                            }
228    
229                            response.sendError(
230                                    HttpServletResponse.SC_FORBIDDEN,
231                                    "Access denied for " + remoteAddr);
232    
233                            return;
234                    }
235    
236                    if (_log.isDebugEnabled()) {
237                            if (_httpsRequired) {
238                                    _log.debug("https is required");
239                            }
240                            else {
241                                    _log.debug("https is not required");
242                            }
243                    }
244    
245                    if (_httpsRequired && !request.isSecure()) {
246                            if (_log.isDebugEnabled()) {
247                                    String completeURL = HttpUtil.getCompleteURL(request);
248    
249                                    _log.debug("Securing " + completeURL);
250                            }
251    
252                            StringBundler redirectURL = new StringBundler(5);
253    
254                            redirectURL.append(Http.HTTPS_WITH_SLASH);
255                            redirectURL.append(request.getServerName());
256                            redirectURL.append(request.getServletPath());
257    
258                            String queryString = request.getQueryString();
259    
260                            if (Validator.isNotNull(queryString)) {
261                                    redirectURL.append(StringPool.QUESTION);
262                                    redirectURL.append(request.getQueryString());
263                            }
264    
265                            if (_log.isDebugEnabled()) {
266                                    _log.debug("Redirect to " + redirectURL);
267                            }
268    
269                            response.sendRedirect(redirectURL.toString());
270                    }
271                    else {
272                            if (_log.isDebugEnabled()) {
273                                    String completeURL = HttpUtil.getCompleteURL(request);
274    
275                                    _log.debug("Not securing " + completeURL);
276                            }
277    
278                            // This authentication should only be run if specified by web.xml
279                            // and JAAS is disabled. Make sure to run this once per session and
280                            // wrap the request if necessary.
281    
282                            if (!PropsValues.PORTAL_JAAS_ENABLE) {
283                                    User user = PortalUtil.getUser(request);
284    
285                                    if ((user != null) && !user.isDefaultUser()) {
286                                            request = setCredentials(
287                                                    request, request.getSession(), user.getUserId(), null);
288                                    }
289                                    else {
290                                            if (_digestAuthEnabled) {
291                                                    request = digestAuth(request, response);
292                                            }
293                                            else if (_basicAuthEnabled) {
294                                                    request = basicAuth(request, response);
295                                            }
296                                    }
297                            }
298    
299                            if (request != null) {
300                                    processFilter(getClass(), request, response, filterChain);
301                            }
302                    }
303            }
304    
305            protected HttpServletRequest setCredentials(
306                            HttpServletRequest request, HttpSession session, long userId,
307                            String authType)
308                    throws Exception {
309    
310                    User user = UserLocalServiceUtil.getUser(userId);
311    
312                    String userIdString = String.valueOf(userId);
313    
314                    request = new ProtectedServletRequest(request, userIdString, authType);
315    
316                    session.setAttribute(WebKeys.USER, user);
317                    session.setAttribute(_AUTHENTICATED_USER, userIdString);
318    
319                    initThreadLocals(request);
320    
321                    return request;
322            }
323    
324            protected void setUsePermissionChecker(boolean usePermissionChecker) {
325                    _usePermissionChecker = usePermissionChecker;
326            }
327    
328            private static final String _AUTHENTICATED_USER =
329                    SecureFilter.class + "_AUTHENTICATED_USER";
330    
331            private static final String _BASIC_REALM =
332                    "Basic realm=\"" + Portal.PORTAL_REALM + "\"";
333    
334            private static final String _DIGEST_REALM =
335                    "Digest realm=\"" + Portal.PORTAL_REALM + "\"";
336    
337            private static Log _log = LogFactoryUtil.getLog(SecureFilter.class);
338    
339            private boolean _basicAuthEnabled;
340            private boolean _digestAuthEnabled;
341            private Set<String> _hostsAllowed = new HashSet<String>();
342            private boolean _httpsRequired;
343            private boolean _usePermissionChecker;
344    
345    }