• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "net/device_bound_sessions/session_json_utils.h"
6 
7 #include "base/json/json_reader.h"
8 
9 namespace net::device_bound_sessions {
10 
11 namespace {
12 
ParseScope(const base::Value::Dict & scope_dict)13 SessionParams::Scope ParseScope(const base::Value::Dict& scope_dict) {
14   SessionParams::Scope scope;
15 
16   std::optional<bool> include_site = scope_dict.FindBool("include_site");
17   scope.include_site = include_site.value_or(false);
18   const base::Value::List* specifications_list =
19       scope_dict.FindList("scope_specification");
20   if (!specifications_list) {
21     return scope;
22   }
23 
24   for (const auto& specification : *specifications_list) {
25     const base::Value::Dict* specification_dict = specification.GetIfDict();
26     if (!specification_dict) {
27       continue;
28     }
29 
30     const std::string* type = specification_dict->FindString("type");
31     const std::string* domain = specification_dict->FindString("domain");
32     const std::string* path = specification_dict->FindString("path");
33     if (type && !type->empty() && domain && !domain->empty() && path &&
34         !path->empty()) {
35       if (*type == "include") {
36         scope.specifications.push_back(SessionParams::Scope::Specification{
37             SessionParams::Scope::Specification::Type::kInclude, *domain,
38             *path});
39       } else if (*type == "exclude") {
40         scope.specifications.push_back(SessionParams::Scope::Specification{
41             SessionParams::Scope::Specification::Type::kExclude, *domain,
42             *path});
43       }
44     }
45   }
46 
47   return scope;
48 }
49 
ParseCredentials(const base::Value::List & credentials_list)50 std::vector<SessionParams::Credential> ParseCredentials(
51     const base::Value::List& credentials_list) {
52   std::vector<SessionParams::Credential> cookie_credentials;
53   for (const auto& json_credential : credentials_list) {
54     SessionParams::Credential credential;
55     const base::Value::Dict* credential_dict = json_credential.GetIfDict();
56     if (!credential_dict) {
57       continue;
58     }
59     const std::string* type = credential_dict->FindString("type");
60     if (!type || *type != "cookie") {
61       continue;
62     }
63     const std::string* name = credential_dict->FindString("name");
64     const std::string* attributes = credential_dict->FindString("attributes");
65     if (name && attributes) {
66       cookie_credentials.push_back(
67           SessionParams::Credential{*name, *attributes});
68     }
69   }
70 
71   return cookie_credentials;
72 }
73 
74 }  // namespace
75 
ParseSessionInstructionJson(std::string_view response_json)76 std::optional<SessionParams> ParseSessionInstructionJson(
77     std::string_view response_json) {
78   // TODO(kristianm): Skip XSSI-escapes, see for example:
79   // https://hg.mozilla.org/mozilla-central/rev/4cee9ec9155e
80   // Discuss with others if XSSI should be part of the standard.
81 
82   // TODO(kristianm): Decide if the standard should require parsing
83   // to fail fully if any item is wrong, or if that item should be
84   // ignored.
85 
86   std::optional<base::Value::Dict> maybe_root = base::JSONReader::ReadDict(
87       response_json, base::JSON_PARSE_RFC, /*max_depth=*/5u);
88   if (!maybe_root) {
89     return std::nullopt;
90   }
91 
92   base::Value::Dict* scope_dict = maybe_root->FindDict("scope");
93 
94   std::string* session_id = maybe_root->FindString("session_identifier");
95   if (!session_id || session_id->empty()) {
96     return std::nullopt;
97   }
98 
99   std::string* refresh_url = maybe_root->FindString("refresh_url");
100 
101   std::vector<SessionParams::Credential> credentials;
102   base::Value::List* credentials_list = maybe_root->FindList("credentials");
103   if (credentials_list) {
104     credentials = ParseCredentials(*credentials_list);
105   }
106 
107   if (credentials.empty()) {
108     return std::nullopt;
109   }
110 
111   return SessionParams(
112       *session_id, refresh_url ? *refresh_url : "",
113       scope_dict ? ParseScope(*scope_dict) : SessionParams::Scope{},
114       std::move(credentials));
115 }
116 
117 }  // namespace net::device_bound_sessions
118