You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by en...@apache.org on 2021/05/26 18:31:46 UTC
[sling-org-apache-sling-auth-form] branch master updated:
SLING-10421 validate configured and client supplied cookie domain value
(#4)
This is an automated email from the ASF dual-hosted git repository.
enorman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-auth-form.git
The following commit(s) were added to refs/heads/master by this push:
new f06f71f SLING-10421 validate configured and client supplied cookie domain value (#4)
f06f71f is described below
commit f06f71fcb4e1100375f8adbeb3d5e6e0a5c755ee
Author: Eric Norman <er...@gmail.com>
AuthorDate: Wed May 26 11:31:32 2021 -0700
SLING-10421 validate configured and client supplied cookie domain value (#4)
also add integration tests to cover the code changes
---
.../auth/form/impl/FormAuthenticationHandler.java | 75 ++++--
...10290IT.java => AuthFormClientTestSupport.java} | 153 ++++--------
.../apache/sling/auth/form/it/SLING10290IT.java | 273 +--------------------
.../auth/form/it/SLING10421InvalidDomainIT.java | 141 +++++++++++
.../auth/form/it/SLING10421ValidDomainIT.java | 147 +++++++++++
5 files changed, 387 insertions(+), 402 deletions(-)
diff --git a/src/main/java/org/apache/sling/auth/form/impl/FormAuthenticationHandler.java b/src/main/java/org/apache/sling/auth/form/impl/FormAuthenticationHandler.java
index bbfc8df..8ee2cd2 100644
--- a/src/main/java/org/apache/sling/auth/form/impl/FormAuthenticationHandler.java
+++ b/src/main/java/org/apache/sling/auth/form/impl/FormAuthenticationHandler.java
@@ -728,14 +728,7 @@ public class FormAuthenticationHandler extends DefaultAuthenticationFeedbackHand
* in an HTTP Cookie.
*/
private static class CookieStorage implements AuthenticationStorage {
-
- /**
- * The Set-Cookie header used to manage the login cookie.
- *
- * @see CookieStorage#setCookie(HttpServletRequest, HttpServletResponse, String,
- * String, int, String)
- */
- private static final String HEADER_SET_COOKIE = "Set-Cookie";
+ private final Logger log = LoggerFactory.getLogger(getClass());
private final String cookieName;
private final String domainCookieName;
@@ -777,6 +770,12 @@ public class FormAuthenticationHandler extends DefaultAuthenticationFeedbackHand
if (cookieDomain == null || cookieDomain.length() == 0) {
cookieDomain = defaultCookieDomain;
}
+
+ if (!isValidCookieDomain(request, cookieDomain)) {
+ log.warn("Sending formauth cookies without a cookie domain because the configured value is invalid for the request");
+ cookieDomain = null;
+ }
+
setCookie(request, response, this.cookieName, cookieValue, -1, cookieDomain);
// send the cookie domain cookie if domain is not null
@@ -797,10 +796,23 @@ public class FormAuthenticationHandler extends DefaultAuthenticationFeedbackHand
oldCookie = cookie;
} else if (this.domainCookieName.equals(cookie.getName())) {
oldCookieDomain = cookie.getValue();
+ if (oldCookieDomain.length() == 0) {
+ oldCookieDomain = null;
+ }
}
}
}
+ if (!isValidCookieDomain(request, oldCookieDomain)) {
+ if (!isValidCookieDomain(request, defaultCookieDomain)) {
+ log.warn("The client supplied domain cookie value was invalid and the configured default cookie domain is also invalid. Will try clearing the cookies without a domain instead");
+ oldCookieDomain = null;
+ } else {
+ log.warn("The client supplied domain cookie value was invalid. Will try clearing the cookies with the default cookie domain instead");
+ oldCookieDomain = defaultCookieDomain;
+ }
+ }
+
// remove the old cookie from the client
if (oldCookie != null) {
setCookie(request, response, this.cookieName, "", 0, oldCookieDomain);
@@ -810,42 +822,51 @@ public class FormAuthenticationHandler extends DefaultAuthenticationFeedbackHand
}
}
+ /**
+ * Validates that the cookie domain is valid for the request host
+ *
+ * @param request the current request
+ * @param cookieDomain the candidate cookie domain value
+ * @return true if valid, false otherwise
+ */
+ private boolean isValidCookieDomain(HttpServletRequest request, String cookieDomain) {
+ boolean valid = false;
+ if (cookieDomain == null) {
+ valid = true;
+ } else {
+ // a valid cookie domain must be a suffix of the host
+ String host = request.getServerName();
+ if (host.endsWith(cookieDomain)) {
+ valid = true;
+ }
+ }
+ return valid;
+ }
+
private void setCookie(final HttpServletRequest request, final HttpServletResponse response, final String name,
final String value, final int age, final String domain) {
final String ctxPath = request.getContextPath();
final String cookiePath = (ctxPath == null || ctxPath.length() == 0) ? "/" : ctxPath;
- /*
- * The Servlet Spec 2.5 does not allow us to set the commonly used HttpOnly
- * attribute on cookies (Servlet API 3.0 does) so we create the Set-Cookie
- * header manually. See http://www.owasp.org/index.php/HttpOnly for information
- * on what the HttpOnly attribute is used for.
- */
-
- final StringBuilder header = new StringBuilder();
-
- // default setup with name, value, cookie path and HttpOnly
- header.append(name).append("=").append(value);
- header.append("; Path=").append(cookiePath);
- header.append("; HttpOnly"); // don't allow JS access
+ Cookie c = new Cookie(name, value);
+ c.setPath(cookiePath);
+ c.setHttpOnly(true); // don't allow JS access
// set the cookie domain if so configured
if (domain != null) {
- header.append("; Domain=").append(domain);
+ c.setDomain(domain);
}
// Only set the Max-Age attribute to remove the cookie
if (age >= 0) {
- header.append("; Max-Age=").append(age);
+ c.setMaxAge(age);
}
// ensure the cookie is secured if this is an https request
- if (request.isSecure()) {
- header.append("; Secure");
- }
+ c.setSecure(request.isSecure());
- response.addHeader(HEADER_SET_COOKIE, header.toString());
+ response.addCookie(c);
}
}
diff --git a/src/test/java/org/apache/sling/auth/form/it/SLING10290IT.java b/src/test/java/org/apache/sling/auth/form/it/AuthFormClientTestSupport.java
similarity index 68%
copy from src/test/java/org/apache/sling/auth/form/it/SLING10290IT.java
copy to src/test/java/org/apache/sling/auth/form/it/AuthFormClientTestSupport.java
index 8b16392..56ae667 100644
--- a/src/test/java/org/apache/sling/auth/form/it/SLING10290IT.java
+++ b/src/test/java/org/apache/sling/auth/form/it/AuthFormClientTestSupport.java
@@ -25,7 +25,6 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.factoryConfiguration;
-import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;
import java.io.IOException;
import java.net.URI;
@@ -34,7 +33,6 @@ import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Date;
import java.util.Dictionary;
import java.util.List;
@@ -63,31 +61,25 @@ import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.junit.After;
import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Option;
-import org.ops4j.pax.exam.junit.PaxExam;
-import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
-import org.ops4j.pax.exam.spi.reactors.PerClass;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
/**
- * integration tests to verify fix for SLING-10290
+ * base class for tests doing http requests to verify forms auth
*/
-@RunWith(PaxExam.class)
-@ExamReactorStrategy(PerClass.class)
-public class SLING10290IT extends AuthFormTestSupport {
+public abstract class AuthFormClientTestSupport extends AuthFormTestSupport {
@Inject
- private ConfigurationAdmin cm;
+ protected ConfigurationAdmin cm;
- private static final String COOKIE_SLING_FORMAUTH = "sling.formauth";
- private static final String HEADER_SET_COOKIE = "Set-Cookie";
+ protected static final String COOKIE_SLING_FORMAUTH = "sling.formauth";
+ protected static final String COOKIE_SLING_FORMAUTH_DOMAIN = "sling.formauth.cookie.domain";
+ protected static final String HEADER_SET_COOKIE = "Set-Cookie";
- private URI baseServerUri;
- private HttpClientContext httpContext;
- private CloseableHttpClient httpClient;
+ protected URI baseServerUri;
+ protected HttpClientContext httpContext;
+ protected CloseableHttpClient httpClient;
@Override
protected Option[] additionalOptions() throws IOException {
@@ -103,11 +95,7 @@ public class SLING10290IT extends AuthFormTestSupport {
// add the test script tinybundle
bundle,
- // change the formauth timeout to 1 minute so we don't have to wait a long
- // time for the testRefreshCookieOnRequestAfterHalfExpirationDuration test
- newConfiguration("org.apache.sling.auth.form.FormAuthenticationHandler")
- .put("form.auth.timeout", "1")
- .asOption(),
+ newFormauthHandlerConfiguration(),
// enable the healthcheck configuration for checking when the server is ready to
// receive http requests. (adapted from the starter healthcheck.json configuration)
@@ -134,6 +122,8 @@ public class SLING10290IT extends AuthFormTestSupport {
};
}
+ protected abstract Option newFormauthHandlerConfiguration();
+
@Before
public void before() throws IOException, URISyntaxException {
// wait for the health checks to be OK
@@ -166,91 +156,11 @@ public class SLING10290IT extends AuthFormTestSupport {
baseServerUri = null;
}
- @Test
- public void testLoginFormRenders() throws IOException {
- HttpGet loginformRequest = new HttpGet(String.format("%s/system/sling/form/login", baseServerUri));
- try (CloseableHttpResponse loginformResponse = httpClient.execute(loginformRequest, httpContext)) {
- assertEquals(HttpServletResponse.SC_OK, loginformResponse.getStatusLine().getStatusCode());
- String content = EntityUtils.toString(loginformResponse.getEntity());
- assertTrue(content.contains("Login to Apache Sling"));
- assertTrue(content.contains("loginform"));
- }
- }
-
- @Test
- public void testLogout() throws IOException, MalformedCookieException {
- doFormsLogin();
- HttpGet logoutRequest = new HttpGet(String.format("%s/system/sling/logout", baseServerUri));
- try (CloseableHttpResponse logoutResponse = httpClient.execute(logoutRequest, httpContext)) {
- assertEquals(HttpServletResponse.SC_MOVED_TEMPORARILY, logoutResponse.getStatusLine().getStatusCode());
- Cookie parsedFormauthCookie = parseFormAuthCookieFromHeaders(logoutResponse);
- assertNotNull("Expected a formauth cookie in the response", parsedFormauthCookie);
- assertEquals("Expected the formauth cookie value to be empty", "", parsedFormauthCookie.getValue());
- assertTrue("Expected the formauth cookie to be expired", parsedFormauthCookie.isExpired(new Date()));
- Cookie formauthCookie2 = getFormAuthCookieFromCookieStore();
- assertNull("Did not expected a formauth cookie in the cookie store", formauthCookie2);
- }
- }
-
- /**
- * Verify that the formauth cookie is sent appropriately after login
- */
- @Test
- public void testSetCookieOnFirstRequestAfterLogin() throws MalformedCookieException, IOException {
- doFormsLogin();
- }
-
- /**
- * Verify that the formauth cookie is not re-sent on each request after login
- */
- @Test
- public void testNoSetCookieOnSecondRequestAfterLogin() throws MalformedCookieException, IOException {
- // 1. login as the test user
- doFormsLogin();
-
- // 2. do another request
- HttpGet request = new HttpGet(whoamiUri());
- try (CloseableHttpResponse response = httpClient.execute(request, httpContext)) {
- assertEquals(HttpServletResponse.SC_OK, response.getStatusLine().getStatusCode());
- Cookie parsedFormauthCookie = parseFormAuthCookieFromHeaders(response);
- assertNull("Did not expect a formauth cookie in the response", parsedFormauthCookie);
- }
- }
-
- /**
- * Verify that the formauth cookie is refreshed on the first request after half the session duration
- * has occurred
- */
- @Test
- public void testRefreshCookieOnRequestAfterHalfExpirationDuration() throws InterruptedException, MalformedCookieException, IOException {
- // 1. login as the test user
- doFormsLogin();
-
- // 2. wait for half the session timeout expiration duration
- Thread.sleep((Duration.ofMinutes(1).toMillis() / 2) + 1); // NOSONAR
-
- // 3. do another request to trigger the cookie refresh
- HttpGet request = new HttpGet(whoamiUri());
- try (CloseableHttpResponse response = httpClient.execute(request, httpContext)) {
- assertEquals(HttpServletResponse.SC_OK, response.getStatusLine().getStatusCode());
- Cookie parsedFormauthCookie = parseFormAuthCookieFromHeaders(response);
- assertNotNull("Expected a refreshed formauth cookie in the response", parsedFormauthCookie);
- }
-
- // 4. do another request to verify that subsequent request after
- // the cookie refresh do not send an additional formauth cookie
- try (CloseableHttpResponse response = httpClient.execute(request, httpContext)) {
- assertEquals(HttpServletResponse.SC_OK, response.getStatusLine().getStatusCode());
- Cookie parsedFormauthCookie2 = parseFormAuthCookieFromHeaders(response);
- assertNull("Did not expect a formauth cookie in the response", parsedFormauthCookie2);
- }
- }
-
/**
* Calculate the base server URI from the current configuration of the
* httpservice
*/
- private URI getBaseServerUri() throws IOException, URISyntaxException {
+ protected URI getBaseServerUri() throws IOException, URISyntaxException {
assertNotNull(cm);
Configuration httpServiceConfiguration = cm.getConfiguration("org.apache.felix.http");
Dictionary<String, Object> properties = httpServiceConfiguration.getProperties();
@@ -292,14 +202,17 @@ public class SLING10290IT extends AuthFormTestSupport {
/**
* @return the address of the whoami script
*/
- private String whoamiUri() {
+ protected String whoamiUri() {
return String.format("%s/content.SLING10290IT.html", baseServerUri);
}
/**
* Perform the http calls to login the test user via the forms based login
*/
- private void doFormsLogin() throws MalformedCookieException, IOException {
+ protected void doFormsLogin() throws MalformedCookieException, IOException {
+ doFormsLogin(null, null);
+ }
+ protected void doFormsLogin(ValidateFormauthCookie formauthCookieValidator, ValidateFormauthDomainCookie domainCookieValidator) throws MalformedCookieException, IOException {
// before login, there should be no formauth cookie in the cookie store
Cookie formauthCookie = getFormAuthCookieFromCookieStore();
assertNull("Did not expect formauth cookie in the cookie store", formauthCookie);
@@ -329,6 +242,14 @@ public class SLING10290IT extends AuthFormTestSupport {
// verify that the expected set-cookie header arrived
Cookie parsedFormauthCookie = parseFormAuthCookieFromHeaders(response);
assertNotNull("Expected a formauth cookie in the response", parsedFormauthCookie);
+
+ if (formauthCookieValidator != null) {
+ formauthCookieValidator.validate(parsedFormauthCookie);
+ }
+ if (domainCookieValidator != null) {
+ Cookie parsedDomainCookie = parseCookieFromHeaders(response, COOKIE_SLING_FORMAUTH_DOMAIN);
+ domainCookieValidator.validate(parsedDomainCookie);
+ }
}
// after login, there should be now be a cookie in the cookie store
@@ -356,12 +277,15 @@ public class SLING10290IT extends AuthFormTestSupport {
*
* @return the formauth cookie or null if not found
*/
- private Cookie getFormAuthCookieFromCookieStore() {
+ protected Cookie getFormAuthCookieFromCookieStore() {
+ return getCookieFromCookieStore(COOKIE_SLING_FORMAUTH);
+ }
+ protected Cookie getCookieFromCookieStore(String cookieName) {
Cookie formauthCookie = null;
List<Cookie> cookies = httpContext.getCookieStore().getCookies();
if (cookies != null) {
for (Cookie c : cookies) {
- if (COOKIE_SLING_FORMAUTH.equals(c.getName())) {
+ if (cookieName.equals(c.getName())) {
formauthCookie = c;
}
}
@@ -375,7 +299,10 @@ public class SLING10290IT extends AuthFormTestSupport {
* @param response the response from the http request
* @return the found cookie or null if not found
*/
- private Cookie parseFormAuthCookieFromHeaders(HttpResponse response) throws MalformedCookieException {
+ protected Cookie parseFormAuthCookieFromHeaders(HttpResponse response) throws MalformedCookieException {
+ return parseCookieFromHeaders(response, COOKIE_SLING_FORMAUTH);
+ }
+ protected Cookie parseCookieFromHeaders(HttpResponse response, String cookieName) throws MalformedCookieException {
Header [] cookieHeaders = response.getHeaders(HEADER_SET_COOKIE);
assertNotNull(cookieHeaders);
@@ -386,7 +313,7 @@ public class SLING10290IT extends AuthFormTestSupport {
for (Header cookieHeader : cookieHeaders) {
List<Cookie> parsedCookies = cookieSpec.parse(cookieHeader, origin);
for (Cookie c : parsedCookies) {
- if (COOKIE_SLING_FORMAUTH.equals(c.getName())) {
+ if (cookieName.equals(c.getName())) {
if (parsedFormauthCookie != null) {
fail(String.format("Did not expect more than one %s cookie", c.getName()));
}
@@ -397,4 +324,12 @@ public class SLING10290IT extends AuthFormTestSupport {
return parsedFormauthCookie;
}
+ protected static interface ValidateFormauthCookie {
+ void validate(Cookie cookie);
+ }
+
+ protected static interface ValidateFormauthDomainCookie {
+ void validate(Cookie cookie);
+ }
+
}
diff --git a/src/test/java/org/apache/sling/auth/form/it/SLING10290IT.java b/src/test/java/org/apache/sling/auth/form/it/SLING10290IT.java
index 8b16392..144f14a 100644
--- a/src/test/java/org/apache/sling/auth/form/it/SLING10290IT.java
+++ b/src/test/java/org/apache/sling/auth/form/it/SLING10290IT.java
@@ -18,152 +18,44 @@
*/
package org.apache.sling.auth.form.it;
-import static org.apache.sling.testing.paxexam.SlingOptions.slingScriptingSightly;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.factoryConfiguration;
import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;
import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Date;
-import java.util.Dictionary;
-import java.util.List;
-import javax.inject.Inject;
import javax.servlet.http.HttpServletResponse;
-import org.apache.http.Header;
-import org.apache.http.HttpResponse;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.config.CookieSpecs;
-import org.apache.http.client.config.RequestConfig;
-import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.cookie.Cookie;
-import org.apache.http.cookie.CookieOrigin;
-import org.apache.http.cookie.CookieSpec;
import org.apache.http.cookie.MalformedCookieException;
-import org.apache.http.impl.client.BasicCookieStore;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.impl.cookie.RFC6265StrictSpec;
-import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.PaxExam;
import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
import org.ops4j.pax.exam.spi.reactors.PerClass;
-import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
/**
* integration tests to verify fix for SLING-10290
*/
@RunWith(PaxExam.class)
@ExamReactorStrategy(PerClass.class)
-public class SLING10290IT extends AuthFormTestSupport {
-
- @Inject
- private ConfigurationAdmin cm;
-
- private static final String COOKIE_SLING_FORMAUTH = "sling.formauth";
- private static final String HEADER_SET_COOKIE = "Set-Cookie";
-
- private URI baseServerUri;
- private HttpClientContext httpContext;
- private CloseableHttpClient httpClient;
+public class SLING10290IT extends AuthFormClientTestSupport {
@Override
- protected Option[] additionalOptions() throws IOException {
- // create a tinybundle that contains a test script
- final List<String> resourcePaths = Arrays.asList("/apps/sling/OrderedFolder/SLING10290IT.html");
- final String bundleResourcesHeader = String.join(",", resourcePaths);
- final Option bundle = buildBundleResourcesBundle(bundleResourcesHeader, resourcePaths);
-
- return new Option[]{
- // add sightly support for the test script
- slingScriptingSightly(),
-
- // add the test script tinybundle
- bundle,
-
- // change the formauth timeout to 1 minute so we don't have to wait a long
- // time for the testRefreshCookieOnRequestAfterHalfExpirationDuration test
- newConfiguration("org.apache.sling.auth.form.FormAuthenticationHandler")
- .put("form.auth.timeout", "1")
- .asOption(),
-
- // enable the healthcheck configuration for checking when the server is ready to
- // receive http requests. (adapted from the starter healthcheck.json configuration)
- factoryConfiguration("org.apache.felix.hc.generalchecks.FrameworkStartCheck")
- .put("hc.tags", new String[] {"systemalive"})
- .put("targetStartLevel", 5)
- .asOption(),
- factoryConfiguration("org.apache.felix.hc.generalchecks.ServicesCheck")
- .put("hc.tags", new String[] {"systemalive"})
- .put("services.list", new String[] {
- "org.apache.sling.jcr.api.SlingRepository",
- "org.apache.sling.engine.auth.Authenticator",
- "org.apache.sling.api.resource.ResourceResolverFactory",
- "org.apache.sling.api.servlets.ServletResolver",
- "javax.script.ScriptEngineManager"
- })
- .asOption(),
- factoryConfiguration("org.apache.felix.hc.generalchecks.BundlesStartedCheck")
- .put("hc.tags", new String[] {"bundles"})
- .asOption(),
- factoryConfiguration("org.apache.sling.jcr.contentloader.hc.BundleContentLoadedCheck")
- .put("hc.tags", new String[] {"bundles"})
- .asOption(),
- };
- }
-
- @Before
- public void before() throws IOException, URISyntaxException {
- // wait for the health checks to be OK
- waitForServerReady(Duration.ofMinutes(1).toMillis(), 500);
-
- // calculate the address of the http server
- baseServerUri = getBaseServerUri();
- assertNotNull(baseServerUri);
-
- // prepare the http client for the test user
- httpContext = HttpClientContext.create();
- httpContext.setCookieStore(new BasicCookieStore());
- RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT).build();
- httpContext.setRequestConfig(requestConfig);
- httpClient = HttpClients.custom()
- .disableRedirectHandling()
- .build();
- }
-
- @After
- public void after() throws IOException {
- // close/cleanup the test user http client
- if (httpClient != null) {
- httpClient.close();
- httpClient = null;
- }
-
- // clear out other state
- httpContext = null;
- baseServerUri = null;
+ protected Option newFormauthHandlerConfiguration() {
+ // change the formauth timeout to 1 minute so we don't have to wait a long
+ // time for the testRefreshCookieOnRequestAfterHalfExpirationDuration test
+ return newConfiguration("org.apache.sling.auth.form.FormAuthenticationHandler")
+ .put("form.auth.timeout", "1")
+ .asOption();
}
@Test
@@ -246,155 +138,4 @@ public class SLING10290IT extends AuthFormTestSupport {
}
}
- /**
- * Calculate the base server URI from the current configuration of the
- * httpservice
- */
- private URI getBaseServerUri() throws IOException, URISyntaxException {
- assertNotNull(cm);
- Configuration httpServiceConfiguration = cm.getConfiguration("org.apache.felix.http");
- Dictionary<String, Object> properties = httpServiceConfiguration.getProperties();
-
- String host;
- Object hostObj = properties.get("org.apache.felix.http.host");
- if (hostObj == null) {
- host = "localhost";
- } else {
- assertTrue(hostObj instanceof String);
- host = (String)hostObj;
- }
- assertNotNull(host);
-
- String scheme = null;
- Object portObj = null;
- Object httpsEnableObj = properties.get("org.apache.felix.https.enable");
- if ("true".equals(httpsEnableObj)) {
- scheme = "https";
- portObj = properties.get("org.osgi.service.http.port.secure");
- } else {
- Object httpEnableObj = properties.get("org.apache.felix.http.enable");
- if (httpEnableObj == null || "true".equals(httpEnableObj)) {
- scheme = "http";
- portObj = properties.get("org.osgi.service.http.port");
- } else {
- fail("Expected either http or https to be enabled");
- }
- }
- int port = -1;
- if (portObj instanceof Number) {
- port = ((Number)portObj).intValue();
- }
- assertTrue(port > 0);
-
- return new URI(String.format("%s://%s:%d", scheme, host, port));
- }
-
- /**
- * @return the address of the whoami script
- */
- private String whoamiUri() {
- return String.format("%s/content.SLING10290IT.html", baseServerUri);
- }
-
- /**
- * Perform the http calls to login the test user via the forms based login
- */
- private void doFormsLogin() throws MalformedCookieException, IOException {
- // before login, there should be no formauth cookie in the cookie store
- Cookie formauthCookie = getFormAuthCookieFromCookieStore();
- assertNull("Did not expect formauth cookie in the cookie store", formauthCookie);
-
- // verify that the script shows us as not logged in
- HttpGet whoamiRequest = new HttpGet(whoamiUri());
- try (CloseableHttpResponse whoamiResponse = httpClient.execute(whoamiRequest, httpContext)) {
- assertEquals(HttpServletResponse.SC_OK, whoamiResponse.getStatusLine().getStatusCode());
- String content = EntityUtils.toString(whoamiResponse.getEntity());
- assertTrue(content.contains("whoAmI"));
- assertTrue(content.contains("anonymous"));
- }
-
- // send the form login request
- List<NameValuePair> parameters = new ArrayList<>();
- parameters.add(new BasicNameValuePair("j_username", FORM_AUTH_VERIFY_USER));
- parameters.add(new BasicNameValuePair("j_password", FORM_AUTH_VERIFY_PWD));
- parameters.add(new BasicNameValuePair("_charset_", StandardCharsets.UTF_8.name()));
- parameters.add(new BasicNameValuePair("resource", "/content.SLING10290IT.html"));
- HttpPost request = new HttpPost(String.format("%s/j_security_check", baseServerUri));
- request.setEntity(new UrlEncodedFormEntity(parameters));
- Header locationHeader = null;
- try (CloseableHttpResponse response = httpClient.execute(request, httpContext)) {
- assertEquals(HttpServletResponse.SC_MOVED_TEMPORARILY, response.getStatusLine().getStatusCode());
- locationHeader = response.getFirstHeader("Location");
-
- // verify that the expected set-cookie header arrived
- Cookie parsedFormauthCookie = parseFormAuthCookieFromHeaders(response);
- assertNotNull("Expected a formauth cookie in the response", parsedFormauthCookie);
- }
-
- // after login, there should be now be a cookie in the cookie store
- Cookie formauthCookie2 = getFormAuthCookieFromCookieStore();
- assertNotNull("Expected a formauth cookie in the cookie store", formauthCookie2);
-
- // and then follow the redirect
- assertNotNull("Expected a 'Location' header", locationHeader);
- // verify that the script shows us logged in as the test user
- HttpGet followedRequest = new HttpGet(locationHeader.getValue());
- try (CloseableHttpResponse followedResponse = httpClient.execute(followedRequest, httpContext)) {
- assertEquals(HttpServletResponse.SC_OK, followedResponse.getStatusLine().getStatusCode());
- String content = EntityUtils.toString(followedResponse.getEntity());
- assertTrue(content.contains("whoAmI"));
- assertTrue(content.contains(FORM_AUTH_VERIFY_USER));
-
- // there should be no new formauth cookie on the followed response
- Cookie parsedFormauthCookie2 = parseFormAuthCookieFromHeaders(followedResponse);
- assertNull("Did not expect a formauth cookie in the response", parsedFormauthCookie2);
- }
- }
-
- /**
- * Retrieve the formauth cookie from the cookie store
- *
- * @return the formauth cookie or null if not found
- */
- private Cookie getFormAuthCookieFromCookieStore() {
- Cookie formauthCookie = null;
- List<Cookie> cookies = httpContext.getCookieStore().getCookies();
- if (cookies != null) {
- for (Cookie c : cookies) {
- if (COOKIE_SLING_FORMAUTH.equals(c.getName())) {
- formauthCookie = c;
- }
- }
- }
- return formauthCookie;
- }
-
- /**
- * Parse the formauth cookie out of the headers sent on the response
- *
- * @param response the response from the http request
- * @return the found cookie or null if not found
- */
- private Cookie parseFormAuthCookieFromHeaders(HttpResponse response) throws MalformedCookieException {
- Header [] cookieHeaders = response.getHeaders(HEADER_SET_COOKIE);
- assertNotNull(cookieHeaders);
-
- Cookie parsedFormauthCookie = null;
- CookieSpec cookieSpec = new RFC6265StrictSpec();
- CookieOrigin origin = new CookieOrigin(baseServerUri.getHost(), baseServerUri.getPort(),
- baseServerUri.getPath(), "https".equals(baseServerUri.getScheme()));
- for (Header cookieHeader : cookieHeaders) {
- List<Cookie> parsedCookies = cookieSpec.parse(cookieHeader, origin);
- for (Cookie c : parsedCookies) {
- if (COOKIE_SLING_FORMAUTH.equals(c.getName())) {
- if (parsedFormauthCookie != null) {
- fail(String.format("Did not expect more than one %s cookie", c.getName()));
- }
- parsedFormauthCookie = c;
- }
- }
- }
- return parsedFormauthCookie;
- }
-
}
diff --git a/src/test/java/org/apache/sling/auth/form/it/SLING10421InvalidDomainIT.java b/src/test/java/org/apache/sling/auth/form/it/SLING10421InvalidDomainIT.java
new file mode 100644
index 0000000..d822b0a
--- /dev/null
+++ b/src/test/java/org/apache/sling/auth/form/it/SLING10421InvalidDomainIT.java
@@ -0,0 +1,141 @@
+/*
+ * 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.sling.auth.form.it;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;
+
+import java.io.IOException;
+import java.util.Date;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.impl.cookie.BasicClientCookie;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+/**
+ * SLING-10421 validate proper cookie handling when the formauth
+ * handler has been configured with an invalid cookie domain
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class SLING10421InvalidDomainIT extends AuthFormClientTestSupport {
+
+ @Override
+ protected Option newFormauthHandlerConfiguration() {
+ // change the default cookie domain config
+ return newConfiguration("org.apache.sling.auth.form.FormAuthenticationHandler")
+ .put("form.default.cookie.domain", "invalid")
+ .asOption();
+ }
+
+ /**
+ * SLING-10421 validate configured and client supplied cookie domain value
+ */
+ @Test
+ public void testLoginWithInvalidConfiguredCookieDomain() throws IOException, MalformedCookieException {
+ doFormsLogin();
+ }
+
+ /**
+ * SLING-10421 validate configured and client supplied cookie domain value
+ */
+ @Test
+ public void testLogoutWithInvalidConfiguredCookieDomain() throws IOException, MalformedCookieException {
+ doFormsLogin();
+
+ HttpGet logoutRequest = new HttpGet(String.format("%s/system/sling/logout", baseServerUri));
+ try (CloseableHttpResponse logoutResponse = httpClient.execute(logoutRequest, httpContext)) {
+ assertEquals(HttpServletResponse.SC_MOVED_TEMPORARILY, logoutResponse.getStatusLine().getStatusCode());
+ Cookie parsedFormauthCookie = parseFormAuthCookieFromHeaders(logoutResponse);
+ assertNotNull("Expected a formauth cookie in the response", parsedFormauthCookie);
+ assertEquals("Expected the formauth cookie value to be empty", "", parsedFormauthCookie.getValue());
+ assertTrue("Expected the formauth cookie to be expired", parsedFormauthCookie.isExpired(new Date()));
+ assertEquals("Expected the formauth cookie domain to be localhost", "localhost", parsedFormauthCookie.getDomain());
+
+ Cookie parsedFormauthDomainCookie = parseCookieFromHeaders(logoutResponse, COOKIE_SLING_FORMAUTH_DOMAIN);
+ assertNull("Did not expected a formauth domain cookie in the response", parsedFormauthDomainCookie);
+
+ Cookie formauthCookie2 = getFormAuthCookieFromCookieStore();
+ assertNull("Did not expected a formauth cookie in the cookie store", formauthCookie2);
+
+ Cookie formauthDomainCookie2 = getCookieFromCookieStore(COOKIE_SLING_FORMAUTH_DOMAIN);
+ assertNull("Did not expected a formauth domain cookie in the cookie store", formauthDomainCookie2);
+ }
+ }
+
+ /**
+ * SLING-10421 validate configured and client supplied cookie domain value
+ */
+ @Test
+ public void testLogoutWithInvalidDomainCookieValue() throws IOException, MalformedCookieException {
+ doFormsLogin();
+
+ // add an invalid domain cookie to the cookie store
+ CookieStore cookieStore = httpContext.getCookieStore();
+ BasicClientCookie invalidCookie = new BasicClientCookie(COOKIE_SLING_FORMAUTH_DOMAIN, "invalid");
+ invalidCookie.setPath("/");
+ invalidCookie.setDomain("localhost");
+ cookieStore.addCookie(invalidCookie);
+
+ HttpGet logoutRequest = new HttpGet(String.format("%s/system/sling/logout", baseServerUri));
+ try (CloseableHttpResponse logoutResponse = httpClient.execute(logoutRequest, httpContext)) {
+ assertEquals(HttpServletResponse.SC_MOVED_TEMPORARILY, logoutResponse.getStatusLine().getStatusCode());
+ Cookie parsedFormauthCookie = parseFormAuthCookieFromHeaders(logoutResponse);
+ assertNotNull("Expected a formauth cookie in the response", parsedFormauthCookie);
+ assertEquals("Expected the formauth cookie value to be empty", "", parsedFormauthCookie.getValue());
+ assertTrue("Expected the formauth cookie to be expired", parsedFormauthCookie.isExpired(new Date()));
+ assertEquals("Expected the formauth cookie domain to be localhost", "localhost", parsedFormauthCookie.getDomain());
+
+ Cookie parsedFormauthDomainCookie = parseCookieFromHeaders(logoutResponse, COOKIE_SLING_FORMAUTH_DOMAIN);
+ assertNull("Did not expected a formauth domain cookie in the response", parsedFormauthDomainCookie);
+
+ Cookie formauthCookie2 = getFormAuthCookieFromCookieStore();
+ assertNull("Did not expected a formauth cookie in the cookie store", formauthCookie2);
+
+ Cookie formauthDomainCookie2 = getCookieFromCookieStore(COOKIE_SLING_FORMAUTH_DOMAIN);
+ assertSame("Did not expected a new formauth domain cookie in the cookie store", invalidCookie, formauthDomainCookie2);
+ }
+ }
+
+ @Override
+ protected void doFormsLogin() throws MalformedCookieException, IOException {
+ super.doFormsLogin(cookie -> {
+ assertEquals("Expected a formauth cookie with domain equal to host", "localhost", cookie.getDomain());
+ },
+ domainCookie -> {
+ assertNull("Did not expect a formauth domain cookie", domainCookie);
+ });
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/auth/form/it/SLING10421ValidDomainIT.java b/src/test/java/org/apache/sling/auth/form/it/SLING10421ValidDomainIT.java
new file mode 100644
index 0000000..116a3b5
--- /dev/null
+++ b/src/test/java/org/apache/sling/auth/form/it/SLING10421ValidDomainIT.java
@@ -0,0 +1,147 @@
+/*
+ * 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.sling.auth.form.it;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;
+
+import java.io.IOException;
+import java.util.Date;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.cookie.MalformedCookieException;
+import org.apache.http.impl.cookie.BasicClientCookie;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+/**
+ * SLING-10421 validate proper cookie handling when the formauth
+ * handler has been configured with an valid cookie domain
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class SLING10421ValidDomainIT extends AuthFormClientTestSupport {
+
+ @Override
+ protected Option newFormauthHandlerConfiguration() {
+ // change the default cookie domain config
+ return newConfiguration("org.apache.sling.auth.form.FormAuthenticationHandler")
+ .put("form.default.cookie.domain", "localhost")
+ .asOption();
+ }
+
+ /**
+ * SLING-10421 validate configured and client supplied cookie domain value
+ */
+ @Test
+ public void testLoginWithValidConfiguredCookieDomain() throws IOException, MalformedCookieException {
+ doFormsLogin();
+ }
+
+ /**
+ * SLING-10421 validate configured and client supplied cookie domain value
+ */
+ @Test
+ public void testLogoutWithValidConfiguredCookieDomain() throws IOException, MalformedCookieException {
+ doFormsLogin();
+
+ HttpGet logoutRequest = new HttpGet(String.format("%s/system/sling/logout", baseServerUri));
+ try (CloseableHttpResponse logoutResponse = httpClient.execute(logoutRequest, httpContext)) {
+ assertEquals(HttpServletResponse.SC_MOVED_TEMPORARILY, logoutResponse.getStatusLine().getStatusCode());
+ Cookie parsedFormauthCookie = parseFormAuthCookieFromHeaders(logoutResponse);
+ assertNotNull("Expected a formauth cookie in the response", parsedFormauthCookie);
+ assertEquals("Expected the formauth cookie value to be empty", "", parsedFormauthCookie.getValue());
+ assertTrue("Expected the formauth cookie to be expired", parsedFormauthCookie.isExpired(new Date()));
+ assertEquals("Expected the formauth cookie domain to be localhost", "localhost", parsedFormauthCookie.getDomain());
+
+ Cookie parsedFormauthDomainCookie = parseCookieFromHeaders(logoutResponse, COOKIE_SLING_FORMAUTH_DOMAIN);
+ assertNotNull("Expected a formauth domain cookie in the response", parsedFormauthDomainCookie);
+ assertEquals("Expected the formauth domain cookie value to be empty", "", parsedFormauthDomainCookie.getValue());
+ assertTrue("Expected the formauth domain cookie to be expired", parsedFormauthDomainCookie.isExpired(new Date()));
+ assertEquals("Expected the formauth cookie domain to be localhost", "localhost", parsedFormauthDomainCookie.getDomain());
+
+ Cookie formauthCookie2 = getFormAuthCookieFromCookieStore();
+ assertNull("Did not expected a formauth cookie in the cookie store", formauthCookie2);
+
+ Cookie formauthDomainCookie2 = getCookieFromCookieStore(COOKIE_SLING_FORMAUTH_DOMAIN);
+ assertNull("Did not expected a formauth domain cookie in the cookie store", formauthDomainCookie2);
+ }
+ }
+
+ /**
+ * SLING-10421 validate configured and client supplied cookie domain value
+ */
+ @Test
+ public void testLogoutWithInvalidDomainCookieValue() throws IOException, MalformedCookieException {
+ doFormsLogin();
+
+ // add an invalid domain cookie to the cookie store
+ CookieStore cookieStore = httpContext.getCookieStore();
+ BasicClientCookie invalidCookie = new BasicClientCookie(COOKIE_SLING_FORMAUTH_DOMAIN, "invalid");
+ invalidCookie.setPath("/");
+ invalidCookie.setDomain("localhost");
+ cookieStore.addCookie(invalidCookie);
+
+ HttpGet logoutRequest = new HttpGet(String.format("%s/system/sling/logout", baseServerUri));
+ try (CloseableHttpResponse logoutResponse = httpClient.execute(logoutRequest, httpContext)) {
+ assertEquals(HttpServletResponse.SC_MOVED_TEMPORARILY, logoutResponse.getStatusLine().getStatusCode());
+ Cookie parsedFormauthCookie = parseFormAuthCookieFromHeaders(logoutResponse);
+ assertNotNull("Expected a formauth cookie in the response", parsedFormauthCookie);
+ assertEquals("Expected the formauth cookie value to be empty", "", parsedFormauthCookie.getValue());
+ assertTrue("Expected the formauth cookie to be expired", parsedFormauthCookie.isExpired(new Date()));
+ assertEquals("Expected the formauth cookie domain to be localhost", "localhost", parsedFormauthCookie.getDomain());
+
+ Cookie parsedFormauthDomainCookie = parseCookieFromHeaders(logoutResponse, COOKIE_SLING_FORMAUTH_DOMAIN);
+ assertNotNull("Expected a formauth domain cookie in the response", parsedFormauthDomainCookie);
+ assertEquals("Expected the formauth domain cookie value to be empty", "", parsedFormauthDomainCookie.getValue());
+ assertTrue("Expected the formauth domain cookie to be expired", parsedFormauthDomainCookie.isExpired(new Date()));
+ assertEquals("Expected the formauth cookie domain to be localhost", "localhost", parsedFormauthDomainCookie.getDomain());
+
+ Cookie formauthCookie2 = getFormAuthCookieFromCookieStore();
+ assertNull("Did not expected a formauth cookie in the cookie store", formauthCookie2);
+
+ Cookie formauthDomainCookie2 = getCookieFromCookieStore(COOKIE_SLING_FORMAUTH_DOMAIN);
+ assertNull("Did not expected a formauth domain cookie in the cookie store", formauthDomainCookie2);
+ }
+ }
+
+ @Override
+ protected void doFormsLogin() throws MalformedCookieException, IOException {
+ super.doFormsLogin(cookie -> {
+ assertEquals("Expected a formauth cookie with domain equal to localhost", "localhost", cookie.getDomain());
+ },
+ domainCookie -> {
+ assertNotNull("Expected a formauth domain cookie", domainCookie);
+ assertEquals("Expected the domain cookie value to be localhost", "localhost", domainCookie.getValue());
+ });
+ }
+
+}