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