You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2014/01/09 16:01:42 UTC

svn commit: r1556823 - in /tomcat/trunk/test/org/apache/catalina: authenticator/TestSSOnonLoginAndBasicAuthenticator.java startup/TesterServletEncodeUrl.java

Author: markt
Date: Thu Jan  9 15:01:41 2014
New Revision: 1556823

URL: http://svn.apache.org/r1556823
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=55960
Improve SSO unit tests
Patch by Brian Burch

Added:
    tomcat/trunk/test/org/apache/catalina/startup/TesterServletEncodeUrl.java
      - copied, changed from r1556819, tomcat/trunk/test/org/apache/catalina/startup/TesterServlet.java
Modified:
    tomcat/trunk/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java

Modified: tomcat/trunk/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java?rev=1556823&r1=1556822&r2=1556823&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java (original)
+++ tomcat/trunk/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java Thu Jan  9 15:01:41 2014
@@ -22,14 +22,19 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import javax.servlet.http.HttpServletResponse;
+
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import org.junit.Test;
 
 import org.apache.catalina.Context;
-import org.apache.catalina.startup.TesterServlet;
+import org.apache.catalina.Session;
+import org.apache.catalina.session.ManagerBase;
+import org.apache.catalina.startup.TesterServletEncodeUrl;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.catalina.startup.TomcatBaseTest;
 import org.apache.tomcat.util.buf.ByteChunk;
@@ -47,12 +52,36 @@ import org.apache.tomcat.util.descriptor
  * simply cannot access protected resources. These tests exercise the
  * the way successfully authenticating a different webapp under the
  * BasicAuthenticator triggers the additional SSO logic for both webapps.
+ *
+ * <p>
+ * The two Authenticators are thoroughly exercised by two other unit test
+ * classes: TestBasicAuthParser and TestNonLoginAndBasicAuthenticator.
+ * This class mainly examines the way the Single SignOn Valve interacts with
+ * two webapps when the second cannot be authenticated directly, but needs
+ * to inherit its authentication via the other.
+ *
+ * <p>
+ * When the server and client can both use cookies, the authentication
+ * is preserved through the exchange of a JSSOSESSIONID cookie, which
+ * is different to the individual and unique JSESSIONID cookies assigned
+ * separately to the two webapp sessions.
+ *
+ * <p>
+ * The other situation examined is where the server returns authentication
+ * cookies, but the client is configured to ignore them. The Tomcat
+ * documentation clearly states that SSO <i>requires</i> the client to
+ * support cookies, so access to resources in other webapp containers
+ * receives no SSO assistance.
  */
 public class TestSSOnonLoginAndBasicAuthenticator extends TomcatBaseTest {
 
+    protected static final boolean USE_COOKIES = true;
+    protected static final boolean NO_COOKIES = !USE_COOKIES;
+
     private static final String USER = "user";
     private static final String PWD = "pwd";
     private static final String ROLE = "role";
+    private static final String NICE_METHOD = "Basic";
 
     private static final String HTTP_PREFIX = "http://localhost:";
     private static final String CONTEXT_PATH_NOLOGIN = "/nologin";
@@ -60,216 +89,370 @@ public class TestSSOnonLoginAndBasicAuth
     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 long SHORT_TIMEOUT_DELAY_MSECS =
-                                    ((SHORT_TIMEOUT_SECS + 3) * 1000);
-    private static final int LONG_TIMEOUT_SECS = 10;
-    private static final long LONG_TIMEOUT_DELAY_MSECS =
-                                    ((LONG_TIMEOUT_SECS + 5) * 1000);
-
-    private static String CLIENT_AUTH_HEADER = "authorization";
-    private static String SERVER_COOKIES = "Set-Cookie";
-    private static String BROWSER_COOKIES = "Cookie";
-
+    // session expiry in web.xml is defined in minutes
+    private static final int SHORT_SESSION_TIMEOUT_MINS = 1;
+    private static final int LONG_SESSION_TIMEOUT_MINS = 2;
+
+    // we don't change the expiry scan interval - just the iteration count
+    private static final int MANAGER_SCAN_INTERVAL_SECS = 10;
+    private static final int MANAGER_EXPIRE_SESSIONS_FAST = 1;
+
+    // now compute some delays - beware of the units!
+    private static final int EXTRA_DELAY_SECS = 5;
+    private static final long REASONABLE_MSECS_TO_EXPIRY =
+            (((MANAGER_SCAN_INTERVAL_SECS * MANAGER_EXPIRE_SESSIONS_FAST)
+                    + EXTRA_DELAY_SECS) * 1000);
+
+    private static final String CLIENT_AUTH_HEADER = "authorization";
+    private static final String SERVER_AUTH_HEADER = "WWW-Authenticate";
+    private static final String SERVER_COOKIE_HEADER = "Set-Cookie";
+    private static final String CLIENT_COOKIE_HEADER = "Cookie";
+    private static final String ENCODE_SESSION_PARAM = "jsessionid";
+    private static final String ENCODE_SSOSESSION_PARAM = "jssosessionid";
+
+    private static final
+            TestSSOnonLoginAndBasicAuthenticator.BasicCredentials
+                    NO_CREDENTIALS = null;
+    private static final
+            TestSSOnonLoginAndBasicAuthenticator.BasicCredentials
+                    GOOD_CREDENTIALS =
+                new TestSSOnonLoginAndBasicAuthenticator.BasicCredentials(
+                            NICE_METHOD, USER, PWD);
+
+    private Tomcat tomcat;
+    private Context basicContext;
+    private Context nonloginContext;
     private List<String> cookies;
+    private String encodedURL;
 
     /*
-     * Try to access an unprotected resource without an established
-     * SSO session.
-     * This should be permitted.
+     * Run some sanity checks without an established SSO session
+     * to make sure the test environment is correct.
      */
     @Test
-    public void testAcceptPublicNonLogin() throws Exception {
+    public void testEssentialEnvironment() throws Exception {
+
+        // should be permitted to access an unprotected resource.
         doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PUBLIC,
-                        false, false, 200);
+                       USE_COOKIES, HttpServletResponse.SC_OK);
+
+        // should not be permitted to access a protected resource
+        // with the two Authenticators used in the remaining tests.
+        doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED,
+                USE_COOKIES, HttpServletResponse.SC_FORBIDDEN);
+        doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED,
+                NO_CREDENTIALS, USE_COOKIES,
+                HttpServletResponse.SC_UNAUTHORIZED);
     }
 
