Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / chromeos / policy / cloud_external_data_manager_base.cc
blob3fcc6b2bcf6076c9e6fc91582712561e5dade24d
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.
5 #include "chrome/browser/chromeos/policy/cloud_external_data_manager_base.h"
7 #include <map>
8 #include <string>
9 #include <vector>
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/callback.h"
14 #include "base/location.h"
15 #include "base/logging.h"
16 #include "base/message_loop/message_loop_proxy.h"
17 #include "base/sequenced_task_runner.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/values.h"
20 #include "chrome/browser/chromeos/policy/cloud_external_data_store.h"
21 #include "components/policy/core/common/cloud/cloud_policy_store.h"
22 #include "components/policy/core/common/cloud/external_policy_data_fetcher.h"
23 #include "components/policy/core/common/cloud/external_policy_data_updater.h"
24 #include "components/policy/core/common/external_data_fetcher.h"
25 #include "components/policy/core/common/policy_map.h"
26 #include "net/url_request/url_request_context_getter.h"
28 namespace policy {
30 namespace {
32 // Fetch data for at most two external data references at the same time.
33 const int kMaxParallelFetches = 2;
35 // Allows policies to reference |max_external_data_size_for_testing| bytes of
36 // external data even if no |max_size| was specified in policy_templates.json.
37 int max_external_data_size_for_testing = 0;
39 } // namespace
41 // Backend for the CloudExternalDataManagerBase that handles all data download,
42 // verification, caching and retrieval.
43 class CloudExternalDataManagerBase::Backend {
44 public:
45 // |get_policy_details| is used to determine the maximum size that the
46 // data referenced by each policy can have. This class can be instantiated on
47 // any thread but from then on, may be accessed via the |task_runner_| only.
48 // All FetchCallbacks will be invoked via |callback_task_runner|.
49 Backend(const GetChromePolicyDetailsCallback& get_policy_details,
50 scoped_refptr<base::SequencedTaskRunner> task_runner,
51 scoped_refptr<base::SequencedTaskRunner> callback_task_runner);
53 // Allows downloaded external data to be cached in |external_data_store|.
54 // Ownership of the store is taken. The store can be destroyed by calling
55 // SetExternalDataStore(scoped_ptr<CloudExternalDataStore>()).
56 void SetExternalDataStore(
57 scoped_ptr<CloudExternalDataStore> external_data_store);
59 // Allows downloading of external data via the |external_policy_data_fetcher|.
60 void Connect(
61 scoped_ptr<ExternalPolicyDataFetcher> external_policy_data_fetcher);
63 // Prevents further external data downloads and aborts any downloads currently
64 // in progress
65 void Disconnect();
67 // Called when the external data references that this backend is responsible
68 // for change. |metadata| maps from policy names to the metadata specifying
69 // the external data that each of the policies references.
70 void OnMetadataUpdated(scoped_ptr<Metadata> metadata);
72 // Called by the |updater_| when the external |data| referenced by |policy|
73 // has been successfully downloaded and verified to match |hash|.
74 bool OnDownloadSuccess(const std::string& policy,
75 const std::string& hash,
76 const std::string& data);
78 // Retrieves the external data referenced by |policy| and invokes |callback|
79 // with the result. If |policy| does not reference any external data, the
80 // |callback| is invoked with a NULL pointer. Otherwise, the |callback| is
81 // invoked with the referenced data once it has been successfully retrieved.
82 // If retrieval is temporarily impossible (e.g. the data is not cached yet and
83 // there is no network connectivity), the |callback| will be invoked when the
84 // temporary hindrance is resolved. If retrieval is permanently impossible
85 // (e.g. |policy| references data that does not exist on the server), the
86 // |callback| will never be invoked.
87 // If the data for |policy| is not cached yet, only one download is started,
88 // even if this method is invoked multiple times. The |callback|s passed are
89 // enqueued and all invoked once the data has been successfully retrieved.
90 void Fetch(const std::string& policy,
91 const ExternalDataFetcher::FetchCallback& callback);
93 // Try to download and cache all external data referenced by |metadata_|.
94 void FetchAll();
96 private:
97 // List of callbacks to invoke when the attempt to retrieve external data
98 // referenced by a policy completes successfully or fails permanently.
99 typedef std::vector<ExternalDataFetcher::FetchCallback> FetchCallbackList;
101 // Map from policy names to the lists of callbacks defined above.
102 typedef std::map<std::string, FetchCallbackList> FetchCallbackMap;
104 // Looks up the maximum size that the data referenced by |policy| can have.
105 size_t GetMaxExternalDataSize(const std::string& policy) const;
107 // Invokes |callback| via the |callback_task_runner_|, passing |data| as a
108 // parameter.
109 void RunCallback(const ExternalDataFetcher::FetchCallback& callback,
110 scoped_ptr<std::string> data) const;
112 // Tells the |updater_| to download the external data referenced by |policy|.
113 // If Connect() was not called yet and no |updater_| exists, does nothing.
114 void StartDownload(const std::string& policy);
116 // Used to determine the maximum size that the data referenced by each policy
117 // can have.
118 GetChromePolicyDetailsCallback get_policy_details_;
120 scoped_refptr<base::SequencedTaskRunner> task_runner_;
121 scoped_refptr<base::SequencedTaskRunner> callback_task_runner_;
123 // Contains the policies for which a download of the referenced external data
124 // has been requested. Each policy is mapped to a list of callbacks to invoke
125 // when the download completes successfully or fails permanently. If no
126 // callback needs to be invoked (because the download was requested via
127 // FetchAll()), a map entry will still exist but the list of callbacks it maps
128 // to will be empty.
129 FetchCallbackMap pending_downloads_;
131 // Indicates that OnMetadataUpdated() has been called at least once and the
132 // contents of |metadata_| is initialized.
133 bool metadata_set_;
135 // Maps from policy names to the metadata specifying the external data that
136 // each of the policies references.
137 Metadata metadata_;
139 // Used to cache external data referenced by policies.
140 scoped_ptr<CloudExternalDataStore> external_data_store_;
142 // Used to download external data referenced by policies.
143 scoped_ptr<ExternalPolicyDataUpdater> updater_;
145 DISALLOW_COPY_AND_ASSIGN(Backend);
148 CloudExternalDataManagerBase::Backend::Backend(
149 const GetChromePolicyDetailsCallback& get_policy_details,
150 scoped_refptr<base::SequencedTaskRunner> task_runner,
151 scoped_refptr<base::SequencedTaskRunner> callback_task_runner)
152 : get_policy_details_(get_policy_details),
153 task_runner_(task_runner),
154 callback_task_runner_(callback_task_runner),
155 metadata_set_(false) {
158 void CloudExternalDataManagerBase::Backend::SetExternalDataStore(
159 scoped_ptr<CloudExternalDataStore> external_data_store) {
160 external_data_store_.reset(external_data_store.release());
161 if (metadata_set_ && external_data_store_)
162 external_data_store_->Prune(metadata_);
165 void CloudExternalDataManagerBase::Backend::Connect(
166 scoped_ptr<ExternalPolicyDataFetcher> external_policy_data_fetcher) {
167 DCHECK(!updater_);
168 updater_.reset(new ExternalPolicyDataUpdater(
169 task_runner_,
170 external_policy_data_fetcher.Pass(),
171 kMaxParallelFetches));
172 for (FetchCallbackMap::const_iterator it = pending_downloads_.begin();
173 it != pending_downloads_.end(); ++it) {
174 StartDownload(it->first);
178 void CloudExternalDataManagerBase::Backend::Disconnect() {
179 updater_.reset();
182 void CloudExternalDataManagerBase::Backend::OnMetadataUpdated(
183 scoped_ptr<Metadata> metadata) {
184 metadata_set_ = true;
185 Metadata old_metadata;
186 metadata_.swap(old_metadata);
187 if (metadata)
188 metadata_.swap(*metadata);
190 if (external_data_store_)
191 external_data_store_->Prune(metadata_);
193 for (FetchCallbackMap::iterator it = pending_downloads_.begin();
194 it != pending_downloads_.end(); ) {
195 const std::string policy = it->first;
196 Metadata::const_iterator metadata = metadata_.find(policy);
197 if (metadata == metadata_.end()) {
198 // |policy| no longer references external data.
199 if (updater_) {
200 // Cancel the external data download.
201 updater_->CancelExternalDataFetch(policy);
203 for (FetchCallbackList::const_iterator callback = it->second.begin();
204 callback != it->second.end(); ++callback) {
205 // Invoke all callbacks for |policy|, indicating permanent failure.
206 RunCallback(*callback, scoped_ptr<std::string>());
208 pending_downloads_.erase(it++);
209 continue;
212 if (updater_ && metadata->second != old_metadata[policy]) {
213 // |policy| still references external data but the reference has changed.
214 // Cancel the external data download and start a new one.
215 updater_->CancelExternalDataFetch(policy);
216 StartDownload(policy);
218 ++it;
222 bool CloudExternalDataManagerBase::Backend::OnDownloadSuccess(
223 const std::string& policy,
224 const std::string& hash,
225 const std::string& data) {
226 DCHECK(metadata_.find(policy) != metadata_.end());
227 DCHECK_EQ(hash, metadata_[policy].hash);
228 if (external_data_store_)
229 external_data_store_->Store(policy, hash, data);
231 const FetchCallbackList& pending_callbacks = pending_downloads_[policy];
232 for (FetchCallbackList::const_iterator it = pending_callbacks.begin();
233 it != pending_callbacks.end(); ++it) {
234 RunCallback(*it, make_scoped_ptr(new std::string(data)));
236 pending_downloads_.erase(policy);
237 return true;
240 void CloudExternalDataManagerBase::Backend::Fetch(
241 const std::string& policy,
242 const ExternalDataFetcher::FetchCallback& callback) {
243 Metadata::const_iterator metadata = metadata_.find(policy);
244 if (metadata == metadata_.end()) {
245 // If |policy| does not reference any external data, indicate permanent
246 // failure.
247 RunCallback(callback, scoped_ptr<std::string>());
248 return;
251 if (pending_downloads_.find(policy) != pending_downloads_.end()) {
252 // If a download of the external data referenced by |policy| has already
253 // been requested, add |callback| to the list of callbacks for |policy| and
254 // return.
255 pending_downloads_[policy].push_back(callback);
256 return;
259 scoped_ptr<std::string> data(new std::string);
260 if (external_data_store_ && external_data_store_->Load(
261 policy, metadata->second.hash, GetMaxExternalDataSize(policy),
262 data.get())) {
263 // If the external data referenced by |policy| exists in the cache and
264 // matches the expected hash, pass it to the callback.
265 RunCallback(callback, data.Pass());
266 return;
269 // Request a download of the the external data referenced by |policy| and
270 // initialize the list of callbacks by adding |callback|.
271 pending_downloads_[policy].push_back(callback);
272 StartDownload(policy);
275 void CloudExternalDataManagerBase::Backend::FetchAll() {
276 // Loop through all external data references.
277 for (Metadata::const_iterator it = metadata_.begin(); it != metadata_.end();
278 ++it) {
279 const std::string& policy = it->first;
280 scoped_ptr<std::string> data(new std::string);
281 if (pending_downloads_.find(policy) != pending_downloads_.end() ||
282 (external_data_store_ && external_data_store_->Load(
283 policy, it->second.hash, GetMaxExternalDataSize(policy),
284 data.get()))) {
285 // If a download of the external data referenced by |policy| has already
286 // been requested or the data exists in the cache and matches the expected
287 // hash, there is nothing to be done.
288 continue;
290 // Request a download of the the external data referenced by |policy| and
291 // initialize the list of callbacks to an empty list.
292 pending_downloads_[policy];
293 StartDownload(policy);
297 size_t CloudExternalDataManagerBase::Backend::GetMaxExternalDataSize(
298 const std::string& policy) const {
299 if (max_external_data_size_for_testing)
300 return max_external_data_size_for_testing;
302 // Look up the maximum size that the data referenced by |policy| can have in
303 // get_policy_details, which is constructed from the information in
304 // policy_templates.json, allowing the maximum data size to be specified as
305 // part of the policy definition.
306 const PolicyDetails* details = get_policy_details_.Run(policy);
307 if (details)
308 return details->max_external_data_size;
309 NOTREACHED();
310 return 0;
313 void CloudExternalDataManagerBase::Backend::RunCallback(
314 const ExternalDataFetcher::FetchCallback& callback,
315 scoped_ptr<std::string> data) const {
316 callback_task_runner_->PostTask(FROM_HERE,
317 base::Bind(callback, base::Passed(&data)));
320 void CloudExternalDataManagerBase::Backend::StartDownload(
321 const std::string& policy) {
322 DCHECK(pending_downloads_.find(policy) != pending_downloads_.end());
323 if (!updater_)
324 return;
326 const MetadataEntry& metadata = metadata_[policy];
327 updater_->FetchExternalData(
328 policy,
329 ExternalPolicyDataUpdater::Request(metadata.url,
330 metadata.hash,
331 GetMaxExternalDataSize(policy)),
332 base::Bind(&CloudExternalDataManagerBase::Backend::OnDownloadSuccess,
333 base::Unretained(this),
334 policy,
335 metadata.hash));
338 CloudExternalDataManagerBase::CloudExternalDataManagerBase(
339 const GetChromePolicyDetailsCallback& get_policy_details,
340 scoped_refptr<base::SequencedTaskRunner> backend_task_runner,
341 scoped_refptr<base::SequencedTaskRunner> io_task_runner)
342 : backend_task_runner_(backend_task_runner),
343 io_task_runner_(io_task_runner),
344 backend_(new Backend(get_policy_details,
345 backend_task_runner_,
346 base::MessageLoopProxy::current())) {
349 CloudExternalDataManagerBase::~CloudExternalDataManagerBase() {
350 DCHECK(CalledOnValidThread());
351 io_task_runner_->DeleteSoon(FROM_HERE,
352 external_policy_data_fetcher_backend_.release());
353 backend_task_runner_->DeleteSoon(FROM_HERE, backend_.release());
356 void CloudExternalDataManagerBase::SetExternalDataStore(
357 scoped_ptr<CloudExternalDataStore> external_data_store) {
358 DCHECK(CalledOnValidThread());
359 backend_task_runner_->PostTask(FROM_HERE, base::Bind(
360 &Backend::SetExternalDataStore,
361 base::Unretained(backend_.get()),
362 base::Passed(&external_data_store)));
365 void CloudExternalDataManagerBase::SetPolicyStore(
366 CloudPolicyStore* policy_store) {
367 DCHECK(CalledOnValidThread());
368 CloudExternalDataManager::SetPolicyStore(policy_store);
369 if (policy_store_ && policy_store_->is_initialized())
370 OnPolicyStoreLoaded();
373 void CloudExternalDataManagerBase::OnPolicyStoreLoaded() {
374 // Collect all external data references made by policies in |policy_store_|
375 // and pass them to the |backend_|.
376 DCHECK(CalledOnValidThread());
377 scoped_ptr<Metadata> metadata(new Metadata);
378 const PolicyMap& policy_map = policy_store_->policy_map();
379 for (PolicyMap::const_iterator it = policy_map.begin();
380 it != policy_map.end(); ++it) {
381 if (!it->second.external_data_fetcher) {
382 // Skip policies that do not reference external data.
383 continue;
385 const base::DictionaryValue* dict = NULL;
386 std::string url;
387 std::string hex_hash;
388 std::vector<uint8> hash;
389 if (it->second.value && it->second.value->GetAsDictionary(&dict) &&
390 dict->GetStringWithoutPathExpansion("url", &url) &&
391 dict->GetStringWithoutPathExpansion("hash", &hex_hash) &&
392 !url.empty() && !hex_hash.empty() &&
393 base::HexStringToBytes(hex_hash, &hash)) {
394 // Add the external data reference to |metadata| if it is valid (URL and
395 // hash are not empty, hash can be decoded as a hex string).
396 (*metadata)[it->first] =
397 MetadataEntry(url, std::string(hash.begin(), hash.end()));
401 backend_task_runner_->PostTask(FROM_HERE, base::Bind(
402 &Backend::OnMetadataUpdated,
403 base::Unretained(backend_.get()),
404 base::Passed(&metadata)));
407 void CloudExternalDataManagerBase::Connect(
408 scoped_refptr<net::URLRequestContextGetter> request_context) {
409 DCHECK(CalledOnValidThread());
410 DCHECK(!external_policy_data_fetcher_backend_);
411 external_policy_data_fetcher_backend_.reset(
412 new ExternalPolicyDataFetcherBackend(io_task_runner_,
413 request_context));
414 backend_task_runner_->PostTask(FROM_HERE, base::Bind(
415 &Backend::Connect,
416 base::Unretained(backend_.get()),
417 base::Passed(external_policy_data_fetcher_backend_->CreateFrontend(
418 backend_task_runner_))));
421 void CloudExternalDataManagerBase::Disconnect() {
422 DCHECK(CalledOnValidThread());
423 io_task_runner_->DeleteSoon(FROM_HERE,
424 external_policy_data_fetcher_backend_.release());
425 backend_task_runner_->PostTask(FROM_HERE, base::Bind(
426 &Backend::Disconnect, base::Unretained(backend_.get())));
429 void CloudExternalDataManagerBase::Fetch(
430 const std::string& policy,
431 const ExternalDataFetcher::FetchCallback& callback) {
432 DCHECK(CalledOnValidThread());
433 backend_task_runner_->PostTask(FROM_HERE, base::Bind(
434 &Backend::Fetch, base::Unretained(backend_.get()), policy, callback));
437 // static
438 void CloudExternalDataManagerBase::SetMaxExternalDataSizeForTesting(
439 int max_size) {
440 max_external_data_size_for_testing = max_size;
443 void CloudExternalDataManagerBase::FetchAll() {
444 DCHECK(CalledOnValidThread());
445 backend_task_runner_->PostTask(FROM_HERE, base::Bind(
446 &Backend::FetchAll, base::Unretained(backend_.get())));
449 } // namespace policy