1 // Copyright 2014 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/supervised_user/supervised_user_allowlist_service.h"
6
7 #include <map>
8 #include <memory>
9 #include <set>
10 #include <string>
11 #include <utility>
12
13 #include "base/bind.h"
14 #include "base/callback.h"
15 #include "base/path_service.h"
16 #include "base/run_loop.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "build/build_config.h"
20 #include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
21 #include "chrome/browser/supervised_user/supervised_user_site_list.h"
22 #include "chrome/common/chrome_paths.h"
23 #include "chrome/common/pref_names.h"
24 #include "chrome/test/base/testing_profile.h"
25 #include "components/prefs/scoped_user_pref_update.h"
26 #include "components/sync/model/sync_change.h"
27 #include "components/sync/model/sync_change_processor.h"
28 #include "components/sync/model/sync_error_factory.h"
29 #include "components/sync/protocol/sync.pb.h"
30 #include "content/public/test/browser_task_environment.h"
31 #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33
34 namespace {
35
36 const char kClientId[] = "client-id";
37
38 class MockSupervisedUserWhitelistInstaller
39 : public component_updater::SupervisedUserWhitelistInstaller {
40 public:
MockSupervisedUserWhitelistInstaller()41 MockSupervisedUserWhitelistInstaller() {}
~MockSupervisedUserWhitelistInstaller()42 ~MockSupervisedUserWhitelistInstaller() override {}
43
registered_allowlists()44 const std::set<std::string>& registered_allowlists() {
45 return registered_allowlists_;
46 }
47
NotifyAllowlistReady(const std::string & crx_id,const base::string16 & title,const base::FilePath & large_icon_path,const base::FilePath & allowlist_path)48 void NotifyAllowlistReady(const std::string& crx_id,
49 const base::string16& title,
50 const base::FilePath& large_icon_path,
51 const base::FilePath& allowlist_path) {
52 for (const auto& callback : ready_callbacks_)
53 callback.Run(crx_id, title, large_icon_path, allowlist_path);
54 }
55
56 // SupervisedUserWhitelistInstaller implementation:
RegisterComponents()57 void RegisterComponents() override {}
58
Subscribe(WhitelistReadyCallback callback)59 void Subscribe(WhitelistReadyCallback callback) override {
60 ready_callbacks_.push_back(callback);
61 }
62
RegisterWhitelist(const std::string & client_id,const std::string & crx_id,const std::string & name)63 void RegisterWhitelist(const std::string& client_id,
64 const std::string& crx_id,
65 const std::string& name) override {
66 EXPECT_EQ(kClientId, client_id);
67 EXPECT_FALSE(AllowlistIsRegistered(crx_id)) << crx_id;
68 registered_allowlists_.insert(crx_id);
69 }
70
UnregisterWhitelist(const std::string & client_id,const std::string & crx_id)71 void UnregisterWhitelist(const std::string& client_id,
72 const std::string& crx_id) override {
73 EXPECT_EQ(kClientId, client_id);
74 EXPECT_TRUE(AllowlistIsRegistered(crx_id)) << crx_id;
75 registered_allowlists_.erase(crx_id);
76 }
77
78 private:
AllowlistIsRegistered(const std::string & crx_id)79 bool AllowlistIsRegistered(const std::string& crx_id) {
80 return registered_allowlists_.count(crx_id) > 0;
81 }
82
83 std::set<std::string> registered_allowlists_;
84 std::vector<WhitelistReadyCallback> ready_callbacks_;
85 };
86
87 } // namespace
88
89 class SupervisedUserAllowlistServiceTest : public testing::Test {
90 public:
SupervisedUserAllowlistServiceTest()91 SupervisedUserAllowlistServiceTest()
92 : installer_(new MockSupervisedUserWhitelistInstaller),
93 service_(new SupervisedUserAllowlistService(profile_.GetPrefs(),
94 installer_.get(),
95 kClientId)) {
96 service_->AddSiteListsChangedCallback(
97 base::Bind(&SupervisedUserAllowlistServiceTest::OnSiteListsChanged,
98 base::Unretained(this)));
99 }
100
101 protected:
PrepareInitialStateAndPreferences()102 void PrepareInitialStateAndPreferences() {
103 // Create two allowlists.
104 DictionaryPrefUpdate update(profile_.GetPrefs(),
105 prefs::kSupervisedUserAllowlists);
106 base::DictionaryValue* dict = update.Get();
107
108 std::unique_ptr<base::DictionaryValue> allowlist_dict(
109 new base::DictionaryValue);
110 allowlist_dict->SetString("name", "Allowlist A");
111 dict->Set("aaaa", std::move(allowlist_dict));
112
113 allowlist_dict.reset(new base::DictionaryValue);
114 allowlist_dict->SetString("name", "Allowlist B");
115 dict->Set("bbbb", std::move(allowlist_dict));
116
117 installer_->RegisterWhitelist(kClientId, "aaaa", "Allowlist A");
118 installer_->RegisterWhitelist(kClientId, "bbbb", "Allowlist B");
119 }
120
CheckFinalStateAndPreferences()121 void CheckFinalStateAndPreferences() {
122 EXPECT_EQ(2u, installer_->registered_allowlists().size());
123 EXPECT_EQ(1u, installer_->registered_allowlists().count("bbbb"));
124 EXPECT_EQ(1u, installer_->registered_allowlists().count("cccc"));
125
126 const base::DictionaryValue* dict =
127 profile_.GetPrefs()->GetDictionary(prefs::kSupervisedUserAllowlists);
128 EXPECT_EQ(2u, dict->size());
129 const base::DictionaryValue* allowlist_dict = nullptr;
130 ASSERT_TRUE(dict->GetDictionary("bbbb", &allowlist_dict));
131 std::string name;
132 ASSERT_TRUE(allowlist_dict->GetString("name", &name));
133 EXPECT_EQ("Allowlist B New", name);
134 ASSERT_TRUE(dict->GetDictionary("cccc", &allowlist_dict));
135 ASSERT_TRUE(allowlist_dict->GetString("name", &name));
136 EXPECT_EQ("Allowlist C", name);
137 }
138
FindAllowlist(const syncer::SyncDataList & data_list,const std::string & id)139 const sync_pb::ManagedUserWhitelistSpecifics* FindAllowlist(
140 const syncer::SyncDataList& data_list,
141 const std::string& id) {
142 for (const syncer::SyncData& data : data_list) {
143 const sync_pb::ManagedUserWhitelistSpecifics& allowlist =
144 data.GetSpecifics().managed_user_whitelist();
145 if (allowlist.id() == id)
146 return &allowlist;
147 }
148 return nullptr;
149 }
150
OnSiteListsChanged(const std::vector<scoped_refptr<SupervisedUserSiteList>> & site_lists)151 void OnSiteListsChanged(
152 const std::vector<scoped_refptr<SupervisedUserSiteList>>& site_lists) {
153 site_lists_ = site_lists;
154 if (!site_lists_changed_callback_.is_null())
155 site_lists_changed_callback_.Run();
156 }
157
158 content::BrowserTaskEnvironment task_environment_;
159 TestingProfile profile_;
160 data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
161
162 std::unique_ptr<MockSupervisedUserWhitelistInstaller> installer_;
163 std::unique_ptr<SupervisedUserAllowlistService> service_;
164
165 std::vector<scoped_refptr<SupervisedUserSiteList>> site_lists_;
166 base::Closure site_lists_changed_callback_;
167 };
168
TEST_F(SupervisedUserAllowlistServiceTest,MergeEmpty)169 TEST_F(SupervisedUserAllowlistServiceTest, MergeEmpty) {
170 service_->Init();
171
172 ASSERT_TRUE(
173 service_->GetAllSyncDataForTesting(syncer::SUPERVISED_USER_ALLOWLISTS)
174 .empty());
175 base::Optional<syncer::ModelError> error = service_->MergeDataAndStartSyncing(
176 syncer::SUPERVISED_USER_ALLOWLISTS, syncer::SyncDataList(),
177 std::unique_ptr<syncer::SyncChangeProcessor>(),
178 std::unique_ptr<syncer::SyncErrorFactory>());
179 EXPECT_TRUE(
180 service_->GetAllSyncDataForTesting(syncer::SUPERVISED_USER_ALLOWLISTS)
181 .empty());
182 EXPECT_FALSE(error.has_value());
183
184 EXPECT_EQ(0u, installer_->registered_allowlists().size());
185 }
186
TEST_F(SupervisedUserAllowlistServiceTest,MergeExisting)187 TEST_F(SupervisedUserAllowlistServiceTest, MergeExisting) {
188 PrepareInitialStateAndPreferences();
189
190 // Initialize the service. The allowlists should not be ready yet.
191 service_->Init();
192 EXPECT_EQ(0u, site_lists_.size());
193
194 // Notify that allowlist A is ready.
195 base::RunLoop run_loop;
196 site_lists_changed_callback_ = run_loop.QuitClosure();
197 base::FilePath test_data_dir;
198 ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
199 base::FilePath allowlist_path =
200 test_data_dir.AppendASCII("whitelists/content_pack/site_list.json");
201 installer_->NotifyAllowlistReady("aaaa", base::ASCIIToUTF16("Title"),
202 base::FilePath(), allowlist_path);
203 run_loop.Run();
204
205 ASSERT_EQ(1u, site_lists_.size());
206 EXPECT_EQ(base::ASCIIToUTF16("Title"), site_lists_[0]->title());
207 EXPECT_EQ(4u, site_lists_[0]->patterns().size());
208
209 // Do the initial merge. One item should be added (allowlist C), one should be
210 // modified (allowlist B), and one item should be removed (allowlist A).
211 syncer::SyncDataList initial_data;
212 initial_data.push_back(
213 SupervisedUserAllowlistService::CreateAllowlistSyncData(
214 "bbbb", "Allowlist B New"));
215 initial_data.push_back(
216 SupervisedUserAllowlistService::CreateAllowlistSyncData("cccc",
217 "Allowlist C"));
218 ASSERT_EQ(
219 2u, service_->GetAllSyncDataForTesting(syncer::SUPERVISED_USER_ALLOWLISTS)
220 .size());
221 base::Optional<syncer::ModelError> error = service_->MergeDataAndStartSyncing(
222 syncer::SUPERVISED_USER_ALLOWLISTS, initial_data,
223 std::unique_ptr<syncer::SyncChangeProcessor>(),
224 std::unique_ptr<syncer::SyncErrorFactory>());
225 EXPECT_EQ(
226 2u, service_->GetAllSyncDataForTesting(syncer::SUPERVISED_USER_ALLOWLISTS)
227 .size());
228 EXPECT_FALSE(error.has_value());
229
230 // Allowlist A (which was previously ready) should be removed now, and
231 // allowlist B was never ready.
232 EXPECT_EQ(0u, site_lists_.size());
233
234 CheckFinalStateAndPreferences();
235 }
236
TEST_F(SupervisedUserAllowlistServiceTest,ApplyChanges)237 TEST_F(SupervisedUserAllowlistServiceTest, ApplyChanges) {
238 PrepareInitialStateAndPreferences();
239
240 service_->Init();
241
242 // Process some changes.
243 syncer::SyncChangeList changes;
244 changes.push_back(syncer::SyncChange(
245 FROM_HERE, syncer::SyncChange::ACTION_ADD,
246 SupervisedUserAllowlistService::CreateAllowlistSyncData("cccc",
247 "Allowlist C")));
248 changes.push_back(syncer::SyncChange(
249 FROM_HERE, syncer::SyncChange::ACTION_UPDATE,
250 SupervisedUserAllowlistService::CreateAllowlistSyncData(
251 "bbbb", "Allowlist B New")));
252 changes.push_back(syncer::SyncChange(
253 FROM_HERE, syncer::SyncChange::ACTION_DELETE,
254 SupervisedUserAllowlistService::CreateAllowlistSyncData("aaaa",
255 "Ignored")));
256 base::Optional<syncer::ModelError> error =
257 service_->ProcessSyncChanges(FROM_HERE, changes);
258 EXPECT_FALSE(error.has_value());
259
260 EXPECT_EQ(0u, site_lists_.size());
261
262 // If allowlist A now becomes ready, it should be ignored.
263 installer_->NotifyAllowlistReady(
264 "aaaa", base::ASCIIToUTF16("Title"), base::FilePath(),
265 base::FilePath(FILE_PATH_LITERAL("/path/to/aaaa")));
266 EXPECT_EQ(0u, site_lists_.size());
267
268 CheckFinalStateAndPreferences();
269 }
270