-    /*
-     * Try to access a protected resource without an established
-     * SSO session.
-     * This should be rejected with SC_FORBIDDEN 403 status.
-     */
     @Test
-    public void testRejectProtectedNonLogin() throws Exception {
+    public void testEssentialEnvironmentWithoutCookies() throws Exception {
+
+        // should be permitted to access an unprotected resource.
+        doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PUBLIC,
+                       NO_COOKIES, HttpServletResponse.SC_OK);
+
+        // should not be permitted to access a protected resource
+        // with the two Authenticators used in the remaining tests.
         doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED,
-                        false, true, 403);
+                NO_COOKIES, HttpServletResponse.SC_FORBIDDEN);
+        doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED,
+                NO_CREDENTIALS, NO_COOKIES,
+                HttpServletResponse.SC_UNAUTHORIZED);
     }
 
     /*
      * Logon to access a protected resource using BASIC authentication,
      * which will establish an SSO session.
      * Wait until the SSO session times-out, then try to re-access
-     * the resource.
-     * This should be rejected with SC_FORBIDDEN 401 status, which
-     * will then be followed by successful re-authentication.
+     * the resource. This should be rejected with SC_FORBIDDEN 401 status.
+     *
+     * Note: this test will run for slightly more than 1 minute.
      */
     @Test
-    public void testBasicLoginSessionTimeout() throws Exception {
-        doTestBasic(USER, PWD, CONTEXT_PATH_LOGIN + URI_PROTECTED,
-                true, 401, false, 200);
-        // wait long enough for my session to expire
-        Thread.sleep(SHORT_TIMEOUT_DELAY_MSECS);
-        doTestBasic(USER, PWD, CONTEXT_PATH_LOGIN + URI_PROTECTED,
-                true, 401, false, 200);
+    public void testBasicAccessAndSessionTimeout() throws Exception {
+
+        setRapidSessionTimeoutDetection();
+
+        doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED,
+                NO_CREDENTIALS, USE_COOKIES,
+                HttpServletResponse.SC_UNAUTHORIZED);
+        doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED,
+                GOOD_CREDENTIALS, USE_COOKIES,
+                HttpServletResponse.SC_OK);
+
+        // verify the SSOID exists as a cookie
+        doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED,
+                GOOD_CREDENTIALS, USE_COOKIES,
+                HttpServletResponse.SC_OK);
+
+        // make the session time out and lose authentication
+        doImminentSessionTimeout(basicContext);
+
+        doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED,
+                NO_CREDENTIALS, USE_COOKIES,
+                HttpServletResponse.SC_UNAUTHORIZED);
     }
 
