init
This commit is contained in:
@@ -0,0 +1,683 @@
|
||||
/*
|
||||
* 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.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
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;
|
||||
import org.apache.tomcat.util.codec.binary.Base64;
|
||||
import org.apache.tomcat.util.descriptor.web.LoginConfig;
|
||||
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
|
||||
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
|
||||
|
||||
/**
|
||||
* Test BasicAuthenticator and NonLoginAuthenticator when a
|
||||
* SingleSignOn Valve is active.
|
||||
*
|
||||
* <p>
|
||||
* In the absence of SSO support, a webapp using NonLoginAuthenticator
|
||||
* 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";
|
||||
private static final String CONTEXT_PATH_LOGIN = "/login";
|
||||
private static final String URI_PROTECTED = "/protected";
|
||||
private static final String URI_PUBLIC = "/anyoneCanAccess";
|
||||
|
||||
// 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 int TIMEOUT_WAIT_SECS = EXTRA_DELAY_SECS +
|
||||
(MANAGER_SCAN_INTERVAL_SECS * MANAGER_EXPIRE_SESSIONS_FAST) * 5;
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
* Run some sanity checks without an established SSO session
|
||||
* to make sure the test environment is correct.
|
||||
*/
|
||||
@Test
|
||||
public void testEssentialEnvironment() throws Exception {
|
||||
|
||||
// should be permitted to access an unprotected resource.
|
||||
doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PUBLIC,
|
||||
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);
|
||||
}
|
||||
|
||||
@Test
|
||||
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,
|
||||
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.
|
||||
*
|
||||
* Note: this test should run for ~10 seconds.
|
||||
*/
|
||||
@Test
|
||||
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 while providing the SSO session cookie received from the
|
||||
* first webapp. This should be successful with SC_OK 200 status.
|
||||
*/
|
||||
@Test
|
||||
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,
|
||||
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, but without sending the SSO session cookie.
|
||||
* This should be rejected with SC_FORBIDDEN 403 status.
|
||||
*/
|
||||
@Test
|
||||
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,
|
||||
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;
|
||||
Assert.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;
|
||||
Assert.assertEquals(firstEncodedURL, secondEncodedURL);
|
||||
Assert.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 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.
|
||||
*
|
||||
* Finally, wait for the non-login session to expire and try again..
|
||||
* This should be rejected with SC_FORBIDDEN 403 status.
|
||||
*
|
||||
* (see bugfix https://bz.apache.org/bugzilla/show_bug.cgi?id=52303)
|
||||
*
|
||||
* Note: this test should run for ~20 seconds.
|
||||
*/
|
||||
@Test
|
||||
public void testBasicExpiredAcceptProtectedWithCookies() throws Exception {
|
||||
|
||||
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,
|
||||
USE_COOKIES, HttpServletResponse.SC_OK);
|
||||
|
||||
// wait long enough for the BASIC session to expire,
|
||||
// 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,
|
||||
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 useCookie,
|
||||
int expectedRC) throws Exception {
|
||||
|
||||
Map<String,List<String>> reqHeaders = new HashMap<>();
|
||||
Map<String,List<String>> respHeaders = new HashMap<>();
|
||||
|
||||
if (useCookie && (cookies != null)) {
|
||||
addCookies(reqHeaders);
|
||||
}
|
||||
|
||||
ByteChunk bc = new ByteChunk();
|
||||
int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders,
|
||||
respHeaders);
|
||||
|
||||
if (expectedRC != HttpServletResponse.SC_OK) {
|
||||
Assert.assertEquals(expectedRC, rc);
|
||||
Assert.assertTrue(bc.getLength() > 0);
|
||||
}
|
||||
else {
|
||||
Assert.assertEquals("OK", bc.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void doTestBasic(String uri,
|
||||
TestSSOnonLoginAndBasicAuthenticator.BasicCredentials credentials,
|
||||
boolean useCookie, int expectedRC) throws Exception {
|
||||
|
||||
Map<String,List<String>> reqHeaders = new HashMap<>();
|
||||
Map<String,List<String>> respHeaders = new HashMap<>();
|
||||
|
||||
if (useCookie && (cookies != null)) {
|
||||
addCookies(reqHeaders);
|
||||
}
|
||||
else {
|
||||
if (credentials != null) {
|
||||
List<String> auth = new ArrayList<>();
|
||||
auth.add(credentials.getCredentials());
|
||||
reqHeaders.put(CLIENT_AUTH_HEADER, auth);
|
||||
}
|
||||
}
|
||||
|
||||
ByteChunk bc = new ByteChunk();
|
||||
int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders,
|
||||
respHeaders);
|
||||
|
||||
Assert.assertEquals("Unexpected Return Code", expectedRC, rc);
|
||||
if (expectedRC != HttpServletResponse.SC_OK) {
|
||||
Assert.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;
|
||||
}
|
||||
}
|
||||
Assert.assertTrue(methodFound);
|
||||
}
|
||||
}
|
||||
else {
|
||||
String thePage = bc.toString();
|
||||
Assert.assertNotNull(thePage);
|
||||
Assert.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 = getTomcatInstance();
|
||||
|
||||
// associate the SingeSignOn Valve before the Contexts
|
||||
SingleSignOn sso = new SingleSignOn();
|
||||
tomcat.getHost().getPipeline().addValve(sso);
|
||||
|
||||
// add the test user and role to the Realm
|
||||
tomcat.addUser(USER, PWD);
|
||||
tomcat.addRole(USER, ROLE);
|
||||
|
||||
// setup both NonLogin and Login webapps
|
||||
setUpNonLogin();
|
||||
setUpLogin();
|
||||
|
||||
tomcat.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
|
||||
tomcat.stop();
|
||||
}
|
||||
|
||||
private void setUpNonLogin() throws Exception {
|
||||
|
||||
// Must have a real docBase for webapps - just use temp
|
||||
nonloginContext = tomcat.addContext(CONTEXT_PATH_NOLOGIN,
|
||||
System.getProperty("java.io.tmpdir"));
|
||||
nonloginContext.setSessionTimeout(LONG_SESSION_TIMEOUT_MINS);
|
||||
|
||||
// Add protected servlet to the context
|
||||
Tomcat.addServlet(nonloginContext, "TesterServlet1",
|
||||
new TesterServletEncodeUrl());
|
||||
nonloginContext.addServletMappingDecoded(URI_PROTECTED, "TesterServlet1");
|
||||
|
||||
SecurityCollection collection1 = new SecurityCollection();
|
||||
collection1.addPatternDecoded(URI_PROTECTED);
|
||||
SecurityConstraint sc1 = new SecurityConstraint();
|
||||
sc1.addAuthRole(ROLE);
|
||||
sc1.addCollection(collection1);
|
||||
nonloginContext.addConstraint(sc1);
|
||||
|
||||
// Add unprotected servlet to the context
|
||||
Tomcat.addServlet(nonloginContext, "TesterServlet2",
|
||||
new TesterServletEncodeUrl());
|
||||
nonloginContext.addServletMappingDecoded(URI_PUBLIC, "TesterServlet2");
|
||||
|
||||
SecurityCollection collection2 = new SecurityCollection();
|
||||
collection2.addPatternDecoded(URI_PUBLIC);
|
||||
SecurityConstraint sc2 = new SecurityConstraint();
|
||||
// do not add a role - which signals access permitted without one
|
||||
sc2.addCollection(collection2);
|
||||
nonloginContext.addConstraint(sc2);
|
||||
|
||||
// Configure the authenticator and inherit the Realm from Engine
|
||||
LoginConfig lc = new LoginConfig();
|
||||
lc.setAuthMethod("NONE");
|
||||
nonloginContext.setLoginConfig(lc);
|
||||
AuthenticatorBase nonloginAuthenticator = new NonLoginAuthenticator();
|
||||
nonloginContext.getPipeline().addValve(nonloginAuthenticator);
|
||||
}
|
||||
|
||||
private void setUpLogin() throws Exception {
|
||||
|
||||
// Must have a real docBase for webapps - just use temp
|
||||
basicContext = tomcat.addContext(CONTEXT_PATH_LOGIN,
|
||||
System.getProperty("java.io.tmpdir"));
|
||||
basicContext.setSessionTimeout(SHORT_SESSION_TIMEOUT_MINS);
|
||||
|
||||
// Add protected servlet to the context
|
||||
Tomcat.addServlet(basicContext, "TesterServlet3",
|
||||
new TesterServletEncodeUrl());
|
||||
basicContext.addServletMappingDecoded(URI_PROTECTED, "TesterServlet3");
|
||||
SecurityCollection collection = new SecurityCollection();
|
||||
collection.addPatternDecoded(URI_PROTECTED);
|
||||
SecurityConstraint sc = new SecurityConstraint();
|
||||
sc.addAuthRole(ROLE);
|
||||
sc.addCollection(collection);
|
||||
basicContext.addConstraint(sc);
|
||||
|
||||
// Add unprotected servlet to the context
|
||||
Tomcat.addServlet(basicContext, "TesterServlet4",
|
||||
new TesterServletEncodeUrl());
|
||||
basicContext.addServletMappingDecoded(URI_PUBLIC, "TesterServlet4");
|
||||
SecurityCollection collection2 = new SecurityCollection();
|
||||
collection2.addPatternDecoded(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");
|
||||
basicContext.setLoginConfig(lc);
|
||||
AuthenticatorBase basicAuthenticator = new BasicAuthenticator();
|
||||
basicContext.getPipeline().addValve(basicAuthenticator);
|
||||
}
|
||||
|
||||
/*
|
||||
* extract and save the server cookies from the incoming response
|
||||
*/
|
||||
protected void saveCookies(Map<String,List<String>> respHeaders) {
|
||||
// we only save the Cookie values, not header prefix
|
||||
List<String> cookieHeaders = respHeaders.get(SERVER_COOKIE_HEADER);
|
||||
if (cookieHeaders == null) {
|
||||
cookies = null;
|
||||
} else {
|
||||
cookies = new ArrayList<>(cookieHeaders.size());
|
||||
for (String cookieHeader : cookieHeaders) {
|
||||
cookies.add(cookieHeader.substring(0, cookieHeader.indexOf(';')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* add all saved cookies to the outgoing request
|
||||
*/
|
||||
protected void addCookies(Map<String,List<String>> reqHeaders) {
|
||||
if ((cookies != null) && (cookies.size() > 0)) {
|
||||
StringBuilder cookieHeader = new StringBuilder();
|
||||
boolean first = true;
|
||||
for (String cookie : cookies) {
|
||||
if (!first) {
|
||||
cookieHeader.append(';');
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
cookieHeader.append(cookie);
|
||||
}
|
||||
List<String> cookieHeaderList = new ArrayList<>(1);
|
||||
cookieHeaderList.add(cookieHeader.toString());
|
||||
reqHeaders.put(CLIENT_COOKIE_HEADER, cookieHeaderList);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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(EXTRA_DELAY_SECS * 1000);
|
||||
} catch (InterruptedException ie) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
// Paranoid verification that active sessions have now gone
|
||||
int count = 0;
|
||||
sessions = manager.findSessions();
|
||||
while (sessions.length != 0 && count < TIMEOUT_WAIT_SECS) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
// Ignore
|
||||
}
|
||||
sessions = manager.findSessions();
|
||||
count++;
|
||||
}
|
||||
|
||||
sessions = manager.findSessions();
|
||||
Assert.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user