tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Brian Burch <br...@pingtoo.com>
Subject New unit tests for Authenticators and SingleSignOn
Date Sat, 14 Jan 2012 08:24:59 GMT
I realise everyone will be busy on the 7.0.24 release, so don't let this 
distract you - it isn't urgent.

Before I started developing my unit tests for SSO, I wrote a test class 
for NonLoginAuthenticator and BasicAuthenticator. This test class checks 
the behaviour of both authenticators when the SSO valve is NOT present.

I decided a single class was better than two because a) there are very 
few cases to be tested with these very simple authenticators, and b) the 
SSO test class would be cloned from it and SSO needs to use multiple 
authenticators.

When someone has time, could you please take a look at my code and (if 
acceptable) commit it to the trunk. I also have two SSO test classes 
ready to go, but I will wait to see whether there is anything wrong with 
the first one.

Here is the source for the class, which passes checkstyle under ant 
validate on my system:


/*
  *  Licensed to the Apache Software Foundation (ASF) under one or more
  *  contributor license agreements.  See the NOTICE file distributed with
  *  this work for additional information regarding copyright ownership.
  *  The ASF licenses this file to You under the Apache License, Version 2.0
  *  (the "License"); you may not use this file except in compliance with
  *  the License.  You may obtain a copy of the License at
  *
  *      http://www.apache.org/licenses/LICENSE-2.0
  *
  *  Unless required by applicable law or agreed to in writing, software
  *  distributed under the License is distributed on an "AS IS" BASIS,
  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
implied.
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
package org.apache.catalina.authenticator;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import org.junit.Test;

import org.apache.catalina.Context;
import org.apache.catalina.deploy.LoginConfig;
import org.apache.catalina.deploy.SecurityCollection;
import org.apache.catalina.deploy.SecurityConstraint;
import org.apache.catalina.startup.TesterServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.catalina.util.Base64;
import org.apache.tomcat.util.buf.ByteChunk;

/**
  * Test BasicAuthenticator and NonLoginAuthenticator when a
  * SingleSignOn Valve is not active.
  *
  * In the absence of SSO support, these two authenticator classes
  * both have quite simple behaviour. By testing them together, we
  * can make sure they operate independently and confirm that no
  * SSO logic has been accidentally triggered.
  *
  * @author Brian Burch
  */
public class TestNonLoginAndBasicAuthenticator extends TomcatBaseTest {

     private static final String USER = "user";
     private static final String PWD = "pwd";
     private static final String ROLE = "role";

     private static final String HTTP_PREFIX = "http://localhost:";
     private static final String CONTEXT_PATH_NOLOGIN = "/nologin";
     private static final String CONTEXT_PATH_LOGIN = "/login";
     private static final String URI_PROTECTED = "/protected";
     private static final String URI_PUBLIC = "/anyoneCanAccess";

     private static final int SHORT_TIMEOUT_SECS = 4;
     private static final int LONG_TIMEOUT_SECS = 10;
     private static final long LONG_TIMEOUT_DELAY_MSECS =
                                     ((LONG_TIMEOUT_SECS + 2) * 1000);

     private static String CLIENT_AUTH_HEADER = "authorization";

     /*
      * Try to access an unprotected resource in a webapp that
      * does not have a login method defined.
      * This should be permitted.
      */
     @Test
     public void testAcceptPublicNonLogin() throws Exception {
         doTestNonLogin(USER, PWD, CONTEXT_PATH_NOLOGIN + URI_PUBLIC,
                         false, 200);
     }

     /*
      * Try to access a protected resource in a webapp that
      * does not have a login method defined.
      * This should be rejected with SC_FORBIDDEN 403 status.
      */
     @Test
     public void testRejectProtectedNonLogin() throws Exception {
         doTestNonLogin(USER, PWD, CONTEXT_PATH_NOLOGIN + URI_PROTECTED,
                         true, 403);
     }