+
     /*
      * Logon to access a protected resource using BASIC authentication,
      * which will establish an SSO session.
      * Immediately try to access a protected resource in the NonLogin
-     * webapp, but without sending the SSO session cookie.
-     * This should be rejected with SC_FORBIDDEN 403 status.
+     * webapp while providing the SSO session cookie received from the
+     * first webapp. This should be successful with SC_OK 200 status.
      */
     @Test
-    public void testBasicLoginRejectProtectedWithoutCookies() throws Exception {
-        doTestBasic(USER, PWD, CONTEXT_PATH_LOGIN + URI_PROTECTED,
-                true, 401, false, 200);
+    public void testBasicLoginThenAcceptWithCookies() throws Exception {
+
+        doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED,
+                NO_CREDENTIALS, NO_COOKIES,
+                HttpServletResponse.SC_UNAUTHORIZED);
+        doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED,
+                GOOD_CREDENTIALS, USE_COOKIES, HttpServletResponse.SC_OK);
+
+        // send the cookie which proves we have an authenticated SSO session
         doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED,
-                        false, true, 403);
+                       USE_COOKIES, HttpServletResponse.SC_OK);
     }
 
     /*
      * Logon to access a protected resource using BASIC authentication,
      * which will establish an SSO session.
      * Immediately try to access a protected resource in the NonLogin
-     * webapp while sending the SSO session cookie provided by the
-     * first webapp.
-     * This should be successful with SC_OK 200 status.
+     * webapp, but without sending the SSO session cookie.
+     * This should be rejected with SC_FORBIDDEN 403 status.
      */
     @Test
-    public void testBasicLoginAcceptProtectedWithCookies() throws Exception {
-        doTestBasic(USER, PWD, CONTEXT_PATH_LOGIN + URI_PROTECTED,
-                true, 401, false, 200);
+    public void testBasicLoginThenRejectWithoutCookie() throws Exception {
+
+        doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED,
+                NO_CREDENTIALS, USE_COOKIES,
+                HttpServletResponse.SC_UNAUTHORIZED);
+        doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED,
+                GOOD_CREDENTIALS, USE_COOKIES,
+                HttpServletResponse.SC_OK);
+
+        // fail to send the authentication cookie to the other webapp.
         doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED,
