blob: 014f7b14fe4710f9869eb94f1067f6bf048d205f [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/policy/cloud_external_data_manager_base.h"
#include <map>
#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "chrome/browser/chromeos/policy/cloud_external_data_store.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
#include "components/policy/core/common/cloud/resource_cache.h"
#include "components/policy/core/common/external_data_fetcher.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_test_utils.h"
#include "components/policy/core/common/policy_types.h"
#include "crypto/sha2.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_request_status.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace policy {
namespace {
// A string policy.
const char kStringPolicy[] = "StringPolicy";
// A policy that may reference up to 10 bytes of external data.
const char k10BytePolicy[] = "10BytePolicy";
// A policy that may reference up to 20 bytes of external data.
const char k20BytePolicy[] = "20BytePolicy";
// A nonexistent policy.
const char kUnknownPolicy[] = "UnknownPolicy";
const char k10BytePolicyURL[] = "http://localhost/10_bytes";
const char k20BytePolicyURL[] = "http://localhost/20_bytes";
const char k10ByteData[] = "10 bytes..";
const char k20ByteData[] = "20 bytes............";
const PolicyDetails kPolicyDetails[] = {
// is_deprecated is_device_policy id max_external_data_size
{ false, false, 1, 0 },
{ false, false, 2, 10 },
{ false, false, 3, 20 },
};
const char kCacheKey[] = "data";
// A variant of net::FakeURLFetcherFactory that makes it an error to request a
// fetcher for an unknown URL.
class FakeURLFetcherFactory : public net::FakeURLFetcherFactory {
public:
FakeURLFetcherFactory();
~FakeURLFetcherFactory() override;
// net::FakeURLFetcherFactory:
std::unique_ptr<net::URLFetcher> CreateURLFetcher(
int id,
const GURL& url,
net::URLFetcher::RequestType request_type,
net::URLFetcherDelegate* delegate,
net::NetworkTrafficAnnotationTag traffic_annotation) override;
private:
DISALLOW_COPY_AND_ASSIGN(FakeURLFetcherFactory);
};
FakeURLFetcherFactory::FakeURLFetcherFactory()
: net::FakeURLFetcherFactory(NULL) {
}
FakeURLFetcherFactory::~FakeURLFetcherFactory() {
}
std::unique_ptr<net::URLFetcher> FakeURLFetcherFactory::CreateURLFetcher(
int id,
const GURL& url,
net::URLFetcher::RequestType request_type,
net::URLFetcherDelegate* delegate,
net::NetworkTrafficAnnotationTag traffic_annotation) {
std::unique_ptr<net::URLFetcher> fetcher =
net::FakeURLFetcherFactory::CreateURLFetcher(
id, url, request_type, delegate, traffic_annotation);
EXPECT_TRUE(fetcher);
return fetcher;
}
} // namespace
class CloudExternalDataManagerBaseTest : public testing::Test {
protected:
CloudExternalDataManagerBaseTest();
void SetUp() override;
void TearDown() override;
void SetUpExternalDataManager();
std::unique_ptr<base::DictionaryValue> ConstructMetadata(
const std::string& url,
const std::string& hash);
void SetExternalDataReference(
const std::string& policy,
std::unique_ptr<base::DictionaryValue> metadata);
ExternalDataFetcher::FetchCallback ConstructFetchCallback(int id);
void ResetCallbackData();
void OnFetchDone(int id, std::unique_ptr<std::string> data);
void FetchAll();
void SetFakeResponse(const std::string& url,
const std::string& repsonse_data,
net::HttpStatusCode response_code,
net::URLRequestStatus::Status status);
base::MessageLoop message_loop_;
base::ScopedTempDir temp_dir_;
std::unique_ptr<ResourceCache> resource_cache_;
MockCloudPolicyStore cloud_policy_store_;
scoped_refptr<net::TestURLRequestContextGetter> request_content_getter_;
FakeURLFetcherFactory fetcher_factory_;
std::unique_ptr<CloudExternalDataManagerBase> external_data_manager_;
std::map<int, std::unique_ptr<std::string>> callback_data_;
PolicyDetailsMap policy_details_;
private:
DISALLOW_COPY_AND_ASSIGN(CloudExternalDataManagerBaseTest);
};
CloudExternalDataManagerBaseTest::CloudExternalDataManagerBaseTest() {
}
void CloudExternalDataManagerBaseTest::SetUp() {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
resource_cache_.reset(
new ResourceCache(temp_dir_.GetPath(), message_loop_.task_runner()));
SetUpExternalDataManager();
// Set |kStringPolicy| to a string value.
cloud_policy_store_.policy_map_.Set(
kStringPolicy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD, base::MakeUnique<base::Value>(std::string()),
nullptr);
// Make |k10BytePolicy| reference 10 bytes of external data.
SetExternalDataReference(
k10BytePolicy,
ConstructMetadata(k10BytePolicyURL,
crypto::SHA256HashString(k10ByteData)));
// Make |k20BytePolicy| reference 20 bytes of external data.
SetExternalDataReference(
k20BytePolicy,
ConstructMetadata(k20BytePolicyURL,
crypto::SHA256HashString(k20ByteData)));
cloud_policy_store_.NotifyStoreLoaded();
request_content_getter_ =
new net::TestURLRequestContextGetter(base::ThreadTaskRunnerHandle::Get());
policy_details_.SetDetails(kStringPolicy, &kPolicyDetails[0]);
policy_details_.SetDetails(k10BytePolicy, &kPolicyDetails[1]);
policy_details_.SetDetails(k20BytePolicy, &kPolicyDetails[2]);
}
void CloudExternalDataManagerBaseTest::TearDown() {
external_data_manager_.reset();
base::RunLoop().RunUntilIdle();
ResetCallbackData();
}
void CloudExternalDataManagerBaseTest::SetUpExternalDataManager() {
external_data_manager_.reset(new CloudExternalDataManagerBase(
policy_details_.GetCallback(), message_loop_.task_runner(),
message_loop_.task_runner()));
external_data_manager_->SetExternalDataStore(
base::MakeUnique<CloudExternalDataStore>(
kCacheKey, message_loop_.task_runner(), resource_cache_.get()));
external_data_manager_->SetPolicyStore(&cloud_policy_store_);
}
std::unique_ptr<base::DictionaryValue>
CloudExternalDataManagerBaseTest::ConstructMetadata(const std::string& url,
const std::string& hash) {
std::unique_ptr<base::DictionaryValue> metadata(new base::DictionaryValue);
metadata->SetKey("url", base::Value(url));
metadata->SetKey("hash",
base::Value(base::HexEncode(hash.c_str(), hash.size())));
return metadata;
}
void CloudExternalDataManagerBaseTest::SetExternalDataReference(
const std::string& policy,
std::unique_ptr<base::DictionaryValue> metadata) {
cloud_policy_store_.policy_map_.Set(
policy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
std::move(metadata),
base::MakeUnique<ExternalDataFetcher>(
external_data_manager_->weak_factory_.GetWeakPtr(), policy));
}
ExternalDataFetcher::FetchCallback
CloudExternalDataManagerBaseTest::ConstructFetchCallback(int id) {
return base::Bind(&CloudExternalDataManagerBaseTest::OnFetchDone,
base::Unretained(this),
id);
}
void CloudExternalDataManagerBaseTest::ResetCallbackData() {
callback_data_.clear();
}
void CloudExternalDataManagerBaseTest::OnFetchDone(
int id,
std::unique_ptr<std::string> data) {
callback_data_[id] = std::move(data);
}
void CloudExternalDataManagerBaseTest::FetchAll() {
external_data_manager_->FetchAll();
}
void CloudExternalDataManagerBaseTest::SetFakeResponse(
const std::string& url,
const std::string& response_data,
net::HttpStatusCode response_code,
net::URLRequestStatus::Status status) {
fetcher_factory_.SetFakeResponse(
GURL(url), response_data, response_code, status);
}
// Verifies that when no valid external data reference has been set for a
// policy, the attempt to retrieve the external data fails immediately.
TEST_F(CloudExternalDataManagerBaseTest, FailToFetchInvalid) {
external_data_manager_->Connect(request_content_getter_);
// Attempt to retrieve external data for |kStringPolicy|, which is a string
// policy that does not reference any external data.
external_data_manager_->Fetch(kStringPolicy, ConstructFetchCallback(0));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, callback_data_.size());
EXPECT_TRUE(callback_data_.find(0) != callback_data_.end());
EXPECT_FALSE(callback_data_[0]);
ResetCallbackData();
// Attempt to retrieve external data for |kUnknownPolicy|, which is not a
// known policy.
external_data_manager_->Fetch(kUnknownPolicy, ConstructFetchCallback(1));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, callback_data_.size());
EXPECT_TRUE(callback_data_.find(1) != callback_data_.end());
EXPECT_FALSE(callback_data_[1]);
ResetCallbackData();
// Set an invalid external data reference for |k10BytePolicy|.
SetExternalDataReference(k10BytePolicy,
ConstructMetadata(std::string(), std::string()));
cloud_policy_store_.NotifyStoreLoaded();
// Attempt to retrieve external data for |k10BytePolicy|, which now has an
// invalid reference.
external_data_manager_->Fetch(k10BytePolicy, ConstructFetchCallback(2));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, callback_data_.size());
EXPECT_TRUE(callback_data_.find(2) != callback_data_.end());
EXPECT_FALSE(callback_data_[2]);
ResetCallbackData();
}
// Verifies that external data referenced by a policy is downloaded and cached
// when first requested. Subsequent requests are served from the cache without
// further download attempts.
TEST_F(CloudExternalDataManagerBaseTest, DownloadAndCache) {
// Serve valid external data for |k10BytePolicy|.
SetFakeResponse(k10BytePolicyURL, k10ByteData, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
external_data_manager_->Connect(request_content_getter_);
// Retrieve external data for |k10BytePolicy|. Verify that a download happens
// and the callback is invoked with the downloaded data.
external_data_manager_->Fetch(k10BytePolicy, ConstructFetchCallback(0));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, callback_data_.size());
ASSERT_TRUE(callback_data_[0]);
EXPECT_EQ(k10ByteData, *callback_data_[0]);
ResetCallbackData();
// Stop serving external data for |k10BytePolicy|.
fetcher_factory_.ClearFakeResponses();
// Retrieve external data for |k10BytePolicy| again. Verify that no download
// is attempted but the callback is still invoked with the expected data,
// served from the cache.
external_data_manager_->Fetch(k10BytePolicy, ConstructFetchCallback(1));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, callback_data_.size());
ASSERT_TRUE(callback_data_[1]);
EXPECT_EQ(k10ByteData, *callback_data_[1]);
ResetCallbackData();
// Explicitly tell the external_data_manager_ to not make any download
// attempts.
external_data_manager_->Disconnect();
// Retrieve external data for |k10BytePolicy| again. Verify that even though
// downloads are not allowed, the callback is still invoked with the expected
// data, served from the cache.
external_data_manager_->Fetch(k10BytePolicy, ConstructFetchCallback(2));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, callback_data_.size());
ASSERT_TRUE(callback_data_[2]);
EXPECT_EQ(k10ByteData, *callback_data_[2]);
ResetCallbackData();
// Verify that the downloaded data is present in the cache.
external_data_manager_.reset();
base::RunLoop().RunUntilIdle();
std::string data;
EXPECT_TRUE(CloudExternalDataStore(kCacheKey, message_loop_.task_runner(),
resource_cache_.get())
.Load(k10BytePolicy, crypto::SHA256HashString(k10ByteData),
10, &data));
EXPECT_EQ(k10ByteData, data);
}
// Verifies that a request to download and cache all external data referenced by
// policies is carried out correctly. Subsequent requests for the data are
// served from the cache without further download attempts.
TEST_F(CloudExternalDataManagerBaseTest, DownloadAndCacheAll) {
// Serve valid external data for |k10BytePolicy| and |k20BytePolicy|.
SetFakeResponse(k10BytePolicyURL, k10ByteData, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
SetFakeResponse(k20BytePolicyURL, k20ByteData, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
external_data_manager_->Connect(request_content_getter_);
// Request that external data referenced by all policies be downloaded.
FetchAll();
base::RunLoop().RunUntilIdle();
// Stop serving external data for |k10BytePolicy| and |k20BytePolicy|.
fetcher_factory_.ClearFakeResponses();
// Retrieve external data for |k10BytePolicy| and |k20BytePolicy|. Verify that
// no downloads are attempted but the callbacks are still invoked with the
// expected data, served from the cache.
external_data_manager_->Fetch(k10BytePolicy, ConstructFetchCallback(0));
external_data_manager_->Fetch(k20BytePolicy, ConstructFetchCallback(1));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2u, callback_data_.size());
ASSERT_TRUE(callback_data_[0]);
EXPECT_EQ(k10ByteData, *callback_data_[0]);
ASSERT_TRUE(callback_data_[1]);
EXPECT_EQ(k20ByteData, *callback_data_[1]);
ResetCallbackData();
// Explicitly tell the external_data_manager_ to not make any download
// attempts.
external_data_manager_->Disconnect();
// Retrieve external data for |k10BytePolicy| and |k20BytePolicy|. Verify that
// even though downloads are not allowed, the callbacks are still invoked with
// the expected data, served from the cache.
external_data_manager_->Fetch(k10BytePolicy, ConstructFetchCallback(2));
external_data_manager_->Fetch(k20BytePolicy, ConstructFetchCallback(3));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2u, callback_data_.size());
ASSERT_TRUE(callback_data_[2]);
EXPECT_EQ(k10ByteData, *callback_data_[2]);
ASSERT_TRUE(callback_data_[3]);
EXPECT_EQ(k20ByteData, *callback_data_[3]);
ResetCallbackData();
// Verify that the downloaded data is present in the cache.
external_data_manager_.reset();
base::RunLoop().RunUntilIdle();
CloudExternalDataStore cache(kCacheKey, message_loop_.task_runner(),
resource_cache_.get());
std::string data;
EXPECT_TRUE(cache.Load(k10BytePolicy,
crypto::SHA256HashString(k10ByteData),
10,
&data));
EXPECT_EQ(k10ByteData, data);
EXPECT_TRUE(cache.Load(k20BytePolicy,
crypto::SHA256HashString(k20ByteData),
20,
&data));
EXPECT_EQ(k20ByteData, data);
}
// Verifies that when the external data referenced by a policy is not present in
// the cache and downloads are not allowed, a request to retrieve the data is
// enqueued and carried out when downloads become possible.
TEST_F(CloudExternalDataManagerBaseTest, DownloadAfterConnect) {
// Attempt to retrieve external data for |k10BytePolicy|. Verify that the
// callback is not invoked as the request remains pending.
external_data_manager_->Fetch(k10BytePolicy, ConstructFetchCallback(0));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(callback_data_.empty());
ResetCallbackData();
// Serve valid external data for |k10BytePolicy| and allow the
// external_data_manager_ to perform downloads.
SetFakeResponse(k10BytePolicyURL, k10ByteData, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
external_data_manager_->Connect(request_content_getter_);
// Verify that a download happens and the callback is invoked with the
// downloaded data.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, callback_data_.size());
ASSERT_TRUE(callback_data_[0]);
EXPECT_EQ(k10ByteData, *callback_data_[0]);
ResetCallbackData();
}
// Verifies that when the external data referenced by a policy is not present in
// the cache and cannot be downloaded at this time, a request to retrieve the
// data is enqueued to be retried later.
TEST_F(CloudExternalDataManagerBaseTest, DownloadError) {
// Make attempts to download the external data for |k20BytePolicy| fail with
// an error.
SetFakeResponse(k20BytePolicyURL, std::string(),
net::HTTP_INTERNAL_SERVER_ERROR,
net::URLRequestStatus::FAILED);
external_data_manager_->Connect(request_content_getter_);
// Attempt to retrieve external data for |k20BytePolicy|. Verify that the
// callback is not invoked as the download attempt fails and the request
// remains pending.
external_data_manager_->Fetch(k20BytePolicy, ConstructFetchCallback(0));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(callback_data_.empty());
ResetCallbackData();
// Modify the external data reference for |k20BytePolicy|, allowing the
// download to be retried immediately.
SetExternalDataReference(
k20BytePolicy,
ConstructMetadata(k20BytePolicyURL,
crypto::SHA256HashString(k10ByteData)));
cloud_policy_store_.NotifyStoreLoaded();
// Attempt to retrieve external data for |k20BytePolicy| again. Verify that
// no callback is invoked still as the download attempt fails again and the
// request remains pending.
external_data_manager_->Fetch(k20BytePolicy, ConstructFetchCallback(1));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(callback_data_.empty());
ResetCallbackData();
// Modify the external data reference for |k20BytePolicy|, allowing the
// download to be retried immediately.
SetExternalDataReference(
k20BytePolicy,
ConstructMetadata(k20BytePolicyURL,
crypto::SHA256HashString(k20ByteData)));
cloud_policy_store_.NotifyStoreLoaded();
// Serve external data for |k20BytePolicy| that does not match the hash
// specified in its current external data reference.
SetFakeResponse(k20BytePolicyURL, k10ByteData, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
// Attempt to retrieve external data for |k20BytePolicy| again. Verify that
// no callback is invoked still as the downloaded succeeds but returns data
// that does not match the external data reference.
external_data_manager_->Fetch(k20BytePolicy, ConstructFetchCallback(2));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(callback_data_.empty());
ResetCallbackData();
// Modify the external data reference for |k20BytePolicy|, allowing the
// download to be retried immediately. The external data reference now matches
// the data being served.
SetExternalDataReference(
k20BytePolicy,
ConstructMetadata(k20BytePolicyURL,
crypto::SHA256HashString(k10ByteData)));
cloud_policy_store_.NotifyStoreLoaded();
// Attempt to retrieve external data for |k20BytePolicy| again. Verify that
// the current callback and the three previously enqueued callbacks are
// invoked with the downloaded data now.
external_data_manager_->Fetch(k20BytePolicy, ConstructFetchCallback(3));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(4u, callback_data_.size());
ASSERT_TRUE(callback_data_[0]);
EXPECT_EQ(k10ByteData, *callback_data_[0]);
ASSERT_TRUE(callback_data_[1]);
EXPECT_EQ(k10ByteData, *callback_data_[1]);
ASSERT_TRUE(callback_data_[2]);
EXPECT_EQ(k10ByteData, *callback_data_[2]);
ASSERT_TRUE(callback_data_[3]);
EXPECT_EQ(k10ByteData, *callback_data_[3]);
ResetCallbackData();
}
// Verifies that when the external data referenced by a policy is present in the
// cache, a request to retrieve it is served from the cache without any download
// attempts.
TEST_F(CloudExternalDataManagerBaseTest, LoadFromCache) {
// Store valid external data for |k10BytePolicy| in the cache.
external_data_manager_.reset();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(CloudExternalDataStore(kCacheKey, message_loop_.task_runner(),
resource_cache_.get())
.Store(k10BytePolicy, crypto::SHA256HashString(k10ByteData),
k10ByteData));
// Instantiate an external_data_manager_ that uses the primed cache.
SetUpExternalDataManager();
external_data_manager_->Connect(request_content_getter_);
// Retrieve external data for |k10BytePolicy|. Verify that no download is
// attempted but the callback is still invoked with the expected data, served
// from the cache.
external_data_manager_->Fetch(k10BytePolicy, ConstructFetchCallback(0));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, callback_data_.size());
ASSERT_TRUE(callback_data_[0]);
EXPECT_EQ(k10ByteData, *callback_data_[0]);
ResetCallbackData();
}
// Verifies that cache entries which do not correspond to the external data
// referenced by any policy are pruned on startup.
TEST_F(CloudExternalDataManagerBaseTest, PruneCacheOnStartup) {
external_data_manager_.reset();
base::RunLoop().RunUntilIdle();
std::unique_ptr<CloudExternalDataStore> cache(new CloudExternalDataStore(
kCacheKey, message_loop_.task_runner(), resource_cache_.get()));
// Store valid external data for |k10BytePolicy| in the cache.
EXPECT_TRUE(cache->Store(k10BytePolicy,
crypto::SHA256HashString(k10ByteData),
k10ByteData));
// Store external data for |k20BytePolicy| that does not match the hash in its
// external data reference.
EXPECT_TRUE(cache->Store(k20BytePolicy,
crypto::SHA256HashString(k10ByteData),
k10ByteData));
// Store external data for |kUnknownPolicy|, which is not a known policy and
// therefore, cannot be referencing any external data.
EXPECT_TRUE(cache->Store(kUnknownPolicy,
crypto::SHA256HashString(k10ByteData),
k10ByteData));
cache.reset();
// Instantiate and destroy an ExternalDataManager that uses the primed cache.
SetUpExternalDataManager();
external_data_manager_.reset();
base::RunLoop().RunUntilIdle();
cache.reset(new CloudExternalDataStore(kCacheKey, message_loop_.task_runner(),
resource_cache_.get()));
std::string data;
// Verify that the valid external data for |k10BytePolicy| is still in the
// cache.
EXPECT_TRUE(cache->Load(k10BytePolicy,
crypto::SHA256HashString(k10ByteData),
10,
&data));
EXPECT_EQ(k10ByteData, data);
// Verify that the external data for |k20BytePolicy| and |kUnknownPolicy| has
// been pruned from the cache.
EXPECT_FALSE(cache->Load(k20BytePolicy,
crypto::SHA256HashString(k10ByteData),
20,
&data));
EXPECT_FALSE(cache->Load(kUnknownPolicy,
crypto::SHA256HashString(k10ByteData),
20,
&data));
}
// Verifies that when the external data referenced by a policy is present in the
// cache and the reference changes, the old data is pruned from the cache.
TEST_F(CloudExternalDataManagerBaseTest, PruneCacheOnChange) {
// Store valid external data for |k20BytePolicy| in the cache.
external_data_manager_.reset();
base::RunLoop().RunUntilIdle();
std::unique_ptr<CloudExternalDataStore> cache(new CloudExternalDataStore(
kCacheKey, message_loop_.task_runner(), resource_cache_.get()));
EXPECT_TRUE(cache->Store(k20BytePolicy,
crypto::SHA256HashString(k20ByteData),
k20ByteData));
cache.reset();
// Instantiate an ExternalDataManager that uses the primed cache.
SetUpExternalDataManager();
external_data_manager_->Connect(request_content_getter_);
// Modify the external data reference for |k20BytePolicy|.
SetExternalDataReference(
k20BytePolicy,
ConstructMetadata(k20BytePolicyURL,
crypto::SHA256HashString(k10ByteData)));
cloud_policy_store_.NotifyStoreLoaded();
// Verify that the old external data for |k20BytePolicy| has been pruned from
// the cache.
external_data_manager_.reset();
base::RunLoop().RunUntilIdle();
cache.reset(new CloudExternalDataStore(kCacheKey, message_loop_.task_runner(),
resource_cache_.get()));
std::string data;
EXPECT_FALSE(cache->Load(k20BytePolicy,
crypto::SHA256HashString(k20ByteData),
20,
&data));
}
// Verifies that corrupt cache entries are detected and deleted when accessed.
TEST_F(CloudExternalDataManagerBaseTest, CacheCorruption) {
external_data_manager_.reset();
base::RunLoop().RunUntilIdle();
std::unique_ptr<CloudExternalDataStore> cache(new CloudExternalDataStore(
kCacheKey, message_loop_.task_runner(), resource_cache_.get()));
// Store external data for |k10BytePolicy| that exceeds the maximal external
// data size allowed for that policy.
EXPECT_TRUE(cache->Store(k10BytePolicy,
crypto::SHA256HashString(k20ByteData),
k20ByteData));
// Store external data for |k20BytePolicy| that is corrupted and does not
// match the expected hash.
EXPECT_TRUE(cache->Store(k20BytePolicy,
crypto::SHA256HashString(k20ByteData),
k10ByteData));
cache.reset();
SetUpExternalDataManager();
// Serve external data for |k10BytePolicy| that exceeds the maximal external
// data size allowed for that policy.
SetFakeResponse(k10BytePolicyURL, k20ByteData, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
external_data_manager_->Connect(request_content_getter_);
// Modify the external data reference for |k10BytePolicy| to match the
// external data being served.
SetExternalDataReference(
k10BytePolicy,
ConstructMetadata(k10BytePolicyURL,
crypto::SHA256HashString(k20ByteData)));
cloud_policy_store_.NotifyStoreLoaded();
// Retrieve external data for |k10BytePolicy|. Verify that the callback is
// not invoked as the cached and downloaded external data exceed the maximal
// size allowed for this policy and the request remains pending.
external_data_manager_->Fetch(k10BytePolicy, ConstructFetchCallback(0));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(callback_data_.empty());
ResetCallbackData();
// Serve valid external data for |k20BytePolicy|.
SetFakeResponse(k20BytePolicyURL, k20ByteData, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
// Retrieve external data for |k20BytePolicy|. Verify that the callback is
// invoked with the valid downloaded data, not the invalid data in the cache.
external_data_manager_->Fetch(k20BytePolicy, ConstructFetchCallback(1));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, callback_data_.size());
ASSERT_TRUE(callback_data_[1]);
EXPECT_EQ(k20ByteData, *callback_data_[1]);
ResetCallbackData();
external_data_manager_.reset();
base::RunLoop().RunUntilIdle();
cache.reset(new CloudExternalDataStore(kCacheKey, message_loop_.task_runner(),
resource_cache_.get()));
std::string data;
// Verify that the invalid external data for |k10BytePolicy| has been pruned
// from the cache. Load() will return |false| in two cases:
// 1) The cache entry for |k10BytePolicy| has been pruned.
// 2) The cache entry for |k10BytePolicy| still exists but the cached data
// does not match the expected hash or exceeds the maximum size allowed.
// To test for the former, Load() is called with a maximum data size and hash
// that would allow the data originally written to the cache to be loaded.
// When this fails, it is certain that the original data is no longer present
// in the cache.
EXPECT_FALSE(cache->Load(k10BytePolicy,
crypto::SHA256HashString(k20ByteData),
20,
&data));
// Verify that the invalid external data for |k20BytePolicy| has been replaced
// with the downloaded valid data in the cache.
EXPECT_TRUE(cache->Load(k20BytePolicy,
crypto::SHA256HashString(k20ByteData),
20,
&data));
EXPECT_EQ(k20ByteData, data);
}
// Verifies that when the external data reference for a policy changes while a
// download of the external data for that policy is pending, the download is
// immediately retried using the new reference.
TEST_F(CloudExternalDataManagerBaseTest, PolicyChangeWhileDownloadPending) {
// Make attempts to download the external data for |k10BytePolicy| and
// |k20BytePolicy| fail with an error.
SetFakeResponse(k10BytePolicyURL, std::string(),
net::HTTP_INTERNAL_SERVER_ERROR,
net::URLRequestStatus::FAILED);
SetFakeResponse(k20BytePolicyURL, std::string(),
net::HTTP_INTERNAL_SERVER_ERROR,
net::URLRequestStatus::FAILED);
external_data_manager_->Connect(request_content_getter_);
// Attempt to retrieve external data for |k10BytePolicy| and |k20BytePolicy|.
// Verify that no callbacks are invoked as the download attempts fail and the
// requests remain pending.
external_data_manager_->Fetch(k10BytePolicy, ConstructFetchCallback(0));
external_data_manager_->Fetch(k20BytePolicy, ConstructFetchCallback(1));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(callback_data_.empty());
ResetCallbackData();
// Modify the external data reference for |k10BytePolicy| to be invalid.
// Verify that the callback is invoked as the policy no longer has a valid
// external data reference.
cloud_policy_store_.policy_map_.Erase(k10BytePolicy);
cloud_policy_store_.NotifyStoreLoaded();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, callback_data_.size());
EXPECT_TRUE(callback_data_.find(0) != callback_data_.end());
EXPECT_FALSE(callback_data_[0]);
ResetCallbackData();
// Serve valid external data for |k20BytePolicy|.
fetcher_factory_.ClearFakeResponses();
SetFakeResponse(k20BytePolicyURL, k10ByteData, net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
// Modify the external data reference for |k20BytePolicy| to match the
// external data now being served. Verify that the callback is invoked with
// the downloaded data.
SetExternalDataReference(
k20BytePolicy,
ConstructMetadata(k20BytePolicyURL,
crypto::SHA256HashString(k10ByteData)));
cloud_policy_store_.NotifyStoreLoaded();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, callback_data_.size());
ASSERT_TRUE(callback_data_[1]);
EXPECT_EQ(k10ByteData, *callback_data_[1]);
ResetCallbackData();
}
} // namespace policy