You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@locus.apache.org on 2000/02/16 01:30:30 UTC

cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/servlets AuthServlet.java

costin      00/02/15 16:30:30

  Modified:    src/share/org/apache/tomcat/context DefaultCMSetter.java
               src/share/org/apache/tomcat/core BaseInterceptor.java
                        ContextManager.java Request.java RequestImpl.java
                        RequestInterceptor.java
               src/share/org/apache/tomcat/request SecurityCheck.java
  Added:       src/share/org/apache/tomcat/servlets AuthServlet.java
  Log:
  More security. Don't expect anything untill 3.1, the code is probably buggy and incomplete.
  
  Basic Auth works ( i.e. the protocol part - no real authorization ).
  
  To realy authorize you can extend SecurityCheck and override "checkPassword" and "userInRole"
  or implement a new interceptor based on SecurityCheck that will do whatever auth it needs to.
  
  Tomcat will impose no restrictions / APIs on how the authentication works, you can implement
  whatever scheme you want as an interceptor ( and JAAS or any other API for password and role
  checks).
  There are 2 callbacks you need to implement:
  
  - authenticate : if you recognize a request and feel you can authenticate,  do it and set the
  "remoteUser" field. ( XXX if the password is wrong return error - not implemented )
  
  - authorize: the request will have some "roles" set in, and the user. You just need to check
  if the user has one of the authorized roles. The same method will be called to implement the
  isUserInRole() API call ( with internal request with User and Roles fields set, but nothing
  else).
  If the user is not authorized you need to set the handler to a servlet that can perform
  authorization ( either the internal AuthServlet that will do Basic, or a form, or something
  else).
  ( XXX that can move up into ContextManager - it's common enough )
  
  Revision  Changes    Path
  1.14      +8 -1      jakarta-tomcat/src/share/org/apache/tomcat/context/DefaultCMSetter.java
  
  Index: DefaultCMSetter.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/context/DefaultCMSetter.java,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- DefaultCMSetter.java	2000/02/13 20:49:39	1.13
  +++ DefaultCMSetter.java	2000/02/16 00:30:28	1.14
  @@ -89,7 +89,9 @@
        *
        *  - Set up defaults for context interceptors and session if nothing is set
        */
  -    public void addContext(ContextManager cm, Context ctx) {
  +    public void addContext(ContextManager cm, Context ctx)
  +	throws TomcatException
  +    {
   	setEngineHeader( ctx );
   
   	if( ctx.getWorkDir() == null)
  @@ -100,6 +102,11 @@
   	    ctx.setSessionManager(new org.apache.tomcat.session.StandardSessionManager());
   
   	//  Alternative: org.apache.tomcat.session.ServerSessionManager.getManager();
  +	ServletWrapper authWrapper=new ServletWrapper();
  +	authWrapper.setContext( ctx );
  +	authWrapper.setServletClass( "org.apache.tomcat.servlets.AuthServlet" );
  +	authWrapper.setServletName( "authServlet");
  +	ctx.addServlet( authWrapper );
   
   	// XXX Loader properties - need to be set on loader!!
   	//ctx.setServletLoader( new org.apache.tomcat.loader.ServletClassLoaderImpl());
  
  
  
  1.5       +9 -0      jakarta-tomcat/src/share/org/apache/tomcat/core/BaseInterceptor.java
  
  Index: BaseInterceptor.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/BaseInterceptor.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- BaseInterceptor.java	2000/02/15 17:35:37	1.4
  +++ BaseInterceptor.java	2000/02/16 00:30:29	1.5
  @@ -89,6 +89,15 @@
   	return 0;
       }
   
  +    public int authenticate(Request request, Response response) {
  +	return 0;
  +    }
  +
  +    public int authorize(Request request, Response response) {
  +	return 0;
  +    }
  +
  +
       public int preService(Request request, Response response) {
   	return 0;
       }
  
  
  
  1.42      +25 -0     jakarta-tomcat/src/share/org/apache/tomcat/core/ContextManager.java
  
  Index: ContextManager.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/ContextManager.java,v
  retrieving revision 1.41
  retrieving revision 1.42
  diff -u -r1.41 -r1.42
  --- ContextManager.java	2000/02/15 17:35:37	1.41
  +++ ContextManager.java	2000/02/16 00:30:29	1.42
  @@ -525,6 +525,15 @@
   	    // XXX Hardcoded - it will be changed in the next step.( costin )
   	    processRequest( rrequest );
   
  +	    authenticate( rrequest, rresponse );
  +
  +	    int err=authorize( rrequest, rresponse );
  +	    if( err != 0 ) {
  +		// unauthorized access, redirect to login page.
  +		// XXX authorize will set request
  +	    }
  +	    
  +	    
   	    if( rrequest.getWrapper() == null ) {
   		log("ERROR: mapper returned no wrapper ");
   		log(rrequest.toString() );
  @@ -571,6 +580,22 @@
   
   	return 0;
       }
  +
  +    int authenticate( Request req, Response res ) {
  +	for( int i=0; i< requestInterceptors.size(); i++ ) {
  +	    ((RequestInterceptor)requestInterceptors.elementAt(i)).authenticate( req, res );
  +	}
  +	return 0;
  +    }
  +
  +    int authorize( Request req, Response res ) {
  +	for( int i=0; i< requestInterceptors.size(); i++ ) {
  +	    int err = ((RequestInterceptor)requestInterceptors.elementAt(i)).authorize( req, res );
  +	    if ( err != 0 ) return err;
  +	}
  +	return 0;
  +    }
  +
   
       int doBeforeBody( Request req, Response res ) {
   	for( int i=0; i< requestInterceptors.size(); i++ ) {
  
  
  
  1.27      +2 -0      jakarta-tomcat/src/share/org/apache/tomcat/core/Request.java
  
  Index: Request.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/Request.java,v
  retrieving revision 1.26
  retrieving revision 1.27
  diff -u -r1.26 -r1.27
  --- Request.java	2000/02/15 17:35:38	1.26
  +++ Request.java	2000/02/16 00:30:29	1.27
  @@ -191,6 +191,8 @@
   
       String getRemoteUser() ;
   
  +    void setRemoteUser(String s) ;
  +
       boolean isSecure() ;
   	
       Principal getUserPrincipal() ;
  
  
  
  1.18      +6 -3      jakarta-tomcat/src/share/org/apache/tomcat/core/RequestImpl.java
  
  Index: RequestImpl.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/RequestImpl.java,v
  retrieving revision 1.17
  retrieving revision 1.18
  diff -u -r1.17 -r1.18
  --- RequestImpl.java	2000/02/15 17:35:38	1.17
  +++ RequestImpl.java	2000/02/16 00:30:29	1.18
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/RequestImpl.java,v 1.17 2000/02/15 17:35:38 costin Exp $
  - * $Revision: 1.17 $
  - * $Date: 2000/02/15 17:35:38 $
  + * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/RequestImpl.java,v 1.18 2000/02/16 00:30:29 costin Exp $
  + * $Revision: 1.18 $
  + * $Date: 2000/02/16 00:30:29 $
    *
    * ====================================================================
    *
  @@ -255,6 +255,9 @@
   
       public String getPathInfo() {
           return pathInfo;
  +    }
  +    public void setRemoteUser(String s) {
  +	remoteUser=s;
       }
   
       public String getRemoteUser() {
  
  
  
  1.7       +21 -3     jakarta-tomcat/src/share/org/apache/tomcat/core/RequestInterceptor.java
  
  Index: RequestInterceptor.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/RequestInterceptor.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- RequestInterceptor.java	2000/02/12 03:38:50	1.6
  +++ RequestInterceptor.java	2000/02/16 00:30:29	1.7
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/RequestInterceptor.java,v 1.6 2000/02/12 03:38:50 costin Exp $
  - * $Revision: 1.6 $
  - * $Date: 2000/02/12 03:38:50 $
  + * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/RequestInterceptor.java,v 1.7 2000/02/16 00:30:29 costin Exp $
  + * $Revision: 1.7 $
  + * $Date: 2000/02/16 00:30:29 $
    *
    * ====================================================================
    *
  @@ -87,6 +87,24 @@
       /** Handle mapping inside a context
        */
       public int requestMap(Request request);
  +
  +    /** Will extract the user ID from the request, and check the password.
  +     *  It will set the user only if the user/password are correct, or user
  +     *  will be null.
  +     */
  +    public int authenticate(Request request, Response response);
  +
  +    /** Will check if the user is authorized, by checking if it is in one
  +     *  of the roles defined in security constraints.
  +     *
  +     *  This will also work for "isUserInRole()".
  +     *
  +     *  If the user is not authorized, it will return an error code ( 401 ),
  +     *  and will set the response fields for an internal redirect.
  +     *  ContextManager will take care of handling that.
  +     *  
  +     */
  +    public int authorize(Request request, Response response);
   
       /** Called before service method is invoked.
        */
  
  
  
  1.2       +132 -10   jakarta-tomcat/src/share/org/apache/tomcat/request/SecurityCheck.java
  
  Index: SecurityCheck.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/request/SecurityCheck.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- SecurityCheck.java	2000/02/15 17:35:40	1.1
  +++ SecurityCheck.java	2000/02/16 00:30:30	1.2
  @@ -81,23 +81,145 @@
       public SecurityCheck() {
       }
   	
  -    public int beforeBody( Request rrequest, Response response ) {
  -	String roles[]=rrequest.getContainer().getRoles();
  -	if( roles!=null ) {
  -	    System.out.println("XXX XXX XXX EXPECT ROLES " );
  -	    for( int i=0; i< roles.length; i++ ) {
  -		System.out.println(roles[i]);
  +    public int authenticate( Request req, Response response )
  +    {
  +	Context ctx=req.getContext();
  +	if( req.getRemoteUser() != null) return 0; // already authenticated
  +
  +	String authMethod=ctx.getAuthMethod();
  +	if( authMethod==null || "BASIC".equals(authMethod) ) {
  +	    String authorization = req.getHeader("Authorization");
  +	    // XXX we may have multiple headers ?
  +	    if (authorization != null  &&   authorization.startsWith("Basic ")) {
  +		authorization = authorization.substring(6).trim();
  +		String unencoded=base64Decode( authorization );
  +		int colon = unencoded.indexOf(':');
  +		if( ctx.getDebug() > 0 ) ctx.log( "BASIC auth " + authorization + " " + unencoded );
  +		if (colon < 0)
  +		    return 0;
  +		String username = unencoded.substring(0, colon);
  +		String password = unencoded.substring(colon + 1);
  +		if( checkPassword( username, password ) ) {
  +		    req.setRemoteUser( username );
  +		    if( ctx.getDebug() > 0 ) ctx.log( "BASEIC Auth:  " + username );
  +		} else {
  +		    // wrong password
  +		    errorPage( req, response );
  +		}
   	    }
  +	}
  +
  +	if( "DIGEST".equals( authMethod ) ) {
   
   	}
  -	// extract user, passwd
   
  -	// check if user is authenticated
  +	if( "CLIENT-CERT".equals( authMethod ) ) {
   
  -	// check if auth user is in one of the required roles
  -	
  +	}
  +	if( "FORM".equals( authMethod ) ) {
  +	    HttpSession session=req.getSession( false );
  +	    if( session == null )
  +		return 0; // not authenticated
  +	    String username=(String)session.getAttribute( "j_username" );
  +	    String password=(String)session.getAttribute( "j_password" );
  +	    if( checkPassword( username, password ) ) {
  +		req.setRemoteUser( username );
  +		if( ctx.getDebug() > 0 ) ctx.log( "Form Auth:  " + username );
  +	    } else {
  +		// wrong password and user
  +		errorPage( req, response );
  +	    }
  +	    if( ctx.getDebug() > 0 ) ctx.log( "FORM auth " + username + " " + password );
  +	}
  +
   	return 0;
       }
   
  +    /** Wrong user/password
  +     */
  +    private int errorPage( Request req, Response response ) {
  +	System.out.println("Wrong user/password");
  +	return 0;
  +    }
  +    
  +    public int authorize( Request req, Response response )
  +    {
  +	Context ctx=req.getContext();
   
  +	String roles[]=req.getContainer().getRoles();
  +	if( roles==null ) {
  +	    return 0;
  +	}
  +
  +	String user=req.getRemoteUser();
  +	if( user!=null ) {
  +	    if( ctx.getDebug() > 0 ) ctx.log( "Controled access for " + user );
  +	    for( int i=0; i< roles.length; i++ ) {
  +		if( userInRole( user, roles[i] ) )
  +		    return 0;
  +	    }
  +	}
  +
  +	if( ctx.getDebug() > 0 ) ctx.log( "Unauthorized " );
  +	// redirect to the right servlet 
  +	// XXX hardcoded
  +	ServletWrapper authWrapper=ctx.getServletByName( "authServlet" );
  +	req.setWrapper( authWrapper );
  +	
  + 	return HttpServletResponse.SC_UNAUTHORIZED;
  +	// XXX check transport
  +    }
  +
  +    static int base64[]= {
  +	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  +	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  +	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
  +	    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
  +	    64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
  +	    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
  +	    64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
  +	    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
  +	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  +	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  +	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  +	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  +	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  +	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  +	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  +	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
  +    };
  +    
  +    private String base64Decode( String orig ) {
  +	char chars[]=orig.toCharArray();
  +	StringBuffer sb=new StringBuffer();
  +	int i=0;
  +
  +	int shift = 0;   // # of excess bits stored in accum
  +	int acc = 0;
  +	
  +	for (i=0; i<chars.length; i++) {
  +	    int v = base64[ chars[i] & 0xFF ];
  +	    
  +	    if ( v >= 64 ) {
  +		if( chars[i] != '=' )
  +		    System.out.println("Wrong char in base64: " + chars[i]);
  +	    } else {
  +		acc= ( acc << 6 ) | v;
  +		shift += 6;
  +		if ( shift >= 8 ) shift -= 8;
  +		sb.append( (char) ((acc >> shift) & 0xff));
  +	    }
  +	}
  +	return sb.toString();
  +    }
  +
  +    private boolean checkPassword( String user, String pass ) {
  +	System.out.println("Checking " + user + " " + pass );
  +	return true;
  +    }
  +
  +    private boolean userInRole( String user, String role ) {
  +	System.out.println("Checking role " + user + " " + role );
  +	return true;
  +    }
   }
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/servlets/AuthServlet.java
  
  Index: AuthServlet.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */ 
  
  
  package org.apache.tomcat.servlets;
  
  import org.apache.tomcat.util.*;
  import org.apache.tomcat.core.*;
  import java.io.*;
  import javax.servlet.*;
  import javax.servlet.http.*;
  
  
  /**
   * Will authenticate the request for non-form auth
   * ( sort of "default form auth" );
   *
   */
  public class AuthServlet extends HttpServlet {
      
      public void service(HttpServletRequest request,
  			HttpServletResponse response)
  	throws ServletException, IOException
      {
  	Request req=((HttpServletRequestFacade)request).getRealRequest();
  
  	String realm=req.getContext().getRealmName();
  	if(realm==null) realm="default";
  	response.setHeader( "WWW-Authenticate", "Basic \"" + realm + "\"");
  	response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
      }
  }