001    /**
002     * Copyright (c) 2000-2013 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.struts;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.json.JSONFactoryUtil;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.servlet.HttpHeaders;
022    import com.liferay.portal.kernel.servlet.ServletContextPool;
023    import com.liferay.portal.kernel.util.ClassUtil;
024    import com.liferay.portal.kernel.util.ContentTypes;
025    import com.liferay.portal.kernel.util.GetterUtil;
026    import com.liferay.portal.kernel.util.ParamUtil;
027    import com.liferay.portal.kernel.util.SetUtil;
028    import com.liferay.portal.kernel.util.StringPool;
029    import com.liferay.portal.kernel.util.Validator;
030    import com.liferay.portal.security.ac.AccessControlUtil;
031    import com.liferay.portal.security.auth.AuthSettingsUtil;
032    import com.liferay.portal.security.auth.AuthTokenUtil;
033    import com.liferay.portal.security.auth.PortalSessionAuthVerifier;
034    import com.liferay.portal.servlet.SharedSessionServletRequest;
035    import com.liferay.portal.util.PortalUtil;
036    import com.liferay.portal.util.PropsValues;
037    import com.liferay.portal.util.WebKeys;
038    
039    import java.io.OutputStream;
040    
041    import java.util.Set;
042    
043    import javax.servlet.RequestDispatcher;
044    import javax.servlet.ServletContext;
045    import javax.servlet.http.HttpServletRequest;
046    import javax.servlet.http.HttpServletResponse;
047    
048    import org.apache.struts.action.Action;
049    import org.apache.struts.action.ActionForm;
050    import org.apache.struts.action.ActionForward;
051    import org.apache.struts.action.ActionMapping;
052    
053    /**
054     * @author Ming-Gih Lam
055     * @author Brian Wing Shun Chan
056     * @author Tomas Polesovsky
057     */
058    public abstract class JSONAction extends Action {
059    
060            @Override
061            public ActionForward execute(
062                            ActionMapping actionMapping, ActionForm actionForm,
063                            HttpServletRequest request, HttpServletResponse response)
064                    throws Exception {
065    
066                    if (rerouteExecute(request, response)) {
067                            return null;
068                    }
069    
070                    String callback = ParamUtil.getString(request, "callback");
071                    String instance = ParamUtil.getString(request, "inst");
072    
073                    String json = null;
074    
075                    try {
076                            checkAuthToken(request);
077    
078                            json = getJSON(actionMapping, actionForm, request, response);
079    
080                            if (Validator.isNotNull(callback)) {
081                                    json = callback + "(" + json + ");";
082                            }
083                            else if (Validator.isNotNull(instance)) {
084                                    json = "var " + instance + "=" + json + ";";
085                            }
086                    }
087                    catch (SecurityException se) {
088                            if (_log.isWarnEnabled()) {
089                                    _log.warn(se.getMessage());
090                            }
091    
092                            json = JSONFactoryUtil.serializeException(se);
093                    }
094                    catch (Exception e) {
095                            _log.error(e, e);
096    
097                            PortalUtil.sendError(
098                                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e, request,
099                                    response);
100    
101                            return null;
102                    }
103    
104                    boolean refresh = ParamUtil.getBoolean(request, "refresh");
105    
106                    if (refresh) {
107                            return actionMapping.findForward(ActionConstants.COMMON_REFERER);
108                    }
109                    else if (Validator.isNotNull(json)) {
110                            response.setCharacterEncoding(StringPool.UTF8);
111                            response.setContentType(ContentTypes.APPLICATION_JSON);
112                            response.setHeader(
113                                    HttpHeaders.CACHE_CONTROL,
114                                    HttpHeaders.CACHE_CONTROL_NO_CACHE_VALUE);
115    
116                            OutputStream outputStream = response.getOutputStream();
117    
118                            byte[] bytes = json.getBytes(StringPool.UTF8);
119    
120                            outputStream.write(bytes);
121    
122                            outputStream.close();
123                    }
124    
125                    return null;
126            }
127    
128            public abstract String getJSON(
129                            ActionMapping actionMapping, ActionForm actionForm,
130                            HttpServletRequest request, HttpServletResponse response)
131                    throws Exception;
132    
133            public void setServletContext(ServletContext servletContext) {
134                    _servletContext = servletContext;
135            }
136    
137            protected void checkAuthToken(HttpServletRequest request)
138                    throws PortalException {
139    
140                    String authType = GetterUtil.getString(request.getAuthType());
141    
142                    // Support for the legacy JSON API at /c/portal/json_service
143    
144                    if (AccessControlUtil.getAccessControlContext() == null) {
145                            if (authType.equals(HttpServletRequest.BASIC_AUTH) ||
146                                    authType.equals(HttpServletRequest.DIGEST_AUTH)) {
147    
148                                    return;
149                            }
150                    }
151                    else {
152    
153                            // The new web service should only check auth tokens when the user
154                            // is authenticated using portal session cookies
155    
156                            if (!authType.equals(PortalSessionAuthVerifier.AUTH_TYPE)) {
157                                    return;
158                            }
159                    }
160    
161                    if (PropsValues.JSON_SERVICE_AUTH_TOKEN_ENABLED) {
162                            if (!AuthSettingsUtil.isAccessAllowed(request, _hostsAllowed)) {
163                                    AuthTokenUtil.checkCSRFToken(request, getCSRFOrigin(request));
164                            }
165                    }
166            }
167    
168            protected String getCSRFOrigin(HttpServletRequest request) {
169                    return ClassUtil.getClassName(this);
170            }
171    
172            protected String getReroutePath() {
173                    return null;
174            }
175    
176            protected boolean rerouteExecute(
177                            HttpServletRequest request, HttpServletResponse response)
178                    throws Exception {
179    
180                    String reroutePath = getReroutePath();
181    
182                    if (Validator.isNull(reroutePath)) {
183                            return false;
184                    }
185    
186                    String requestServletContextName = ParamUtil.getString(
187                            request, "servletContextName");
188    
189                    if (Validator.isNull(requestServletContextName)) {
190                            return false;
191                    }
192    
193                    ServletContext servletContext = _servletContext;
194    
195                    if (servletContext == null) {
196                            servletContext = (ServletContext)request.getAttribute(WebKeys.CTX);
197                    }
198    
199                    String servletContextName = GetterUtil.getString(
200                            servletContext.getServletContextName());
201    
202                    if (servletContextName.equals(requestServletContextName)) {
203                            return false;
204                    }
205    
206                    ServletContext requestServletContext = ServletContextPool.get(
207                            requestServletContextName);
208    
209                    if (requestServletContext == null) {
210                            return false;
211                    }
212    
213                    RequestDispatcher requestDispatcher =
214                            requestServletContext.getRequestDispatcher(reroutePath);
215    
216                    if (requestDispatcher == null) {
217                            return false;
218                    }
219    
220                    requestDispatcher.forward(
221                            new SharedSessionServletRequest(request, true), response);
222    
223                    return true;
224            }
225    
226            private static Log _log = LogFactoryUtil.getLog(JSONAction.class);
227    
228            private Set<String> _hostsAllowed = SetUtil.fromArray(
229                    PropsValues.JSON_SERVICE_AUTH_TOKEN_HOSTS_ALLOWED);
230            private ServletContext _servletContext;
231    
232    }