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/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"
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;
41 // Backend for the CloudExternalDataManagerBase that handles all data download,
42 // verification, caching and retrieval.
43 class CloudExternalDataManagerBase::Backend
{
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|.
61 scoped_ptr
<ExternalPolicyDataFetcher
> external_policy_data_fetcher
);
63 // Prevents further external data downloads and aborts any downloads currently
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_|.
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
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
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
129 FetchCallbackMap pending_downloads_
;
131 // Indicates that OnMetadataUpdated() has been called at least once and the
132 // contents of |metadata_| is initialized.
135 // Maps from policy names to the metadata specifying the external data that
136 // each of the policies references.
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
) {
168 updater_
.reset(new ExternalPolicyDataUpdater(
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() {
182 void CloudExternalDataManagerBase::Backend::OnMetadataUpdated(
183 scoped_ptr
<Metadata
> metadata
) {
184 metadata_set_
= true;
185 Metadata old_metadata
;
186 metadata_
.swap(old_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.
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
++);
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
);
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
);
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
247 RunCallback(callback
, scoped_ptr
<std::string
>());
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
255 pending_downloads_
[policy
].push_back(callback
);
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
),
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());
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();
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
),
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.
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
);
308 return details
->max_external_data_size
;
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());
326 const MetadataEntry
& metadata
= metadata_
[policy
];
327 updater_
->FetchExternalData(
329 ExternalPolicyDataUpdater::Request(metadata
.url
,
331 GetMaxExternalDataSize(policy
)),
332 base::Bind(&CloudExternalDataManagerBase::Backend::OnDownloadSuccess
,
333 base::Unretained(this),
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.
385 const base::DictionaryValue
* dict
= NULL
;
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_
,
414 backend_task_runner_
->PostTask(FROM_HERE
, base::Bind(
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
));
438 void CloudExternalDataManagerBase::SetMaxExternalDataSizeForTesting(
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