-                        true, false, 200);
+                NO_COOKIES, HttpServletResponse.SC_FORBIDDEN);
+    }
+
+    /*
+     * Logon to access a protected resource using BASIC authentication,
+     * which will establish an SSO session.
+     * Then try to access a protected resource in the NonLogin
+     * webapp by sending the JSESSIONID from the redirect header.
+     * The access request should be rejected because the Basic webapp's
+     * sessionID is not valid for any other container.
+     */
+    @Test
+    public void testBasicAccessThenAcceptAuthWithUri() throws Exception {
+
+        setAlwaysUseSession();
+
+        // first, fail to access the protected resource without credentials
+        doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED,
+                NO_CREDENTIALS, NO_COOKIES,
+                HttpServletResponse.SC_UNAUTHORIZED);
+
+        // now, access the protected resource with good credentials
+        // to establish the session
+        doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED,
+                GOOD_CREDENTIALS, NO_COOKIES,
+                HttpServletResponse.SC_OK);
+
+        // next, access it again to harvest the session id url parameter
+        String forwardParam = "?nextUrl=" + CONTEXT_PATH_LOGIN + URI_PROTECTED;
+        doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED + forwardParam,
+                GOOD_CREDENTIALS, NO_COOKIES,
+                HttpServletResponse.SC_OK);
+
+        // verify the sessionID was encoded in the absolute URL
+        String firstEncodedURL = encodedURL;
+        assertTrue(firstEncodedURL.contains(ENCODE_SESSION_PARAM));
+
+        // access the protected resource with the encoded url (with session id)
+        doTestBasic(firstEncodedURL + forwardParam,
+                NO_CREDENTIALS, NO_COOKIES,
+                HttpServletResponse.SC_OK);
+
+        // verify the sessionID has not changed
+        // verify the SSO sessionID was not encoded
+        String secondEncodedURL = encodedURL;
+        assertEquals(firstEncodedURL, secondEncodedURL);
+        assertFalse(firstEncodedURL.contains(ENCODE_SSOSESSION_PARAM));
+
+        // extract the first container's session ID
+        int ix = secondEncodedURL.indexOf(ENCODE_SESSION_PARAM);
+        String sessionId = secondEncodedURL.substring(ix);
+
+        // expect to fail using that sessionID in a different container
+        doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED + ";" + sessionId,
+                NO_COOKIES, HttpServletResponse.SC_FORBIDDEN);
     }
 
     /*
      * Logon to access a protected resource using BASIC authentication,
      * which will establish an SSO session.
      * Immediately try to access a protected resource in the NonLogin
-     * webapp while sending the SSO session cookie provided by the
-     * first webapp.
-     * This should be successful with SC_OK 200 status.
+     * webapp while providing the SSO session cookie received from the
+     * first webapp. This should be successful with SC_OK 200 status.
      *
      * Then, wait long enough for the BASIC session to expire. (The SSO
      * session should remain active because the NonLogin session has
      * not yet expired).
-     *
      * Try to access the protected resource again, before the SSO session
-     * has expired.
-     * This should be successful with SC_OK 200 status.
+     * has expired. This should be successful with SC_OK 200 status.
      *
      * Finally, wait for the non-login session to expire and try again..
      * This should be rejected with SC_FORBIDDEN 403 status.
      *
      * (see bugfix https://issues.apache.org/bugzilla/show_bug.cgi?id=52303)
+     *
+     * Note: this test will run for slightly more than 3 minutes.
      */
     @Test
     public void testBasicExpiredAcceptProtectedWithCookies() throws Exception {
-        doTestBasic(USER, PWD, CONTEXT_PATH_LOGIN + URI_PROTECTED,
-                true, 401, false, 200);
+
+        setRapidSessionTimeoutDetection();
+
+        // begin with a repeat of testBasicLoginAcceptProtectedWithCookies
+        doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED,
+                NO_CREDENTIALS, USE_COOKIES,
+                HttpServletResponse.SC_UNAUTHORIZED);
+        doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED,
+                GOOD_CREDENTIALS, USE_COOKIES,
+                HttpServletResponse.SC_OK);
         doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED,
-                        true, false, 200);
+                       USE_COOKIES, HttpServletResponse.SC_OK);
 
         // wait long enough for the BASIC session to expire,
-        // but not long enough for NonLogin session expiry
-        Thread.sleep(SHORT_TIMEOUT_DELAY_MSECS);
-        doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED,
-                        true, false, 200);
+        // but not long enough for the NonLogin session expiry.
+        doImminentSessionTimeout(basicContext);
 
-        // wait long enough for my NonLogin session to expire
-        // and tear down the SSO session at the same time.
-        Thread.sleep(LONG_TIMEOUT_DELAY_MSECS);
+        // this successful NonLogin access should replenish the
+        // the individual session expiry time and keep the SSO session alive
         doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED,
-                        false, true, 403);
+                       USE_COOKIES, HttpServletResponse.SC_OK);
+
+        // wait long enough for the NonLogin session to expire,
+        // which will also tear down the SSO session at the same time.
+        doImminentSessionTimeout(nonloginContext);
+
+        doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, USE_COOKIES,
+                HttpServletResponse.SC_FORBIDDEN);
+        doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED,
+                NO_CREDENTIALS, USE_COOKIES,
+                HttpServletResponse.SC_UNAUTHORIZED);
+
     }
 
 
