Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
package org.apache.cxf.jaxrs.impl;

import java.util.Date;
import java.util.Locale;

import jakarta.ws.rs.core.Cookie;
import jakarta.ws.rs.core.NewCookie;
import jakarta.ws.rs.ext.RuntimeDelegate.HeaderDelegate;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.jaxrs.utils.HttpUtils;

public class NewCookieHeaderProvider implements HeaderDelegate<NewCookie> {
Expand All @@ -36,6 +38,7 @@ public class NewCookieHeaderProvider implements HeaderDelegate<NewCookie> {
private static final String SECURE = "Secure";
private static final String EXPIRES = "Expires";
private static final String HTTP_ONLY = "HttpOnly";
private static final String SAME_SITE = "SameSite";

/** from RFC 2068, token special case characters */

Expand All @@ -59,10 +62,14 @@ public NewCookie fromString(String c) {
Date expires = null;
boolean httpOnly = false;
int version = Cookie.DEFAULT_VERSION;
NewCookie.SameSite sameSite = null;

String[] tokens = c.split(";");
for (String token : tokens) {
String theToken = token.trim();
if (theToken.isEmpty()) {
continue;
}

int sepIndex = theToken.indexOf('=');
String paramName = sepIndex != -1 ? theToken.substring(0, sepIndex) : theToken;
Expand All @@ -78,7 +85,12 @@ public NewCookie fromString(String c) {
paramValue = stripQuotes(paramValue);
}

if (paramName.equalsIgnoreCase(MAX_AGE)) {
if (name == null) {
// The first segment is always the cookie's name=value pair, whatever its name
// (RFC 6265, section 5.2); the segments after it are attributes, and unknown ones are ignored
name = paramName;
value = paramValue;
} else if (paramName.equalsIgnoreCase(MAX_AGE)) {
maxAge = Integer.parseInt(paramValue);
} else if (paramName.equalsIgnoreCase(PATH)) {
path = paramValue;
Expand All @@ -94,17 +106,17 @@ public NewCookie fromString(String c) {
httpOnly = true;
} else if (paramName.equalsIgnoreCase(VERSION)) {
version = Integer.parseInt(paramValue);
} else if (paramValue != null) {
name = paramName;
value = paramValue;
} else if (paramName.equalsIgnoreCase(SAME_SITE)) {
sameSite = toSameSite(paramValue);
}
}

if (name == null || value == null) {
throw new IllegalArgumentException("Set-Cookie is malformed : " + c);
}

return new NewCookie(name, value, path, domain, version, comment, maxAge, expires, isSecure, httpOnly);
return new NewCookie(name, value, path, domain, version, comment, maxAge, expires, isSecure, httpOnly,
sameSite);
}

@Override
Expand Down Expand Up @@ -137,11 +149,28 @@ public String toString(NewCookie value) {
if (value.isHttpOnly()) {
sb.append(';').append(HTTP_ONLY);
}
if (value.getSameSite() != null) {
String sameSite = StringUtils.capitalize(value.getSameSite().name().toLowerCase(Locale.ROOT));
sb.append(';').append(SAME_SITE).append('=').append(sameSite);
}
sb.append(';').append(VERSION).append('=').append(value.getVersion());
return sb.toString();

}

/**
* Matches the SameSite attribute value case-insensitively; an unrecognized value is ignored,
* as the RFC 6265bis draft specifies.
*/
private static NewCookie.SameSite toSameSite(String attributeValue) {
for (NewCookie.SameSite sameSite : NewCookie.SameSite.values()) {
if (sameSite.name().equalsIgnoreCase(attributeValue)) {
return sameSite;
}
}
return null;
}

/**
* Append the input value string to the given buffer, wrapping it with
* quotes if need be.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package org.apache.cxf.jaxrs.impl;

import java.util.Date;
import java.util.Locale;

import jakarta.ws.rs.core.NewCookie;
import org.apache.cxf.jaxrs.utils.HttpUtils;
Expand All @@ -28,6 +29,7 @@

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

public class NewCookieHeaderProviderTest {
Expand Down Expand Up @@ -151,4 +153,98 @@ public void testToStringWithPathSlalshOnly() {
+ "Path=/path;Secure;Version=1", c.toString());
}

@Test
public void testFromStringWithSameSite() {
NewCookie c = NewCookie.valueOf("foo=bar;SameSite=Lax;Path=/");
assertEquals("foo", c.getName());
assertEquals("bar", c.getValue());
assertEquals("/", c.getPath());
assertEquals(NewCookie.SameSite.LAX, c.getSameSite());
}

@Test
public void testFromStringWithSameSiteIsCaseInsensitive() {
NewCookie c = NewCookie.valueOf("foo=bar; samesite=none; Secure");
assertEquals("foo", c.getName());
assertEquals("bar", c.getValue());
assertTrue(c.isSecure());
assertEquals(NewCookie.SameSite.NONE, c.getSameSite());
}

@Test
public void testFromStringWithUnrecognizedSameSiteValue() {
NewCookie c = NewCookie.valueOf("foo=bar;SameSite=Unknown");
assertEquals("foo", c.getName());
assertEquals("bar", c.getValue());
assertNull(c.getSameSite());
}

@Test
public void testFromStringIgnoresUnknownAttributes() {
NewCookie c = NewCookie.valueOf("foo=bar;Priority=High;Path=path");
assertEquals("foo", c.getName());
assertEquals("bar", c.getValue());
assertEquals("path", c.getPath());
}

@Test
public void testFromStringWithCookieNamedAfterAnAttribute() {
NewCookie c = NewCookie.valueOf("path=abc; Path=/");
assertEquals("path", c.getName());
assertEquals("abc", c.getValue());
assertEquals("/", c.getPath());
}

@Test
public void testFromStringWithCookieNamedSameSite() {
NewCookie c = NewCookie.valueOf("SameSite=Lax");
assertEquals("SameSite", c.getName());
assertEquals("Lax", c.getValue());
assertNull(c.getSameSite());
}

@Test(expected = IllegalArgumentException.class)
public void testFromStringWithoutNameValuePairIsMalformed() {
NewCookie.valueOf("Secure; foo=bar");
}

@Test
public void testFromStringSkipsEmptySegments() {
NewCookie c = NewCookie.valueOf(" ; foo=bar;;Path=path");
assertEquals("foo", c.getName());
assertEquals("bar", c.getValue());
assertEquals("path", c.getPath());
}

@Test
public void testToStringWithSameSite() {
NewCookie c = new NewCookie.Builder("foo").value("bar").path("path")
.sameSite(NewCookie.SameSite.STRICT).build();
assertEquals("foo=bar;Path=path;SameSite=Strict;Version=1", c.toString());
}

@Test
public void testToStringWithSameSiteUnderTurkishLocale() {
Locale defaultLocale = Locale.getDefault();
Locale.setDefault(Locale.forLanguageTag("tr-TR"));
try {
NewCookie c = new NewCookie.Builder("foo").value("bar")
.sameSite(NewCookie.SameSite.STRICT).build();
assertEquals("foo=bar;SameSite=Strict;Version=1", c.toString());
} finally {
Locale.setDefault(defaultLocale);
}
}

@Test
public void testSameSiteRoundTrip() {
NewCookie c = NewCookie.valueOf(
new NewCookie.Builder("foo").value("bar").sameSite(NewCookie.SameSite.NONE).secure(true).build()
.toString());
assertEquals("foo", c.getName());
assertEquals("bar", c.getValue());
assertTrue(c.isSecure());
assertEquals(NewCookie.SameSite.NONE, c.getSameSite());
}

}
Loading