- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / policy / cloud_external_data_manager_base.cc
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/chromeos/policy/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/message_loop/message_loop_proxy.h"
16 #include "base/sequenced_task_runner.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/values.h"
19 #include "chrome/browser/chromeos/policy/cloud_external_data_store.h"
20 #include "chrome/browser/policy/cloud/cloud_policy_store.h"
21 #include "chrome/browser/policy/cloud/external_policy_data_fetcher.h"
22 #include "chrome/browser/policy/cloud/external_policy_data_updater.h"
23 #include "chrome/browser/policy/external_data_fetcher.h"
24 #include "chrome/browser/policy/policy_map.h"
25 #include "net/url_request/url_request_context_getter.h"
26 #include "policy/policy_constants.h"
27
28 namespace policy {
29
30 namespace {
31
32 // Fetch data for at most two external data references at the same time.
33 const int kMaxParallelFetches = 2;
34
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;
38
39 }  // namespace
40
41 // Backend for the CloudExternalDataManagerBase that handles all data download,
42 // verification, caching and retrieval.
43 class CloudExternalDataManagerBase::Backend {
44  public:
45   // The |policy_definitions| are 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 PolicyDefinitionList* policy_definitions,
50           scoped_refptr<base::SequencedTaskRunner> task_runner,
51           scoped_refptr<base::SequencedTaskRunner> callback_task_runner);
52
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);
58
59   // Allows downloading of external data via the |external_policy_data_fetcher|.
60   void Connect(
61       scoped_ptr<ExternalPolicyDataFetcher> external_policy_data_fetcher);
62
63   // Prevents further external data downloads and aborts any downloads currently
64   // in progress
65   void Disconnect();
66
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);
71
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);
77
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);
92
93   // Try to download and cache all external data referenced by |metadata_|.
94   void FetchAll();
95
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;
100
101   // Map from policy names to the lists of callbacks defined above.
102   typedef std::map<std::string, FetchCallbackList> FetchCallbackMap;
103
104   // Looks up the maximum size that the data referenced by |policy| can have in
105   // |policy_definitions_|.
106   size_t GetMaxExternalDataSize(const std::string& policy) const;
107
108   // Invokes |callback| via the |callback_task_runner_|, passing |data| as a
109   // parameter.
110   void RunCallback(const ExternalDataFetcher::FetchCallback& callback,
111                    scoped_ptr<std::string> data) const;
112
113   // Tells the |updater_| to download the external data referenced by |policy|.
114   // If Connect() was not called yet and no |updater_| exists, does nothing.
115   void StartDownload(const std::string& policy);
116
117   // Used to determine the maximum size that the data referenced by each policy
118   // can have.
119   const PolicyDefinitionList* policy_definitions_;
120
121   scoped_refptr<base::SequencedTaskRunner> task_runner_;
122   scoped_refptr<base::SequencedTaskRunner> callback_task_runner_;
123
124   // Contains the policies for which a download of the referenced external data
125   // has been requested. Each policy is mapped to a list of callbacks to invoke
126   // when the download completes successfully or fails permanently. If no
127   // callback needs to be invoked (because the download was requested via
128   // FetchAll()), a map entry will still exist but the list of callbacks it maps
129   // to will be empty.
130   FetchCallbackMap pending_downloads_;
131
132   // Indicates that OnMetadataUpdated() has been called at least once and the
133   // contents of |metadata_| is initialized.
134   bool metadata_set_;
135
136   // Maps from policy names to the metadata specifying the external data that
137   // each of the policies references.
138   Metadata metadata_;
139
140   // Used to cache external data referenced by policies.
141   scoped_ptr<CloudExternalDataStore> external_data_store_;
142
143   // Used to download external data referenced by policies.
144   scoped_ptr<ExternalPolicyDataUpdater> updater_;
145
146   DISALLOW_COPY_AND_ASSIGN(Backend);
147 };
148
149 CloudExternalDataManagerBase::Backend::Backend(
150     const PolicyDefinitionList* policy_definitions,
151     scoped_refptr<base::SequencedTaskRunner> task_runner,
152     scoped_refptr<base::SequencedTaskRunner> callback_task_runner)
153     : policy_definitions_(policy_definitions),
154       task_runner_(task_runner),
155       callback_task_runner_(callback_task_runner),
156       metadata_set_(false) {
157 }
158
159 void CloudExternalDataManagerBase::Backend::SetExternalDataStore(
160     scoped_ptr<CloudExternalDataStore> external_data_store) {
161   external_data_store_.reset(external_data_store.release());
162   if (metadata_set_ && external_data_store_)
163     external_data_store_->Prune(metadata_);
164 }
165
166 void CloudExternalDataManagerBase::Backend::Connect(
167     scoped_ptr<ExternalPolicyDataFetcher> external_policy_data_fetcher) {
168   DCHECK(!updater_);
169   updater_.reset(new ExternalPolicyDataUpdater(
170       task_runner_,
171       external_policy_data_fetcher.Pass(),
172       kMaxParallelFetches));
173   for (FetchCallbackMap::const_iterator it = pending_downloads_.begin();
174        it != pending_downloads_.end(); ++it) {
175     StartDownload(it->first);
176   }
177 }
178
179 void CloudExternalDataManagerBase::Backend::Disconnect() {
180   updater_.reset();
181 }
182
183 void CloudExternalDataManagerBase::Backend::OnMetadataUpdated(
184     scoped_ptr<Metadata> metadata) {
185   metadata_set_ = true;
186   Metadata old_metadata;
187   metadata_.swap(old_metadata);
188   if (metadata)
189     metadata_.swap(*metadata);
190
191   if (external_data_store_)
192     external_data_store_->Prune(metadata_);
193
194   for (FetchCallbackMap::iterator it = pending_downloads_.begin();
195        it != pending_downloads_.end(); ) {
196     const std::string policy = it->first;
197     Metadata::const_iterator metadata = metadata_.find(policy);
198     if (metadata == metadata_.end()) {
199       // |policy| no longer references external data.
200       if (updater_) {
201         // Cancel the external data download.
202         updater_->CancelExternalDataFetch(policy);
203       }
204       for (FetchCallbackList::const_iterator callback = it->second.begin();
205            callback != it->second.end(); ++callback) {
206         // Invoke all callbacks for |policy|, indicating permanent failure.
207         RunCallback(*callback, scoped_ptr<std::string>());
208       }
209       pending_downloads_.erase(it++);
210       continue;
211     }
212
213     if (updater_ && metadata->second != old_metadata[policy]) {
214       // |policy| still references external data but the reference has changed.
215       // Cancel the external data download and start a new one.
216       updater_->CancelExternalDataFetch(policy);
217       StartDownload(policy);
218     }
219     ++it;
220   }
221 }
222
223 bool CloudExternalDataManagerBase::Backend::OnDownloadSuccess(
224     const std::string& policy,
225     const std::string& hash,
226     const std::string& data) {
227   DCHECK(metadata_.find(policy) != metadata_.end());
228   DCHECK_EQ(hash, metadata_[policy].hash);
229   if (external_data_store_)
230     external_data_store_->Store(policy, hash, data);
231
232   const FetchCallbackList& pending_callbacks = pending_downloads_[policy];
233   for (FetchCallbackList::const_iterator it = pending_callbacks.begin();
234        it != pending_callbacks.end(); ++it) {
235     RunCallback(*it, make_scoped_ptr(new std::string(data)));
236   }
237   pending_downloads_.erase(policy);
238   return true;
239 }
240
241 void CloudExternalDataManagerBase::Backend::Fetch(
242     const std::string& policy,
243     const ExternalDataFetcher::FetchCallback& callback) {
244   Metadata::const_iterator metadata = metadata_.find(policy);
245   if (metadata == metadata_.end()) {
246     // If |policy| does not reference any external data, indicate permanent
247     // failure.
248     RunCallback(callback, scoped_ptr<std::string>());
249     return;
250   }
251
252   if (pending_downloads_.find(policy) != pending_downloads_.end()) {
253     // If a download of the external data referenced by |policy| has already
254     // been requested, add |callback| to the list of callbacks for |policy| and
255     // return.
256     pending_downloads_[policy].push_back(callback);
257     return;
258   }
259
260   scoped_ptr<std::string> data(new std::string);
261   if (external_data_store_ && external_data_store_->Load(
262           policy, metadata->second.hash, GetMaxExternalDataSize(policy),
263           data.get())) {
264     // If the external data referenced by |policy| exists in the cache and
265     // matches the expected hash, pass it to the callback.
266     RunCallback(callback, data.Pass());
267     return;
268   }
269
270   // Request a download of the the external data referenced by |policy| and
271   // initialize the list of callbacks by adding |callback|.
272   pending_downloads_[policy].push_back(callback);
273   StartDownload(policy);
274 }
275
276 void CloudExternalDataManagerBase::Backend::FetchAll() {
277   // Loop through all external data references.
278   for (Metadata::const_iterator it = metadata_.begin(); it != metadata_.end();
279        ++it) {
280     const std::string& policy = it->first;
281     scoped_ptr<std::string> data(new std::string);
282     if (pending_downloads_.find(policy) != pending_downloads_.end() ||
283         (external_data_store_ && external_data_store_->Load(
284              policy, it->second.hash, GetMaxExternalDataSize(policy),
285              data.get()))) {
286       // If a download of the external data referenced by |policy| has already
287       // been requested or the data exists in the cache and matches the expected
288       // hash, there is nothing to be done.
289       continue;
290     }
291     // Request a download of the the external data referenced by |policy| and
292     // initialize the list of callbacks to an empty list.
293     pending_downloads_[policy];
294     StartDownload(policy);
295   }
296 }
297
298 size_t CloudExternalDataManagerBase::Backend::GetMaxExternalDataSize(
299     const std::string& policy) const {
300   if (max_external_data_size_for_testing)
301     return max_external_data_size_for_testing;
302
303   // Look up the maximum size that the data referenced by |policy| can have in
304   // policy_definitions_, which is constructed from the information in
305   // policy_templates.json, allowing the maximum data size to be specified as
306   // part of the policy definition.
307   for (const PolicyDefinitionList::Entry* entry = policy_definitions_->begin;
308        entry != policy_definitions_->end; ++entry) {
309     if (entry->name == policy)
310       return entry->max_external_data_size;
311   }
312   NOTREACHED();
313   return 0;
314 }
315
316 void CloudExternalDataManagerBase::Backend::RunCallback(
317     const ExternalDataFetcher::FetchCallback& callback,
318     scoped_ptr<std::string> data) const {
319   callback_task_runner_->PostTask(FROM_HERE,
320                                   base::Bind(callback, base::Passed(&data)));
321 }
322
323 void CloudExternalDataManagerBase::Backend::StartDownload(
324     const std::string& policy) {
325   DCHECK(pending_downloads_.find(policy) != pending_downloads_.end());
326   if (!updater_)
327     return;
328
329   const MetadataEntry& metadata = metadata_[policy];
330   updater_->FetchExternalData(
331       policy,
332       ExternalPolicyDataUpdater::Request(metadata.url,
333                                          metadata.hash,
334                                          GetMaxExternalDataSize(policy)),
335       base::Bind(&CloudExternalDataManagerBase::Backend::OnDownloadSuccess,
336                  base::Unretained(this),
337                  policy,
338                  metadata.hash));
339 }
340
341 CloudExternalDataManagerBase::CloudExternalDataManagerBase(
342     const PolicyDefinitionList* policy_definitions,
343     scoped_refptr<base::SequencedTaskRunner> backend_task_runner,
344     scoped_refptr<base::SequencedTaskRunner> io_task_runner)
345     : backend_task_runner_(backend_task_runner),
346       io_task_runner_(io_task_runner),
347       backend_(new Backend(policy_definitions,
348                            backend_task_runner_,
349                            base::MessageLoopProxy::current())) {
350 }
351
352 CloudExternalDataManagerBase::~CloudExternalDataManagerBase() {
353   DCHECK(CalledOnValidThread());
354   io_task_runner_->DeleteSoon(FROM_HERE,
355                               external_policy_data_fetcher_backend_.release());
356   backend_task_runner_->DeleteSoon(FROM_HERE, backend_.release());
357 }
358
359 void CloudExternalDataManagerBase::SetExternalDataStore(
360     scoped_ptr<CloudExternalDataStore> external_data_store) {
361   DCHECK(CalledOnValidThread());
362   backend_task_runner_->PostTask(FROM_HERE, base::Bind(
363       &Backend::SetExternalDataStore,
364       base::Unretained(backend_.get()),
365       base::Passed(&external_data_store)));
366 }
367
368 void CloudExternalDataManagerBase::SetPolicyStore(
369     CloudPolicyStore* policy_store) {
370   DCHECK(CalledOnValidThread());
371   CloudExternalDataManager::SetPolicyStore(policy_store);
372   if (policy_store_ && policy_store_->is_initialized())
373     OnPolicyStoreLoaded();
374 }
375
376 void CloudExternalDataManagerBase::OnPolicyStoreLoaded() {
377   // Collect all external data references made by policies in |policy_store_|
378   // and pass them to the |backend_|.
379   DCHECK(CalledOnValidThread());
380   scoped_ptr<Metadata> metadata(new Metadata);
381   const PolicyMap& policy_map = policy_store_->policy_map();
382   for (PolicyMap::const_iterator it = policy_map.begin();
383        it != policy_map.end(); ++it) {
384     if (!it->second.external_data_fetcher) {
385       // Skip policies that do not reference external data.
386       continue;
387     }
388     const base::DictionaryValue* dict = NULL;
389     std::string url;
390     std::string hex_hash;
391     std::vector<uint8> hash;
392     if (it->second.value && it->second.value->GetAsDictionary(&dict) &&
393         dict->GetStringWithoutPathExpansion("url", &url) &&
394         dict->GetStringWithoutPathExpansion("hash", &hex_hash) &&
395         !url.empty() && !hex_hash.empty() &&
396         base::HexStringToBytes(hex_hash, &hash)) {
397       // Add the external data reference to |metadata| if it is valid (URL and
398       // hash are not empty, hash can be decoded as a hex string).
399       (*metadata)[it->first] =
400           MetadataEntry(url, std::string(hash.begin(), hash.end()));
401     }
402   }
403
404   backend_task_runner_->PostTask(FROM_HERE, base::Bind(
405       &Backend::OnMetadataUpdated,
406       base::Unretained(backend_.get()),
407       base::Passed(&metadata)));
408 }
409
410 void CloudExternalDataManagerBase::Connect(
411     scoped_refptr<net::URLRequestContextGetter> request_context) {
412   DCHECK(CalledOnValidThread());
413   DCHECK(!external_policy_data_fetcher_backend_);
414   external_policy_data_fetcher_backend_.reset(
415       new ExternalPolicyDataFetcherBackend(io_task_runner_,
416                                            request_context));
417   backend_task_runner_->PostTask(FROM_HERE, base::Bind(
418       &Backend::Connect,
419       base::Unretained(backend_.get()),
420       base::Passed(external_policy_data_fetcher_backend_->CreateFrontend(
421           backend_task_runner_))));
422 }
423
424 void CloudExternalDataManagerBase::Disconnect() {
425   DCHECK(CalledOnValidThread());
426   io_task_runner_->DeleteSoon(FROM_HERE,
427                               external_policy_data_fetcher_backend_.release());
428   backend_task_runner_->PostTask(FROM_HERE, base::Bind(
429       &Backend::Disconnect, base::Unretained(backend_.get())));
430 }
431
432 void CloudExternalDataManagerBase::Fetch(
433     const std::string& policy,
434     const ExternalDataFetcher::FetchCallback& callback) {
435   DCHECK(CalledOnValidThread());
436   backend_task_runner_->PostTask(FROM_HERE, base::Bind(
437       &Backend::Fetch, base::Unretained(backend_.get()), policy, callback));
438 }
439
440 // static
441 void CloudExternalDataManagerBase::SetMaxExternalDataSizeForTesting(
442     int max_size) {
443   max_external_data_size_for_testing = max_size;
444 }
445
446 void CloudExternalDataManagerBase::FetchAll() {
447   DCHECK(CalledOnValidThread());
448   backend_task_runner_->PostTask(FROM_HERE, base::Bind(
449       &Backend::FetchAll, base::Unretained(backend_.get())));
450 }
451
452 }  // namespace policy