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}