Skip to content

Commit

Permalink
Add SameSite attribute to Cookie
Browse files Browse the repository at this point in the history
Cookie object in Selenium Java doesn't support setting `SameSite`
attribute, described in the spce:
https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-5.3.7

ChromeDriver already supports it. So improving Selenium Java API to
support it as well.

Partially fix #7798

Signed-off-by: Alexei Barantsev <[email protected]>
  • Loading branch information
Gerry Gao authored and barancev committed Mar 7, 2020
1 parent 1a36974 commit 5fa9a75
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 6 deletions.
45 changes: 43 additions & 2 deletions java/client/src/org/openqa/selenium/Cookie.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class Cookie implements Serializable {
private final Date expiry;
private final boolean isSecure;
private final boolean isHttpOnly;
private final String sameSite;

/**
* Creates an insecure non-httpOnly cookie with no domain specified.
Expand Down Expand Up @@ -93,6 +94,24 @@ public Cookie(String name, String value, String domain, String path, Date expiry
*/
public Cookie(String name, String value, String domain, String path, Date expiry,
boolean isSecure, boolean isHttpOnly) {
this(name, value, domain, path, expiry, isSecure, isHttpOnly, null);
}

/**
* Creates a cookie.
*
* @param name The name of the cookie; may not be null or an empty string.
* @param value The cookie value; may not be null.
* @param domain The domain the cookie is visible to.
* @param path The path the cookie is visible to. If left blank or set to null, will be set to
* "/".
* @param expiry The cookie's expiration date; may be null.
* @param isSecure Whether this cookie requires a secure connection.
* @param isHttpOnly Whether this cookie is a httpOnly cookie.
* @param sameSite The samesite attribute of this cookie; e.g. None, Lax, Strict.
*/
public Cookie(String name, String value, String domain, String path, Date expiry,
boolean isSecure, boolean isHttpOnly, String sameSite) {
this.name = name;
this.value = value;
this.path = path == null || "".equals(path) ? "/" : path;
Expand All @@ -107,6 +126,8 @@ public Cookie(String name, String value, String domain, String path, Date expiry
} else {
this.expiry = null;
}

this.sameSite = sameSite;
}

/**
Expand Down Expand Up @@ -158,6 +179,10 @@ public Date getExpiry() {
return expiry;
}

public String getSameSite() {
return sameSite;
}

private static String stripPort(String domain) {
return (domain == null) ? null : domain.split(":")[0];
}
Expand All @@ -178,6 +203,10 @@ public void validate() {
}
}

/**
* JSON object keys are defined in
* https://w3c.github.io/webdriver/#dfn-table-for-cookie-conversion.
*/
public Map<String, Object> toJson() {
Map<String, Object> toReturn = new TreeMap<>();

Expand All @@ -204,6 +233,10 @@ public Map<String, Object> toJson() {
toReturn.put("secure", isSecure());
toReturn.put("httpOnly", isHttpOnly());

if (getSameSite() != null) {
toReturn.put("samesite", getSameSite());
}

return toReturn;
}

Expand All @@ -215,7 +248,8 @@ public String toString() {
.format(expiry))
+ ("".equals(path) ? "" : "; path=" + path)
+ (domain == null ? "" : "; domain=" + domain)
+ (isSecure ? ";secure;" : "");
+ (isSecure ? ";secure;" : "")
+ (sameSite == null ? "" : "; sameSite=" + sameSite);
}

/**
Expand Down Expand Up @@ -252,6 +286,7 @@ public static class Builder {
private Date expiry;
private boolean secure;
private boolean httpOnly;
private String sameSite;

public Builder(String name, String value) {
this.name = name;
Expand Down Expand Up @@ -283,8 +318,14 @@ public Builder isHttpOnly(boolean httpOnly) {
return this;
}

public Builder sameSite(String sameSite) {
this.sameSite = sameSite;
return this;
}

public Cookie build() {
return new Cookie(name, value, domain, path, expiry, secure, httpOnly);
return new Cookie(
name, value, domain, path, expiry, secure, httpOnly, sameSite);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -762,13 +762,16 @@ public Set<Cookie> getCookies() {
((Collection<?>) returned).stream()
.map(o -> (Map<String, Object>) o)
.map(rawCookie -> {
// JSON object keys are defined in
// https://w3c.github.io/webdriver/#dfn-table-for-cookie-conversion.
Cookie.Builder builder =
new Cookie.Builder((String) rawCookie.get("name"), (String) rawCookie.get("value"))
.path((String) rawCookie.get("path"))
.domain((String) rawCookie.get("domain"))
.isSecure(rawCookie.containsKey("secure") && (Boolean) rawCookie.get("secure"))
.isHttpOnly(
rawCookie.containsKey("httpOnly") && (Boolean) rawCookie.get("httpOnly"));
rawCookie.containsKey("httpOnly") && (Boolean) rawCookie.get("httpOnly"))
.sameSite((String) rawCookie.get("samesite"));

Number expiryNum = (Number) rawCookie.get("expiry");
builder.expiresOn(expiryNum == null ? null : new Date(SECONDS.toMillis(expiryNum.longValue())));
Expand Down
11 changes: 10 additions & 1 deletion java/client/test/org/openqa/selenium/CookieTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,20 @@ public void testHttpOnlyDefaultsToFalse() {
assertThat(cookie.isHttpOnly()).isFalse();
}

@Test
public void testCookiesShouldAllowSameSiteToBeSet() {
Cookie cookie = new Cookie("name", "value", "", "/", new Date(), false, true, "Lax");
assertThat(cookie.getSameSite()).isEqualTo("Lax");

Cookie builderCookie = new Cookie.Builder("name", "value").sameSite("Lax").build();
assertThat(builderCookie.getSameSite()).isEqualTo("Lax");
}

@Test
public void testCookieSerializes() throws IOException, ClassNotFoundException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
Cookie cookieToSerialize = new Cookie("Fish", "cod", "", "", null, false);
Cookie cookieToSerialize = new Cookie("Fish", "cod", "", "", null, false, true, "Lax");

objectOutputStream.writeObject(cookieToSerialize);
byte[] serializedCookie = byteArrayOutputStream.toByteArray();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -623,15 +623,17 @@ public void canHandleGetCookiesCommand() throws IOException {
CommandExecutor executor = prepareExecutorMock(
echoCapabilities,
valueResponder(ImmutableList.of(
ImmutableMap.of("name", "cookie1", "value", "value1"),
ImmutableMap.of("name", "cookie1", "value", "value1", "samesite", "Lax"),
ImmutableMap.of("name", "cookie2", "value", "value2"))));

RemoteWebDriver driver = new RemoteWebDriver(executor, new ImmutableCapabilities());
Set<Cookie> cookies = driver.manage().getCookies();

assertThat(cookies)
.hasSize(2)
.contains(new Cookie("cookie1", "value1"), new Cookie("cookie2", "value2"));
.contains(
new Cookie.Builder("cookie1", "value1").sameSite("Lax").build(),
new Cookie("cookie2", "value2"));
verifyCommands(
executor, driver.getSessionId(),
new CommandPayload(DriverCommand.GET_ALL_COOKIES, ImmutableMap.of()));
Expand Down

0 comments on commit 5fa9a75

Please sign in to comment.