     /*
      * Try to access an unprotected resource in a webapp that
      * has a BASIC login method defined.
      * This should be permitted without a challenge.
      */
     @Test
     public void testAcceptPublicBasic() throws Exception {
         doTestBasic(USER, PWD, CONTEXT_PATH_LOGIN + URI_PUBLIC,
                 false, 200, false, 200);
     }

     /*
      * Try to access a protected resource in a webapp that
      * has a BASIC login method defined. The access will be
      * challenged, authenticated and then permitted.
      */
     @Test
     public void testAcceptProtectedBasic() throws Exception {
         doTestBasic(USER, PWD, CONTEXT_PATH_LOGIN + URI_PROTECTED,
                 true, 401, false, 200);
     }

     /*
      * Logon to access a protected resource in a webapp that uses
      * BASIC authentication. Wait until that session times-out,
      * then re-access the resource.
      * This should be rejected with SC_FORBIDDEN 401 status, which
      * can be followed by successful re-authentication.
      */
     @Test
     public void testBasicLoginSessionTimeout() throws Exception {
         doTestBasic(USER, PWD, CONTEXT_PATH_LOGIN + URI_PROTECTED,
                 true, 401, false, 200);
         // wait long enough for the session above to expire
         Thread.sleep(LONG_TIMEOUT_DELAY_MSECS);
         doTestBasic(USER, PWD, CONTEXT_PATH_LOGIN + URI_PROTECTED,
                 true, 401, false, 200);
     }

     /*
      * Logon to access a protected resource in a webapp that uses
      * BASIC authentication. Then try to access a protected resource
      * in a different webapp that does not have a login method.
      * This should be rejected with SC_FORBIDDEN 403 status, confirming
      * there has been no cross-authentication between the webapps.
      */
     @Test
     public void testBasicLoginRejectProtected() throws Exception {
         doTestBasic(USER, PWD, CONTEXT_PATH_LOGIN + URI_PROTECTED,
                 true, 401, false, 200);
         doTestNonLogin(USER, PWD, CONTEXT_PATH_NOLOGIN + URI_PROTECTED,
                 true, 403);
     }


     public void doTestNonLogin(String user, String pwd, String uri,
             boolean expectedReject, int expectedRC)
             throws Exception {

         Map<String,List<String>> reqHeaders =
                 new HashMap<String,List<String>>();
         Map<String,List<String>> respHeaders =
                 new HashMap<String,List<String>>();

         ByteChunk bc = new ByteChunk();
         int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders,
                 respHeaders);

         if (expectedReject) {
             assertEquals(expectedRC, rc);
             assertNull(bc.toString());
         }
         else {
             assertEquals(200, rc);
             assertEquals("OK", bc.toString());
         }
}

     public void doTestBasic(String user, String pwd, String uri,
             boolean expectedReject1, int expectedRC1,
             boolean expectedReject2, int expectedRC2) throws Exception {

         // the first access attempt should be challenged
         Map<String,List<String>> reqHeaders1 =
                 new HashMap<String,List<String>>();
         Map<String,List<String>> respHeaders1 =
                 new HashMap<String,List<String>>();

         ByteChunk bc = new ByteChunk();
         int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders1,
                 respHeaders1);

         if (expectedReject1) {
             assertEquals(expectedRC1, rc);
             assertNull(bc.toString());
         }
         else {
             assertEquals(200, rc);
             assertEquals("OK", bc.toString());
             return;
         }

         // the second access attempt should be sucessful
         String credentials = USER + ":" + PWD;
         byte[] credentialsBytes = ByteChunk.convertToBytes(credentials);
         String base64auth = Base64.encode(credentialsBytes);
         String authLine = "Basic " + base64auth;

         List<String> auth = new ArrayList<String>();
         auth.add(authLine);
         Map<String,List<String>> reqHeaders2 = new 