-    public void doTestNonLogin(String uri, boolean addCookies,
-            boolean expectedReject, int expectedRC)
-            throws Exception {
+    public void doTestNonLogin(String uri, boolean useCookie,
+            int expectedRC) throws Exception {
 
         Map<String,List<String>> reqHeaders = new HashMap<>();
-        if (addCookies) {
-            addCookies(reqHeaders);
-        }
         Map<String,List<String>> respHeaders = new HashMap<>();
 
+        if (useCookie && (cookies != null)) {
+            reqHeaders.put(CLIENT_COOKIE_HEADER + ":", cookies);
+        }
+
         ByteChunk bc = new ByteChunk();
         int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders,
                 respHeaders);
 
-        if (expectedReject) {
+        if (expectedRC != HttpServletResponse.SC_OK) {
             assertEquals(expectedRC, rc);
             assertTrue(bc.getLength() > 0);
         }
         else {
-            assertEquals(200, rc);
             assertEquals("OK", bc.toString());
-            saveCookies(respHeaders);
         }
 }
 
-    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<>();
-        Map<String,List<String>> respHeaders1 = new HashMap<>();
+    private void doTestBasic(String uri,
+            TestSSOnonLoginAndBasicAuthenticator.BasicCredentials credentials,
+            boolean useCookie, int expectedRC) throws Exception {
 
-        ByteChunk bc = new ByteChunk();
-        int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders1,
-                respHeaders1);
+        Map<String,List<String>> reqHeaders = new HashMap<>();
+        Map<String,List<String>> respHeaders = new HashMap<>();
 
-        if (expectedReject1) {
-            assertEquals(expectedRC1, rc);
-            assertTrue(bc.getLength() > 0);
+        if (useCookie && (cookies != null)) {
+            reqHeaders.put(CLIENT_COOKIE_HEADER + ":", cookies);
         }
         else {
-            assertEquals(200, rc);
-            assertEquals("OK", bc.toString());
-            return;
+            if (credentials != null) {
+                List<String> auth = new ArrayList<>();
+                auth.add(credentials.getCredentials());
+                reqHeaders.put(CLIENT_AUTH_HEADER, auth);
+            }
         }
 
-        // the second access attempt should be successful
-        String credentials = user + ":" + pwd;
-
-        String base64auth = Base64.encodeBase64String(
-                credentials.getBytes(StandardCharsets.ISO_8859_1));
-        String authLine = "Basic " + base64auth;
-
-        List<String> auth = new ArrayList<>();
-        auth.add(authLine);
-        Map<String,List<String>> reqHeaders2 = new HashMap<>();
-        reqHeaders2.put(CLIENT_AUTH_HEADER, auth);
-
-        Map<String,List<String>> respHeaders2 = new HashMap<>();
-
-        bc.recycle();
-        rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders2,
-                respHeaders2);
+        ByteChunk bc = new ByteChunk();
+        int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders,
+                respHeaders);
 
-        if (expectedReject2) {
-            assertEquals(expectedRC2, rc);
-            assertNull(bc.toString());
+        assertEquals("Unexpected Return Code", expectedRC, rc);
+        if (expectedRC != HttpServletResponse.SC_OK) {
+            assertTrue(bc.getLength() > 0);
+            if (expectedRC == HttpServletResponse.SC_UNAUTHORIZED) {
+                // The server should identify the acceptable method(s)
+                boolean methodFound = false;
+                List<String> authHeaders = respHeaders.get(SERVER_AUTH_HEADER);
+                for (String authHeader : authHeaders) {
+                    if (authHeader.indexOf(NICE_METHOD) > -1) {
+                        methodFound = true;
+                        break;
+                    }
+                }
+                assertTrue(methodFound);
+            }
         }
         else {
-            assertEquals(200, rc);
-            assertEquals("OK", bc.toString());
-            saveCookies(respHeaders2);
+            String thePage = bc.toString();
+            assertNotNull(thePage);
+            assertTrue(thePage.startsWith("OK"));
+            if (useCookie) {
+                List<String> newCookies = respHeaders.get(SERVER_COOKIE_HEADER);
+                if (newCookies != null) {
+                    // harvest cookies whenever the server sends some new ones
+                    cookies = newCookies;
+                }
+            }
+            else {
+                encodedURL = "";
+                final String start = "<a href=\"";
+                final String end = "\">";
+                int iStart = thePage.indexOf(start);
+                int iEnd = 0;
+                if (iStart > -1) {
+                    iStart += start.length();
+                    iEnd = thePage.indexOf(end, iStart);
+                    if (iEnd > -1) {
+                        encodedURL = thePage.substring(iStart, iEnd);
+                    }
+                }
+            }
         }
     }
 
 
+
+
+    /*
+     * setup two webapps for every test
+     *
+     * note: the super class tearDown method will stop tomcat
+     */
     @Override
     public void setUp() throws Exception {
 
         super.setUp();
 
         // create a tomcat server using the default in-memory Realm
-        Tomcat tomcat = getTomcatInstance();
+        tomcat = getTomcatInstance();
 
         // associate the SingeSignOn Valve before the Contexts
         SingleSignOn sso = new SingleSignOn();
@@ -280,71 +463,92 @@ public class TestSSOnonLoginAndBasicAuth
         tomcat.addRole(USER, ROLE);
 
         // setup both NonLogin and Login webapps
-        setUpNonLogin(tomcat);
-        setUpLogin(tomcat);
+        setUpNonLogin();
+        setUpLogin();
 
         tomcat.start();
     }
 
