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 "components/metrics/structured/key_data_prefs_delegate.h"
6
7 #include <optional>
8 #include <string_view>
9 #include <utility>
10
11 #include "base/check.h"
12 #include "base/logging.h"
13 #include "base/values.h"
14 #include "components/metrics/structured/lib/key_data.h"
15 #include "components/metrics/structured/lib/key_util.h"
16 #include "components/metrics/structured/lib/proto/key.pb.h"
17 #include "components/metrics/structured/project_validator.h"
18 #include "components/metrics/structured/structured_metrics_validator.h"
19 #include "components/prefs/scoped_user_pref_update.h"
20
21 namespace metrics::structured {
22
KeyDataPrefsDelegate(PrefService * local_state,std::string_view pref_name)23 KeyDataPrefsDelegate::KeyDataPrefsDelegate(PrefService* local_state,
24 std::string_view pref_name)
25 : local_state_(local_state), pref_name_(pref_name) {
26 CHECK(local_state_);
27 CHECK(!pref_name_.empty());
28
29 LoadKeysFromPrefs();
30 }
31
32 KeyDataPrefsDelegate::~KeyDataPrefsDelegate() = default;
33
IsReady() const34 bool KeyDataPrefsDelegate::IsReady() const {
35 return true;
36 }
37
GetKey(uint64_t project_name_hash) const38 const KeyProto* KeyDataPrefsDelegate::GetKey(uint64_t project_name_hash) const {
39 CHECK(IsReady());
40 auto it = proto_.keys().find(project_name_hash);
41 if (it != proto_.keys().end()) {
42 return &it->second;
43 }
44 return nullptr;
45 }
46
UpsertKey(uint64_t project_name_hash,base::TimeDelta last_key_rotation,base::TimeDelta key_rotation_period)47 void KeyDataPrefsDelegate::UpsertKey(uint64_t project_name_hash,
48 base::TimeDelta last_key_rotation,
49 base::TimeDelta key_rotation_period) {
50 KeyProto& key_proto = (*(proto_.mutable_keys()))[project_name_hash];
51 key_proto.set_key(util::GenerateNewKey());
52 key_proto.set_last_rotation(last_key_rotation.InDays());
53 key_proto.set_rotation_period(key_rotation_period.InDays());
54
55 UpdatePrefsByProject(project_name_hash, key_proto);
56 }
57
Purge()58 void KeyDataPrefsDelegate::Purge() {
59 // Clears in-memory keys.
60 proto_.mutable_keys()->clear();
61
62 // Clears persisted keys.
63 local_state_->ClearPref(pref_name_);
64 }
65
LoadKeysFromPrefs()66 void KeyDataPrefsDelegate::LoadKeysFromPrefs() {
67 const base::Value::Dict& keys_pref = local_state_->GetDict(pref_name_);
68
69 // Use the validators to get the project name to project hash mapping.
70 const validator::Validators* validators = validator::Validators::Get();
71
72 auto* proto_keys = proto_.mutable_keys();
73
74 for (const auto [project_name, project_keys] : keys_pref) {
75 std::optional<const ProjectValidator*> project_validator =
76 validators->GetProjectValidator(project_name);
77
78 // Check if a project was found for the name.
79 if (!project_validator.has_value()) {
80 continue;
81 }
82
83 const uint64_t project_hash = (*project_validator)->project_hash();
84 const base::Value::Dict* value_dict = project_keys.GetIfDict();
85 if (!value_dict) {
86 LOG(ERROR) << "Key Pref value was expected to be a dict.";
87 continue;
88 }
89
90 std::optional<KeyProto> key_data =
91 util::CreateKeyProtoFromValue(*value_dict);
92
93 if (!key_data.has_value()) {
94 LOG(ERROR) << "Failed to convert pref value into key data.";
95 continue;
96 }
97
98 (*proto_keys)[project_hash] = *key_data;
99 }
100 }
101
UpdatePrefsByProject(uint64_t project_name_hash,const KeyProto & key_proto)102 void KeyDataPrefsDelegate::UpdatePrefsByProject(uint64_t project_name_hash,
103 const KeyProto& key_proto) {
104 ScopedDictPrefUpdate pref_updater(local_state_, pref_name_);
105
106 base::Value::Dict& dict = pref_updater.Get();
107
108 // Get the name of the project for |project_name_hash| to be used to store the
109 // keys in prefs.
110 const validator::Validators* validators = validator::Validators::Get();
111 std::optional<std::string_view> project_name =
112 validators->GetProjectName(project_name_hash);
113
114 if (!project_name.has_value()) {
115 LOG(ERROR) << "Attempting to store key for invalid project: "
116 << project_name_hash;
117 return;
118 }
119
120 dict.Set(*project_name, util::CreateValueFromKeyProto(key_proto));
121 }
122
123 } // namespace metrics::structured
124