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     */
019    package org.apache.wiki.ui;
020    
021    import java.io.IOException;
022    import java.io.PrintWriter;
023    
024    import javax.servlet.*;
025    import javax.servlet.http.HttpServletRequest;
026    import javax.servlet.http.HttpServletRequestWrapper;
027    
028    import org.apache.log4j.Logger;
029    import org.apache.log4j.NDC;
030    
031    import org.apache.wiki.WikiContext;
032    import org.apache.wiki.WikiEngine;
033    import org.apache.wiki.WikiSession;
034    import org.apache.wiki.auth.SessionMonitor;
035    import org.apache.wiki.auth.WikiSecurityException;
036    import org.apache.wiki.tags.WikiTagBase;
037    
038    /**
039     * Filter that verifies that the {@link org.apache.wiki.WikiEngine} is running, and
040     * sets the authentication status for the user's WikiSession. Each HTTP request
041     * processed by this filter is wrapped by a {@link WikiRequestWrapper}. The wrapper's
042     * primary responsibility is to return the correct <code>userPrincipal</code> and
043     * <code>remoteUser</code> for authenticated JSPWiki users (whether 
044     * authenticated by container or by JSPWiki's custom system).
045     * The wrapper's other responsibility is to incorporate JSPWiki built-in roles
046     * into the role-checking algorithm for {@link  HttpServletRequest#isUserInRole(String)}.
047     * Just before the request is wrapped, the method {@link org.apache.wiki.auth.AuthenticationManager#login(HttpServletRequest)} executes;
048     * this method contains all of the logic needed to grab any user login credentials set 
049     * by the container or by cookies.
050     *  
051     *
052     */
053    public class WikiServletFilter implements Filter
054    {
055        protected static final Logger log = Logger.getLogger( WikiServletFilter.class );
056        protected WikiEngine m_engine = null;
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 WikiEngine cannot be started.
071         */
072        public void init( FilterConfig config ) throws ServletException
073        {
074            ServletContext context = config.getServletContext();
075    
076            // TODO REMOVEME when resolving JSPWIKI-129
077            if( System.getSecurityManager() != null )
078            {
079                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." );
080            }
081    
082            m_engine = WikiEngine.getInstance( context, null );
083        }
084    
085        /**
086         * Destroys the WikiServletFilter.
087         */
088        public void destroy()
089        {
090        }
091    
092        /**
093        * Checks that the WikiEngine is running ok, wraps the current
094        * HTTP request, and sets the correct authentication state for the users's
095        * WikiSession. First, the method {@link org.apache.wiki.auth.AuthenticationManager#login(HttpServletRequest)}
096        * executes, which sets the authentication state. Then, the request is wrapped with a
097        * {@link WikiRequestWrapper}.
098        * @param request the current HTTP request object
099        * @param response the current HTTP response object
100        * @param chain The Filter chain passed down.
101        * @throws ServletException if {@link org.apache.wiki.auth.AuthenticationManager#login(HttpServletRequest)} fails for any reason
102        * @throws IOException If writing to the servlet response fails. 
103        */
104        public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException
105        {
106            //
107            //  Sanity check; it might be true in some conditions, but we need to know where.
108            //
109            if( chain == null )
110            {
111                throw new ServletException("FilterChain is null, even if it should not be.  Please report this to the jspwiki development team.");
112            }
113            
114            if( m_engine == null )
115            {
116                PrintWriter out = response.getWriter();
117                out.print("<html><head><title>Fatal problem with JSPWiki</title></head>");
118                out.print("<body>");
119                out.print("<h1>JSPWiki has not been started</h1>");
120                out.print("<p>JSPWiki is not running.  This is probably due to a configuration error in your jspwiki.properties file, ");
121                out.print("or a problem with your servlet container.  Please double-check everything before issuing a bug report ");
122                out.print("at jspwiki.apache.org.</p>");
123                out.print("<p>We apologize for the inconvenience.  No, really, we do.  We're trying to ");
124                out.print("JSPWiki as easy as we can, but there is only so much we have time to test ");
125                out.print("platforms.</p>");
126                out.print( "<p>Please go to the <a href='Install.jsp'>installer</a> to continue.</p>" );
127                out.print("</body></html>");
128                return;
129            }   
130            
131            // If we haven't done so, wrap the request
132            HttpServletRequest httpRequest = (HttpServletRequest) request;
133            
134            // Set the character encoding
135            httpRequest.setCharacterEncoding( m_engine.getContentEncoding() );
136            
137            if( m_engine.getBaseURL().length() == 0 && !httpRequest.getRequestURI().endsWith("Install.jsp") )
138            {
139                PrintWriter out = response.getWriter();
140                
141                out.print( "<html><head><title>JSPWiki installation start</title></head>" );
142                out.print( "<body>" );
143                out.print( "<h1>JSPWiki installation</h1>" );
144                out.print( "<p>Hello!  It appears that this is your first jspwiki installation." );
145                out.print( "(Or, you have removed jspwiki.baseURL from your property file.) " );
146                out.print( "Therefore, you will need to start the installation process. " );
147                out.print( "Please <a href='Install.jsp'>continue to the installer</a>." );
148                out.print( "</p>");
149                out.print( "<p>If you just used the installer, then please restart your servlet container to get rid of this message.</p>" );
150                out.print("</body></html>");            
151                return;
152            }
153            
154            if ( !isWrapped( request ) )
155            {
156                // Prepare the WikiSession
157                try
158                {
159                    m_engine.getAuthenticationManager().login( httpRequest );
160                    WikiSession wikiSession = SessionMonitor.getInstance( m_engine ).find( httpRequest.getSession() );
161                    httpRequest = new WikiRequestWrapper( m_engine, httpRequest );
162                    if ( log.isDebugEnabled() )
163                    {
164                        log.debug( "Executed security filters for user=" + wikiSession.getLoginPrincipal().getName() + ", path=" + httpRequest.getRequestURI() );
165                    }
166                }
167                catch ( WikiSecurityException e )
168                {
169                    throw new ServletException( e );
170                }
171            }
172    
173            try
174            {
175                NDC.push( m_engine.getApplicationName()+":"+httpRequest.getRequestURL() );
176                
177                chain.doFilter( httpRequest, response );
178            }
179            finally
180            {
181                NDC.pop();
182                NDC.remove();
183            }
184    
185        }
186    
187        /**
188         *  Figures out the wiki context from the request.  This method does not create the
189         *  context if it does not exist.
190         *  
191         *  @param request The request to examine
192         *  @return A valid WikiContext value (or null, if the context could not be located).
193         */
194        protected WikiContext getWikiContext( ServletRequest  request )
195        {
196            HttpServletRequest httpRequest = (HttpServletRequest) request;
197        
198            WikiContext ctx = (WikiContext) httpRequest.getAttribute( WikiTagBase.ATTR_CONTEXT );
199            
200            return ctx;
201        }
202    
203        /** 
204         * Determines whether the request has been previously wrapped with a WikiRequestWrapper. 
205         * We find the wrapper by recursively unwrapping successive request wrappers, if they have been supplied.
206         * @param request the current HTTP request
207         * @return <code>true</code> if the request has previously been wrapped;
208         * <code>false</code> otherwise
209         */
210        private boolean isWrapped( ServletRequest request )
211        {
212            while ( !(request instanceof WikiRequestWrapper )
213                && request != null
214                && request instanceof HttpServletRequestWrapper )
215            {
216                request = ((HttpServletRequestWrapper) request).getRequest();
217            }
218            return request instanceof WikiRequestWrapper ? true : false;
219        }
220    
221    }