001/* 002 Licensed to the Apache Software Foundation (ASF) under one 003 or more contributor license agreements. See the NOTICE file 004 distributed with this work for additional information 005 regarding copyright ownership. The ASF licenses this file 006 to you under the Apache License, Version 2.0 (the 007 "License"); you may not use this file except in compliance 008 with the License. You may obtain a copy of the License at 009 010 http://www.apache.org/licenses/LICENSE-2.0 011 012 Unless required by applicable law or agreed to in writing, 013 software distributed under the License is distributed on an 014 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 KIND, either express or implied. See the License for the 016 specific language governing permissions and limitations 017 under the License. 018 */ 019package org.apache.wiki.ui; 020 021import org.apache.logging.log4j.LogManager; 022import org.apache.logging.log4j.Logger; 023import org.apache.logging.log4j.ThreadContext; 024import org.apache.wiki.api.core.Context; 025import org.apache.wiki.api.core.Engine; 026import org.apache.wiki.api.core.Session; 027import org.apache.wiki.api.spi.Wiki; 028import org.apache.wiki.auth.AuthenticationManager; 029import org.apache.wiki.auth.SessionMonitor; 030import org.apache.wiki.auth.WikiSecurityException; 031 032import javax.servlet.Filter; 033import javax.servlet.FilterChain; 034import javax.servlet.FilterConfig; 035import javax.servlet.ServletContext; 036import javax.servlet.ServletException; 037import javax.servlet.ServletRequest; 038import javax.servlet.ServletResponse; 039import javax.servlet.http.HttpServletRequest; 040import javax.servlet.http.HttpServletRequestWrapper; 041import java.io.IOException; 042import java.io.PrintWriter; 043 044/** 045 * Filter that verifies that the {@link org.apache.wiki.api.core.Engine} is running, and sets the authentication status for the user's 046 * Session. Each HTTP request processed by this filter is wrapped by a {@link WikiRequestWrapper}. The wrapper's primary responsibility 047 * is to return the correct <code>userPrincipal</code> and <code>remoteUser</code> for authenticated JSPWiki users (whether authenticated 048 * by container or by JSPWiki's custom system). The wrapper's other responsibility is to incorporate JSPWiki built-in roles 049 * into the role-checking algorithm for {@link HttpServletRequest#isUserInRole(String)}. Just before the request is wrapped, the method 050 * {@link org.apache.wiki.auth.AuthenticationManager#login(HttpServletRequest)} executes; this method contains all of the logic needed to 051 * grab any user login credentials set by the container or by cookies. 052 */ 053public class WikiServletFilter implements Filter { 054 055 private static final Logger LOG = LogManager.getLogger( WikiServletFilter.class ); 056 protected Engine m_engine; 057 058 /** 059 * Creates a Wiki Servlet Filter. 060 */ 061 public WikiServletFilter() 062 { 063 super(); 064 } 065 066 /** 067 * Initializes the WikiServletFilter. 068 * 069 * @param config The FilterConfig. 070 * @throws ServletException If a Engine cannot be started. 071 */ 072 @Override 073 public void init( final FilterConfig config ) throws ServletException { 074 final ServletContext context = config.getServletContext(); 075 076 // TODO REMOVEME when resolving JSPWIKI-129 077 if( System.getSecurityManager() != null ) { 078 context.log( "== JSPWIKI WARNING == : This container is running with a security manager. JSPWiki does not yet really support that right now. See issue JSPWIKI-129 for details and information on how to proceed." ); 079 } 080 081 m_engine = Wiki.engine().find( context, null ); 082 } 083 084 /** 085 * Destroys the WikiServletFilter. 086 */ 087 @Override 088 public void destroy() { 089 } 090 091 /** 092 * Checks that the Engine is running ok, wraps the current HTTP request, and sets the correct authentication state for the users's 093 * Session. First, the method {@link org.apache.wiki.auth.AuthenticationManager#login(HttpServletRequest)} 094 * executes, which sets the authentication state. Then, the request is wrapped with a 095 * {@link WikiRequestWrapper}. 096 * @param request the current HTTP request object 097 * @param response the current HTTP response object 098 * @param chain The Filter chain passed down. 099 * @throws ServletException if {@link org.apache.wiki.auth.AuthenticationManager#login(HttpServletRequest)} fails for any reason 100 * @throws IOException If writing to the servlet response fails. 101 */ 102 @Override 103 public void doFilter( final ServletRequest request, final ServletResponse response, final FilterChain chain ) throws IOException, ServletException { 104 // Sanity check; it might be true in some conditions, but we need to know where. 105 if( chain == null ) { 106 throw new ServletException("FilterChain is null, even if it should not be. Please report this to the jspwiki development team."); 107 } 108 109 if( m_engine == null ) { 110 final PrintWriter out = response.getWriter(); 111 out.print("<!DOCTYPE html><html lang=\"en\"><head><title>Fatal problem with JSPWiki</title></head>"); 112 out.print("<body>"); 113 out.print("<h1>JSPWiki has not been started</h1>"); 114 out.print("<p>JSPWiki is not running. This is probably due to a configuration error in your jspwiki.properties file, "); 115 out.print("or a problem with your servlet container. Please double-check everything before issuing a bug report "); 116 out.print("at jspwiki.apache.org.</p>"); 117 out.print("<p>We apologize for the inconvenience. No, really, we do. We're trying to "); 118 out.print("JSPWiki as easy as we can, but there is only so much we have time to test "); 119 out.print("platforms.</p>"); 120 out.print( "<p>Please go to the <a href='Install.jsp'>installer</a> to continue.</p>" ); 121 out.print("</body></html>"); 122 return; 123 } 124 125 // If we haven't done so, wrap the request 126 HttpServletRequest httpRequest = ( HttpServletRequest )request; 127 128 // Set the character encoding 129 httpRequest.setCharacterEncoding( m_engine.getContentEncoding().displayName() ); 130 131 if ( !isWrapped( request ) ) { 132 // Prepare the Session 133 try { 134 m_engine.getManager( AuthenticationManager.class ).login( httpRequest ); 135 final Session wikiSession = SessionMonitor.getInstance( m_engine ).find( httpRequest.getSession() ); 136 httpRequest = new WikiRequestWrapper( m_engine, httpRequest ); 137 LOG.debug( "Executed security filters for user={}, path={}",wikiSession.getLoginPrincipal().getName(), httpRequest.getRequestURI() ); 138 } catch( final WikiSecurityException e ) { 139 throw new ServletException( e ); 140 } 141 } 142 143 try { 144 ThreadContext.push( m_engine.getApplicationName() + ":" + httpRequest.getRequestURL() ); 145 chain.doFilter( httpRequest, response ); 146 } finally { 147 ThreadContext.pop(); 148 ThreadContext.remove( m_engine.getApplicationName() + ":" + httpRequest.getRequestURL() ); 149 } 150 } 151 152 /** 153 * Figures out the wiki context from the request. This method does not create the context if it does not exist. 154 * 155 * @param request The request to examine 156 * @return A valid WikiContext value (or null, if the context could not be located). 157 */ 158 protected Context getWikiContext( final ServletRequest request ) { 159 final HttpServletRequest httpRequest = (HttpServletRequest) request; 160 return ( Context )httpRequest.getAttribute( Context.ATTR_CONTEXT ); 161 } 162 163 /** 164 * Determines whether the request has been previously wrapped with a WikiRequestWrapper. 165 * We find the wrapper by recursively unwrapping successive request wrappers, if they have been supplied. 166 * 167 * @param request the current HTTP request 168 * @return <code>true</code> if the request has previously been wrapped; <code>false</code> otherwise 169 */ 170 private boolean isWrapped( ServletRequest request ) { 171 while( !(request instanceof WikiRequestWrapper ) && request instanceof HttpServletRequestWrapper ) { 172 request = ( ( HttpServletRequestWrapper ) request ).getRequest(); 173 } 174 return request instanceof WikiRequestWrapper; 175 } 176 177}