-    private void setUpNonLogin(Tomcat tomcat) throws Exception {
+    @Override
+    public void tearDown() throws Exception {
+
+        tomcat.stop();
+    }
+
+    private void setUpNonLogin() throws Exception {
 
         // Must have a real docBase for webapps - just use temp
-        Context ctxt = tomcat.addContext(CONTEXT_PATH_NOLOGIN,
+        nonloginContext = tomcat.addContext(CONTEXT_PATH_NOLOGIN,
                 System.getProperty("java.io.tmpdir"));
-        ctxt.setSessionTimeout(LONG_TIMEOUT_SECS);
+        nonloginContext.setSessionTimeout(LONG_SESSION_TIMEOUT_MINS);
 
-        // Add protected servlet
-        Tomcat.addServlet(ctxt, "TesterServlet1", new TesterServlet());
-        ctxt.addServletMapping(URI_PROTECTED, "TesterServlet1");
+        // Add protected servlet to the context
+        Tomcat.addServlet(nonloginContext, "TesterServlet1",
+                new TesterServletEncodeUrl());
+        nonloginContext.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);
+        nonloginContext.addConstraint(sc1);
 
-        // Add unprotected servlet
-        Tomcat.addServlet(ctxt, "TesterServlet2", new TesterServlet());
-        ctxt.addServletMapping(URI_PUBLIC, "TesterServlet2");
+        // Add unprotected servlet to the context
+        Tomcat.addServlet(nonloginContext, "TesterServlet2",
+                new TesterServletEncodeUrl());
+        nonloginContext.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);
+        nonloginContext.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());
+        nonloginContext.setLoginConfig(lc);
+        AuthenticatorBase nonloginAuthenticator = new NonLoginAuthenticator();
+        nonloginContext.getPipeline().addValve(nonloginAuthenticator);
     }
 