HashMap<String,List<String>>();
         reqHeaders2.put(CLIENT_AUTH_HEADER, auth);

         Map<String,List<String>> respHeaders2 =
             new HashMap<String,List<String>>();

         bc.reset();
         rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders2,
                 respHeaders2);

         if (expectedReject2) {
             assertEquals(expectedRC2, rc);
             assertNull(bc.toString());
         }
         else {
             assertEquals(200, rc);
             assertEquals("OK", bc.toString());
         }
     }


     @Override
     public void setUp() throws Exception {

         super.setUp();

         // create a tomcat server using the default in-memory Realm
         Tomcat tomcat = getTomcatInstance();

         // add the test user and role to the Realm
         tomcat.addUser(USER, PWD);
         tomcat.addRole(USER, ROLE);

         // setup both NonLogin and Login webapps
         setUpNonLogin(tomcat);
         setUpLogin(tomcat);

         tomcat.start();
     }

     private void setUpNonLogin(Tomcat tomcat) throws Exception {

         // Must have a real docBase for webapps - just use temp
         Context ctxt = tomcat.addContext(CONTEXT_PATH_NOLOGIN,
                 System.getProperty("java.io.tmpdir"));
         ctxt.setSessionTimeout(LONG_TIMEOUT_SECS);

         // Add protected servlet
         Tomcat.addServlet(ctxt, "TesterServlet1", new TesterServlet());
         ctxt.addServletMapping(URI_PROTECTED, "TesterServlet1");

         SecurityCollection collection1 = new SecurityCollection();
         collection1.addPattern(URI_PROTECTED);
         SecurityConstraint sc1 = new SecurityConstraint();
         sc1.addAuthRole(ROLE);
         sc1.addCollection(collection1);
         ctxt.addConstraint(sc1);

         // Add unprotected servlet
         Tomcat.addServlet(ctxt, "TesterServlet2", new TesterServlet());
         ctxt.addServletMapping(URI_PUBLIC, "TesterServlet2");

         SecurityCollection collection2 = new SecurityCollection();
         collection2.addPattern(URI_PUBLIC);
         SecurityConstraint sc2 = new SecurityConstraint();
         // do not add a role - which signals access permitted without one
         sc2.addCollection(collection2);
         ctxt.addConstraint(sc2);

         // Configure the authenticator and inherit the Realm from Engine
         LoginConfig lc = new LoginConfig();
         lc.setAuthMethod("NONE");
         ctxt.setLoginConfig(lc);
         ctxt.getPipeline().addValve(new NonLoginAuthenticator());
     }

     private void setUpLogin(Tomcat tomcat) throws Exception {

         // Must have a real docBase for webapps - just use temp
         Context ctxt = tomcat.addContext(CONTEXT_PATH_LOGIN,
                 System.getProperty("java.io.tmpdir"));
         ctxt.setSessionTimeout(SHORT_TIMEOUT_SECS);

         // Add protected servlet
         Tomcat.addServlet(ctxt, "TesterServlet3", new TesterServlet());
         ctxt.addServletMapping(URI_PROTECTED, "TesterServlet3");
         SecurityCollection collection = new SecurityCollection();
         collection.addPattern(URI_PROTECTED);
         SecurityConstraint sc = new SecurityConstraint();
         sc.addAuthRole(ROLE);
         sc.addCollection(collection);
         ctxt.addConstraint(sc);

         // Add unprotected servlet
         Tomcat.addServlet(ctxt, "TesterServlet4", new TesterServlet());
         ctxt.addServletMapping(URI_PUBLIC, "TesterServlet4");

         SecurityCollection collection2 = new SecurityCollection();
         collection2.addPattern(URI_PUBLIC);
         SecurityConstraint sc2 = new SecurityConstraint();
         // do not add a role - which signals access permitted without one
         sc2.addCollection(collection2);
         ctxt.addConstraint(sc2);

         // Configure the authenticator and inherit the Realm from Engine
         LoginConfig lc = new LoginConfig();
         lc.setAuthMethod("BASIC");
         ctxt.setLoginConfig(lc);
         ctxt.getPipeline().addValve(new BasicAuthenticator());
     }

}


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Mime
View raw message