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

Side by Side Diff: chrome/browser/policy/cloud/cloud_external_data_manager_base.cc

Issue 19462006: Implement fetching, caching and retrieval of external policy data (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebased. Created 7 years, 4 months 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2013 The Chromium Authors. All rights reserved.
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 "chrome/browser/policy/cloud/cloud_external_data_manager_base.h"
6
7 #include <map>
8 #include <string>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/sequenced_task_runner.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/values.h"
18 #include "chrome/browser/policy/cloud/cloud_external_data_store.h"
19 #include "chrome/browser/policy/cloud/cloud_policy_store.h"
20 #include "chrome/browser/policy/cloud/external_policy_data_updater.h"
21 #include "chrome/browser/policy/external_data_fetcher.h"
22 #include "chrome/browser/policy/policy_map.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "net/url_request/url_request_context_getter.h"
25 #include "policy/policy_constants.h"
26
27 namespace policy {
28
29 namespace {
30
31 // Fetch data for at most two external data references at the same time.
32 const int kMaxParallelFetches = 2;
33
34 void RunCallbackOnUIThread(const ExternalDataFetcher::FetchCallback& callback,
35 scoped_ptr<std::string> data) {
36 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
37 callback.Run(data.Pass());
38 }
39
40 } // namespace
41
42 // Backend for the CloudExternalDataManagerBase that handles all data download,
43 // verification, caching and retrieval.
44 class CloudExternalDataManagerBase::Backend {
45 public:
46 // The |policy_definitions| are used to determine the maximum size that the
47 // data referenced by each policy can have. This class is instantiated on the
48 // UI thread but from then on, is accessed via the |task_runner_| only.
49 Backend(const PolicyDefinitionList* policy_definitions,
50 scoped_refptr<base::SequencedTaskRunner> task_runner);
51
52 // Allows downloaded external data to be cached in |external_data_store|.
53 // Ownership of the store is taken. The store can be destroyed by calling
54 // SetExternalDataStore(scoped_ptr<CloudExternalDataStore>()).
55 void SetExternalDataStore(
56 scoped_ptr<CloudExternalDataStore> external_data_store);
57
58 // Allows downloading of external data by constructing URLFetchers from
59 // |request_context|.
60 void Connect(scoped_refptr<net::URLRequestContextGetter> request_context);
61
62 // Prevents further external data downloads and aborts any downloads currently
63 // in progress
64 void Disconnect();
65
66 // Called when the external data references that this backend is responsible
67 // for change. |metadata| maps from policy names to the metadata specifying
68 // the external data that each of the policies references.
69 void OnMetadataUpdated(scoped_ptr<Metadata> metadata);
70
71 // Called by the |updater_| when the external |data| referenced by |policy|
72 // has been successfully downloaded and verified to match |hash|.
73 bool OnDownloadSuccess(const std::string& policy,
74 const std::string& hash,
75 const std::string& data);
76
77 // Retrieves the external data referenced by |policy| and invokes |callback|
78 // with the result. If |policy| does not reference any external data, the
79 // |callback| is invoked with a NULL pointer. Otherwise, the |callback| is
80 // invoked with the referenced data once it has been successfully retrieved.
81 // If retrieval is temporarily impossible (e.g. the data is not cached yet and
82 // there is no network connectivity), the |callback| will be invoked when the
83 // temporary hindrance is resolved. If retrieval is permanently impossible
84 // (e.g. |policy| references data that does not exist on the server), the
85 // |callback| will never be invoked.
86 // If the data for |policy| is not cached yet, only one download is started,
87 // even if this method is invoked multiple times. The |callback|s passed are
88 // enqueued and all invoked once the data has been successfully retrieved.
89 void Fetch(const std::string& policy,
90 const ExternalDataFetcher::FetchCallback& callback);
91
92 // Try to download and cache all external data referenced by |metadata_|.
93 void FetchAll();
94
95 private:
96 // List of callbacks to invoke when the attempt to retrieve external data
97 // referenced by a policy completes successfully or fails permanently.
98 typedef std::vector<ExternalDataFetcher::FetchCallback> FetchCallbackList;
99
100 // Map from policy names to the lists of callbacks defined above.
101 typedef std::map<std::string, FetchCallbackList> FetchCallbackMap;
102
103 // Looks up the maximum size that the data referenced by |policy| can have in
104 // |policy_definitions_|.
105 size_t GetMaxExternalDataSize(const std::string& policy) const;
106
107 // Invokes |callback| on the UI thread, passing |data| as a parameter.
108 void RunCallback(const ExternalDataFetcher::FetchCallback& callback,
109 scoped_ptr<std::string> data) const;
110
111 // Tells the |updater_| to download the external data referenced by |policy|.
112 // If Connect() was not called yet and no |updater_| exists, does nothing.
113 void StartDownload(const std::string& policy);
114
115 // Used to determine the maximum size that the data referenced by each policy
116 // can have.
117 const PolicyDefinitionList* policy_definitions_;
118
119 scoped_refptr<base::SequencedTaskRunner> task_runner_;
120
121 // Contains the policies for which a download of the referenced external data
122 // has been requested. Each policy is mapped to a list of callbacks to invoke
123 // when the download completes successfully or fails permanently. If no
124 // callback needs to be invoked (because the download was requested via
125 // FetchAll()), a map entry will still exist but the list of callbacks it maps
126 // to will be empty.
127 FetchCallbackMap pending_downloads_;
128
129 // Indicates that OnMetadataUpdated() has been called at least once and the
130 // contents of |metadata_| is initialized.
131 bool metadata_set_;
132
133 // Maps from policy names to the metadata specifying the external data that
134 // each of the policies references.
135 Metadata metadata_;
136
137 // Used to cache external data referenced by policies.
138 scoped_ptr<CloudExternalDataStore> external_data_store_;
139
140 // Used to download external data referenced by policies.
141 scoped_ptr<ExternalPolicyDataUpdater> updater_;
142
143 DISALLOW_COPY_AND_ASSIGN(Backend);
144 };
145
146 CloudExternalDataManagerBase::Backend::Backend(
147 const PolicyDefinitionList* policy_definitions,
148 scoped_refptr<base::SequencedTaskRunner> task_runner)
149 : policy_definitions_(policy_definitions),
150 task_runner_(task_runner),
151 metadata_set_(false) {
152 }
153
154 void CloudExternalDataManagerBase::Backend::SetExternalDataStore(
155 scoped_ptr<CloudExternalDataStore> external_data_store) {
156 external_data_store_.reset(external_data_store.release());
157 if (metadata_set_ && external_data_store_)
158 external_data_store_->Prune(metadata_);
159 }
160
161 void CloudExternalDataManagerBase::Backend::Connect(
162 scoped_refptr<net::URLRequestContextGetter> request_context) {
163 DCHECK(!updater_);
164 updater_.reset(new ExternalPolicyDataUpdater(task_runner_,
165 request_context,
166 kMaxParallelFetches));
167 for (FetchCallbackMap::const_iterator it = pending_downloads_.begin();
168 it != pending_downloads_.end(); ++it) {
169 StartDownload(it->first);
170 }
171 }
172
173 void CloudExternalDataManagerBase::Backend::Disconnect() {
174 updater_.reset();
175 }
176
177 void CloudExternalDataManagerBase::Backend::OnMetadataUpdated(
178 scoped_ptr<Metadata> metadata) {
179 metadata_set_ = true;
180 Metadata old_metadata;
181 metadata_.swap(old_metadata);
182 if (metadata)
183 metadata_.swap(*metadata);
184
185 if (external_data_store_)
186 external_data_store_->Prune(metadata_);
187
188 for (FetchCallbackMap::iterator it = pending_downloads_.begin();
189 it != pending_downloads_.end(); ) {
190 const std::string policy = it->first;
191 Metadata::const_iterator metadata = metadata_.find(policy);
192 if (metadata == metadata_.end()) {
193 // |policy| no longer references external data.
194 if (updater_) {
195 // Cancel the external data download.
196 updater_->CancelExternalDataFetch(policy);
197 }
198 for (FetchCallbackList::const_iterator callback = it->second.begin();
199 callback != it->second.end(); ++callback) {
200 // Invoke all callbacks for |policy|, indicating permanent failure.
201 RunCallback(*callback, scoped_ptr<std::string>());
202 }
203 pending_downloads_.erase(it++);
204 continue;
205 }
206
207 if (updater_ && metadata->second != old_metadata[policy]) {
208 // |policy| still references external data but the reference has changed.
209 // Cancel the external data download and start a new one.
210 updater_->CancelExternalDataFetch(policy);
211 StartDownload(policy);
212 }
213 ++it;
214 }
215 }
216
217 bool CloudExternalDataManagerBase::Backend::OnDownloadSuccess(
218 const std::string& policy,
219 const std::string& hash,
220 const std::string& data) {
221 DCHECK(metadata_.find(policy) != metadata_.end());
222 DCHECK_EQ(hash, metadata_[policy].hash);
223 if (external_data_store_)
224 external_data_store_->Store(policy, hash, data);
225
226 const FetchCallbackList& pending_callbacks = pending_downloads_[policy];
227 for (FetchCallbackList::const_iterator it = pending_callbacks.begin();
228 it != pending_callbacks.end(); ++it) {
229 RunCallback(*it, make_scoped_ptr(new std::string(data)));
230 }
231 pending_downloads_.erase(policy);
232 return true;
233 }
234
235 void CloudExternalDataManagerBase::Backend::Fetch(
236 const std::string& policy,
237 const ExternalDataFetcher::FetchCallback& callback) {
238 Metadata::const_iterator metadata = metadata_.find(policy);
239 if (metadata == metadata_.end()) {
240 // If |policy| does not reference any external data, indicate permanent
241 // failure.
242 RunCallback(callback, scoped_ptr<std::string>());
243 return;
244 }
245
246 if (pending_downloads_.find(policy) != pending_downloads_.end()) {
247 // If a download of the external data referenced by |policy| has already
248 // been requested, add |callback| to the list of callbacks for |policy| and
249 // return.
250 pending_downloads_[policy].push_back(callback);
251 return;
252 }
253
254 scoped_ptr<std::string> data(new std::string);
255 if (external_data_store_ && external_data_store_->Load(
256 policy, metadata->second.hash, GetMaxExternalDataSize(policy),
257 data.get())) {
258 // If the external data referenced by |policy| exists in the cache and
259 // matches the expected hash, pass it to the callback.
260 RunCallback(callback, data.Pass());
261 return;
262 }
263
264 // Request a download of the the external data referenced by |policy| and
265 // initialize the list of callbacks by adding |callback|.
266 pending_downloads_[policy].push_back(callback);
267 StartDownload(policy);
268 }
269
270 void CloudExternalDataManagerBase::Backend::FetchAll() {
271 // Loop through all external data references.
272 for (Metadata::const_iterator it = metadata_.begin(); it != metadata_.end();
273 ++it) {
274 const std::string& policy = it->first;
275 scoped_ptr<std::string> data(new std::string);
276 if (pending_downloads_.find(policy) != pending_downloads_.end() ||
277 (external_data_store_ && external_data_store_->Load(
278 policy, it->second.hash, GetMaxExternalDataSize(policy),
279 data.get()))) {
280 // If a download of the external data referenced by |policy| has already
281 // been requested or the data exists in the cache and matches the expected
282 // hash, there is nothing to be done.
283 continue;
284 }
285 // Request a download of the the external data referenced by |policy| and
286 // initialize the list of callbacks to an empty list.
287 pending_downloads_[policy];
288 StartDownload(policy);
289 }
290 }
291
292 size_t CloudExternalDataManagerBase::Backend::GetMaxExternalDataSize(
293 const std::string& policy) const {
294 // Look up the maximum size that the data referenced by |policy| can have in
295 // policy_definitions_, which is constructed from the information in
296 // policy_templates.json, allowing the maximum data size to be specified as
297 // part of the policy definition.
298 for (const PolicyDefinitionList::Entry* entry = policy_definitions_->begin;
299 entry != policy_definitions_->end; ++entry) {
300 if (entry->name == policy)
301 return entry->max_external_data_size;
302 }
303 NOTREACHED();
304 return 0;
305 }
306
307 void CloudExternalDataManagerBase::Backend::RunCallback(
308 const ExternalDataFetcher::FetchCallback& callback,
309 scoped_ptr<std::string> data) const {
310 content::BrowserThread::PostTask(
311 content::BrowserThread::UI, FROM_HERE,
312 base::Bind(RunCallbackOnUIThread, callback, base::Passed(&data)));
313 }
314
315 void CloudExternalDataManagerBase::Backend::StartDownload(
316 const std::string& policy) {
317 DCHECK(pending_downloads_.find(policy) != pending_downloads_.end());
318 if (!updater_)
319 return;
320
321 const MetadataEntry& metadata = metadata_[policy];
322 updater_->FetchExternalData(
323 policy,
324 ExternalPolicyDataUpdater::Request(metadata.url,
325 metadata.hash,
326 GetMaxExternalDataSize(policy)),
327 base::Bind(&CloudExternalDataManagerBase::Backend::OnDownloadSuccess,
328 base::Unretained(this),
329 policy,
330 metadata.hash));
331 }
332
333 CloudExternalDataManagerBase::CloudExternalDataManagerBase(
334 const PolicyDefinitionList* policy_definitions,
335 scoped_refptr<base::SequencedTaskRunner> backend_task_runner)
336 : backend_task_runner_(backend_task_runner),
337 backend_(new Backend(policy_definitions, backend_task_runner_)) {
338 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
339 }
340
341 CloudExternalDataManagerBase::~CloudExternalDataManagerBase() {
342 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
343 backend_task_runner_->DeleteSoon(FROM_HERE, backend_);
344 }
345
346 void CloudExternalDataManagerBase::SetExternalDataStore(
347 scoped_ptr<CloudExternalDataStore> external_data_store) {
348 backend_task_runner_->PostTask(FROM_HERE, base::Bind(
349 &Backend::SetExternalDataStore,
350 base::Unretained(backend_),
351 base::Passed(&external_data_store)));
352 }
353
354 void CloudExternalDataManagerBase::SetPolicyStore(
355 CloudPolicyStore* policy_store) {
356 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
357 CloudExternalDataManager::SetPolicyStore(policy_store);
358 if (policy_store_ && policy_store_->is_initialized())
359 OnPolicyStoreLoaded();
360 }
361
362 void CloudExternalDataManagerBase::OnPolicyStoreLoaded() {
363 // Collect all external data references made by policies in |policy_store_|
364 // and pass them to the |backend_|.
365 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
366 scoped_ptr<Metadata> metadata(new Metadata);
367 const PolicyMap& policy_map = policy_store_->policy_map();
368 for (PolicyMap::const_iterator it = policy_map.begin();
369 it != policy_map.end(); ++it) {
370 if (!it->second.external_data_fetcher) {
371 // Skip policies that do not reference external data.
372 continue;
373 }
374 const base::DictionaryValue* dict = NULL;
375 std::string url;
376 std::string hex_hash;
377 std::vector<uint8> hash;
378 if (it->second.value && it->second.value->GetAsDictionary(&dict) &&
379 dict->GetStringWithoutPathExpansion("url", &url) &&
380 dict->GetStringWithoutPathExpansion("hash", &hex_hash) &&
381 !url.empty() && !hex_hash.empty() &&
382 base::HexStringToBytes(hex_hash, &hash)) {
383 // Add the external data reference to |metadata| if it is valid (URL and
384 // hash are not empty, hash can be decoded as a hex string).
385 (*metadata)[it->first] =
386 MetadataEntry(url, std::string(hash.begin(), hash.end()));
387 }
388 }
389
390 backend_task_runner_->PostTask(FROM_HERE, base::Bind(
391 &Backend::OnMetadataUpdated,
392 base::Unretained(backend_),
393 base::Passed(&metadata)));
394 }
395
396 void CloudExternalDataManagerBase::Connect(
397 scoped_refptr<net::URLRequestContextGetter> request_context) {
398 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
399 backend_task_runner_->PostTask(FROM_HERE, base::Bind(
400 &Backend::Connect, base::Unretained(backend_), request_context));
401 }
402
403 void CloudExternalDataManagerBase::Disconnect() {
404 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
405 backend_task_runner_->PostTask(FROM_HERE, base::Bind(
406 &Backend::Disconnect, base::Unretained(backend_)));
407 }
408
409 void CloudExternalDataManagerBase::Fetch(
410 const std::string& policy,
411 const ExternalDataFetcher::FetchCallback& callback) {
412 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
413 backend_task_runner_->PostTask(FROM_HERE, base::Bind(
414 &Backend::Fetch, base::Unretained(backend_), policy, callback));
415 }
416
417 void CloudExternalDataManagerBase::FetchAll() {
418 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
419 backend_task_runner_->PostTask(FROM_HERE, base::Bind(
420 &Backend::FetchAll, base::Unretained(backend_)));
421 }
422
423 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698