ASF Bugzilla – Attachment 31173 Details for
Bug 55960
TestSSOnonLoginAndBasicAuthenticator is flawed and incomplete
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
updated test class and new servlet test class
sso.patch (text/plain), 33.07 KB, created by
Brian Burch
on 2014-01-06 14:47:05 UTC
(
hide
)
Description:
updated test class and new servlet test class
Filename:
MIME Type:
Creator:
Brian Burch
Created:
2014-01-06 14:47:05 UTC
Size:
33.07 KB
patch
obsolete
>Index: test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java >=================================================================== >--- test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java (revision 1555554) >+++ test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java (working copy) >@@ -22,14 +22,19 @@ > 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 @@ > * 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 @@ > 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); >+ // 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; > >- private static String CLIENT_AUTH_HEADER = "authorization"; >- private static String SERVER_COOKIES = "Set-Cookie"; >- private static String BROWSER_COOKIES = "Cookie"; >+ // 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); >+ // but not long enough for the NonLogin session expiry. >+ doImminentSessionTimeout(basicContext); >+ >+ // this successful NonLogin access should replenish the >+ // the individual session expiry time and keep the SSO session alive > doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, >- true, false, 200); >+ USE_COOKIES, HttpServletResponse.SC_OK); > >- // 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); >- doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, >- false, true, 403); >+ // 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 { >+ private void doTestBasic(String uri, >+ TestSSOnonLoginAndBasicAuthenticator.BasicCredentials credentials, >+ boolean useCookie, int expectedRC) throws Exception { > >- // the first access attempt should be challenged >- Map<String,List<String>> reqHeaders1 = new HashMap<>(); >- Map<String,List<String>> respHeaders1 = new HashMap<>(); >+ Map<String,List<String>> reqHeaders = new HashMap<>(); >+ Map<String,List<String>> respHeaders = new HashMap<>(); > >- ByteChunk bc = new ByteChunk(); >- int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders1, >- respHeaders1); >- >- 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; >+ ByteChunk bc = new ByteChunk(); >+ int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders, >+ respHeaders); > >- 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); >- >- 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 @@ > 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); >+ basicContext.setSessionTimeout(SHORT_SESSION_TIMEOUT_MINS); > >- // Add protected servlet >- Tomcat.addServlet(ctxt, "TesterServlet3", new TesterServlet()); >- ctxt.addServletMapping(URI_PROTECTED, "TesterServlet3"); >- >+ // 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 @@ > 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,90 @@ > 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); > } > } >-} >\ No newline at end of file >+ >+ /* >+ * 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; >+ } >+ } >+} >Index: test/org/apache/catalina/startup/TesterServletEncodeUrl.java >=================================================================== >--- test/org/apache/catalina/startup/TesterServletEncodeUrl.java (working copy) >+++ test/org/apache/catalina/startup/TesterServletEncodeUrl.java (working copy) >@@ -24,10 +24,21 @@ > 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 @@ > 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>."); >+ } > } > }
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 55960
: 31173