blob: 5c6b46e406c8f442ff58042c9870507ac808771b [file] [log] [blame]
[email protected]c333e792012-01-06 16:57:391// Copyright (c) 2012 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
dcheng1fc00f12015-12-26 22:18:035#include "chrome/browser/extensions/permissions_updater.h"
6
7#include <utility>
8
[email protected]57999812013-02-24 05:40:529#include "base/files/file_path.h"
[email protected]ffbec692012-02-26 20:26:4210#include "base/json/json_file_value_serializer.h"
[email protected]c333e792012-01-06 16:57:3911#include "base/memory/ref_counted.h"
[email protected]78089f02012-07-19 06:11:2812#include "base/run_loop.h"
[email protected]23a85362014-07-07 23:26:1913#include "base/strings/stringprintf.h"
[email protected]c333e792012-01-06 16:57:3914#include "base/values.h"
[email protected]49a01e642013-07-12 00:29:4515#include "chrome/browser/chrome_notification_types.h"
[email protected]c333e792012-01-06 16:57:3916#include "chrome/browser/extensions/extension_service.h"
[email protected]f484f8d52014-06-12 08:38:1817#include "chrome/browser/extensions/extension_service_test_base.h"
rdevlin.croninb8dffe52015-02-07 00:58:0118#include "chrome/browser/extensions/extension_util.h"
[email protected]c333e792012-01-06 16:57:3919#include "chrome/common/chrome_paths.h"
[email protected]04e4bbe2013-04-27 07:44:2420#include "chrome/common/extensions/extension_test_util.h"
[email protected]c333e792012-01-06 16:57:3921#include "chrome/test/base/testing_profile.h"
rdevlin.croninb8dffe52015-02-07 00:58:0122#include "components/crx_file/id_util.h"
[email protected]c333e792012-01-06 16:57:3923#include "content/public/browser/notification_observer.h"
24#include "content/public/browser/notification_registrar.h"
25#include "content/public/browser/notification_service.h"
[email protected]dccba4f82014-05-29 00:52:5626#include "extensions/browser/extension_prefs.h"
[email protected]e4452d32013-11-15 23:07:4127#include "extensions/common/extension.h"
[email protected]23a85362014-07-07 23:26:1928#include "extensions/common/extension_builder.h"
29#include "extensions/common/feature_switch.h"
[email protected]5a55f3f2013-10-29 01:08:2930#include "extensions/common/permissions/permission_set.h"
[email protected]076ebeda2014-06-06 21:47:2631#include "extensions/common/permissions/permissions_data.h"
[email protected]23a85362014-07-07 23:26:1932#include "extensions/common/value_builder.h"
[email protected]c333e792012-01-06 16:57:3933#include "testing/gtest/include/gtest/gtest.h"
34
[email protected]04e4bbe2013-04-27 07:44:2435using extension_test_util::LoadManifest;
36
[email protected]c333e792012-01-06 16:57:3937namespace extensions {
38
39namespace {
40
rdevlin.cronin77cb0ef2015-09-16 17:03:4841scoped_refptr<const Extension> CreateExtensionWithOptionalPermissions(
42 scoped_ptr<base::Value> optional_permissions,
43 scoped_ptr<base::Value> permissions,
44 const std::string& name) {
45 return ExtensionBuilder()
46 .SetLocation(Manifest::INTERNAL)
dcheng794d2bd2016-02-27 03:51:3247 .SetManifest(
rdevlin.cronin77cb0ef2015-09-16 17:03:4848 DictionaryBuilder()
49 .Set("name", name)
50 .Set("description", "foo")
51 .Set("manifest_version", 2)
52 .Set("version", "0.1.2.3")
dcheng1fc00f12015-12-26 22:18:0353 .Set("permissions", std::move(permissions))
dcheng794d2bd2016-02-27 03:51:3254 .Set("optional_permissions", std::move(optional_permissions))
55 .Build())
rdevlin.cronin77cb0ef2015-09-16 17:03:4856 .SetID(crx_file::id_util::GenerateId(name))
57 .Build();
58}
59
[email protected]c333e792012-01-06 16:57:3960// A helper class that listens for NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED.
61class PermissionsUpdaterListener : public content::NotificationObserver {
62 public:
63 PermissionsUpdaterListener()
64 : received_notification_(false), waiting_(false) {
65 registrar_.Add(this,
[email protected]adf5a102014-07-31 12:44:0666 extensions::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
[email protected]c333e792012-01-06 16:57:3967 content::NotificationService::AllSources());
68 }
69
70 void Reset() {
71 received_notification_ = false;
72 waiting_ = false;
73 extension_ = NULL;
74 permissions_ = NULL;
75 }
76
77 void Wait() {
78 if (received_notification_)
79 return;
80
81 waiting_ = true;
[email protected]78089f02012-07-19 06:11:2882 base::RunLoop run_loop;
83 run_loop.Run();
[email protected]c333e792012-01-06 16:57:3984 }
85
86 bool received_notification() const { return received_notification_; }
[email protected]dc24976f2013-06-02 21:15:0987 const Extension* extension() const { return extension_.get(); }
88 const PermissionSet* permissions() const { return permissions_.get(); }
89 UpdatedExtensionPermissionsInfo::Reason reason() const { return reason_; }
[email protected]c333e792012-01-06 16:57:3990
91 private:
dchengae36a4a2014-10-21 12:36:3692 void Observe(int type,
93 const content::NotificationSource& source,
94 const content::NotificationDetails& details) override {
[email protected]c333e792012-01-06 16:57:3995 received_notification_ = true;
96 UpdatedExtensionPermissionsInfo* info =
97 content::Details<UpdatedExtensionPermissionsInfo>(details).ptr();
98
99 extension_ = info->extension;
rdevlin.cronine2d0fd02015-09-24 22:35:49100 permissions_ = info->permissions.Clone();
[email protected]c333e792012-01-06 16:57:39101 reason_ = info->reason;
102
103 if (waiting_) {
104 waiting_ = false;
ki.stfuc4f8e242015-10-09 20:40:20105 base::MessageLoopForUI::current()->QuitWhenIdle();
[email protected]c333e792012-01-06 16:57:39106 }
107 }
108
109 bool received_notification_;
110 bool waiting_;
111 content::NotificationRegistrar registrar_;
112 scoped_refptr<const Extension> extension_;
rdevlin.cronine2d0fd02015-09-24 22:35:49113 scoped_ptr<const PermissionSet> permissions_;
[email protected]c333e792012-01-06 16:57:39114 UpdatedExtensionPermissionsInfo::Reason reason_;
115};
116
117class PermissionsUpdaterTest : public ExtensionServiceTestBase {
118};
119
[email protected]04e4bbe2013-04-27 07:44:24120scoped_refptr<Extension> LoadOurManifest() {
[email protected]650b2d52013-02-10 03:41:45121 base::FilePath path;
[email protected]04e4bbe2013-04-27 07:44:24122 path = path.AppendASCII("api_test")
[email protected]c333e792012-01-06 16:57:39123 .AppendASCII("permissions")
[email protected]04e4bbe2013-04-27 07:44:24124 .AppendASCII("optional");
125 return LoadManifest(path.AsUTF8Unsafe(),
126 "manifest.json",
127 Manifest::INTERNAL,
128 Extension::NO_FLAGS);
[email protected]c333e792012-01-06 16:57:39129}
130
131void AddPattern(URLPatternSet* extent, const std::string& pattern) {
132 int schemes = URLPattern::SCHEME_ALL;
133 extent->AddPattern(URLPattern(schemes, pattern));
134}
135
136} // namespace
137
138// Test that the PermissionUpdater can correctly add and remove active
139// permissions. This tests all of PermissionsUpdater's public methods because
[email protected]23a85362014-07-07 23:26:19140// GrantActivePermissions and SetPermissions are used by AddPermissions.
[email protected]c333e792012-01-06 16:57:39141TEST_F(PermissionsUpdaterTest, AddAndRemovePermissions) {
142 InitializeEmptyExtensionService();
143
144 // Load the test extension.
[email protected]04e4bbe2013-04-27 07:44:24145 scoped_refptr<Extension> extension = LoadOurManifest();
146 ASSERT_TRUE(extension.get());
[email protected]c333e792012-01-06 16:57:39147
[email protected]c2e66e12012-06-27 06:27:06148 APIPermissionSet default_apis;
149 default_apis.insert(APIPermission::kManagement);
[email protected]e737c442013-11-15 15:55:24150 ManifestPermissionSet empty_manifest_permissions;
151
[email protected]c333e792012-01-06 16:57:39152 URLPatternSet default_hosts;
153 AddPattern(&default_hosts, "http://a.com/*");
rdevlin.cronine2d0fd02015-09-24 22:35:49154 PermissionSet default_permissions(default_apis, empty_manifest_permissions,
155 default_hosts, URLPatternSet());
[email protected]c333e792012-01-06 16:57:39156
157 // Make sure it loaded properly.
rdevlin.cronine2d0fd02015-09-24 22:35:49158 ASSERT_EQ(default_permissions,
rdevlin.cronind630c302015-09-30 20:19:33159 extension->permissions_data()->active_permissions());
rdevlin.cronine2d0fd02015-09-24 22:35:49160
161 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_.get());
162 scoped_ptr<const PermissionSet> active_permissions;
163 scoped_ptr<const PermissionSet> granted_permissions;
[email protected]c333e792012-01-06 16:57:39164
165 // Add a few permissions.
[email protected]c2e66e12012-06-27 06:27:06166 APIPermissionSet apis;
[email protected]81327f12014-07-29 04:24:11167 apis.insert(APIPermission::kNotifications);
[email protected]c333e792012-01-06 16:57:39168 URLPatternSet hosts;
169 AddPattern(&hosts, "http://*.c.com/*");
170
rdevlin.cronine2d0fd02015-09-24 22:35:49171 {
172 PermissionSet delta(apis, empty_manifest_permissions, hosts,
173 URLPatternSet());
[email protected]c333e792012-01-06 16:57:39174
175 PermissionsUpdaterListener listener;
rdevlin.cronind630c302015-09-30 20:19:33176 PermissionsUpdater(profile_.get()).AddPermissions(extension.get(), delta);
[email protected]c333e792012-01-06 16:57:39177
178 listener.Wait();
179
180 // Verify that the permission notification was sent correctly.
181 ASSERT_TRUE(listener.received_notification());
dchengc7047942014-08-26 05:05:31182 ASSERT_EQ(extension.get(), listener.extension());
[email protected]c333e792012-01-06 16:57:39183 ASSERT_EQ(UpdatedExtensionPermissionsInfo::ADDED, listener.reason());
rdevlin.cronine2d0fd02015-09-24 22:35:49184 ASSERT_EQ(delta, *listener.permissions());
[email protected]c333e792012-01-06 16:57:39185
186 // Make sure the extension's active permissions reflect the change.
rdevlin.cronine2d0fd02015-09-24 22:35:49187 active_permissions = PermissionSet::CreateUnion(default_permissions, delta);
[email protected]cadac622013-06-11 16:46:36188 ASSERT_EQ(*active_permissions.get(),
rdevlin.cronind630c302015-09-30 20:19:33189 extension->permissions_data()->active_permissions());
[email protected]c333e792012-01-06 16:57:39190
191 // Verify that the new granted and active permissions were also stored
192 // in the extension preferences. In this case, the granted permissions should
193 // be equal to the active permissions.
rdevlin.cronine2d0fd02015-09-24 22:35:49194 ASSERT_EQ(*active_permissions.get(),
195 *prefs->GetActivePermissions(extension->id()));
196 granted_permissions = active_permissions->Clone();
197 ASSERT_EQ(*granted_permissions,
198 *prefs->GetGrantedPermissions(extension->id()));
199 }
[email protected]c333e792012-01-06 16:57:39200
rdevlin.cronine2d0fd02015-09-24 22:35:49201 {
[email protected]c333e792012-01-06 16:57:39202 // In the second part of the test, we'll remove the permissions that we
[email protected]81327f12014-07-29 04:24:11203 // just added except for 'notifications'.
204 apis.erase(APIPermission::kNotifications);
rdevlin.cronine2d0fd02015-09-24 22:35:49205 PermissionSet delta(apis, empty_manifest_permissions, hosts, URLPatternSet());
[email protected]c333e792012-01-06 16:57:39206
rdevlin.cronine2d0fd02015-09-24 22:35:49207 PermissionsUpdaterListener listener;
208 PermissionsUpdater(profile_.get())
rdevlin.cronind630c302015-09-30 20:19:33209 .RemovePermissions(extension.get(), delta,
rdevlin.cronine2d0fd02015-09-24 22:35:49210 PermissionsUpdater::REMOVE_SOFT);
[email protected]c333e792012-01-06 16:57:39211 listener.Wait();
212
213 // Verify that the notification was correct.
214 ASSERT_TRUE(listener.received_notification());
dchengc7047942014-08-26 05:05:31215 ASSERT_EQ(extension.get(), listener.extension());
[email protected]c333e792012-01-06 16:57:39216 ASSERT_EQ(UpdatedExtensionPermissionsInfo::REMOVED, listener.reason());
rdevlin.cronine2d0fd02015-09-24 22:35:49217 ASSERT_EQ(delta, *listener.permissions());
[email protected]c333e792012-01-06 16:57:39218
219 // Make sure the extension's active permissions reflect the change.
220 active_permissions =
rdevlin.cronine2d0fd02015-09-24 22:35:49221 PermissionSet::CreateDifference(*active_permissions, delta);
rdevlin.cronind630c302015-09-30 20:19:33222 ASSERT_EQ(*active_permissions,
223 extension->permissions_data()->active_permissions());
[email protected]c333e792012-01-06 16:57:39224
225 // Verify that the extension prefs hold the new active permissions and the
226 // same granted permissions.
rdevlin.cronine2d0fd02015-09-24 22:35:49227 ASSERT_EQ(*active_permissions, *prefs->GetActivePermissions(extension->id()));
[email protected]c333e792012-01-06 16:57:39228
rdevlin.cronine2d0fd02015-09-24 22:35:49229 ASSERT_EQ(*granted_permissions,
230 *prefs->GetGrantedPermissions(extension->id()));
231 }
[email protected]c333e792012-01-06 16:57:39232}
233
rdevlin.cronin77cb0ef2015-09-16 17:03:48234TEST_F(PermissionsUpdaterTest, RevokingPermissions) {
235 InitializeEmptyExtensionService();
236
237 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
238
239 auto api_permission_set = [](APIPermission::ID id) {
240 APIPermissionSet apis;
241 apis.insert(id);
rdevlin.cronine2d0fd02015-09-24 22:35:49242 return make_scoped_ptr(new PermissionSet(apis, ManifestPermissionSet(),
243 URLPatternSet(), URLPatternSet()));
rdevlin.cronin77cb0ef2015-09-16 17:03:48244 };
245
246 auto url_permission_set = [](const GURL& url) {
247 URLPatternSet set;
248 URLPattern pattern(URLPattern::SCHEME_ALL, url.spec());
249 set.AddPattern(pattern);
rdevlin.cronine2d0fd02015-09-24 22:35:49250 return make_scoped_ptr(new PermissionSet(
rdevlin.cronin77cb0ef2015-09-16 17:03:48251 APIPermissionSet(), ManifestPermissionSet(), set, URLPatternSet()));
252 };
253
254 {
255 // Test revoking optional permissions.
256 ListBuilder optional_permissions;
257 optional_permissions.Append("tabs").Append("cookies").Append("management");
258 ListBuilder required_permissions;
259 required_permissions.Append("topSites");
260 scoped_refptr<const Extension> extension =
261 CreateExtensionWithOptionalPermissions(optional_permissions.Build(),
262 required_permissions.Build(),
263 "My Extension");
264
265 PermissionsUpdater updater(profile());
266 EXPECT_TRUE(updater.GetRevokablePermissions(extension.get())->IsEmpty());
267
268 // Add the optional "cookies" permission.
269 updater.AddPermissions(extension.get(),
rdevlin.cronind630c302015-09-30 20:19:33270 *api_permission_set(APIPermission::kCookie));
rdevlin.cronin77cb0ef2015-09-16 17:03:48271 const PermissionsData* permissions = extension->permissions_data();
272 // The extension should have the permission in its active permissions and
273 // its granted permissions (stored in prefs). And, the permission should
274 // be revokable.
275 EXPECT_TRUE(permissions->HasAPIPermission(APIPermission::kCookie));
rdevlin.cronine2d0fd02015-09-24 22:35:49276 scoped_ptr<const PermissionSet> granted_permissions =
rdevlin.cronin77cb0ef2015-09-16 17:03:48277 prefs->GetGrantedPermissions(extension->id());
278 EXPECT_TRUE(granted_permissions->HasAPIPermission(APIPermission::kCookie));
279 EXPECT_TRUE(updater.GetRevokablePermissions(extension.get())
280 ->HasAPIPermission(APIPermission::kCookie));
281
282 // Repeat with "tabs".
283 updater.AddPermissions(extension.get(),
rdevlin.cronind630c302015-09-30 20:19:33284 *api_permission_set(APIPermission::kTab));
rdevlin.cronin77cb0ef2015-09-16 17:03:48285 EXPECT_TRUE(permissions->HasAPIPermission(APIPermission::kTab));
286 granted_permissions = prefs->GetGrantedPermissions(extension->id());
287 EXPECT_TRUE(granted_permissions->HasAPIPermission(APIPermission::kTab));
288 EXPECT_TRUE(updater.GetRevokablePermissions(extension.get())
289 ->HasAPIPermission(APIPermission::kTab));
290
291 // Remove the "tabs" permission. The extension should no longer have it
292 // in its active or granted permissions, and it shouldn't be revokable.
293 // The extension should still have the "cookies" permission.
294 updater.RemovePermissions(extension.get(),
rdevlin.cronind630c302015-09-30 20:19:33295 *api_permission_set(APIPermission::kTab),
rdevlin.cronin77cb0ef2015-09-16 17:03:48296 PermissionsUpdater::REMOVE_HARD);
297 EXPECT_FALSE(permissions->HasAPIPermission(APIPermission::kTab));
298 granted_permissions = prefs->GetGrantedPermissions(extension->id());
299 EXPECT_FALSE(granted_permissions->HasAPIPermission(APIPermission::kTab));
300 EXPECT_FALSE(updater.GetRevokablePermissions(extension.get())
301 ->HasAPIPermission(APIPermission::kTab));
302 EXPECT_TRUE(permissions->HasAPIPermission(APIPermission::kCookie));
303 granted_permissions = prefs->GetGrantedPermissions(extension->id());
304 EXPECT_TRUE(granted_permissions->HasAPIPermission(APIPermission::kCookie));
305 EXPECT_TRUE(updater.GetRevokablePermissions(extension.get())
306 ->HasAPIPermission(APIPermission::kCookie));
307 }
308
309 {
310 // Test revoking non-optional host permissions with click-to-script.
311 FeatureSwitch::ScopedOverride scoped_override(
312 FeatureSwitch::scripts_require_action(), true);
313 ListBuilder optional_permissions;
314 optional_permissions.Append("tabs");
315 ListBuilder required_permissions;
316 required_permissions.Append("topSites")
317 .Append("http://*/*")
318 .Append("http://*.google.com/*");
319 scoped_refptr<const Extension> extension =
320 CreateExtensionWithOptionalPermissions(optional_permissions.Build(),
321 required_permissions.Build(),
322 "My Extension");
323 PermissionsUpdater updater(profile());
324 updater.InitializePermissions(extension.get());
325
326 // By default, all-hosts was withheld, so the extension shouldn't have
327 // access to any site (like foo.com).
328 const GURL kOrigin("http://foo.com");
329 EXPECT_FALSE(extension->permissions_data()
330 ->active_permissions()
rdevlin.cronind630c302015-09-30 20:19:33331 .HasExplicitAccessToOrigin(kOrigin));
rdevlin.cronin77cb0ef2015-09-16 17:03:48332 EXPECT_TRUE(extension->permissions_data()
333 ->withheld_permissions()
rdevlin.cronind630c302015-09-30 20:19:33334 .HasExplicitAccessToOrigin(kOrigin));
rdevlin.cronin77cb0ef2015-09-16 17:03:48335
336 const GURL kRequiredOrigin("http://www.google.com/");
337 EXPECT_TRUE(extension->permissions_data()
338 ->active_permissions()
rdevlin.cronind630c302015-09-30 20:19:33339 .HasExplicitAccessToOrigin(kRequiredOrigin));
rdevlin.cronin77cb0ef2015-09-16 17:03:48340 EXPECT_FALSE(updater.GetRevokablePermissions(extension.get())
341 ->HasExplicitAccessToOrigin(kRequiredOrigin));
342
343 // Give the extension access to foo.com. Now, the foo.com permission should
344 // be revokable.
rdevlin.cronind630c302015-09-30 20:19:33345 updater.AddPermissions(extension.get(), *url_permission_set(kOrigin));
rdevlin.cronin77cb0ef2015-09-16 17:03:48346 EXPECT_TRUE(extension->permissions_data()
347 ->active_permissions()
rdevlin.cronind630c302015-09-30 20:19:33348 .HasExplicitAccessToOrigin(kOrigin));
rdevlin.cronin77cb0ef2015-09-16 17:03:48349 EXPECT_TRUE(updater.GetRevokablePermissions(extension.get())
350 ->HasExplicitAccessToOrigin(kOrigin));
351
352 // Revoke the foo.com permission. The extension should no longer have
353 // access to foo.com, and the revokable permissions should be empty.
rdevlin.cronind630c302015-09-30 20:19:33354 updater.RemovePermissions(extension.get(), *url_permission_set(kOrigin),
rdevlin.cronin77cb0ef2015-09-16 17:03:48355 PermissionsUpdater::REMOVE_HARD);
356 EXPECT_FALSE(extension->permissions_data()
357 ->active_permissions()
rdevlin.cronind630c302015-09-30 20:19:33358 .HasExplicitAccessToOrigin(kOrigin));
rdevlin.cronin77cb0ef2015-09-16 17:03:48359 EXPECT_TRUE(extension->permissions_data()
360 ->withheld_permissions()
rdevlin.cronind630c302015-09-30 20:19:33361 .HasExplicitAccessToOrigin(kOrigin));
rdevlin.cronin77cb0ef2015-09-16 17:03:48362 EXPECT_TRUE(updater.GetRevokablePermissions(extension.get())->IsEmpty());
363 }
364}
365
[email protected]c333e792012-01-06 16:57:39366} // namespace extensions