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.api.core.Engine; 025import org.apache.wiki.api.spi.Wiki; 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.List; 040import java.util.Map; 041import java.util.concurrent.ConcurrentHashMap; 042 043 044/** 045 * This provides a simple ajax servlet for handling /ajax/<ClassName> requests. HttpServlet classes need to be registered using 046 * {@link WikiAjaxDispatcherServlet#registerServlet(WikiAjaxServlet)} 047 * 048 * @since 2.10.2-svn12 049 */ 050public class WikiAjaxDispatcherServlet extends HttpServlet { 051 052 private static final long serialVersionUID = 1L; 053 private static final Map< String, AjaxServletContainer > ajaxServlets = new ConcurrentHashMap<>(); 054 private static final Logger log = Logger.getLogger( WikiAjaxDispatcherServlet.class.getName() ); 055 private String PATH_AJAX = "/ajax/"; 056 private Engine m_engine; 057 058 /** 059 * {@inheritDoc} 060 * 061 * This sets the AjaxPath to "/ajax/" as configured in "jspwiki.ajax.url.prefix". 062 * Note: Do not change this without also changing the web.xml file. 063 */ 064 @Override 065 public void init( final ServletConfig config ) throws ServletException { 066 super.init( config ); 067 m_engine = Wiki.engine().find( config ); 068 PATH_AJAX = "/" + TextUtil.getStringProperty( m_engine.getWikiProperties(), "jspwiki.ajax.url.prefix", "ajax" ) + "/"; 069 log.info( "WikiAjaxDispatcherServlet initialized." ); 070 } 071 072 /** 073 * Register a {@link WikiAjaxServlet} using the servlet mapping as the alias 074 */ 075 public static void registerServlet( final WikiAjaxServlet servlet ) { 076 registerServlet( servlet.getServletMapping(), servlet ); 077 } 078 079 /** 080 * Register a {@link WikiAjaxServlet} with a specific alias, and default permission {@link PagePermission#VIEW}. 081 */ 082 public static void registerServlet( final String alias, final WikiAjaxServlet servlet ) { 083 registerServlet( alias, servlet, PagePermission.VIEW ); 084 } 085 086 /** 087 * Regster a {@link WikiAjaxServlet} given an alias, the servlet, and the permission. 088 * This creates a temporary bundle object called {@link WikiAjaxDispatcherServlet.AjaxServletContainer} 089 * 090 * @param alias the uri link to this servlet 091 * @param servlet the servlet being registered 092 * @param perm the permission required to execute the servlet. 093 */ 094 public static void registerServlet( final String alias, final WikiAjaxServlet servlet, final Permission perm ) { 095 log.info( "WikiAjaxDispatcherServlet registering " + alias + "=" + servlet + " perm=" + perm ); 096 ajaxServlets.put( alias, new AjaxServletContainer( alias, servlet, perm ) ); 097 } 098 099 /** 100 * Calls {@link #performAction} 101 */ 102 @Override 103 public void doPost( final HttpServletRequest req, final HttpServletResponse res ) throws IOException, ServletException { 104 performAction( req, res ); 105 } 106 107 /** 108 * Calls {@link #performAction} 109 */ 110 @Override 111 public void doGet( final HttpServletRequest req, final HttpServletResponse res ) throws IOException, ServletException { 112 performAction( req, res ); 113 } 114 115 /** 116 * The main method which get the requestURI "/ajax/<ServletName>", gets the {@link #getServletName} and finds the servlet using 117 * {@link #findServletByName}. It then calls {@link WikiAjaxServlet#service} method. 118 * 119 * @param req the inbound request 120 * @param res the outbound response 121 * @throws IOException if WikiEngine's content encoding is valid 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 String params = req.getParameter( "params" ); 137 log.debug( "params=" + params ); 138 List< String > paramValues = new ArrayList<>(); 139 if( params != null ) { 140 if( StringUtils.isNotBlank( params ) ) { 141 paramValues = Arrays.asList( params.trim().split( "," ) ); 142 } 143 } 144 servlet.service( req, res, actionName, paramValues ); 145 } else { 146 log.warn( "Servlet container " + container + " not authorised. Permission required." ); 147 } 148 } else { 149 log.error( "No registered class for servletName=" + servletName + " in path=" + path ); 150 throw new ServletException( "No registered class for servletName=" + servletName ); 151 } 152 } 153 } 154 155 /** 156 * Validate the permission of the {@link WikiAjaxServlet} using the {@link AuthorizationManager#checkPermission} 157 * 158 * @param req the servlet request 159 * @param container the container info of the servlet 160 * @return true if permission is valid 161 */ 162 private boolean validatePermission( final HttpServletRequest req, final AjaxServletContainer container ) { 163 final Engine e = Wiki.engine().find( req.getSession().getServletContext(), null ); 164 boolean valid = false; 165 if( container != null ) { 166 valid = e.getManager( AuthorizationManager.class ).checkPermission( Wiki.session().find( e, req ), container.permission ); 167 } 168 return valid; 169 } 170 171 /** 172 * Get the ServletName from the requestURI "/ajax/<ServletName>", using {@link AjaxUtil#getNextPathPart}. 173 * 174 * @param path The requestURI, which must contains "/ajax/<ServletName>" in the path 175 * @return The ServletName for the requestURI, or null 176 * @throws ServletException if the path is invalid 177 */ 178 public String getServletName( final String path ) throws ServletException { 179 return AjaxUtil.getNextPathPart( path, PATH_AJAX ); 180 } 181 182 /** 183 * Find the {@link AjaxServletContainer} as registered in {@link #registerServlet}. 184 * 185 * @param servletAlias the name of the servlet from {@link #getServletName} 186 * @return The first servlet found, or null. 187 */ 188 private AjaxServletContainer findServletContainer( final String servletAlias ) { 189 return ajaxServlets.get( servletAlias ); 190 } 191 192 /** 193 * Find the {@link WikiAjaxServlet} given the servletAlias that it was registered with. 194 * 195 * @param servletAlias the value provided to {@link #registerServlet} 196 * @return the {@link WikiAjaxServlet} given the servletAlias that it was registered with. 197 */ 198 public WikiAjaxServlet findServletByName( final String servletAlias ) { 199 final AjaxServletContainer container = ajaxServlets.get( servletAlias ); 200 if( container != null ) { 201 return container.servlet; 202 } 203 return null; 204 } 205 206 private static class AjaxServletContainer { 207 208 final String alias; 209 final WikiAjaxServlet servlet; 210 final Permission permission; 211 212 public AjaxServletContainer( final String alias, final WikiAjaxServlet servlet, final 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 225}