-    private void setUpLogin(Tomcat tomcat) throws Exception {
+    private void setUpLogin() throws Exception {
 
         // Must have a real docBase for webapps - just use temp
-        Context ctxt = tomcat.addContext(CONTEXT_PATH_LOGIN,
+        basicContext = 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");
+        basicContext.setSessionTimeout(SHORT_SESSION_TIMEOUT_MINS);
 
+        // Add protected servlet to the context
+        Tomcat.addServlet(basicContext, "TesterServlet3",
+                new TesterServletEncodeUrl());
+        basicContext.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);
+        basicContext.addConstraint(sc);
 
-        // Configure the appropriate authenticator
+        // Add unprotected servlet to the context
+        Tomcat.addServlet(basicContext, "TesterServlet4",
+                new TesterServletEncodeUrl());
+        basicContext.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);
+        basicContext.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());
+        basicContext.setLoginConfig(lc);
+        AuthenticatorBase basicAuthenticator = new BasicAuthenticator();
+        basicContext.getPipeline().addValve(basicAuthenticator);
     }
 
     /*
@@ -353,7 +557,7 @@ public class TestSSOnonLoginAndBasicAuth
     protected void saveCookies(Map<String,List<String>> respHeaders) {
 
         // we only save the Cookie values, not header prefix
-        cookies = respHeaders.get(SERVER_COOKIES);
+        cookies = respHeaders.get(SERVER_COOKIE_HEADER);
     }
 
     /*
@@ -362,7 +566,91 @@ public class TestSSOnonLoginAndBasicAuth
     protected void addCookies(Map<String,List<String>> reqHeaders) {
 
         if ((cookies != null) && (cookies.size() > 0)) {
-            reqHeaders.put(BROWSER_COOKIES + ":", cookies);
+            reqHeaders.put(CLIENT_COOKIE_HEADER + ":", cookies);
+        }
+    }
+
+    /*
+     * Force non-default behaviour for both Authenticators.
+     * The session id will not be regenerated after authentication,
+     * which is less secure but needed for browsers that will not
+     * handle cookies.
+     */
+    private void setAlwaysUseSession() {
+
+        ((AuthenticatorBase) basicContext.getAuthenticator())
+                .setAlwaysUseSession(true);
+        ((AuthenticatorBase) nonloginContext.getAuthenticator())
+                .setAlwaysUseSession(true);
+    }
+
+    /*
+     * Force faster timeout for an active Container than can
+     * be defined in web.xml. By getting to the active Session we
+     * can choose seconds instead of minutes.
+     * Note: shamelessly cloned from ManagerBase - beware of synch issues
+     *       on the underlying sessions.
+     */
+    private void doImminentSessionTimeout(Context activeContext) {
+
+        ManagerBase manager = (ManagerBase) activeContext.getManager();
+        Session[] sessions = manager.findSessions();
+        for (int i = 0; i < sessions.length; i++) {
+            if (sessions[i]!=null && sessions[i].isValid()) {
+                sessions[i].setMaxInactiveInterval(EXTRA_DELAY_SECS);
+                // leave it to be expired by the manager
+            }
+        }
+        try {
+            Thread.sleep(REASONABLE_MSECS_TO_EXPIRY);
+        } catch (InterruptedException ie) {
+            // ignored
+        }
+
+        // paranoid verification that active sessions have now gone
+        sessions = manager.findSessions();
+        assertTrue(sessions.length == 0);
+    }
+
+    /*
+     * Force rapid timeout scanning for both webapps
+     * The StandardManager default service cycle time is 10 seconds,
+     * with a session expiry scan every 6 cycles.
+     */
+    private void setRapidSessionTimeoutDetection() {
+
+        ((ManagerBase) basicContext.getManager())
+                .setProcessExpiresFrequency(MANAGER_EXPIRE_SESSIONS_FAST);
+        ((ManagerBase) nonloginContext.getManager())
+                .setProcessExpiresFrequency(MANAGER_EXPIRE_SESSIONS_FAST);
+    }
+
+    /*
+     * Encapsulate the logic to generate an HTTP header
+     * for BASIC Authentication.
+     * Note: only used internally, so no need to validate arguments.
+     */
+    private static final class BasicCredentials {
+
+        private final String method;
+        private final String username;
+        private final String password;
+        private final String credentials;
+
+        private BasicCredentials(String aMethod,
+                String aUsername, String aPassword) {
+            method = aMethod;
+            username = aUsername;
+            password = aPassword;
+            String userCredentials = username + ":" + password;
+            byte[] credentialsBytes =
+                    userCredentials.getBytes(StandardCharsets.ISO_8859_1);
+            String base64auth = Base64.encodeBase64String(credentialsBytes);
+            credentials= method + " " + base64auth;
+        }
+
+        private String getCredentials() {
+            return credentials;
         }
     }
 }
\ No newline at end of file

Copied: tomcat/trunk/test/org/apache/catalina/startup/TesterServletEncodeUrl.java (from r1556819, tomcat/trunk/test/org/apache/catalina/startup/TesterServlet.java)
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/startup/TesterServletEncodeUrl.java?p2=tomcat/trunk/test/org/apache/catalina/startup/TesterServletEncodeUrl.java&p1=tomcat/trunk/test/org/apache/catalina/startup/TesterServlet.java&r1=1556819&r2=1556823&rev=1556823&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/startup/TesterServlet.java (original)
+++ tomcat/trunk/test/org/apache/catalina/startup/TesterServletEncodeUrl.java Thu Jan  9 15:01:41 2014
@@ -24,10 +24,21 @@ import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-public class TesterServlet extends HttpServlet {
+/**
+ * A test servlet that will always encode the url in case the client requires
+ * session persistence but is not configured to support cookies.
+ */
+public class TesterServletEncodeUrl extends HttpServlet {
 
     private static final long serialVersionUID = 1L;
 
+    /**
+     * Almost minimal processing for a servlet.
+     *
+     * @param nextUrl The url the caller would like to go to next. If
+     *                supplied, put an encoded url into the returned
+     *                html page as a hyperlink.
+     */
     @Override
     protected void doGet(HttpServletRequest req, HttpServletResponse resp)
             throws ServletException, IOException {
@@ -35,5 +46,14 @@ public class TesterServlet extends HttpS
         resp.setContentType("text/plain");
         PrintWriter out = resp.getWriter();
         out.print("OK");
+
+        String param = req.getParameter("nextUrl");
+        if (param!=null) {
+            // append an encoded url to carry the sessionids
+            String targetUrl = resp.encodeURL(param);
+            out.print(". You want to go <a href=\"");
+            out.print(targetUrl);
+            out.print("\">here next</a>.");
+        }
     }
 }



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