001    // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 
002    // The Molly framework is freely distributable under the terms of an
003    // MIT-style license. For details, see the molly pages web site at:
004    // http://www.mollypages.org/. Use, modify, have fun !
005    
006    package fc.web.servlet;
007    
008    import java.io.*;
009    import java.net.*;
010    import javax.servlet.*;
011    import javax.servlet.http.*;
012    
013    import java.util.*;
014    
015    import fc.io.*;
016    import fc.web.*;
017    import fc.util.*;
018    
019    /** 
020    An abstract servlet filter that only allows authenticated access to a
021    resource. This filter will redirect the user to the <tt>login_page</tt> if
022    the user is not logged in. The originally requested target URL (if any)
023    will be saved via the method. The saved URL will be complete, i.e., will
024    start from the protocol string <tt>http..</tt> upto and including any query
025    string. Note: if the original URL was submitted via POST, the POST data is
026    <u>not</u> saved, only the target URL is saved.
027    <p>
028    There are 2 ways that the original URL can be saved. One is to save it as a
029    temporary cookie. This implies that the client must have cookies enabled.
030    The other way is to tack it on to the URL as a url parameter. <u>In either
031    case, the original target is saved as a parameter with
032    name=<tt>login.target</tt> and value=<tt>URLEncoder.encode(target_page)</tt></u>
033    <p>
034    The following keys must be specified as the init parameters.
035    <ul>
036      <li>key name: <tt>appName</tt>, the name of the webapp (the name is an
037      arbitrary string but must be the same as is specified in the init parameter
038      of {@link WebApp}).
039      <li>key name: <tt>login_page</tt>, the value should be an absolute
040      path (from the website's document root) to the login page or URL.
041    The following key is optional:
042      <li>key name: <tt>use_cookie</tt>, the value should be <tt>"true"</tt>
043      or <tt>"false"</tt>. <tt>true</tt> means save the original target page
044      as a cookie, <tt>false</tt> means save that page as part of the URL.
045      Defaults to <tt>false</tt>
046    </ul>
047    
048    <b>Note:</b> this class cannot be directly instantiated (since it's
049    abstract) and should not be specified as the name of some filter in the
050    servlet container's web.xml file.
051    
052    @author hursh jain
053    **/
054    public abstract class AuthFilter implements Filter 
055    {
056    private static  final boolean dbg = false;
057    
058    boolean     use_cookie = true;
059    FilterConfig  config;
060    ServletContext  context;
061    String      login_page;
062    String      appName;
063      
064    public void init(FilterConfig config) throws ServletException 
065      {
066      this.config = config;
067      this.context = config.getServletContext();
068      login_page = config.getInitParameter("login_page");
069    
070      if (login_page == null)
071        throw new ServletException("AuthFilter: the login_page parameter was missing");
072      
073      appName = config.getInitParameter("appName");
074      if (appName == null)
075        throw new ServletException("AuthFilter: the appName parameter was missing");
076        
077      String tmp = config.getInitParameter("use_cookie");
078      if (tmp != null)
079        this.use_cookie = Boolean.valueOf(tmp).booleanValue();
080      }
081    
082    public void destroy() 
083      {
084      config = null;
085      context = null;
086      }
087    
088    public void doFilter (
089      final ServletRequest req, final ServletResponse res, final FilterChain chain) 
090    throws ServletException, IOException
091      {
092      if (dbg) System.out.println(">>>AuthFilter: doFilter() START");
093      
094        if ( (! (req instanceof HttpServletRequest)) ||
095         (! (res instanceof HttpServletResponse)) ) 
096        {
097            throw new ServletException("Request type was not HTTP/HTTPS");
098          }
099        
100        final HttpServletRequest  request = (HttpServletRequest) req;
101      final HttpServletResponse response = (HttpServletResponse) res;
102    
103      boolean loggedin = false;
104      try {
105        loggedin = isUserLoggedIn(request, response);
106        }
107      catch (Exception e) {
108        throw new ServletException(e);
109        }
110        
111      if (! loggedin ) 
112        {
113        if (dbg) System.out.println(">>>AuthFilter: user not logged in");
114        showLoginPage(request, response);
115        }
116      else {
117        if (dbg) System.out.println(">>>AuthFilter: user logged in"); 
118        //only if user is logged in
119        chain.doFilter(req, res);
120        }
121    
122        if (dbg) System.out.println(">>>AuthFilter: doFilter() END");
123      }
124      
125    /**
126    Redirects the user to a login page. The login page must set login status in
127    some way understandable by invokations of this method.
128    */
129    protected void showLoginPage(
130      final HttpServletRequest req, final HttpServletResponse res)
131    throws ServletException, IOException
132      {
133      /*
134       requestURL gives us the protocol, port, the entire URL
135       (including path info) whereas requestURI would only give
136       us the entire path but not the protocol, port etc. We
137       save the full URL and the query string
138      */
139      final StringBuffer buf = req.getRequestURL();
140    
141      final String query = req.getQueryString();
142      if (query != null) {
143        buf.append('?');
144        buf.append(query);
145        }
146    
147      //we encode because the login target may itself contain
148      //chars like ? and = etc
149      String login_target = URLEncoder.encode(buf.toString());
150    
151      //set target to original request page
152      if (use_cookie) {
153        final Cookie cookie = new Cookie("target", login_target);
154        cookie.setPath("/");
155        cookie.setMaxAge(-1); //only need it for browser session
156        res.addCookie(cookie);
157        if (dbg) System.out.println("setting cookie: " + cookie.getValue());
158        }
159      else {
160        login_page =  login_page + "?target=" +  login_target;
161        }
162      
163      res.sendRedirect(login_page);  //login_page NOT target  
164      }
165    
166    /**
167    This method should somehow check to see if the user is logged in or not.
168    Typically, this will be done via getting a session_id (either from a cookie
169    or a URL) and using that session_id to search for a memory or database
170    session data to see if that session still exists and has not expired.
171    */
172    public abstract boolean isUserLoggedIn(
173      HttpServletRequest req, HttpServletResponse res)
174    throws Exception;
175          
176    } //~class AuthFilter
177