Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(280)

Side by Side Diff: components/variations/variations_http_header_provider.cc

Issue 2558913003: Restrict transmission of external exp ids to signed in users. (Closed)
Patch Set: Address nit. Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/variations/variations_http_header_provider.h" 5 #include "components/variations/variations_http_header_provider.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <set> 9 #include <set>
10 #include <string> 10 #include <string>
11 #include <vector> 11 #include <vector>
12 12
13 #include "base/base64.h" 13 #include "base/base64.h"
14 #include "base/memory/singleton.h" 14 #include "base/memory/singleton.h"
15 #include "base/metrics/histogram_macros.h" 15 #include "base/metrics/histogram_macros.h"
16 #include "base/strings/string_number_conversions.h" 16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_split.h" 17 #include "base/strings/string_split.h"
18 #include "base/strings/string_util.h" 18 #include "base/strings/string_util.h"
19 #include "base/threading/thread_task_runner_handle.h" 19 #include "base/threading/thread_task_runner_handle.h"
20 #include "components/variations/proto/client_variations.pb.h" 20 #include "components/variations/proto/client_variations.pb.h"
21 21
22 namespace variations { 22 namespace variations {
23 23
24 // static 24 // static
25 VariationsHttpHeaderProvider* VariationsHttpHeaderProvider::GetInstance() { 25 VariationsHttpHeaderProvider* VariationsHttpHeaderProvider::GetInstance() {
26 return base::Singleton<VariationsHttpHeaderProvider>::get(); 26 return base::Singleton<VariationsHttpHeaderProvider>::get();
27 } 27 }
28 28
29 std::string VariationsHttpHeaderProvider::GetClientDataHeader() { 29 std::string VariationsHttpHeaderProvider::GetClientDataHeader(
30 bool is_signed_in) {
30 // Lazily initialize the header, if not already done, before attempting to 31 // Lazily initialize the header, if not already done, before attempting to
31 // transmit it. 32 // transmit it.
32 InitVariationIDsCacheIfNeeded(); 33 InitVariationIDsCacheIfNeeded();
33 34
34 std::string variation_ids_header_copy; 35 std::string variation_ids_header_copy;
35 { 36 {
36 base::AutoLock scoped_lock(lock_); 37 base::AutoLock scoped_lock(lock_);
37 variation_ids_header_copy = variation_ids_header_; 38 variation_ids_header_copy = is_signed_in
39 ? cached_variation_ids_header_signed_in_
40 : cached_variation_ids_header_;
38 } 41 }
39 return variation_ids_header_copy; 42 return variation_ids_header_copy;
40 } 43 }
41 44
42 std::string VariationsHttpHeaderProvider::GetVariationsString() { 45 std::string VariationsHttpHeaderProvider::GetVariationsString() {
43 InitVariationIDsCacheIfNeeded(); 46 InitVariationIDsCacheIfNeeded();
44 47
45 // Construct a space-separated string with leading and trailing spaces from 48 // Construct a space-separated string with leading and trailing spaces from
46 // the variations set. Note: The ids in it will be in sorted order per the 49 // the variations set. Note: The ids in it will be in sorted order per the
47 // std::set contract. 50 // std::set contract.
48 std::string ids_string = " "; 51 std::string ids_string = " ";
49 { 52 {
50 base::AutoLock scoped_lock(lock_); 53 base::AutoLock scoped_lock(lock_);
51 for (VariationID id : GetAllVariationIds()) { 54 for (const VariationIDEntry& entry : GetAllVariationIds()) {
52 ids_string.append(base::IntToString(id)); 55 if (entry.second == GOOGLE_WEB_PROPERTIES) {
53 ids_string.push_back(' '); 56 ids_string.append(base::IntToString(entry.first));
57 ids_string.push_back(' ');
58 }
54 } 59 }
55 } 60 }
56 return ids_string; 61 return ids_string;
57 } 62 }
58 63
59 bool VariationsHttpHeaderProvider::ForceVariationIds( 64 bool VariationsHttpHeaderProvider::ForceVariationIds(
60 const std::string& command_line_variation_ids, 65 const std::string& command_line_variation_ids,
61 std::vector<std::string>* variation_ids) { 66 std::vector<std::string>* variation_ids) {
62 if (!command_line_variation_ids.empty()) { 67 if (!command_line_variation_ids.empty()) {
63 // Combine |variation_ids| with |command_line_variation_ids|. 68 // Combine |variation_ids| with |command_line_variation_ids|.
64 std::vector<std::string> variation_ids_flags = 69 std::vector<std::string> variation_ids_flags =
65 base::SplitString(command_line_variation_ids, ",", 70 base::SplitString(command_line_variation_ids, ",",
66 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); 71 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
67 variation_ids->insert(variation_ids->end(), variation_ids_flags.begin(), 72 variation_ids->insert(variation_ids->end(), variation_ids_flags.begin(),
68 variation_ids_flags.end()); 73 variation_ids_flags.end());
69 } 74 }
70 75
71 if (!variation_ids->empty()) { 76 if (!variation_ids->empty()) {
72 // Create default variation ids which will always be included in the 77 // Create default variation ids which will always be included in the
73 // X-Client-Data request header. 78 // X-Client-Data request header.
74 return SetDefaultVariationIds(*variation_ids); 79 return SetDefaultVariationIds(*variation_ids);
75 } 80 }
76 return true; 81 return true;
77 } 82 }
78 83
79
80 bool VariationsHttpHeaderProvider::SetDefaultVariationIds( 84 bool VariationsHttpHeaderProvider::SetDefaultVariationIds(
81 const std::vector<std::string>& variation_ids) { 85 const std::vector<std::string>& variation_ids) {
82 default_variation_ids_set_.clear(); 86 default_variation_ids_set_.clear();
83 default_trigger_id_set_.clear();
84 for (const std::string& entry : variation_ids) { 87 for (const std::string& entry : variation_ids) {
85 if (entry.empty()) { 88 if (entry.empty()) {
86 default_variation_ids_set_.clear(); 89 default_variation_ids_set_.clear();
87 default_trigger_id_set_.clear();
88 return false; 90 return false;
89 } 91 }
90 bool trigger_id = 92 bool trigger_id =
91 base::StartsWith(entry, "t", base::CompareCase::SENSITIVE); 93 base::StartsWith(entry, "t", base::CompareCase::SENSITIVE);
92 // Remove the "t" prefix if it's there. 94 // Remove the "t" prefix if it's there.
93 std::string trimmed_entry = trigger_id ? entry.substr(1) : entry; 95 std::string trimmed_entry = trigger_id ? entry.substr(1) : entry;
94 96
95 int variation_id = 0; 97 int variation_id = 0;
96 if (!base::StringToInt(trimmed_entry, &variation_id)) { 98 if (!base::StringToInt(trimmed_entry, &variation_id)) {
97 default_variation_ids_set_.clear(); 99 default_variation_ids_set_.clear();
98 default_trigger_id_set_.clear();
99 return false; 100 return false;
100 } 101 }
101 if (trigger_id) 102 default_variation_ids_set_.insert(VariationIDEntry(
102 default_trigger_id_set_.insert(variation_id); 103 variation_id,
103 else 104 trigger_id ? GOOGLE_WEB_PROPERTIES_TRIGGER : GOOGLE_WEB_PROPERTIES));
104 default_variation_ids_set_.insert(variation_id);
105 } 105 }
106 return true; 106 return true;
107 } 107 }
108 108
109 void VariationsHttpHeaderProvider::ResetForTesting() { 109 void VariationsHttpHeaderProvider::ResetForTesting() {
110 base::AutoLock scoped_lock(lock_); 110 base::AutoLock scoped_lock(lock_);
111 111
112 // Stop observing field trials so that it can be restarted when this is 112 // Stop observing field trials so that it can be restarted when this is
113 // re-inited. Note: This is a no-op if this is not currently observing. 113 // re-inited. Note: This is a no-op if this is not currently observing.
114 base::FieldTrialList::RemoveObserver(this); 114 base::FieldTrialList::RemoveObserver(this);
115 variation_ids_cache_initialized_ = false; 115 variation_ids_cache_initialized_ = false;
116 } 116 }
117 117
118 VariationsHttpHeaderProvider::VariationsHttpHeaderProvider() 118 VariationsHttpHeaderProvider::VariationsHttpHeaderProvider()
119 : variation_ids_cache_initialized_(false) {} 119 : variation_ids_cache_initialized_(false) {}
120 120
121 VariationsHttpHeaderProvider::~VariationsHttpHeaderProvider() {} 121 VariationsHttpHeaderProvider::~VariationsHttpHeaderProvider() {}
122 122
123 void VariationsHttpHeaderProvider::OnFieldTrialGroupFinalized( 123 void VariationsHttpHeaderProvider::OnFieldTrialGroupFinalized(
124 const std::string& trial_name, 124 const std::string& trial_name,
125 const std::string& group_name) { 125 const std::string& group_name) {
126 VariationID new_id =
127 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_name, group_name);
128 VariationID new_trigger_id = GetGoogleVariationID(
129 GOOGLE_WEB_PROPERTIES_TRIGGER, trial_name, group_name);
130 if (new_id == EMPTY_ID && new_trigger_id == EMPTY_ID)
131 return;
132
133 base::AutoLock scoped_lock(lock_); 126 base::AutoLock scoped_lock(lock_);
134 if (new_id != EMPTY_ID) 127 const size_t old_size = variation_ids_set_.size();
135 variation_ids_set_.insert(new_id); 128 CacheVariationsId(trial_name, group_name, GOOGLE_WEB_PROPERTIES);
136 if (new_trigger_id != EMPTY_ID) 129 CacheVariationsId(trial_name, group_name, GOOGLE_WEB_PROPERTIES_SIGNED_IN);
137 variation_trigger_ids_set_.insert(new_trigger_id); 130 CacheVariationsId(trial_name, group_name, GOOGLE_WEB_PROPERTIES_TRIGGER);
138 131 if (variation_ids_set_.size() != old_size)
139 UpdateVariationIDsHeaderValue(); 132 UpdateVariationIDsHeaderValue();
140 } 133 }
141 134
142 void VariationsHttpHeaderProvider::OnSyntheticTrialsChanged( 135 void VariationsHttpHeaderProvider::OnSyntheticTrialsChanged(
143 const std::vector<SyntheticTrialGroup>& groups) { 136 const std::vector<SyntheticTrialGroup>& groups) {
144 base::AutoLock scoped_lock(lock_); 137 base::AutoLock scoped_lock(lock_);
145 138
146 synthetic_variation_ids_set_.clear(); 139 synthetic_variation_ids_set_.clear();
147 for (const SyntheticTrialGroup& group : groups) { 140 for (const SyntheticTrialGroup& group : groups) {
148 const VariationID id = 141 VariationID id =
149 GetGoogleVariationIDFromHashes(GOOGLE_WEB_PROPERTIES, group.id); 142 GetGoogleVariationIDFromHashes(GOOGLE_WEB_PROPERTIES, group.id);
150 if (id != EMPTY_ID) 143 if (id != EMPTY_ID) {
151 synthetic_variation_ids_set_.insert(id); 144 synthetic_variation_ids_set_.insert(
145 VariationIDEntry(id, GOOGLE_WEB_PROPERTIES));
146 }
147 id = GetGoogleVariationIDFromHashes(GOOGLE_WEB_PROPERTIES_SIGNED_IN,
148 group.id);
149 if (id != EMPTY_ID) {
150 synthetic_variation_ids_set_.insert(
151 VariationIDEntry(id, GOOGLE_WEB_PROPERTIES_SIGNED_IN));
152 }
152 } 153 }
153 UpdateVariationIDsHeaderValue(); 154 UpdateVariationIDsHeaderValue();
154 } 155 }
155 156
156 void VariationsHttpHeaderProvider::InitVariationIDsCacheIfNeeded() { 157 void VariationsHttpHeaderProvider::InitVariationIDsCacheIfNeeded() {
157 base::AutoLock scoped_lock(lock_); 158 base::AutoLock scoped_lock(lock_);
158 if (variation_ids_cache_initialized_) 159 if (variation_ids_cache_initialized_)
159 return; 160 return;
160 161
161 // Register for additional cache updates. This is done first to avoid a race 162 // Register for additional cache updates. This is done first to avoid a race
162 // that could cause registered FieldTrials to be missed. 163 // that could cause registered FieldTrials to be missed.
163 DCHECK(base::ThreadTaskRunnerHandle::IsSet()); 164 DCHECK(base::ThreadTaskRunnerHandle::IsSet());
164 base::FieldTrialList::AddObserver(this); 165 base::FieldTrialList::AddObserver(this);
165 166
166 base::TimeTicks before_time = base::TimeTicks::Now(); 167 base::TimeTicks before_time = base::TimeTicks::Now();
167 168
168 base::FieldTrial::ActiveGroups initial_groups; 169 base::FieldTrial::ActiveGroups initial_groups;
169 base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups); 170 base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups);
170 171
171 for (const auto& entry : initial_groups) { 172 for (const auto& entry : initial_groups) {
172 const VariationID id = 173 CacheVariationsId(entry.trial_name, entry.group_name,
173 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, entry.trial_name, 174 GOOGLE_WEB_PROPERTIES);
174 entry.group_name); 175 CacheVariationsId(entry.trial_name, entry.group_name,
175 if (id != EMPTY_ID) 176 GOOGLE_WEB_PROPERTIES_SIGNED_IN);
176 variation_ids_set_.insert(id); 177 CacheVariationsId(entry.trial_name, entry.group_name,
177 178 GOOGLE_WEB_PROPERTIES_TRIGGER);
178 const VariationID trigger_id =
179 GetGoogleVariationID(GOOGLE_WEB_PROPERTIES_TRIGGER, entry.trial_name,
180 entry.group_name);
181
182 if (trigger_id != EMPTY_ID)
183 variation_trigger_ids_set_.insert(trigger_id);
184 } 179 }
185 UpdateVariationIDsHeaderValue(); 180 UpdateVariationIDsHeaderValue();
186 181
187 UMA_HISTOGRAM_CUSTOM_COUNTS( 182 UMA_HISTOGRAM_CUSTOM_COUNTS(
188 "Variations.HeaderConstructionTime", 183 "Variations.HeaderConstructionTime",
189 (base::TimeTicks::Now() - before_time).InMicroseconds(), 1, 184 (base::TimeTicks::Now() - before_time).InMicroseconds(), 1,
190 base::TimeDelta::FromSeconds(1).InMicroseconds(), 50); 185 base::TimeDelta::FromSeconds(1).InMicroseconds(), 50);
191 186
192 variation_ids_cache_initialized_ = true; 187 variation_ids_cache_initialized_ = true;
193 } 188 }
194 189
190 void VariationsHttpHeaderProvider::CacheVariationsId(
191 const std::string& trial_name,
192 const std::string& group_name,
193 IDCollectionKey key) {
194 const VariationID id = GetGoogleVariationID(key, trial_name, group_name);
195 if (id != EMPTY_ID)
196 variation_ids_set_.insert(VariationIDEntry(id, key));
197 }
198
195 void VariationsHttpHeaderProvider::UpdateVariationIDsHeaderValue() { 199 void VariationsHttpHeaderProvider::UpdateVariationIDsHeaderValue() {
196 lock_.AssertAcquired(); 200 lock_.AssertAcquired();
197 201
198 // The header value is a serialized protobuffer of Variation IDs which is 202 // The header value is a serialized protobuffer of Variation IDs which is
199 // base64 encoded before transmitting as a string. 203 // base64 encoded before transmitting as a string.
200 variation_ids_header_.clear(); 204 cached_variation_ids_header_.clear();
205 cached_variation_ids_header_signed_in_.clear();
201 206
202 if (variation_ids_set_.empty() && default_variation_ids_set_.empty() && 207 // If successful, swap the header value with the new one.
203 variation_trigger_ids_set_.empty() && default_trigger_id_set_.empty() && 208 // Note that the list of IDs and the header could be temporarily out of sync
204 synthetic_variation_ids_set_.empty()) { 209 // if IDs are added as the header is recreated. The receiving servers are OK
205 return; 210 // with such discrepancies.
211 cached_variation_ids_header_ = GenerateBase64EncodedProto(false);
212 cached_variation_ids_header_signed_in_ = GenerateBase64EncodedProto(true);
213 }
214
215 std::string VariationsHttpHeaderProvider::GenerateBase64EncodedProto(
216 bool is_signed_in) {
217 std::set<VariationIDEntry> all_variation_ids_set = GetAllVariationIds();
218
219 ClientVariations proto;
220 for (const VariationIDEntry& entry : all_variation_ids_set) {
221 switch (entry.second) {
222 case GOOGLE_WEB_PROPERTIES_SIGNED_IN:
223 if (is_signed_in)
224 proto.add_variation_id(entry.first);
225 break;
226 case GOOGLE_WEB_PROPERTIES:
227 proto.add_variation_id(entry.first);
228 break;
229 case GOOGLE_WEB_PROPERTIES_TRIGGER:
230 proto.add_trigger_variation_id(entry.first);
231 break;
232 case CHROME_SYNC_SERVICE:
233 case ID_COLLECTION_COUNT:
234 // These cases included to get full enum coverage for switch, so that
235 // new enums introduce compiler warnings. Nothing to do for these.
236 break;
237 }
206 } 238 }
207 239
240 const size_t total_id_count =
241 proto.variation_id_size() + proto.trigger_variation_id_size();
242
243 if (total_id_count == 0)
244 return std::string();
245
208 // This is the bottleneck for the creation of the header, so validate the size 246 // This is the bottleneck for the creation of the header, so validate the size
209 // here. Force a hard maximum on the ID count in case the Variations server 247 // here. Force a hard maximum on the ID count in case the Variations server
210 // returns too many IDs and DOSs receiving servers with large requests. 248 // returns too many IDs and DOSs receiving servers with large requests.
211 const size_t total_id_count =
212 variation_ids_set_.size() + variation_trigger_ids_set_.size();
213 DCHECK_LE(total_id_count, 10U); 249 DCHECK_LE(total_id_count, 10U);
214 UMA_HISTOGRAM_COUNTS_100("Variations.Headers.ExperimentCount", 250 UMA_HISTOGRAM_COUNTS_100("Variations.Headers.ExperimentCount",
215 total_id_count); 251 total_id_count);
216 if (total_id_count > 20) 252 if (total_id_count > 20)
217 return; 253 return std::string();
218
219 std::set<VariationID> all_variation_ids_set = GetAllVariationIds();
220 std::set<VariationID> all_trigger_ids_set = default_trigger_id_set_;
221 for (VariationID id : variation_trigger_ids_set_)
222 all_trigger_ids_set.insert(id);
223
224 ClientVariations proto;
225 for (VariationID id : all_variation_ids_set)
226 proto.add_variation_id(id);
227 for (VariationID id : all_trigger_ids_set)
228 proto.add_trigger_variation_id(id);
229 254
230 std::string serialized; 255 std::string serialized;
231 proto.SerializeToString(&serialized); 256 proto.SerializeToString(&serialized);
232 257
233 std::string hashed; 258 std::string hashed;
234 base::Base64Encode(serialized, &hashed); 259 base::Base64Encode(serialized, &hashed);
235 // If successful, swap the header value with the new one. 260 return hashed;
236 // Note that the list of IDs and the header could be temporarily out of sync
237 // if IDs are added as the header is recreated. The receiving servers are OK
238 // with such discrepancies.
239 variation_ids_header_ = hashed;
240 } 261 }
241 262
242 std::set<VariationID> VariationsHttpHeaderProvider::GetAllVariationIds() { 263 std::set<VariationsHttpHeaderProvider::VariationIDEntry>
264 VariationsHttpHeaderProvider::GetAllVariationIds() {
243 lock_.AssertAcquired(); 265 lock_.AssertAcquired();
244 266
245 std::set<VariationID> all_variation_ids_set = default_variation_ids_set_; 267 std::set<VariationIDEntry> all_variation_ids_set = default_variation_ids_set_;
246 for (VariationID id : variation_ids_set_) 268 for (const VariationIDEntry& entry : variation_ids_set_) {
247 all_variation_ids_set.insert(id); 269 all_variation_ids_set.insert(entry);
248 for (VariationID id : synthetic_variation_ids_set_) 270 }
249 all_variation_ids_set.insert(id); 271 for (const VariationIDEntry& entry : synthetic_variation_ids_set_) {
272 all_variation_ids_set.insert(entry);
273 }
250 return all_variation_ids_set; 274 return all_variation_ids_set;
251 } 275 }
252 276
253 } // namespace variations 277 } // namespace variations
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698