    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.logging.log4j.LogManager;
    024import org.apache.logging.log4j.Logger;
    025import org.apache.wiki.api.core.Engine;
    026import org.apache.wiki.api.spi.Wiki;
    027import org.apache.wiki.auth.AuthorizationManager;
    028import org.apache.wiki.auth.permissions.PagePermission;
    029import org.apache.wiki.util.TextUtil;
    030
    031import javax.servlet.ServletConfig;
    032import javax.servlet.ServletException;
    033import javax.servlet.http.HttpServlet;
    034import javax.servlet.http.HttpServletRequest;
    035import javax.servlet.http.HttpServletResponse;
    036import java.io.IOException;
    037import java.security.Permission;
    038import java.util.ArrayList;
    039import java.util.Arrays;
    040import java.util.List;
    041import java.util.Map;
    042import java.util.concurrent.ConcurrentHashMap;
    043
    044
    045/**
    046 * This provides a simple ajax servlet for handling /ajax/<ClassName> requests. HttpServlet classes need to be registered using
    047 * {@link WikiAjaxDispatcherServlet#registerServlet(WikiAjaxServlet)}
    048 *
    049 * @since 2.10.2-svn12
    050 */
    051public class WikiAjaxDispatcherServlet extends HttpServlet {
    052
    053    private static final long serialVersionUID = 1L;
    054    private static final Map< String, AjaxServletContainer > ajaxServlets = new ConcurrentHashMap<>();
    055    private static final Logger LOG = LogManager.getLogger( WikiAjaxDispatcherServlet.class.getName() );
    056    private String PATH_AJAX = "/ajax/";
    057    private Engine m_engine;
    058
    059    /**
    060     * {@inheritDoc}
    061     *
    062     * This sets the AjaxPath to "/ajax/" as configured in "jspwiki.ajax.url.prefix".
    063     * Note: Do not change this without also changing the web.xml file.
    064     */
    065    @Override
    066    public void init( final ServletConfig config ) throws ServletException {
    067        super.init( config );
    068        m_engine = Wiki.engine().find( config );
    069        PATH_AJAX = "/" + TextUtil.getStringProperty( m_engine.getWikiProperties(), "jspwiki.ajax.url.prefix", "ajax" ) + "/";
    070        LOG.info( "WikiAjaxDispatcherServlet initialized." );
    071    }
    072
    073    /**
    074     * Register a {@link WikiAjaxServlet} using the servlet mapping as the alias
    075     */
    076    public static void registerServlet( final WikiAjaxServlet servlet ) {
    077        registerServlet( servlet.getServletMapping(), servlet );
    078    }
    079
    080    /**
    081     * Register a {@link WikiAjaxServlet} with a specific alias, and default permission {@link PagePermission#VIEW}.
    082     */
    083    public static void registerServlet( final String alias, final WikiAjaxServlet servlet ) {
    084        registerServlet( alias, servlet, PagePermission.VIEW );
    085    }
    086
    087    /**
    088     * Regster a {@link WikiAjaxServlet} given an alias, the servlet, and the permission.
    089     * This creates a temporary bundle object called {@link WikiAjaxDispatcherServlet.AjaxServletContainer}
    090     *
    091     * @param alias the uri link to this servlet
    092     * @param servlet the servlet being registered
    093     * @param perm the permission required to execute the servlet.
    094     */
    095    public static void registerServlet( final String alias, final WikiAjaxServlet servlet, final Permission perm ) {
    096        LOG.info( "WikiAjaxDispatcherServlet registering " + alias + "=" + servlet + " perm=" + perm );
    097        ajaxServlets.put( alias, new AjaxServletContainer( alias, servlet, perm ) );
    098    }
    099
    100    /**
    101     * Calls {@link #performAction}
    102     */
    103    @Override
    104    public void doPost( final HttpServletRequest req, final HttpServletResponse res ) throws IOException, ServletException {
    105        performAction( req, res );
    106    }
    107
    108    /**
    109     * Calls {@link #performAction}
    110     */
    111    @Override
    112    public void doGet( final HttpServletRequest req, final HttpServletResponse res ) throws IOException, ServletException {
    113        performAction( req, res );
    114    }
    115
    116    /**
    117     * The main method which get the requestURI "/ajax/<ServletName>", gets the {@link #getServletName} and finds the servlet using
    118     * {@link #findServletByName}. It then calls {@link WikiAjaxServlet#service} method.
    119     *
    120     * @param req the inbound request
    121     * @param res the outbound response
    122     * @throws IOException if WikiEngine's content encoding is valid
    123     * @throws ServletException if no registered servlet can be found
    124     */
    125    private void performAction( final HttpServletRequest req, final HttpServletResponse res ) throws IOException, ServletException {
    126        final String path = req.getRequestURI();
    127        final String servletName = getServletName( path );
    128        if( servletName != null) {
    129            final AjaxServletContainer container = findServletContainer( servletName );
    130            if( container != null ) {
    131                final WikiAjaxServlet servlet = container.servlet;
    132                if ( validatePermission( req, container ) ) {
    133                    req.setCharacterEncoding( m_engine.getContentEncoding().displayName() );
    134                    res.setCharacterEncoding( m_engine.getContentEncoding().displayName() );
    135                    final String actionName = AjaxUtil.getNextPathPart( req.getRequestURI(), servlet.getServletMapping() );
    136                    LOG.debug( "actionName=" + actionName );
    137                    final String params = req.getParameter( "params" );
    138                    LOG.debug( "params=" + params );
    139                    List< String > paramValues = new ArrayList<>();
    140                    if( params != null ) {
    141                        if( StringUtils.isNotBlank( params ) ) {
    142                            paramValues = Arrays.asList( params.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( final HttpServletRequest req, final AjaxServletContainer container ) {
    164        final Engine e = Wiki.engine().find( req.getSession().getServletContext(), null );
    165        boolean valid = false;
    166        if( container != null ) {
    167            valid = e.getManager( AuthorizationManager.class ).checkPermission( Wiki.session().find( 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( final 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( final 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( final 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
    209        final String alias;
    210        final WikiAjaxServlet servlet;
    211        final Permission permission;
    212
    213        public AjaxServletContainer( final String alias, final WikiAjaxServlet servlet, final Permission permission ) {
    214            this.alias = alias;
    215            this.servlet = servlet;
    216            this.permission = permission;
    217        }
    218
    219        @Override
    220        public String toString() {
    221            return getClass().getSimpleName() + " " + alias + "=" + servlet.getClass().getSimpleName() + " permission=" + permission;
    222        }
    223
    224    }
    225
    226}