blob: 6a9499570cc63e0999beba7d341d88c62880b7b4 [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"
dchengc963c7142016-04-08 03:55:2211#include "base/memory/ptr_util.h"
[email protected]c333e792012-01-06 16:57:3912#include "base/memory/ref_counted.h"
[email protected]78089f02012-07-19 06:11:2813#include "base/run_loop.h"
[email protected]23a85362014-07-07 23:26:1914#include "base/strings/stringprintf.h"
[email protected]c333e792012-01-06 16:57:3915#include "base/values.h"
[email protected]49a01e642013-07-12 00:29:4516#include "chrome/browser/chrome_notification_types.h"
[email protected]c333e792012-01-06 16:57:3917#include "chrome/browser/extensions/extension_service.h"
[email protected]f484f8d52014-06-12 08:38:1818#include "chrome/browser/extensions/extension_service_test_base.h"
rdevlin.croninb8dffe52015-02-07 00:58:0119#include "chrome/browser/extensions/extension_util.h"
[email protected]c333e792012-01-06 16:57:3920#include "chrome/common/chrome_paths.h"
[email protected]04e4bbe2013-04-27 07:44:2421#include "chrome/common/extensions/extension_test_util.h"
[email protected]c333e792012-01-06 16:57:3922#include "chrome/test/base/testing_profile.h"
rdevlin.croninb8dffe52015-02-07 00:58:0123#include "components/crx_file/id_util.h"
[email protected]c333e792012-01-06 16:57:3924#include "content/public/browser/notification_observer.h"
25#include "content/public/browser/notification_registrar.h"
26#include "content/public/browser/notification_service.h"
[email protected]dccba4f82014-05-29 00:52:5627#include "extensions/browser/extension_prefs.h"
[email protected]e4452d32013-11-15 23:07:4128#include "extensions/common/extension.h"
[email protected]23a85362014-07-07 23:26:1929#include "extensions/common/extension_builder.h"
30#include "extensions/common/feature_switch.h"
[email protected]5a55f3f2013-10-29 01:08:2931#include "extensions/common/permissions/permission_set.h"
[email protected]076ebeda2014-06-06 21:47:2632#include "extensions/common/permissions/permissions_data.h"
[email protected]23a85362014-07-07 23:26:1933#include "extensions/common/value_builder.h"
[email protected]c333e792012-01-06 16:57:3934#include "testing/gtest/include/gtest/gtest.h"
35
[email protected]04e4bbe2013-04-27 07:44:2436using extension_test_util::LoadManifest;
37
[email protected]c333e792012-01-06 16:57:3938namespace extensions {
39
40namespace {
41
rdevlin.cronin77cb0ef2015-09-16 17:03:4842scoped_refptr<const Extension> CreateExtensionWithOptionalPermissions(
dchengc963c7142016-04-08 03:55:2243 std::unique_ptr<base::Value> optional_permissions,
44 std::unique_ptr<base::Value> permissions,
rdevlin.cronin77cb0ef2015-09-16 17:03:4845 const std::string& name) {
46 return ExtensionBuilder()
47 .SetLocation(Manifest::INTERNAL)
dcheng794d2bd2016-02-27 03:51:3248 .SetManifest(
rdevlin.cronin77cb0ef2015-09-16 17:03:4849 DictionaryBuilder()
50 .Set("name", name)
51 .Set("description", "foo")
52 .Set("manifest_version", 2)
53 .Set("version", "0.1.2.3")
dcheng1fc00f12015-12-26 22:18:0354 .Set("permissions", std::move(permissions))
dcheng794d2bd2016-02-27 03:51:3255 .Set("optional_permissions", std::move(optional_permissions))
56 .Build())
rdevlin.cronin77cb0ef2015-09-16 17:03:4857 .SetID(crx_file::id_util::GenerateId(name))
58 .Build();
59}
60
[email protected]c333e792012-01-06 16:57:3961// A helper class that listens for NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED.
62class PermissionsUpdaterListener : public content::NotificationObserver {
63 public:
64 PermissionsUpdaterListener()
65 : received_notification_(false), waiting_(false) {
66 registrar_.Add(this,
[email protected]adf5a102014-07-31 12:44:0667 extensions::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
[email protected]c333e792012-01-06 16:57:3968 content::NotificationService::AllSources());
69 }
70
71 void Reset() {
72 received_notification_ = false;
73 waiting_ = false;
74 extension_ = NULL;
75 permissions_ = NULL;
76 }
77
78 void Wait() {
79 if (received_notification_)
80 return;
81
82 waiting_ = true;
[email protected]78089f02012-07-19 06:11:2883 base::RunLoop run_loop;
84 run_loop.Run();
[email protected]c333e792012-01-06 16:57:3985 }
86
87 bool received_notification() const { return received_notification_; }
[email protected]dc24976f2013-06-02 21:15:0988 const Extension* extension() const { return extension_.get(); }
89 const PermissionSet* permissions() const { return permissions_.get(); }
90 UpdatedExtensionPermissionsInfo::Reason reason() const { return reason_; }
[email protected]c333e792012-01-06 16:57:3991
92 private:
dchengae36a4a2014-10-21 12:36:3693 void Observe(int type,
94 const content::NotificationSource& source,
95 const content::NotificationDetails& details) override {
[email protected]c333e792012-01-06 16:57:3996 received_notification_ = true;
97 UpdatedExtensionPermissionsInfo* info =
98 content::Details<UpdatedExtensionPermissionsInfo>(details).ptr();
99
100 extension_ = info->extension;
rdevlin.cronine2d0fd02015-09-24 22:35:49101 permissions_ = info->permissions.Clone();
[email protected]c333e792012-01-06 16:57:39102 reason_ = info->reason;
103
104 if (waiting_) {
105 waiting_ = false;
ki.stfuc4f8e242015-10-09 20:40:20106 base::MessageLoopForUI::current()->QuitWhenIdle();
[email protected]c333e792012-01-06 16:57:39107 }
108 }
109
110 bool received_notification_;
111 bool waiting_;
112 content::NotificationRegistrar registrar_;
113 scoped_refptr<const Extension> extension_;
dchengc963c7142016-04-08 03:55:22114 std::unique_ptr<const PermissionSet> permissions_;
[email protected]c333e792012-01-06 16:57:39115 UpdatedExtensionPermissionsInfo::Reason reason_;
116};
117
118class PermissionsUpdaterTest : public ExtensionServiceTestBase {
119};
120
[email protected]04e4bbe2013-04-27 07:44:24121scoped_refptr<Extension> LoadOurManifest() {
[email protected]650b2d52013-02-10 03:41:45122 base::FilePath path;
[email protected]04e4bbe2013-04-27 07:44:24123 path = path.AppendASCII("api_test")
[email protected]c333e792012-01-06 16:57:39124 .AppendASCII("permissions")
[email protected]04e4bbe2013-04-27 07:44:24125 .AppendASCII("optional");
126 return LoadManifest(path.AsUTF8Unsafe(),
127 "manifest.json",
128 Manifest::INTERNAL,
129 Extension::NO_FLAGS);
[email protected]c333e792012-01-06 16:57:39130}
131
132void AddPattern(URLPatternSet* extent, const std::string& pattern) {
133 int schemes = URLPattern::SCHEME_ALL;
134 extent->AddPattern(URLPattern(schemes, pattern));
135}
136
137} // namespace
138
139// Test that the PermissionUpdater can correctly add and remove active
140// permissions. This tests all of PermissionsUpdater's public methods because
[email protected]23a85362014-07-07 23:26:19141// GrantActivePermissions and SetPermissions are used by AddPermissions.
[email protected]c333e792012-01-06 16:57:39142TEST_F(PermissionsUpdaterTest, AddAndRemovePermissions) {
143 InitializeEmptyExtensionService();
144
145 // Load the test extension.
[email protected]04e4bbe2013-04-27 07:44:24146 scoped_refptr<Extension> extension = LoadOurManifest();
147 ASSERT_TRUE(extension.get());
[email protected]c333e792012-01-06 16:57:39148
[email protected]c2e66e12012-06-27 06:27:06149 APIPermissionSet default_apis;
150 default_apis.insert(APIPermission::kManagement);
[email protected]e737c442013-11-15 15:55:24151 ManifestPermissionSet empty_manifest_permissions;
152
[email protected]c333e792012-01-06 16:57:39153 URLPatternSet default_hosts;
154 AddPattern(&default_hosts, "http://a.com/*");
rdevlin.cronine2d0fd02015-09-24 22:35:49155 PermissionSet default_permissions(default_apis, empty_manifest_permissions,
156 default_hosts, URLPatternSet());
[email protected]c333e792012-01-06 16:57:39157
158 // Make sure it loaded properly.
rdevlin.cronine2d0fd02015-09-24 22:35:49159 ASSERT_EQ(default_permissions,
rdevlin.cronind630c302015-09-30 20:19:33160 extension->permissions_data()->active_permissions());
rdevlin.cronine2d0fd02015-09-24 22:35:49161
162 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_.get());
dchengc963c7142016-04-08 03:55:22163 std::unique_ptr<const PermissionSet> active_permissions;
164 std::unique_ptr<const PermissionSet> granted_permissions;
[email protected]c333e792012-01-06 16:57:39165
166 // Add a few permissions.
[email protected]c2e66e12012-06-27 06:27:06167 APIPermissionSet apis;
[email protected]81327f12014-07-29 04:24:11168 apis.insert(APIPermission::kNotifications);
[email protected]c333e792012-01-06 16:57:39169 URLPatternSet hosts;
170 AddPattern(&hosts, "http://*.c.com/*");
171
rdevlin.cronine2d0fd02015-09-24 22:35:49172 {
173 PermissionSet delta(apis, empty_manifest_permissions, hosts,
174 URLPatternSet());
[email protected]c333e792012-01-06 16:57:39175
176 PermissionsUpdaterListener listener;
rdevlin.cronind630c302015-09-30 20:19:33177 PermissionsUpdater(profile_.get()).AddPermissions(extension.get(), delta);
[email protected]c333e792012-01-06 16:57:39178
179 listener.Wait();
180
181 // Verify that the permission notification was sent correctly.
182 ASSERT_TRUE(listener.received_notification());
dchengc7047942014-08-26 05:05:31183 ASSERT_EQ(extension.get(), listener.extension());
[email protected]c333e792012-01-06 16:57:39184 ASSERT_EQ(UpdatedExtensionPermissionsInfo::ADDED, listener.reason());
rdevlin.cronine2d0fd02015-09-24 22:35:49185 ASSERT_EQ(delta, *listener.permissions());
[email protected]c333e792012-01-06 16:57:39186
187 // Make sure the extension's active permissions reflect the change.
rdevlin.cronine2d0fd02015-09-24 22:35:49188 active_permissions = PermissionSet::CreateUnion(default_permissions, delta);
[email protected]cadac622013-06-11 16:46:36189 ASSERT_EQ(*active_permissions.get(),
rdevlin.cronind630c302015-09-30 20:19:33190 extension->permissions_data()->active_permissions());
[email protected]c333e792012-01-06 16:57:39191
192 // Verify that the new granted and active permissions were also stored
193 // in the extension preferences. In this case, the granted permissions should
194 // be equal to the active permissions.
rdevlin.cronine2d0fd02015-09-24 22:35:49195 ASSERT_EQ(*active_permissions.get(),
196 *prefs->GetActivePermissions(extension->id()));
197 granted_permissions = active_permissions->Clone();
198 ASSERT_EQ(*granted_permissions,
199 *prefs->GetGrantedPermissions(extension->id()));
200 }
[email protected]c333e792012-01-06 16:57:39201
rdevlin.cronine2d0fd02015-09-24 22:35:49202 {
[email protected]c333e792012-01-06 16:57:39203 // In the second part of the test, we'll remove the permissions that we
[email protected]81327f12014-07-29 04:24:11204 // just added except for 'notifications'.
205 apis.erase(APIPermission::kNotifications);
rdevlin.cronine2d0fd02015-09-24 22:35:49206 PermissionSet delta(apis, empty_manifest_permissions, hosts, URLPatternSet());
[email protected]c333e792012-01-06 16:57:39207
rdevlin.cronine2d0fd02015-09-24 22:35:49208 PermissionsUpdaterListener listener;
209 PermissionsUpdater(profile_.get())
rdevlin.cronind630c302015-09-30 20:19:33210 .RemovePermissions(extension.get(), delta,
rdevlin.cronine2d0fd02015-09-24 22:35:49211 PermissionsUpdater::REMOVE_SOFT);
[email protected]c333e792012-01-06 16:57:39212 listener.Wait();
213
214 // Verify that the notification was correct.
215 ASSERT_TRUE(listener.received_notification());
dchengc7047942014-08-26 05:05:31216 ASSERT_EQ(extension.get(), listener.extension());
[email protected]c333e792012-01-06 16:57:39217 ASSERT_EQ(UpdatedExtensionPermissionsInfo::REMOVED, listener.reason());
rdevlin.cronine2d0fd02015-09-24 22:35:49218 ASSERT_EQ(delta, *listener.permissions());
[email protected]c333e792012-01-06 16:57:39219
220 // Make sure the extension's active permissions reflect the change.
221 active_permissions =
rdevlin.cronine2d0fd02015-09-24 22:35:49222 PermissionSet::CreateDifference(*active_permissions, delta);
rdevlin.cronind630c302015-09-30 20:19:33223 ASSERT_EQ(*active_permissions,
224 extension->permissions_data()->active_permissions());
[email protected]c333e792012-01-06 16:57:39225
226 // Verify that the extension prefs hold the new active permissions and the
227 // same granted permissions.
rdevlin.cronine2d0fd02015-09-24 22:35:49228 ASSERT_EQ(*active_permissions, *prefs->GetActivePermissions(extension->id()));
[email protected]c333e792012-01-06 16:57:39229
rdevlin.cronine2d0fd02015-09-24 22:35:49230 ASSERT_EQ(*granted_permissions,
231 *prefs->GetGrantedPermissions(extension->id()));
232 }
[email protected]c333e792012-01-06 16:57:39233}
234
rdevlin.cronin77cb0ef2015-09-16 17:03:48235TEST_F(PermissionsUpdaterTest, RevokingPermissions) {
236 InitializeEmptyExtensionService();
237
238 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
239
240 auto api_permission_set = [](APIPermission::ID id) {
241 APIPermissionSet apis;
242 apis.insert(id);
dchengc963c7142016-04-08 03:55:22243 return base::WrapUnique(new PermissionSet(
244 apis, ManifestPermissionSet(), URLPatternSet(), URLPatternSet()));
rdevlin.cronin77cb0ef2015-09-16 17:03:48245 };
246
247 auto url_permission_set = [](const GURL& url) {
248 URLPatternSet set;
249 URLPattern pattern(URLPattern::SCHEME_ALL, url.spec());
250 set.AddPattern(pattern);
dchengc963c7142016-04-08 03:55:22251 return base::WrapUnique(new PermissionSet(
rdevlin.cronin77cb0ef2015-09-16 17:03:48252 APIPermissionSet(), ManifestPermissionSet(), set, URLPatternSet()));
253 };
254
255 {
256 // Test revoking optional permissions.
257 ListBuilder optional_permissions;
258 optional_permissions.Append("tabs").Append("cookies").Append("management");
259 ListBuilder required_permissions;
260 required_permissions.Append("topSites");
261 scoped_refptr<const Extension> extension =
262 CreateExtensionWithOptionalPermissions(optional_permissions.Build(),
263 required_permissions.Build(),
264 "My Extension");
265
266 PermissionsUpdater updater(profile());
267 EXPECT_TRUE(updater.GetRevokablePermissions(extension.get())->IsEmpty());
268
269 // Add the optional "cookies" permission.
270 updater.AddPermissions(extension.get(),
rdevlin.cronind630c302015-09-30 20:19:33271 *api_permission_set(APIPermission::kCookie));
rdevlin.cronin77cb0ef2015-09-16 17:03:48272 const PermissionsData* permissions = extension->permissions_data();
273 // The extension should have the permission in its active permissions and
274 // its granted permissions (stored in prefs). And, the permission should
275 // be revokable.
276 EXPECT_TRUE(permissions->HasAPIPermission(APIPermission::kCookie));
dchengc963c7142016-04-08 03:55:22277 std::unique_ptr<const PermissionSet> granted_permissions =
rdevlin.cronin77cb0ef2015-09-16 17:03:48278 prefs->GetGrantedPermissions(extension->id());
279 EXPECT_TRUE(granted_permissions->HasAPIPermission(APIPermission::kCookie));
280 EXPECT_TRUE(updater.GetRevokablePermissions(extension.get())
281 ->HasAPIPermission(APIPermission::kCookie));
282
283 // Repeat with "tabs".
284 updater.AddPermissions(extension.get(),
rdevlin.cronind630c302015-09-30 20:19:33285 *api_permission_set(APIPermission::kTab));
rdevlin.cronin77cb0ef2015-09-16 17:03:48286 EXPECT_TRUE(permissions->HasAPIPermission(APIPermission::kTab));
287 granted_permissions = prefs->GetGrantedPermissions(extension->id());
288 EXPECT_TRUE(granted_permissions->HasAPIPermission(APIPermission::kTab));
289 EXPECT_TRUE(updater.GetRevokablePermissions(extension.get())
290 ->HasAPIPermission(APIPermission::kTab));
291
292 // Remove the "tabs" permission. The extension should no longer have it
293 // in its active or granted permissions, and it shouldn't be revokable.
294 // The extension should still have the "cookies" permission.
295 updater.RemovePermissions(extension.get(),
rdevlin.cronind630c302015-09-30 20:19:33296 *api_permission_set(APIPermission::kTab),
rdevlin.cronin77cb0ef2015-09-16 17:03:48297 PermissionsUpdater::REMOVE_HARD);
298 EXPECT_FALSE(permissions->HasAPIPermission(APIPermission::kTab));
299 granted_permissions = prefs->GetGrantedPermissions(extension->id());
300 EXPECT_FALSE(granted_permissions->HasAPIPermission(APIPermission::kTab));
301 EXPECT_FALSE(updater.GetRevokablePermissions(extension.get())
302 ->HasAPIPermission(APIPermission::kTab));
303 EXPECT_TRUE(permissions->HasAPIPermission(APIPermission::kCookie));
304 granted_permissions = prefs->GetGrantedPermissions(extension->id());
305 EXPECT_TRUE(granted_permissions->HasAPIPermission(APIPermission::kCookie));
306 EXPECT_TRUE(updater.GetRevokablePermissions(extension.get())
307 ->HasAPIPermission(APIPermission::kCookie));
308 }
309
310 {
311 // Test revoking non-optional host permissions with click-to-script.
312 FeatureSwitch::ScopedOverride scoped_override(
313 FeatureSwitch::scripts_require_action(), true);
314 ListBuilder optional_permissions;
315 optional_permissions.Append("tabs");
316 ListBuilder required_permissions;
317 required_permissions.Append("topSites")
318 .Append("http://*/*")
319 .Append("http://*.google.com/*");
320 scoped_refptr<const Extension> extension =
321 CreateExtensionWithOptionalPermissions(optional_permissions.Build(),
322 required_permissions.Build(),
323 "My Extension");
324 PermissionsUpdater updater(profile());
325 updater.InitializePermissions(extension.get());
326
327 // By default, all-hosts was withheld, so the extension shouldn't have
328 // access to any site (like foo.com).
329 const GURL kOrigin("http://foo.com");
330 EXPECT_FALSE(extension->permissions_data()
331 ->active_permissions()
rdevlin.cronind630c302015-09-30 20:19:33332 .HasExplicitAccessToOrigin(kOrigin));
rdevlin.cronin77cb0ef2015-09-16 17:03:48333 EXPECT_TRUE(extension->permissions_data()
334 ->withheld_permissions()
rdevlin.cronind630c302015-09-30 20:19:33335 .HasExplicitAccessToOrigin(kOrigin));
rdevlin.cronin77cb0ef2015-09-16 17:03:48336
337 const GURL kRequiredOrigin("http://www.google.com/");
338 EXPECT_TRUE(extension->permissions_data()
339 ->active_permissions()
rdevlin.cronind630c302015-09-30 20:19:33340 .HasExplicitAccessToOrigin(kRequiredOrigin));
rdevlin.cronin77cb0ef2015-09-16 17:03:48341 EXPECT_FALSE(updater.GetRevokablePermissions(extension.get())
342 ->HasExplicitAccessToOrigin(kRequiredOrigin));
343
344 // Give the extension access to foo.com. Now, the foo.com permission should
345 // be revokable.
rdevlin.cronind630c302015-09-30 20:19:33346 updater.AddPermissions(extension.get(), *url_permission_set(kOrigin));
rdevlin.cronin77cb0ef2015-09-16 17:03:48347 EXPECT_TRUE(extension->permissions_data()
348 ->active_permissions()
rdevlin.cronind630c302015-09-30 20:19:33349 .HasExplicitAccessToOrigin(kOrigin));
rdevlin.cronin77cb0ef2015-09-16 17:03:48350 EXPECT_TRUE(updater.GetRevokablePermissions(extension.get())
351 ->HasExplicitAccessToOrigin(kOrigin));
352
353 // Revoke the foo.com permission. The extension should no longer have
354 // access to foo.com, and the revokable permissions should be empty.
rdevlin.cronind630c302015-09-30 20:19:33355 updater.RemovePermissions(extension.get(), *url_permission_set(kOrigin),
rdevlin.cronin77cb0ef2015-09-16 17:03:48356 PermissionsUpdater::REMOVE_HARD);
357 EXPECT_FALSE(extension->permissions_data()
358 ->active_permissions()
rdevlin.cronind630c302015-09-30 20:19:33359 .HasExplicitAccessToOrigin(kOrigin));
rdevlin.cronin77cb0ef2015-09-16 17:03:48360 EXPECT_TRUE(extension->permissions_data()
361 ->withheld_permissions()
rdevlin.cronind630c302015-09-30 20:19:33362 .HasExplicitAccessToOrigin(kOrigin));
rdevlin.cronin77cb0ef2015-09-16 17:03:48363 EXPECT_TRUE(updater.GetRevokablePermissions(extension.get())->IsEmpty());
364 }
365}
366
[email protected]c333e792012-01-06 16:57:39367} // namespace extensions