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