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