From f5d3c14afdb3a00ec4eb32ff2771f0a503171208 Mon Sep 17 00:00:00 2001 From: Arturo Bernal Date: Sun, 14 Mar 2021 17:44:29 +0100 Subject: [PATCH] HTTPCLIENT-2139 - Cookie Header HttpOnly attribute --- .../apache/hc/client5/http/cookie/Cookie.java | 14 ++++ .../hc/client5/http/cookie/SetCookie.java | 11 +++ .../http/impl/cookie/BasicClientCookie.java | 26 +++++++ .../impl/cookie/BasicHttpOnlyHandler.java | 71 +++++++++++++++++++ .../impl/cookie/RFC6265CookieSpecFactory.java | 2 + .../http/impl/cookie/RFC6265LaxSpec.java | 1 + .../http/impl/cookie/RFC6265StrictSpec.java | 1 + .../cookie/TestBasicCookieAttribHandlers.java | 9 +++ 8 files changed, 135 insertions(+) create mode 100644 httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/BasicHttpOnlyHandler.java diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/Cookie.java b/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/Cookie.java index 4a8ac10c7..e2ee347a0 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/Cookie.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/Cookie.java @@ -44,6 +44,7 @@ public interface Cookie { String MAX_AGE_ATTR = "max-age"; String SECURE_ATTR = "secure"; String EXPIRES_ATTR = "expires"; + String HTTP_ONLY_ATTR = "httpOnly"; /** * @since 5.0 @@ -126,5 +127,18 @@ public interface Cookie { */ Date getCreationDate(); + /** + * Checks whether this Cookie has been marked as {@code httpOnly}. + *

The default implementation returns {@code false}. + * + * @return true if this Cookie has been marked as {@code httpOnly}, + * false otherwise + * + * @since 5.2 + */ + default boolean isHttpOnly(){ + return false; + } + } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/SetCookie.java b/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/SetCookie.java index 620006aae..546476ac9 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/SetCookie.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/SetCookie.java @@ -85,5 +85,16 @@ public interface SetCookie extends Cookie { */ void setSecure (boolean secure); + /** + * Marks or unmarks this Cookie as {@code httpOnly}. + * + * @param httpOnly true if this cookie is to be marked as + * {@code httpOnly}, false otherwise + * + * @since 5.2 + */ + default void setHttpOnly (final boolean httpOnly){ + } + } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/BasicClientCookie.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/BasicClientCookie.java index 62fb23348..e2cf88fd0 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/BasicClientCookie.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/BasicClientCookie.java @@ -215,6 +215,19 @@ public final class BasicClientCookie implements SetCookie, Cloneable, Serializab isSecure = secure; } + /** + * Sets the http-only attribute of the cookie. + * + * @param httpOnly true if this cookie is to be marked as + * {@code httpOnly}, false otherwise + * + * @since 5.2 + */ + @Override + public void setHttpOnly(final boolean httpOnly) { + this.httpOnly = httpOnly; + } + /** * Returns true if this cookie has expired. * @param date Current time @@ -236,6 +249,16 @@ public final class BasicClientCookie implements SetCookie, Cloneable, Serializab return creationDate; } + /** + * @return true if this Cookie has been marked as {@code httpOnly}, false otherwise + * @see #setHttpOnly(boolean) + * @since 5.2 + */ + @Override + public boolean isHttpOnly() { + return httpOnly; + } + /** * @since 4.4 */ @@ -317,5 +340,8 @@ public final class BasicClientCookie implements SetCookie, Cloneable, Serializab private Date creationDate; + /** The {@code httpOnly} flag. */ + private boolean httpOnly; + } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/BasicHttpOnlyHandler.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/BasicHttpOnlyHandler.java new file mode 100644 index 000000000..adb2067a7 --- /dev/null +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/BasicHttpOnlyHandler.java @@ -0,0 +1,71 @@ +/* + * ==================================================================== + * 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package org.apache.hc.client5.http.impl.cookie; + +import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; +import org.apache.hc.client5.http.cookie.Cookie; +import org.apache.hc.client5.http.cookie.CookieOrigin; +import org.apache.hc.client5.http.cookie.MalformedCookieException; +import org.apache.hc.client5.http.cookie.SetCookie; +import org.apache.hc.core5.annotation.Contract; +import org.apache.hc.core5.annotation.ThreadingBehavior; +import org.apache.hc.core5.util.Args; + +/** + * Cookie {@code HttpOnly} attribute handler. + * + * @since 5.2 + */ +@Contract(threading = ThreadingBehavior.STATELESS) +public class BasicHttpOnlyHandler implements CommonCookieAttributeHandler { + + public BasicHttpOnlyHandler() { + super(); + } + + @Override + public void parse(final SetCookie cookie, final String value) + throws MalformedCookieException { + Args.notNull(cookie, "Cookie"); + cookie.setHttpOnly(true); + } + + @Override + public void validate(final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException { + } + + @Override + public boolean match(final Cookie cookie, final CookieOrigin origin) { + return true; + } + + @Override + public String getAttributeName() { + return Cookie.HTTP_ONLY_ATTR; + } + +} \ No newline at end of file diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265CookieSpecFactory.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265CookieSpecFactory.java index 13994098f..e213aaa8f 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265CookieSpecFactory.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265CookieSpecFactory.java @@ -87,6 +87,7 @@ public class RFC6265CookieSpecFactory implements CookieSpecFactory { new BasicDomainHandler(), this.publicSuffixMatcher), new BasicMaxAgeHandler(), new BasicSecureHandler(), + new BasicHttpOnlyHandler(), new BasicExpiresHandler(RFC6265StrictSpec.DATE_PATTERNS)); break; case IE_MEDIUM_SECURITY: @@ -103,6 +104,7 @@ public class RFC6265CookieSpecFactory implements CookieSpecFactory { new BasicDomainHandler(), this.publicSuffixMatcher), new BasicMaxAgeHandler(), new BasicSecureHandler(), + new BasicHttpOnlyHandler(), new BasicExpiresHandler(RFC6265StrictSpec.DATE_PATTERNS)); break; default: diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265LaxSpec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265LaxSpec.java index 56fed126f..0684f14cf 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265LaxSpec.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265LaxSpec.java @@ -47,6 +47,7 @@ public class RFC6265LaxSpec extends RFC6265CookieSpecBase { new BasicDomainHandler(), new LaxMaxAgeHandler(), new BasicSecureHandler(), + new BasicHttpOnlyHandler(), new LaxExpiresHandler()); } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265StrictSpec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265StrictSpec.java index 22ec3e02a..262fb955c 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265StrictSpec.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/RFC6265StrictSpec.java @@ -53,6 +53,7 @@ public class RFC6265StrictSpec extends RFC6265CookieSpecBase { new BasicDomainHandler(), new BasicMaxAgeHandler(), new BasicSecureHandler(), + new BasicHttpOnlyHandler(), new BasicExpiresHandler(DATE_PATTERNS)); } diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/cookie/TestBasicCookieAttribHandlers.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/cookie/TestBasicCookieAttribHandlers.java index b10fbe9be..e78e1c4b7 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/cookie/TestBasicCookieAttribHandlers.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/cookie/TestBasicCookieAttribHandlers.java @@ -501,5 +501,14 @@ public class TestBasicCookieAttribHandlers { cookie.setAttribute(Cookie.DOMAIN_ATTR, "localhost"); Assert.assertTrue(h.match(cookie, new CookieOrigin("localhost", 80, "/stuff", false))); } + @Test + public void testBasicHttpOnlyParse() throws Exception { + final BasicClientCookie cookie = new BasicClientCookie("name", "value"); + final CookieAttributeHandler h = new BasicHttpOnlyHandler(); + h.parse(cookie, "true"); + Assert.assertTrue(cookie.isHttpOnly()); + h.parse(cookie, "anyone"); + Assert.assertTrue(cookie.isHttpOnly()); + } }