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"
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/sequenced_task_runner.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/thread_task_runner_handle.h"
20 #include "base/values.h"
21 #include "chrome/browser/chromeos/policy/cloud_external_data_store.h"
22 #include "components/policy/core/common/cloud/cloud_policy_store.h"
23 #include "components/policy/core/common/cloud/external_policy_data_fetcher.h"
24 #include "components/policy/core/common/cloud/external_policy_data_updater.h"
25 #include "components/policy/core/common/external_data_fetcher.h"
26 #include "components/policy/core/common/policy_map.h"
27 #include "net/url_request/url_request_context_getter.h"
33 // Fetch data for at most two external data references at the same time.
34 const int kMaxParallelFetches
= 2;
36 // Allows policies to reference |max_external_data_size_for_testing| bytes of
37 // external data even if no |max_size| was specified in policy_templates.json.
38 int max_external_data_size_for_testing
= 0;
42 // Backend for the CloudExternalDataManagerBase that handles all data download,
43 // verification, caching and retrieval.
44 class CloudExternalDataManagerBase::Backend
{
46 // |get_policy_details| is used to determine the maximum size that the
47 // data referenced by each policy can have. This class can be instantiated on
48 // any thread but from then on, may be accessed via the |task_runner_| only.
49 // All FetchCallbacks will be invoked via |callback_task_runner|.
50 Backend(const GetChromePolicyDetailsCallback
& get_policy_details
,
51 scoped_refptr
<base::SequencedTaskRunner
> task_runner
,
52 scoped_refptr
<base::SequencedTaskRunner
> callback_task_runner
);
54 // Allows downloaded external data to be cached in |external_data_store|.
55 // Ownership of the store is taken. The store can be destroyed by calling
56 // SetExternalDataStore(scoped_ptr<CloudExternalDataStore>()).
57 void SetExternalDataStore(
58 scoped_ptr
<CloudExternalDataStore
> external_data_store
);
60 // Allows downloading of external data via the |external_policy_data_fetcher|.
62 scoped_ptr
<ExternalPolicyDataFetcher
> external_policy_data_fetcher
);
64 // Prevents further external data downloads and aborts any downloads currently
68 // Called when the external data references that this backend is responsible
69 // for change. |metadata| maps from policy names to the metadata specifying
70 // the external data that each of the policies references.
71 void OnMetadataUpdated(scoped_ptr
<Metadata
> metadata
);
73 // Called by the |updater_| when the external |data| referenced by |policy|
74 // has been successfully downloaded and verified to match |hash|.
75 bool OnDownloadSuccess(const std::string
& policy
,
76 const std::string
& hash
,
77 const std::string
& data
);
79 // Retrieves the external data referenced by |policy| and invokes |callback|
80 // with the result. If |policy| does not reference any external data, the
81 // |callback| is invoked with a NULL pointer. Otherwise, the |callback| is
82 // invoked with the referenced data once it has been successfully retrieved.
83 // If retrieval is temporarily impossible (e.g. the data is not cached yet and
84 // there is no network connectivity), the |callback| will be invoked when the
85 // temporary hindrance is resolved. If retrieval is permanently impossible
86 // (e.g. |policy| references data that does not exist on the server), the
87 // |callback| will never be invoked.
88 // If the data for |policy| is not cached yet, only one download is started,
89 // even if this method is invoked multiple times. The |callback|s passed are
90 // enqueued and all invoked once the data has been successfully retrieved.
91 void Fetch(const std::string
& policy
,
92 const ExternalDataFetcher::FetchCallback
& callback
);
94 // Try to download and cache all external data referenced by |metadata_|.
98 // List of callbacks to invoke when the attempt to retrieve external data
99 // referenced by a policy completes successfully or fails permanently.
100 typedef std::vector
<ExternalDataFetcher::FetchCallback
> FetchCallbackList
;
102 // Map from policy names to the lists of callbacks defined above.
103 typedef std::map
<std::string
, FetchCallbackList
> FetchCallbackMap
;
105 // Looks up the maximum size that the data referenced by |policy| can have.
106 size_t GetMaxExternalDataSize(const std::string
& policy
) const;
108 // Invokes |callback| via the |callback_task_runner_|, passing |data| as a
110 void RunCallback(const ExternalDataFetcher::FetchCallback
& callback
,
111 scoped_ptr
<std::string
> data
) const;
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
);
117 // Used to determine the maximum size that the data referenced by each policy
119 GetChromePolicyDetailsCallback get_policy_details_
;
121 scoped_refptr
<base::SequencedTaskRunner
> task_runner_
;
122 scoped_refptr
<base::SequencedTaskRunner
> callback_task_runner_
;
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
130 FetchCallbackMap pending_downloads_
;
132 // Indicates that OnMetadataUpdated() has been called at least once and the
133 // contents of |metadata_| is initialized.
136 // Maps from policy names to the metadata specifying the external data that
137 // each of the policies references.
140 // Used to cache external data referenced by policies.
141 scoped_ptr
<CloudExternalDataStore
> external_data_store_
;
143 // Used to download external data referenced by policies.
144 scoped_ptr
<ExternalPolicyDataUpdater
> updater_
;
146 DISALLOW_COPY_AND_ASSIGN(Backend
);
149 CloudExternalDataManagerBase::Backend::Backend(
150 const GetChromePolicyDetailsCallback
& get_policy_details
,
151 scoped_refptr
<base::SequencedTaskRunner
> task_runner
,
152 scoped_refptr
<base::SequencedTaskRunner
> callback_task_runner
)
153 : get_policy_details_(get_policy_details
),
154 task_runner_(task_runner
),
155 callback_task_runner_(callback_task_runner
),
156 metadata_set_(false) {
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_
);
166 void CloudExternalDataManagerBase::Backend::Connect(
167 scoped_ptr
<ExternalPolicyDataFetcher
> external_policy_data_fetcher
) {
169 updater_
.reset(new ExternalPolicyDataUpdater(
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
);
179 void CloudExternalDataManagerBase::Backend::Disconnect() {
183 void CloudExternalDataManagerBase::Backend::OnMetadataUpdated(
184 scoped_ptr
<Metadata
> metadata
) {
185 metadata_set_
= true;
186 Metadata old_metadata
;
187 metadata_
.swap(old_metadata
);
189 metadata_
.swap(*metadata
);
191 if (external_data_store_
)
192 external_data_store_
->Prune(metadata_
);
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.
201 // Cancel the external data download.
202 updater_
->CancelExternalDataFetch(policy
);
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
>());
209 pending_downloads_
.erase(it
++);
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
);
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
);
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
)));
237 pending_downloads_
.erase(policy
);
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
248 RunCallback(callback
, scoped_ptr
<std::string
>());
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
256 pending_downloads_
[policy
].push_back(callback
);
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
),
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());
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
);
276 void CloudExternalDataManagerBase::Backend::FetchAll() {
277 // Loop through all external data references.
278 for (Metadata::const_iterator it
= metadata_
.begin(); it
!= metadata_
.end();
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
),
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.
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
);
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
;
303 // Look up the maximum size that the data referenced by |policy| can have in
304 // get_policy_details, 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 const PolicyDetails
* details
= get_policy_details_
.Run(policy
);
309 return details
->max_external_data_size
;
314 void CloudExternalDataManagerBase::Backend::RunCallback(
315 const ExternalDataFetcher::FetchCallback
& callback
,
316 scoped_ptr
<std::string
> data
) const {
317 callback_task_runner_
->PostTask(FROM_HERE
,
318 base::Bind(callback
, base::Passed(&data
)));
321 void CloudExternalDataManagerBase::Backend::StartDownload(
322 const std::string
& policy
) {
323 DCHECK(pending_downloads_
.find(policy
) != pending_downloads_
.end());
327 const MetadataEntry
& metadata
= metadata_
[policy
];
328 updater_
->FetchExternalData(
330 ExternalPolicyDataUpdater::Request(metadata
.url
,
332 GetMaxExternalDataSize(policy
)),
333 base::Bind(&CloudExternalDataManagerBase::Backend::OnDownloadSuccess
,
334 base::Unretained(this),
339 CloudExternalDataManagerBase::CloudExternalDataManagerBase(
340 const GetChromePolicyDetailsCallback
& get_policy_details
,
341 scoped_refptr
<base::SequencedTaskRunner
> backend_task_runner
,
342 scoped_refptr
<base::SequencedTaskRunner
> io_task_runner
)
343 : backend_task_runner_(backend_task_runner
),
344 io_task_runner_(io_task_runner
),
345 backend_(new Backend(get_policy_details
,
346 backend_task_runner_
,
347 base::ThreadTaskRunnerHandle::Get())) {
350 CloudExternalDataManagerBase::~CloudExternalDataManagerBase() {
351 DCHECK(CalledOnValidThread());
352 io_task_runner_
->DeleteSoon(FROM_HERE
,
353 external_policy_data_fetcher_backend_
.release());
354 backend_task_runner_
->DeleteSoon(FROM_HERE
, backend_
.release());
357 void CloudExternalDataManagerBase::SetExternalDataStore(
358 scoped_ptr
<CloudExternalDataStore
> external_data_store
) {
359 DCHECK(CalledOnValidThread());
360 backend_task_runner_
->PostTask(FROM_HERE
, base::Bind(
361 &Backend::SetExternalDataStore
,
362 base::Unretained(backend_
.get()),
363 base::Passed(&external_data_store
)));
366 void CloudExternalDataManagerBase::SetPolicyStore(
367 CloudPolicyStore
* policy_store
) {
368 DCHECK(CalledOnValidThread());
369 CloudExternalDataManager::SetPolicyStore(policy_store
);
370 if (policy_store_
&& policy_store_
->is_initialized())
371 OnPolicyStoreLoaded();
374 void CloudExternalDataManagerBase::OnPolicyStoreLoaded() {
375 // Collect all external data references made by policies in |policy_store_|
376 // and pass them to the |backend_|.
377 DCHECK(CalledOnValidThread());
378 scoped_ptr
<Metadata
> metadata(new Metadata
);
379 const PolicyMap
& policy_map
= policy_store_
->policy_map();
380 for (PolicyMap::const_iterator it
= policy_map
.begin();
381 it
!= policy_map
.end(); ++it
) {
382 if (!it
->second
.external_data_fetcher
) {
383 // Skip policies that do not reference external data.
386 const base::DictionaryValue
* dict
= NULL
;
388 std::string hex_hash
;
389 std::vector
<uint8
> hash
;
390 if (it
->second
.value
&& it
->second
.value
->GetAsDictionary(&dict
) &&
391 dict
->GetStringWithoutPathExpansion("url", &url
) &&
392 dict
->GetStringWithoutPathExpansion("hash", &hex_hash
) &&
393 !url
.empty() && !hex_hash
.empty() &&
394 base::HexStringToBytes(hex_hash
, &hash
)) {
395 // Add the external data reference to |metadata| if it is valid (URL and
396 // hash are not empty, hash can be decoded as a hex string).
397 (*metadata
)[it
->first
] =
398 MetadataEntry(url
, std::string(hash
.begin(), hash
.end()));
402 backend_task_runner_
->PostTask(FROM_HERE
, base::Bind(
403 &Backend::OnMetadataUpdated
,
404 base::Unretained(backend_
.get()),
405 base::Passed(&metadata
)));
408 void CloudExternalDataManagerBase::Connect(
409 scoped_refptr
<net::URLRequestContextGetter
> request_context
) {
410 DCHECK(CalledOnValidThread());
411 DCHECK(!external_policy_data_fetcher_backend_
);
412 external_policy_data_fetcher_backend_
.reset(
413 new ExternalPolicyDataFetcherBackend(io_task_runner_
,
415 backend_task_runner_
->PostTask(FROM_HERE
, base::Bind(
417 base::Unretained(backend_
.get()),
418 base::Passed(external_policy_data_fetcher_backend_
->CreateFrontend(
419 backend_task_runner_
))));
422 void CloudExternalDataManagerBase::Disconnect() {
423 DCHECK(CalledOnValidThread());
424 io_task_runner_
->DeleteSoon(FROM_HERE
,
425 external_policy_data_fetcher_backend_
.release());
426 backend_task_runner_
->PostTask(FROM_HERE
, base::Bind(
427 &Backend::Disconnect
, base::Unretained(backend_
.get())));
430 void CloudExternalDataManagerBase::Fetch(
431 const std::string
& policy
,
432 const ExternalDataFetcher::FetchCallback
& callback
) {
433 DCHECK(CalledOnValidThread());
434 backend_task_runner_
->PostTask(FROM_HERE
, base::Bind(
435 &Backend::Fetch
, base::Unretained(backend_
.get()), policy
, callback
));
439 void CloudExternalDataManagerBase::SetMaxExternalDataSizeForTesting(
441 max_external_data_size_for_testing
= max_size
;
444 void CloudExternalDataManagerBase::FetchAll() {
445 DCHECK(CalledOnValidThread());
446 backend_task_runner_
->PostTask(FROM_HERE
, base::Bind(
447 &Backend::FetchAll
, base::Unretained(backend_
.get())));
450 } // namespace policy