001/*
002
003    Licensed to the Apache Software Foundation (ASF) under one
004    or more contributor license agreements.  See the NOTICE file
005    distributed with this work for additional information
006    regarding copyright ownership.  The ASF licenses this file
007    to you under the Apache License, Version 2.0 (the
008    "License"); you may not use this file except in compliance
009    with the License.  You may obtain a copy of the License at
010
011       http://www.apache.org/licenses/LICENSE-2.0
012
013    Unless required by applicable law or agreed to in writing,
014    software distributed under the License is distributed on an
015    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016    KIND, either express or implied.  See the License for the
017    specific language governing permissions and limitations
018    under the License.
019 */
020package org.apache.wiki.ajax;
021
022import org.apache.commons.lang3.StringUtils;
023import org.apache.log4j.Logger;
024import org.apache.wiki.WikiEngine;
025import org.apache.wiki.WikiSession;
026import org.apache.wiki.auth.AuthorizationManager;
027import org.apache.wiki.auth.permissions.PagePermission;
028import org.apache.wiki.util.TextUtil;
029
030import javax.servlet.ServletConfig;
031import javax.servlet.ServletException;
032import javax.servlet.http.HttpServlet;
033import javax.servlet.http.HttpServletRequest;
034import javax.servlet.http.HttpServletResponse;
035import java.io.IOException;
036import java.security.Permission;
037import java.util.ArrayList;
038import java.util.Arrays;
039import java.util.HashMap;
040import java.util.List;
041import java.util.Map;
042
043/**
044 * This provides a simple ajax servlet for handling /ajax/<ClassName> requests.
045 * HttpServlet classes need to be registered using {@link WikiAjaxDispatcherServlet#registerServlet(WikiAjaxServlet)}
046 *
047 * @since 2.10.2-svn12
048 */
049public class WikiAjaxDispatcherServlet extends HttpServlet {
050    private static final long serialVersionUID = 1L;
051    private static Map<String,AjaxServletContainer> ajaxServlets = new HashMap<String,AjaxServletContainer>();
052    static final Logger log = Logger.getLogger(WikiAjaxDispatcherServlet.class.getName());
053    private String PATH_AJAX = "/ajax/";
054    private WikiEngine m_engine;
055
056    /**
057     * {@inheritDoc}
058     *
059     * This sets the AjaxPath to "/ajax/" as configured in "jspwiki.ajax.url.prefix".
060     * Note: Do not change this without also changing the web.xml file.
061     */
062    @Override
063    public void init(ServletConfig config)
064            throws ServletException {
065        super.init(config);
066        m_engine = WikiEngine.getInstance(config);
067        PATH_AJAX = "/"+TextUtil.getStringProperty(m_engine.getWikiProperties(), "jspwiki.ajax.url.prefix", "ajax")+"/";
068        log.info("WikiAjaxDispatcherServlet initialized.");
069    }
070
071    /**
072     * Register a {@link WikiAjaxServlet} using the servlet mapping as the alias
073     */
074    public static void registerServlet(WikiAjaxServlet servlet) {
075        registerServlet(servlet.getServletMapping(),servlet);
076    }
077
078    /**
079     * Register a {@link WikiAjaxServlet} with a specific alias, and default permission {@link PagePermission#VIEW}.
080     */
081    public static void registerServlet(String alias, WikiAjaxServlet servlet) {
082        registerServlet(alias, servlet, PagePermission.VIEW);
083    }
084
085    /**
086     * Regster a {@link WikiAjaxServlet} given an alias, the servlet, and the permission.
087     * This creates a temporary bundle object called {@link WikiAjaxDispatcherServlet.AjaxServletContainer}
088     * @param alias the uri link to this servlet
089     * @param servlet the servlet being registered
090     * @param perm the permission required to execute the servlet.
091     */
092    public static void registerServlet(String alias, WikiAjaxServlet servlet, Permission perm) {
093        log.info("WikiAjaxDispatcherServlet registering "+alias+"="+servlet+" perm="+perm);
094        ajaxServlets.put(alias,new AjaxServletContainer(alias, servlet, perm));
095    }
096
097    /**
098     * Calls {@link #performAction}
099     */
100    @Override
101    public void doPost(HttpServletRequest req, HttpServletResponse res)
102            throws IOException, ServletException {
103        performAction(req,res);
104    }
105
106    /**
107     * Calls {@link #performAction}
108     */
109    @Override
110    public void doGet(HttpServletRequest req, HttpServletResponse res)
111            throws IOException, ServletException {
112        performAction(req,res);
113    }
114
115    /**
116     * The main method which get the requestURI "/ajax/<ServletName>", gets the
117     * {@link #getServletName} and finds the servlet using {@link #findServletByName}.
118     * It then calls {@link WikiAjaxServlet#service} method.
119     * @param req the inbound request
120     * @param res the outbound response
121     * @throws IOException
122     * @throws ServletException if no registered servlet can be found
123     */
124    private void performAction( final HttpServletRequest req, final HttpServletResponse res ) throws IOException, ServletException {
125        final String path = req.getRequestURI();
126        final String servletName = getServletName(path);
127        if (servletName!=null) {
128            final AjaxServletContainer container = findServletContainer(servletName);
129            if (container != null) {
130                final WikiAjaxServlet servlet = container.servlet;
131                if ( validatePermission(req,container) ) {
132                    req.setCharacterEncoding(m_engine.getContentEncoding().displayName());
133                    res.setCharacterEncoding(m_engine.getContentEncoding().displayName());
134                    final String actionName = AjaxUtil.getNextPathPart(req.getRequestURI(), servlet.getServletMapping());
135                    log.debug("actionName="+actionName);
136                    final Object params = req.getParameter("params");
137                    log.debug("params="+params);
138                    List<String> paramValues = new ArrayList<String>();
139                    if (params instanceof String) {
140                        final String paramString = (String)params;
141                        if (StringUtils.isNotBlank(paramString)) {
142                            paramValues = Arrays.asList(paramString.trim().split(","));
143                        }
144                    }
145                    servlet.service(req, res, actionName, paramValues);
146                } else {
147                    log.warn("Servlet container "+container+" not authorised. Permission required.");
148                }
149            } else {
150                log.error("No registered class for servletName=" + servletName + " in path=" + path);
151                throw new ServletException("No registered class for servletName=" + servletName);
152            }
153        }
154    }
155
156    /**
157     * Validate the permission of the {@link WikiAjaxServlet} using the {@link AuthorizationManager#checkPermission}
158     *
159     * @param req the servlet request
160     * @param container the container info of the servlet
161     * @return true if permission is valid
162     */
163    private boolean validatePermission(HttpServletRequest req, AjaxServletContainer container) {
164        final WikiEngine e = WikiEngine.getInstance(req.getSession().getServletContext(), null);
165        boolean valid = false;
166        if (container != null) {
167            valid = e.getAuthorizationManager().checkPermission(WikiSession.getWikiSession(e, req), container.permission);
168        }
169        return valid;
170    }
171
172    /**
173     * Get the ServletName from the requestURI "/ajax/<ServletName>", using {@link AjaxUtil#getNextPathPart}.
174     *
175     * @param path The requestURI, which must contains "/ajax/<ServletName>" in the path
176     * @return The ServletName for the requestURI, or null
177     * @throws ServletException if the path is invalid
178     */
179    public String getServletName(String path) throws ServletException {
180        return AjaxUtil.getNextPathPart(path, PATH_AJAX);
181    }
182
183    /**
184     * Find the {@link AjaxServletContainer} as registered in {@link #registerServlet}.
185     *
186     * @param servletAlias the name of the servlet from {@link #getServletName}
187     * @return The first servlet found, or null.
188     */
189    private AjaxServletContainer findServletContainer(String servletAlias) {
190        return ajaxServlets.get(servletAlias);
191    }
192
193    /**
194     * Find the {@link WikiAjaxServlet} given the servletAlias that it was registered with.
195     *
196     * @param servletAlias the value provided to {@link #registerServlet}
197     * @return the {@link WikiAjaxServlet} given the servletAlias that it was registered with.
198     */
199    public WikiAjaxServlet findServletByName(String servletAlias) {
200        final AjaxServletContainer container = ajaxServlets.get(servletAlias);
201        if (container != null) {
202            return container.servlet;
203        }
204        return null;
205    }
206
207    private static class AjaxServletContainer {
208        String alias;
209        WikiAjaxServlet servlet;
210        Permission permission;
211
212        public AjaxServletContainer(String alias, WikiAjaxServlet servlet, Permission permission) {
213            this.alias = alias;
214            this.servlet = servlet;
215            this.permission = permission;
216        }
217
218        @Override
219        public String toString() {
220            return getClass().getSimpleName()+" "+alias+"="+servlet.getClass().getSimpleName()+" permission="+permission;
221        }
222    }
223
224}