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.
5 #include "base/memory/scoped_ptr.h"
6 #include "chrome/browser/extensions/extension_service.h"
7 #include "chrome/browser/extensions/permissions_updater.h"
8 #include "chrome/browser/extensions/test_extension_environment.h"
9 #include "chrome/common/extensions/permissions/chrome_permission_message_provider.h"
10 #include "chrome/grit/generated_resources.h"
11 #include "chrome/test/base/testing_profile.h"
12 #include "components/crx_file/id_util.h"
13 #include "extensions/browser/extension_prefs.h"
14 #include "extensions/common/extension.h"
15 #include "extensions/common/extension_builder.h"
16 #include "extensions/common/manifest.h"
17 #include "extensions/common/manifest_handlers/permissions_parser.h"
18 #include "extensions/common/permissions/permission_set.h"
19 #include "extensions/common/permissions/permissions_data.h"
20 #include "extensions/common/test_util.h"
21 #include "extensions/common/value_builder.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "ui/base/l10n/l10n_util.h"
25 namespace extensions
{
27 // Tests that ChromePermissionMessageProvider provides not only correct, but
28 // meaningful permission messages that coalesce correctly where appropriate.
29 // There are 3 types of permission messages that need to be tested:
30 // 1. The combined list of active permissions, displayed at install time (or
31 // when the app has been disabled automatically and needs to be re-enabled)
32 // 2. The split list of active permissions, displayed in the App Info dialog,
33 // where the optional permissions are individually revokable
34 // 3. The list of requested optional permissions, displayed in a prompt to the
35 // user when the app requests these during runtime
36 // Some of these tests are prefixed AntiTest_, since they demonstrate existing
37 // problematic functionality. These tests are prefixed with AntiTest_ and will
38 // be changed as the correct behaviour is implemented. TODOs in the test explain
39 // the currently problematic behaviour.
40 class PermissionMessagesUnittest
: public testing::Test
{
42 PermissionMessagesUnittest()
43 : message_provider_(new ChromePermissionMessageProvider()) {}
44 ~PermissionMessagesUnittest() override
{}
46 // Overridden from testing::Test:
47 void SetUp() override
{
48 testing::Test::SetUp();
52 void CreateAndInstallAppWithPermissions(ListBuilder
& required_permissions
,
53 ListBuilder
& optional_permissions
) {
54 app_
= test_util::BuildApp(ExtensionBuilder().Pass())
57 .Set("permissions", required_permissions
)
58 .Set("optional_permissions", optional_permissions
))
59 .SetID(crx_file::id_util::GenerateId("app"))
60 .SetLocation(Manifest::INTERNAL
)
62 env_
.GetExtensionService()->AddExtension(app_
.get());
65 void CreateAndInstallExtensionWithPermissions(
66 ListBuilder
& required_permissions
,
67 ListBuilder
& optional_permissions
) {
68 app_
= test_util::BuildExtension(ExtensionBuilder().Pass())
71 .Set("permissions", required_permissions
)
72 .Set("optional_permissions", optional_permissions
))
73 .SetID(crx_file::id_util::GenerateId("extension"))
74 .SetLocation(Manifest::INTERNAL
)
76 env_
.GetExtensionService()->AddExtension(app_
.get());
79 // Returns the permission messages that would display in the prompt that
80 // requests all the optional permissions for the current |app_|.
81 std::vector
<base::string16
> GetOptionalPermissionMessages() {
82 scoped_refptr
<const PermissionSet
> granted_permissions
=
83 env_
.GetExtensionPrefs()->GetGrantedPermissions(app_
->id());
84 scoped_refptr
<const PermissionSet
> optional_permissions
=
85 PermissionsParser::GetOptionalPermissions(app_
.get());
86 scoped_refptr
<const PermissionSet
> requested_permissions
=
87 PermissionSet::CreateDifference(optional_permissions
.get(),
88 granted_permissions
.get());
89 return GetMessages(requested_permissions
);
92 void GrantOptionalPermissions() {
93 PermissionsUpdater
perms_updater(env_
.profile());
94 perms_updater
.AddPermissions(
96 PermissionsParser::GetOptionalPermissions(app_
.get()).get());
99 std::vector
<base::string16
> active_permissions() {
100 return GetMessages(app_
->permissions_data()->active_permissions());
103 std::vector
<base::string16
> required_permissions() {
104 return GetMessages(PermissionsParser::GetRequiredPermissions(app_
.get()));
107 std::vector
<base::string16
> optional_permissions() {
108 return GetMessages(PermissionsParser::GetOptionalPermissions(app_
.get()));
112 std::vector
<base::string16
> GetMessages(
113 scoped_refptr
<const PermissionSet
> permissions
) {
114 std::vector
<base::string16
> messages
;
115 for (const CoalescedPermissionMessage
& msg
:
116 message_provider_
->GetPermissionMessages(
117 message_provider_
->GetAllPermissionIDs(permissions
.get(),
119 messages
.push_back(msg
.message());
124 extensions::TestExtensionEnvironment env_
;
125 scoped_ptr
<ChromePermissionMessageProvider
> message_provider_
;
126 scoped_refptr
<const Extension
> app_
;
128 DISALLOW_COPY_AND_ASSIGN(PermissionMessagesUnittest
);
131 // If an app has both the 'history' and 'tabs' permission, one should hide the
132 // other (the 'history' permission has superset permissions).
133 TEST_F(PermissionMessagesUnittest
, HistoryHidesTabsMessage
) {
134 CreateAndInstallExtensionWithPermissions(
135 ListBuilder().Append("tabs").Append("history").Pass(),
136 ListBuilder().Pass());
138 ASSERT_EQ(1U, required_permissions().size());
140 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_WRITE
),
141 required_permissions()[0]);
143 ASSERT_EQ(0U, optional_permissions().size());
146 // If an app requests the 'history' permission, but already has the 'tabs'
147 // permission, only the new coalesced message is displayed.
148 TEST_F(PermissionMessagesUnittest
, MixedPermissionMessagesCoalesceOnceGranted
) {
149 CreateAndInstallExtensionWithPermissions(
150 ListBuilder().Append("tabs").Pass(),
151 ListBuilder().Append("history").Pass());
153 ASSERT_EQ(1U, required_permissions().size());
155 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_READ
),
156 required_permissions()[0]);
158 ASSERT_EQ(1U, optional_permissions().size());
160 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_WRITE
),
161 optional_permissions()[0]);
163 ASSERT_EQ(1U, active_permissions().size());
165 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_READ
),
166 active_permissions()[0]);
168 ASSERT_EQ(1U, GetOptionalPermissionMessages().size());
170 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_WRITE
),
171 GetOptionalPermissionMessages()[0]);
173 GrantOptionalPermissions();
175 ASSERT_EQ(1U, active_permissions().size());
177 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_WRITE
),
178 active_permissions()[0]);
181 // AntiTest: This behavior should be changed and improved.
182 // If an app requests the 'tabs' permission but already has the 'history'
183 // permission, a prompt is displayed. However, no prompt should appear at all,
184 // since 'tabs' is a subset of 'history' and the final list of permissions are
185 // not affected by this grant.
186 TEST_F(PermissionMessagesUnittest
,
187 AntiTest_PromptCanRequestSubsetOfAlreadyGrantedPermissions
) {
188 CreateAndInstallExtensionWithPermissions(
189 ListBuilder().Append("history").Pass(),
190 ListBuilder().Append("tabs").Pass());
192 ASSERT_EQ(1U, required_permissions().size());
194 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_WRITE
),
195 required_permissions()[0]);
197 ASSERT_EQ(1U, optional_permissions().size());
199 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_READ
),
200 optional_permissions()[0]);
202 ASSERT_EQ(1U, active_permissions().size());
204 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_WRITE
),
205 active_permissions()[0]);
207 // TODO(sashab): This prompt should display no permissions, since READ is a
208 // subset permission of WRITE.
209 ASSERT_EQ(1U, GetOptionalPermissionMessages().size());
211 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_READ
),
212 GetOptionalPermissionMessages()[0]);
214 GrantOptionalPermissions();
216 ASSERT_EQ(1U, active_permissions().size());
218 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_WRITE
),
219 active_permissions()[0]);
222 // AntiTest: This behavior should be changed and improved.
223 // If an app requests the 'sessions' permission, nothing is displayed in the
224 // permission request prompt. However, the required permissions for the app are
225 // actually modified, so the prompt *should* display a message to prevent this
226 // permission from being granted for free.
227 TEST_F(PermissionMessagesUnittest
,
228 AntiTest_PromptCanBeEmptyButCausesChangeInPermissions
) {
229 CreateAndInstallExtensionWithPermissions(
230 ListBuilder().Append("tabs").Pass(),
231 ListBuilder().Append("sessions").Pass());
233 ASSERT_EQ(1U, required_permissions().size());
235 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_READ
),
236 required_permissions()[0]);
238 ASSERT_EQ(0U, optional_permissions().size());
240 ASSERT_EQ(1U, active_permissions().size());
242 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_READ
),
243 active_permissions()[0]);
245 // TODO(sashab): This prompt should display the sessions permission message,
246 // as well as warn the user that it can affect the existing 'tab' permission.
247 ASSERT_EQ(0U, GetOptionalPermissionMessages().size());
249 GrantOptionalPermissions();
251 ASSERT_EQ(1U, active_permissions().size());
252 EXPECT_EQ(l10n_util::GetStringUTF16(
253 IDS_EXTENSION_PROMPT_WARNING_HISTORY_READ_AND_SESSIONS
),
254 active_permissions()[0]);
257 } // namespace extensions