xref: /aosp_15_r20/external/cronet/net/cookies/cookie_base.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef NET_COOKIES_COOKIE_BASE_H_
6 #define NET_COOKIES_COOKIE_BASE_H_
7 
8 #include <optional>
9 #include <string>
10 #include <tuple>
11 
12 #include "net/base/net_export.h"
13 #include "net/cookies/cookie_access_params.h"
14 #include "net/cookies/cookie_access_result.h"
15 #include "net/cookies/cookie_constants.h"
16 #include "net/cookies/cookie_options.h"
17 #include "net/cookies/cookie_partition_key.h"
18 
19 class GURL;
20 
21 namespace net {
22 
23 // A base class for cookies and cookie-like objects. Encapsulates logic for
24 // determining whether a cookie could be sent/set, based on its attributes and
25 // the request context.
26 class NET_EXPORT CookieBase {
27  public:
28   // StrictlyUniqueCookieKey always populates the cookie's source scheme and
29   // source port.
30   using StrictlyUniqueCookieKey = std::tuple<std::optional<CookiePartitionKey>,
31                                              /*name=*/std::string,
32                                              /*domain=*/std::string,
33                                              /*path=*/std::string,
34                                              CookieSourceScheme,
35                                              /*source_port=*/int>;
36 
37   // Conditionally populates the source scheme and source port depending on the
38   // state of their associated feature.
39   using UniqueCookieKey = std::tuple<std::optional<CookiePartitionKey>,
40                                      /*name=*/std::string,
41                                      /*domain=*/std::string,
42                                      /*path=*/std::string,
43                                      std::optional<CookieSourceScheme>,
44                                      /*source_port=*/std::optional<int>>;
45 
46   // Same as UniqueCookieKey but for use with Domain cookies, which do not
47   // consider the source_port.
48   using UniqueDomainCookieKey = std::tuple<std::optional<CookiePartitionKey>,
49                                            /*name=*/std::string,
50                                            /*domain=*/std::string,
51                                            /*path=*/std::string,
52                                            std::optional<CookieSourceScheme>>;
53 
54   // Returns if the cookie should be included (and if not, why) for the given
55   // request |url| using the CookieInclusionStatus enum. HTTP only cookies can
56   // be filter by using appropriate cookie |options|.
57   //
58   // PLEASE NOTE that this method does not check whether a cookie is expired or
59   // not!
60   CookieAccessResult IncludeForRequestURL(
61       const GURL& url,
62       const CookieOptions& options,
63       const CookieAccessParams& params) const;
64 
65   // Returns if the cookie with given attributes can be set in context described
66   // by |options| and |params|, and if no, describes why.
67   //
68   // |cookie_access_result| is an optional input status, to allow for status
69   // chaining from callers. It helps callers provide the status of a
70   // cookie that may have warnings associated with it.
71   CookieAccessResult IsSetPermittedInContext(
72       const GURL& source_url,
73       const CookieOptions& options,
74       const CookieAccessParams& params,
75       const std::vector<std::string>& cookieable_schemes,
76       const std::optional<CookieAccessResult>& cookie_access_result =
77           std::nullopt) const;
78 
79   // Returns true if the given |url_path| path-matches this cookie's cookie-path
80   // as described in section 5.1.4 in RFC 6265. This returns true if |path_| and
81   // |url_path| are identical, or if |url_path| is a subdirectory of |path_|.
82   bool IsOnPath(const std::string& url_path) const;
83 
84   // This returns true if this cookie's |domain_| indicates that it can be
85   // accessed by |host|.
86   //
87   // In the case where |domain_| has no leading dot, this is a host cookie and
88   // will only domain match if |host| is identical to |domain_|.
89   //
90   // In the case where |domain_| has a leading dot, this is a domain cookie. It
91   // will match |host| if |domain_| is a suffix of |host|, or if |domain_| is
92   // exactly equal to |host| plus a leading dot.
93   //
94   // Note that this isn't quite the same as the "domain-match" algorithm in RFC
95   // 6265bis, since our implementation uses the presence of a leading dot in the
96   // |domain_| string in place of the spec's host-only-flag. That is, if
97   // |domain_| has no leading dot, then we only consider it matching if |host|
98   // is identical (which reflects the intended behavior when the cookie has a
99   // host-only-flag), whereas the RFC also treats them as domain-matching if
100   // |domain_| is a subdomain of |host|.
101   bool IsDomainMatch(const std::string& host) const;
102 
Name()103   const std::string& Name() const { return name_; }
104   // We represent the cookie's host-only-flag as the absence of a leading dot in
105   // Domain(). See IsDomainCookie() and IsHostCookie() below.
106   // If you want the "cookie's domain" as described in RFC 6265bis, use
107   // DomainWithoutDot().
Domain()108   const std::string& Domain() const { return domain_; }
Path()109   const std::string& Path() const { return path_; }
CreationDate()110   const base::Time& CreationDate() const { return creation_date_; }
SecureAttribute()111   bool SecureAttribute() const { return secure_; }
IsHttpOnly()112   bool IsHttpOnly() const { return httponly_; }
SameSite()113   CookieSameSite SameSite() const { return same_site_; }
114 
115   // Returns true if this cookie can only be accessed in a secure context.
116   bool IsSecure() const;
117 
IsPartitioned()118   bool IsPartitioned() const { return partition_key_.has_value(); }
PartitionKey()119   const std::optional<CookiePartitionKey>& PartitionKey() const {
120     return partition_key_;
121   }
122 
123   // Returns whether this cookie is Partitioned and its partition key matches a
124   // a same-site context by checking if the cookies domain site is the same as
125   // the partition key's site.
126   // This function should not be used for third-party cookie blocking
127   // enforcement-related decisions. That logic should rely on `IsPartitioned`.
128   // These functions are for recording metrics about partitioned cookie usage.
129   // Returns false if the cookie has no partition key.
130   bool IsFirstPartyPartitioned() const;
131 
132   // Returns whether the cookie is partitioned in a third-party context.
133   // This function should not be used for third-party cookie blocking
134   // enforcement-related decisions. That logic should rely on `IsPartitioned`.
135   // These functions are for recording metrics about partitioned cookie usage.
136   // Returns false if the cookie has no partition key.
137   bool IsThirdPartyPartitioned() const;
138 
139   // Returns an enum indicating the scheme of the origin that
140   // set this cookie. This is not part of the cookie spec but is being used to
141   // collect metrics for a potential change to the cookie spec
142   // (https://tools.ietf.org/html/draft-west-cookie-incrementalism-01#section-3.4)
SourceScheme()143   CookieSourceScheme SourceScheme() const { return source_scheme_; }
144   // Returns the port of the origin that originally set this cookie (the
145   // source port). This is not part of the cookie spec but is being used to
146   // collect metrics for a potential change to the cookie spec.
SourcePort()147   int SourcePort() const { return source_port_; }
148 
IsDomainCookie()149   bool IsDomainCookie() const { return !domain_.empty() && domain_[0] == '.'; }
IsHostCookie()150   bool IsHostCookie() const { return !IsDomainCookie(); }
151 
152   // Returns the cookie's domain, with the leading dot removed, if present.
153   // This corresponds to the "cookie's domain" as described in RFC 6265bis.
154   std::string DomainWithoutDot() const;
155 
StrictlyUniqueKey()156   StrictlyUniqueCookieKey StrictlyUniqueKey() const {
157     return std::make_tuple(partition_key_, name_, domain_, path_,
158                            source_scheme_, source_port_);
159   }
160 
161   // Returns a key such that two cookies with the same UniqueKey() are
162   // guaranteed to be equivalent in the sense of IsEquivalent().
163   // The `partition_key_` field will always be nullopt when partitioned cookies
164   // are not enabled.
165   // The source_scheme and source_port fields depend on whether or not their
166   // associated features are enabled.
167   UniqueCookieKey UniqueKey() const;
168 
169   // Same as UniqueKey() except it does not contain a source_port field. For use
170   // with Domain cookies, which do not consider the source_port.
171   UniqueDomainCookieKey UniqueDomainKey() const;
172 
SetSourceScheme(CookieSourceScheme source_scheme)173   void SetSourceScheme(CookieSourceScheme source_scheme) {
174     source_scheme_ = source_scheme;
175   }
176 
177   // Set the source port value. Performs a range check and sets the port to
178   // url::PORT_INVALID if value isn't in [0,65535] or url::PORT_UNSPECIFIED.
179   void SetSourcePort(int port);
180 
SetCreationDate(const base::Time & date)181   void SetCreationDate(const base::Time& date) { creation_date_ = date; }
182 
183  protected:
184   CookieBase();
185   CookieBase(const CookieBase& other);
186   CookieBase(CookieBase&& other);
187   CookieBase& operator=(const CookieBase& other);
188   CookieBase& operator=(CookieBase&& other);
189   virtual ~CookieBase();
190 
191   CookieBase(std::string name,
192              std::string domain,
193              std::string path,
194              base::Time creation,
195              bool secure,
196              bool httponly,
197              CookieSameSite same_site,
198              std::optional<CookiePartitionKey> partition_key,
199              CookieSourceScheme source_scheme = CookieSourceScheme::kUnset,
200              int source_port = url::PORT_UNSPECIFIED);
201 
202   // Returns the effective SameSite mode to apply to this cookie. Depends on the
203   // value of the given SameSite attribute and the access semantics of the
204   // cookie.
205   // Note: If you are converting to a different representation of a cookie, you
206   // probably want to use SameSite() instead of this method. Otherwise, if you
207   // are considering using this method, consider whether you should use
208   // IncludeForRequestURL() or IsSetPermittedInContext() instead of doing the
209   // SameSite computation yourself.
210   CookieEffectiveSameSite GetEffectiveSameSite(
211       CookieAccessSemantics access_semantics) const;
212 
213   // Returns whether the cookie was created at most |age_threshold| ago.
214   bool IsRecentlyCreated(base::TimeDelta age_threshold) const;
215 
216   // Checks if `port` is within [0,65535] or url::PORT_UNSPECIFIED. Returns
217   // `port` if so and url::PORT_INVALID otherwise.
218   static int ValidateAndAdjustSourcePort(int port);
219 
220  private:
221   // Allows subclasses to add custom logic for e.g. logging metrics. Called
222   // after inclusion has been determined for the respective access.
PostIncludeForRequestURL(const CookieAccessResult & access_result,const CookieOptions & options_used,CookieOptions::SameSiteCookieContext::ContextType cookie_inclusion_context_used)223   virtual void PostIncludeForRequestURL(
224       const CookieAccessResult& access_result,
225       const CookieOptions& options_used,
226       CookieOptions::SameSiteCookieContext::ContextType
227           cookie_inclusion_context_used) const {}
PostIsSetPermittedInContext(const CookieAccessResult & access_result,const CookieOptions & options_used)228   virtual void PostIsSetPermittedInContext(
229       const CookieAccessResult& access_result,
230       const CookieOptions& options_used) const {}
231 
232   // Keep defaults here in sync with
233   // services/network/public/interfaces/cookie_manager.mojom.
234   std::string name_;
235   std::string domain_;
236   std::string path_;
237   base::Time creation_date_;
238   bool secure_{false};
239   bool httponly_{false};
240   CookieSameSite same_site_{CookieSameSite::NO_RESTRICTION};
241   // This will be std::nullopt for all cookies not set with the Partitioned
242   // attribute or without a nonce. If the value is non-null, then the cookie
243   // will only be delivered when the top-frame site matches the partition key
244   // and the nonce (if present). If the partition key is non-null and opaque,
245   // this means the Partitioned cookie was created on an opaque origin or with
246   // a nonce.
247   std::optional<CookiePartitionKey> partition_key_;
248   CookieSourceScheme source_scheme_{CookieSourceScheme::kUnset};
249   // This can be [0,65535], PORT_UNSPECIFIED, or PORT_INVALID.
250   // PORT_UNSPECIFIED is used for cookies which already existed in the cookie
251   // store prior to this change and therefore their port is unknown.
252   // PORT_INVALID is an error for when an out of range port is provided.
253   int source_port_{url::PORT_UNSPECIFIED};
254 };
255 
256 }  // namespace net
257 
258 #endif  // NET_COOKIES_COOKIE_BASE_H_
259