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.
6 #include "base/callback.h"
7 #include "base/files/file_path.h"
8 #include "base/files/file_util.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/json/json_file_value_serializer.h"
11 #include "base/json/json_writer.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/path_service.h"
14 #include "base/prefs/testing_pref_service.h"
15 #include "base/run_loop.h"
16 #include "base/sequenced_task_runner.h"
17 #include "base/strings/string_util.h"
18 #include "base/test/scoped_path_override.h"
19 #include "base/values.h"
20 #include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
21 #include "chrome/common/pref_names.h"
22 #include "components/component_updater/component_updater_paths.h"
23 #include "components/component_updater/component_updater_service.h"
24 #include "components/crx_file/id_util.h"
25 #include "components/update_client/crx_update_item.h"
26 #include "components/update_client/update_client.h"
27 #include "components/update_client/utils.h"
28 #include "testing/gmock/include/gmock/gmock.h"
29 #include "testing/gtest/include/gtest/gtest.h"
31 using update_client::CrxComponent
;
32 using update_client::CrxUpdateItem
;
34 namespace component_updater
{
38 const char kClientId
[] = "client-id";
39 const char kCrxId
[] = "abcdefghijklmnopponmlkjihgfedcba";
40 const char kName
[] = "Some Whitelist";
41 const char kOtherClientId
[] = "other-client-id";
42 const char kVersion
[] = "1.2.3.4";
43 const char kWhitelistContents
[] = "{\"foo\": \"bar\"}";
44 const char kWhitelistFile
[] = "whitelist.json";
46 std::string
CrxIdToHashToCrxId(const std::string
& kCrxId
) {
47 CrxComponent component
;
49 SupervisedUserWhitelistInstaller::GetHashFromCrxId(kCrxId
);
50 EXPECT_EQ(16u, component
.pk_hash
.size());
51 return GetCrxComponentID(component
);
54 std::string
JsonToString(const base::DictionaryValue
* dict
) {
56 base::JSONWriter::Write(dict
, &json
);
60 class MockComponentUpdateService
: public ComponentUpdateService
,
61 public OnDemandUpdater
{
63 MockComponentUpdateService(
64 const scoped_refptr
<base::SequencedTaskRunner
>& task_runner
)
65 : task_runner_(task_runner
), on_demand_update_called_(false) {}
67 ~MockComponentUpdateService() override
{}
69 bool on_demand_update_called() const { return on_demand_update_called_
; }
71 const CrxComponent
* registered_component() { return component_
.get(); }
73 void set_registration_callback(const base::Closure
& registration_callback
) {
74 registration_callback_
= registration_callback
;
77 // ComponentUpdateService implementation:
78 void AddObserver(Observer
* observer
) override
{ ADD_FAILURE(); }
79 void RemoveObserver(Observer
* observer
) override
{ ADD_FAILURE(); }
81 Status
Start() override
{
83 return Status::kError
;
86 Status
Stop() override
{
88 return Status::kError
;
91 std::vector
<std::string
> GetComponentIDs() const override
{
93 return std::vector
<std::string
>();
96 Status
RegisterComponent(const CrxComponent
& component
) override
{
97 EXPECT_EQ(nullptr, component_
.get());
98 component_
.reset(new CrxComponent(component
));
99 if (!registration_callback_
.is_null())
100 registration_callback_
.Run();
105 Status
UnregisterComponent(const std::string
& crx_id
) override
{
108 return Status::kError
;
111 EXPECT_EQ(GetCrxComponentID(*component_
), crx_id
);
112 if (!component_
->installer
->Uninstall()) {
114 return Status::kError
;
121 OnDemandUpdater
& GetOnDemandUpdater() override
{ return *this; }
123 void MaybeThrottle(const std::string
& kCrxId
,
124 const base::Closure
& callback
) override
{
128 scoped_refptr
<base::SequencedTaskRunner
> GetSequencedTaskRunner() override
{
132 bool GetComponentDetails(const std::string
& component_id
,
133 CrxUpdateItem
* item
) const override
{
138 // OnDemandUpdater implementation:
139 Status
OnDemandUpdate(const std::string
& crx_id
) override
{
140 on_demand_update_called_
= true;
143 ADD_FAILURE() << "Trying to update unregistered component " << crx_id
;
144 return Status::kError
;
147 EXPECT_EQ(GetCrxComponentID(*component_
), crx_id
);
152 scoped_refptr
<base::SequencedTaskRunner
> task_runner_
;
153 scoped_ptr
<CrxComponent
> component_
;
154 base::Closure registration_callback_
;
155 bool on_demand_update_called_
;
158 class WhitelistLoadObserver
{
160 explicit WhitelistLoadObserver(SupervisedUserWhitelistInstaller
* installer
)
161 : weak_ptr_factory_(this) {
162 installer
->Subscribe(base::Bind(&WhitelistLoadObserver::OnWhitelistReady
,
163 weak_ptr_factory_
.GetWeakPtr()));
166 void Wait() { run_loop_
.Run(); }
168 const base::FilePath
& whitelist_path() { return whitelist_path_
; }
171 void OnWhitelistReady(const std::string
& crx_id
,
172 const base::FilePath
& whitelist_path
) {
173 EXPECT_EQ(base::FilePath::StringType(), whitelist_path_
.value());
174 whitelist_path_
= whitelist_path
;
178 base::FilePath whitelist_path_
;
180 base::RunLoop run_loop_
;
181 base::WeakPtrFactory
<WhitelistLoadObserver
> weak_ptr_factory_
;
186 class SupervisedUserWhitelistInstallerTest
: public testing::Test
{
188 SupervisedUserWhitelistInstallerTest()
189 : path_override_(DIR_SUPERVISED_USER_WHITELISTS
),
190 component_update_service_(message_loop_
.task_runner()),
192 SupervisedUserWhitelistInstaller::Create(&component_update_service_
,
196 ~SupervisedUserWhitelistInstallerTest() override
{}
198 void SetUp() override
{
199 SupervisedUserWhitelistInstaller::RegisterPrefs(local_state_
.registry());
201 ASSERT_TRUE(PathService::Get(DIR_SUPERVISED_USER_WHITELISTS
,
202 &whitelist_base_directory_
));
203 whitelist_directory_
= whitelist_base_directory_
.AppendASCII(kCrxId
);
204 whitelist_version_directory_
= whitelist_directory_
.AppendASCII(kVersion
);
205 whitelist_path_
= whitelist_version_directory_
.AppendASCII(kWhitelistFile
);
207 scoped_ptr
<base::DictionaryValue
> contentPackDict(
208 new base::DictionaryValue
);
209 contentPackDict
->SetString("sites", kWhitelistFile
);
210 manifest_
.Set("content_pack", contentPackDict
.release());
211 manifest_
.SetString("version", kVersion
);
213 scoped_ptr
<base::DictionaryValue
> whitelist_dict(new base::DictionaryValue
);
214 whitelist_dict
->SetString("name", kName
);
215 scoped_ptr
<base::ListValue
> clients(new base::ListValue
);
216 clients
->AppendString(kClientId
);
217 clients
->AppendString(kOtherClientId
);
218 whitelist_dict
->Set("clients", clients
.release());
219 pref_
.Set(kCrxId
, whitelist_dict
.release());
223 void PrepareWhitelistDirectory(const base::FilePath
& whitelist_directory
) {
224 base::FilePath whitelist_path
=
225 whitelist_directory
.AppendASCII(kWhitelistFile
);
226 size_t whitelist_contents_length
= sizeof(kWhitelistContents
) - 1;
227 ASSERT_EQ(static_cast<int>(whitelist_contents_length
),
228 base::WriteFile(whitelist_path
, kWhitelistContents
,
229 whitelist_contents_length
));
230 base::FilePath manifest_file
=
231 whitelist_directory
.AppendASCII("manifest.json");
232 ASSERT_TRUE(JSONFileValueSerializer(manifest_file
).Serialize(manifest_
));
235 void RegisterExistingComponents() {
236 local_state_
.Set(prefs::kRegisteredSupervisedUserWhitelists
, pref_
);
237 base::RunLoop run_loop
;
238 installer_
->RegisterComponents();
239 run_loop
.RunUntilIdle();
242 void CheckRegisteredComponent(const char* version
) {
243 const CrxComponent
* component
=
244 component_update_service_
.registered_component();
245 ASSERT_TRUE(component
);
246 EXPECT_EQ(kName
, component
->name
);
247 EXPECT_EQ(kCrxId
, GetCrxComponentID(*component
));
248 EXPECT_EQ(version
, component
->version
.GetString());
251 base::MessageLoop message_loop_
;
252 base::ScopedPathOverride path_override_
;
253 MockComponentUpdateService component_update_service_
;
254 TestingPrefServiceSimple local_state_
;
255 scoped_ptr
<SupervisedUserWhitelistInstaller
> installer_
;
256 base::FilePath whitelist_base_directory_
;
257 base::FilePath whitelist_directory_
;
258 base::FilePath whitelist_version_directory_
;
259 base::FilePath whitelist_path_
;
260 base::DictionaryValue manifest_
;
261 base::DictionaryValue pref_
;
264 TEST_F(SupervisedUserWhitelistInstallerTest
, GetHashFromCrxId
) {
266 std::string extension_id
= "abcdefghijklmnopponmlkjihgfedcba";
267 ASSERT_EQ(extension_id
, CrxIdToHashToCrxId(extension_id
));
271 std::string extension_id
= "aBcDeFgHiJkLmNoPpOnMlKjIhGfEdCbA";
272 ASSERT_EQ(base::StringToLowerASCII(extension_id
),
273 CrxIdToHashToCrxId(extension_id
));
277 std::string extension_id
= crx_file::id_util::GenerateId("Moose");
278 ASSERT_EQ(extension_id
, CrxIdToHashToCrxId(extension_id
));
282 TEST_F(SupervisedUserWhitelistInstallerTest
, InstallNewWhitelist
) {
283 base::RunLoop registration_run_loop
;
284 component_update_service_
.set_registration_callback(
285 registration_run_loop
.QuitClosure());
287 WhitelistLoadObserver
observer(installer_
.get());
288 installer_
->RegisterWhitelist(kClientId
, kCrxId
, kName
);
289 registration_run_loop
.Run();
291 ASSERT_NO_FATAL_FAILURE(CheckRegisteredComponent("0.0.0.0"));
292 EXPECT_TRUE(component_update_service_
.on_demand_update_called());
294 // Registering the same whitelist for another client should not do anything.
295 installer_
->RegisterWhitelist(kOtherClientId
, kCrxId
, kName
);
297 base::ScopedTempDir temp_dir
;
298 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
299 base::FilePath unpacked_path
= temp_dir
.path();
300 ASSERT_NO_FATAL_FAILURE(PrepareWhitelistDirectory(unpacked_path
));
302 const CrxComponent
* component
=
303 component_update_service_
.registered_component();
304 ASSERT_TRUE(component
);
305 ASSERT_TRUE(component
->installer
->Install(manifest_
, unpacked_path
));
307 EXPECT_EQ(whitelist_path_
.value(), observer
.whitelist_path().value());
309 std::string whitelist_contents
;
310 ASSERT_TRUE(base::ReadFileToString(whitelist_path_
, &whitelist_contents
));
311 EXPECT_EQ(kWhitelistContents
, whitelist_contents
);
313 EXPECT_EQ(JsonToString(&pref_
),
314 JsonToString(local_state_
.GetDictionary(
315 prefs::kRegisteredSupervisedUserWhitelists
)));
318 TEST_F(SupervisedUserWhitelistInstallerTest
,
319 RegisterAndUninstallExistingWhitelist
) {
320 ASSERT_TRUE(base::CreateDirectory(whitelist_version_directory_
));
321 ASSERT_NO_FATAL_FAILURE(
322 PrepareWhitelistDirectory(whitelist_version_directory_
));
324 // Create another whitelist directory, with an ID that is not registered.
325 base::FilePath other_directory
=
326 whitelist_base_directory_
.AppendASCII("paobncmdlekfjgihhigjfkeldmcnboap");
327 ASSERT_TRUE(base::CreateDirectory(other_directory
));
328 ASSERT_NO_FATAL_FAILURE(PrepareWhitelistDirectory(other_directory
));
330 // Create a directory that is not a valid whitelist directory.
331 base::FilePath non_whitelist_directory
=
332 whitelist_base_directory_
.AppendASCII("Not a whitelist");
333 ASSERT_TRUE(base::CreateDirectory(non_whitelist_directory
));
335 RegisterExistingComponents();
337 ASSERT_NO_FATAL_FAILURE(CheckRegisteredComponent(kVersion
));
338 EXPECT_FALSE(component_update_service_
.on_demand_update_called());
340 // Check that unregistered whitelists have been removed:
341 // The registered whitelist directory should still exist.
342 EXPECT_TRUE(base::DirectoryExists(whitelist_directory_
));
344 // The other directory should be gone.
345 EXPECT_FALSE(base::DirectoryExists(other_directory
));
347 // The non-whitelist directory should still exist as well.
348 EXPECT_TRUE(base::DirectoryExists(non_whitelist_directory
));
350 // Unregistering for the first client should do nothing.
352 base::RunLoop run_loop
;
353 installer_
->UnregisterWhitelist(kClientId
, kCrxId
);
354 run_loop
.RunUntilIdle();
356 EXPECT_TRUE(component_update_service_
.registered_component());
357 EXPECT_TRUE(base::DirectoryExists(whitelist_version_directory_
));
359 // Unregistering for the second client should uninstall the whitelist.
361 base::RunLoop run_loop
;
362 installer_
->UnregisterWhitelist(kOtherClientId
, kCrxId
);
363 run_loop
.RunUntilIdle();
365 EXPECT_FALSE(component_update_service_
.registered_component());
366 EXPECT_FALSE(base::DirectoryExists(whitelist_directory_
));
369 } // namespace component_updater