blob: a1490b69dbc06583c4923c7fbb8be2c1f400dc73 [file] [log] [blame]
// Copyright 2014 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/supervised_user/supervised_user_whitelist_service.h"
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
#include "chrome/browser/supervised_user/supervised_user_site_list.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/sync/model/sync_change.h"
#include "components/sync/model/sync_change_processor.h"
#include "components/sync/model/sync_error_factory.h"
#include "components/sync/protocol/sync.pb.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
#if !defined(OS_ANDROID)
#include "services/data_decoder/public/cpp/testing_json_parser.h"
#endif
namespace {
const char kClientId[] = "client-id";
class MockSupervisedUserWhitelistInstaller
: public component_updater::SupervisedUserWhitelistInstaller {
public:
MockSupervisedUserWhitelistInstaller() {}
~MockSupervisedUserWhitelistInstaller() override {}
const std::set<std::string>& registered_whitelists() {
return registered_whitelists_;
}
void NotifyWhitelistReady(const std::string& crx_id,
const base::string16& title,
const base::FilePath& large_icon_path,
const base::FilePath& whitelist_path) {
for (const auto& callback : ready_callbacks_)
callback.Run(crx_id, title, large_icon_path, whitelist_path);
}
// SupervisedUserWhitelistInstaller implementation:
void RegisterComponents() override {}
void Subscribe(const WhitelistReadyCallback& callback) override {
ready_callbacks_.push_back(callback);
}
void RegisterWhitelist(const std::string& client_id,
const std::string& crx_id,
const std::string& name) override {
EXPECT_EQ(kClientId, client_id);
EXPECT_FALSE(WhitelistIsRegistered(crx_id)) << crx_id;
registered_whitelists_.insert(crx_id);
}
void UnregisterWhitelist(const std::string& client_id,
const std::string& crx_id) override {
EXPECT_EQ(kClientId, client_id);
EXPECT_TRUE(WhitelistIsRegistered(crx_id)) << crx_id;
registered_whitelists_.erase(crx_id);
}
private:
bool WhitelistIsRegistered(const std::string& crx_id) {
return registered_whitelists_.count(crx_id) > 0;
}
std::set<std::string> registered_whitelists_;
std::vector<WhitelistReadyCallback> ready_callbacks_;
};
} // namespace
class SupervisedUserWhitelistServiceTest : public testing::Test {
public:
SupervisedUserWhitelistServiceTest()
: installer_(new MockSupervisedUserWhitelistInstaller),
service_(new SupervisedUserWhitelistService(profile_.GetPrefs(),
installer_.get(),
kClientId)) {
service_->AddSiteListsChangedCallback(
base::Bind(&SupervisedUserWhitelistServiceTest::OnSiteListsChanged,
base::Unretained(this)));
}
protected:
void PrepareInitialStateAndPreferences() {
// Create two whitelists.
DictionaryPrefUpdate update(profile_.GetPrefs(),
prefs::kSupervisedUserWhitelists);
base::DictionaryValue* dict = update.Get();
std::unique_ptr<base::DictionaryValue> whitelist_dict(
new base::DictionaryValue);
whitelist_dict->SetString("name", "Whitelist A");
dict->Set("aaaa", std::move(whitelist_dict));
whitelist_dict.reset(new base::DictionaryValue);
whitelist_dict->SetString("name", "Whitelist B");
dict->Set("bbbb", std::move(whitelist_dict));
installer_->RegisterWhitelist(kClientId, "aaaa", "Whitelist A");
installer_->RegisterWhitelist(kClientId, "bbbb", "Whitelist B");
}
void CheckFinalStateAndPreferences() {
EXPECT_EQ(2u, installer_->registered_whitelists().size());
EXPECT_EQ(1u, installer_->registered_whitelists().count("bbbb"));
EXPECT_EQ(1u, installer_->registered_whitelists().count("cccc"));
const base::DictionaryValue* dict =
profile_.GetPrefs()->GetDictionary(prefs::kSupervisedUserWhitelists);
EXPECT_EQ(2u, dict->size());
const base::DictionaryValue* whitelist_dict = nullptr;
ASSERT_TRUE(dict->GetDictionary("bbbb", &whitelist_dict));
std::string name;
ASSERT_TRUE(whitelist_dict->GetString("name", &name));
EXPECT_EQ("Whitelist B New", name);
ASSERT_TRUE(dict->GetDictionary("cccc", &whitelist_dict));
ASSERT_TRUE(whitelist_dict->GetString("name", &name));
EXPECT_EQ("Whitelist C", name);
}
const sync_pb::ManagedUserWhitelistSpecifics* FindWhitelist(
const syncer::SyncDataList& data_list,
const std::string& id) {
for (const syncer::SyncData& data : data_list) {
const sync_pb::ManagedUserWhitelistSpecifics& whitelist =
data.GetSpecifics().managed_user_whitelist();
if (whitelist.id() == id)
return &whitelist;
}
return nullptr;
}
void OnSiteListsChanged(
const std::vector<scoped_refptr<SupervisedUserSiteList>>& site_lists) {
site_lists_ = site_lists;
if (!site_lists_changed_callback_.is_null())
site_lists_changed_callback_.Run();
}
content::TestBrowserThreadBundle thread_bundle_;
TestingProfile profile_;
#if !defined(OS_ANDROID)
data_decoder::TestingJsonParser::ScopedFactoryOverride factory_override_;
#endif
std::unique_ptr<MockSupervisedUserWhitelistInstaller> installer_;
std::unique_ptr<SupervisedUserWhitelistService> service_;
std::vector<scoped_refptr<SupervisedUserSiteList>> site_lists_;
base::Closure site_lists_changed_callback_;
};
TEST_F(SupervisedUserWhitelistServiceTest, MergeEmpty) {
service_->Init();
syncer::SyncMergeResult result = service_->MergeDataAndStartSyncing(
syncer::SUPERVISED_USER_WHITELISTS, syncer::SyncDataList(),
std::unique_ptr<syncer::SyncChangeProcessor>(),
std::unique_ptr<syncer::SyncErrorFactory>());
EXPECT_FALSE(result.error().IsSet());
EXPECT_EQ(0, result.num_items_added());
EXPECT_EQ(0, result.num_items_modified());
EXPECT_EQ(0, result.num_items_deleted());
EXPECT_EQ(0, result.num_items_before_association());
EXPECT_EQ(0, result.num_items_after_association());
EXPECT_EQ(0u, installer_->registered_whitelists().size());
}
TEST_F(SupervisedUserWhitelistServiceTest, MergeExisting) {
PrepareInitialStateAndPreferences();
// Initialize the service. The whitelists should not be ready yet.
service_->Init();
EXPECT_EQ(0u, site_lists_.size());
// Notify that whitelist A is ready.
base::RunLoop run_loop;
site_lists_changed_callback_ = run_loop.QuitClosure();
base::FilePath test_data_dir;
ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
base::FilePath whitelist_path =
test_data_dir.AppendASCII("whitelists/content_pack/site_list.json");
installer_->NotifyWhitelistReady("aaaa", base::ASCIIToUTF16("Title"),
base::FilePath(), whitelist_path);
run_loop.Run();
ASSERT_EQ(1u, site_lists_.size());
EXPECT_EQ(base::ASCIIToUTF16("Title"), site_lists_[0]->title());
EXPECT_EQ(4u, site_lists_[0]->patterns().size());
// Do the initial merge. One item should be added (whitelist C), one should be
// modified (whitelist B), and one item should be removed (whitelist A).
syncer::SyncDataList initial_data;
initial_data.push_back(
SupervisedUserWhitelistService::CreateWhitelistSyncData(
"bbbb", "Whitelist B New"));
initial_data.push_back(
SupervisedUserWhitelistService::CreateWhitelistSyncData(
"cccc", "Whitelist C"));
syncer::SyncMergeResult result = service_->MergeDataAndStartSyncing(
syncer::SUPERVISED_USER_WHITELISTS, initial_data,
std::unique_ptr<syncer::SyncChangeProcessor>(),
std::unique_ptr<syncer::SyncErrorFactory>());
EXPECT_FALSE(result.error().IsSet());
EXPECT_EQ(1, result.num_items_added());
EXPECT_EQ(1, result.num_items_modified());
EXPECT_EQ(1, result.num_items_deleted());
EXPECT_EQ(2, result.num_items_before_association());
EXPECT_EQ(2, result.num_items_after_association());
// Whitelist A (which was previously ready) should be removed now, and
// whitelist B was never ready.
EXPECT_EQ(0u, site_lists_.size());
CheckFinalStateAndPreferences();
}
TEST_F(SupervisedUserWhitelistServiceTest, ApplyChanges) {
PrepareInitialStateAndPreferences();
service_->Init();
// Process some changes.
syncer::SyncChangeList changes;
changes.push_back(syncer::SyncChange(
FROM_HERE, syncer::SyncChange::ACTION_ADD,
SupervisedUserWhitelistService::CreateWhitelistSyncData(
"cccc", "Whitelist C")));
changes.push_back(syncer::SyncChange(
FROM_HERE, syncer::SyncChange::ACTION_UPDATE,
SupervisedUserWhitelistService::CreateWhitelistSyncData(
"bbbb", "Whitelist B New")));
changes.push_back(syncer::SyncChange(
FROM_HERE, syncer::SyncChange::ACTION_DELETE,
SupervisedUserWhitelistService::CreateWhitelistSyncData(
"aaaa", "Ignored")));
syncer::SyncError error = service_->ProcessSyncChanges(FROM_HERE, changes);
EXPECT_FALSE(error.IsSet());
EXPECT_EQ(0u, site_lists_.size());
// If whitelist A now becomes ready, it should be ignored.
installer_->NotifyWhitelistReady(
"aaaa", base::ASCIIToUTF16("Title"), base::FilePath(),
base::FilePath(FILE_PATH_LITERAL("/path/to/aaaa")));
EXPECT_EQ(0u, site_lists_.size());
CheckFinalStateAndPreferences();
}
TEST_F(SupervisedUserWhitelistServiceTest, GetAllSyncData) {
PrepareInitialStateAndPreferences();
syncer::SyncDataList sync_data =
service_->GetAllSyncData(syncer::SUPERVISED_USER_WHITELISTS);
ASSERT_EQ(2u, sync_data.size());
const sync_pb::ManagedUserWhitelistSpecifics* whitelist =
FindWhitelist(sync_data, "aaaa");
ASSERT_TRUE(whitelist);
EXPECT_EQ("Whitelist A", whitelist->name());
whitelist = FindWhitelist(sync_data, "bbbb");
ASSERT_TRUE(whitelist);
EXPECT_EQ("Whitelist B", whitelist->name());
}