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>login_page</tt>, the value should be an absolute
037      path (from the website's document root) to the login page or URL.
038    The following key is optional:
039      <li>key name: <tt>use_cookie</tt>, the value should be <tt>"true"</tt>
040      or <tt>"false"</tt>. <tt>true</tt> means save the original target page
041      as a cookie, <tt>false</tt> means save that page as part of the URL.
042      Defaults to <tt>false</tt>
043    </ul>
044    
045    <b>Note:</b> this class cannot be directly instantiated (since it's
046    abstract) and should not be specified as the name of some filter in the
047    servlet container's web.xml file.
048    
049    @author hursh jain
050    **/
051    public abstract class AuthFilter implements Filter 
052    {
053    private static  final boolean dbg = false;
054    
055    boolean     use_cookie = true;
056    FilterConfig  config;
057    ServletContext  context;
058    String      login_page;
059      
060    public void init(FilterConfig config) throws ServletException 
061      {
062      this.config = config;
063      this.context = config.getServletContext();
064      login_page = config.getInitParameter("login_page");
065    
066      if (login_page == null)
067        throw new ServletException("AuthFilter: the login_page parameter was missing");
068      
069      String tmp = config.getInitParameter("use_cookie");
070      if (tmp != null)
071        this.use_cookie = Boolean.valueOf(tmp).booleanValue();
072      }
073    
074    public void destroy() 
075      {
076      config = null;
077      context = null;
078      }
079    
080    public void doFilter (
081      final ServletRequest req, final ServletResponse res, final FilterChain chain) 
082    throws ServletException, IOException
083      {
084      if (dbg) System.out.println(">>>AuthFilter: doFilter() START");
085      
086        if ( (! (req instanceof HttpServletRequest)) ||
087         (! (res instanceof HttpServletResponse)) ) 
088        {
089            throw new ServletException("Request type was not HTTP/HTTPS");
090          }
091        
092        final HttpServletRequest  request = (HttpServletRequest) req;
093      final HttpServletResponse response = (HttpServletResponse) res;
094    
095      boolean loggedin = false;
096      try {
097        loggedin = isUserLoggedIn(request, response);
098        }
099      catch (Exception e) {
100        throw new ServletException(e);
101        }
102        
103      if (! loggedin ) 
104        {
105        if (dbg) System.out.println(">>>AuthFilter: user not logged in");
106        showLoginPage(request, response);
107        }
108      else {
109        if (dbg) System.out.println(">>>AuthFilter: user logged in"); 
110        //only if user is logged in
111        chain.doFilter(req, res);
112        }
113    
114        if (dbg) System.out.println(">>>AuthFilter: doFilter() END");
115      }
116      
117    /**
118    Redirects the user to a login page. The login page must set login status in
119    some way understandable by invokations of this method.
120    */
121    protected void showLoginPage(
122      final HttpServletRequest req, final HttpServletResponse res)
123    throws ServletException, IOException
124      {
125      /*
126       requestURL gives us the protocol, port, the entire URL
127       (including path info) whereas requestURI would only give
128       us the entire path but not the protocol, port etc. We
129       save the full URL and the query string
130      */
131      final StringBuffer buf = req.getRequestURL();
132    
133      final String query = req.getQueryString();
134      if (query != null) {
135        buf.append('?');
136        buf.append(query);
137        }
138    
139      //we encode because the login target may itself contain
140      //chars like ? and = etc
141      String login_target = URLEncoder.encode(buf.toString());
142    
143      //set target to original request page
144      if (use_cookie) {
145        final Cookie cookie = new Cookie("target", login_target);
146        cookie.setPath("/");
147        cookie.setMaxAge(-1); //only need it for browser session
148        res.addCookie(cookie);
149        if (dbg) System.out.println("setting cookie: " + cookie.getValue());
150        }
151      else {
152        login_page =  login_page + "?target=" +  login_target;
153        }
154      
155      res.sendRedirect(login_page);  //login_page NOT target  
156      }
157    
158    /**
159    This method should somehow check to see if the user is logged in or not.
160    Typically, this will be done via getting a session_id (either from a cookie
161    or a URL) and using that session_id to search for a memory or database
162    session data to see if that session still exists and has not expired.
163    */
164    public abstract boolean isUserLoggedIn(
165      HttpServletRequest req, HttpServletResponse res)
166    throws Exception;
167          
168    } //~class AuthFilter
169