1 // 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.
5 #include "chrome/browser/extensions/permissions_updater.h"
7 #include "base/json/json_writer.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/values.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/extensions/api/permissions/permissions_api_helpers.h"
12 #include "chrome/browser/extensions/extension_util.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/common/extensions/api/permissions.h"
15 #include "content/public/browser/notification_observer.h"
16 #include "content/public/browser/notification_registrar.h"
17 #include "content/public/browser/notification_service.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "extensions/browser/event_router.h"
20 #include "extensions/browser/extension_prefs.h"
21 #include "extensions/common/extension.h"
22 #include "extensions/common/extension_messages.h"
23 #include "extensions/common/manifest_handlers/permissions_parser.h"
24 #include "extensions/common/permissions/permission_set.h"
25 #include "extensions/common/permissions/permissions_data.h"
26 #include "extensions/common/url_pattern.h"
27 #include "extensions/common/url_pattern_set.h"
29 using content::RenderProcessHost
;
30 using extensions::permissions_api_helpers::PackPermissionSet
;
32 namespace extensions
{
34 namespace permissions
= api::permissions
;
38 // Returns a set of single origin permissions from |permissions| that match
39 // |bounds|. This is necessary for two reasons:
40 // a) single origin active permissions can get filtered out in
41 // GetBoundedActivePermissions because they are not recognized as a subset
42 // of all-host permissions
43 // b) active permissions that do not match any manifest permissions can
44 // exist if a manifest permission is dropped
45 URLPatternSet
FilterSingleOriginPermissions(const URLPatternSet
& permissions
,
46 const URLPatternSet
& bounds
) {
47 URLPatternSet single_origin_permissions
;
48 for (URLPatternSet::const_iterator iter
= permissions
.begin();
49 iter
!= permissions
.end();
51 if (iter
->MatchesSingleOrigin() &&
52 bounds
.MatchesURL(GURL(iter
->GetAsString()))) {
53 single_origin_permissions
.AddPattern(*iter
);
56 return single_origin_permissions
;
59 // Returns a PermissionSet that has the active permissions of the extension,
60 // bounded to its current manifest.
61 scoped_refptr
<const PermissionSet
> GetBoundedActivePermissions(
62 const Extension
* extension
,
63 const scoped_refptr
<const PermissionSet
>& active_permissions
) {
64 // If the extension has used the optional permissions API, it will have a
65 // custom set of active permissions defined in the extension prefs. Here,
66 // we update the extension's active permissions based on the prefs.
67 if (!active_permissions
.get())
68 return extension
->permissions_data()->active_permissions();
70 scoped_refptr
<const PermissionSet
> required_permissions
=
71 PermissionsParser::GetRequiredPermissions(extension
);
73 // We restrict the active permissions to be within the bounds defined in the
74 // extension's manifest.
75 // a) active permissions must be a subset of optional + default permissions
76 // b) active permissions must contains all default permissions
77 scoped_refptr
<PermissionSet
> total_permissions
= PermissionSet::CreateUnion(
78 required_permissions
.get(),
79 PermissionsParser::GetOptionalPermissions(extension
).get());
81 // Make sure the active permissions contain no more than optional + default.
82 scoped_refptr
<PermissionSet
> adjusted_active
=
83 PermissionSet::CreateIntersection(total_permissions
.get(),
84 active_permissions
.get());
86 // Make sure the active permissions contain the default permissions.
87 adjusted_active
= PermissionSet::CreateUnion(required_permissions
.get(),
88 adjusted_active
.get());
90 return adjusted_active
;
93 // Divvy up the |url patterns| between those we grant and those we do not. If
94 // |withhold_permissions| is false (because the requisite feature is not
95 // enabled), no permissions are withheld.
96 void SegregateUrlPermissions(const URLPatternSet
& url_patterns
,
97 bool withhold_permissions
,
98 URLPatternSet
* granted
,
99 URLPatternSet
* withheld
) {
100 for (URLPatternSet::const_iterator iter
= url_patterns
.begin();
101 iter
!= url_patterns
.end();
103 if (withhold_permissions
&& iter
->ImpliesAllHosts())
104 withheld
->AddPattern(*iter
);
106 granted
->AddPattern(*iter
);
112 PermissionsUpdater::PermissionsUpdater(content::BrowserContext
* browser_context
)
113 : browser_context_(browser_context
), init_flag_(INIT_FLAG_NONE
) {
116 PermissionsUpdater::PermissionsUpdater(content::BrowserContext
* browser_context
,
118 : browser_context_(browser_context
), init_flag_(init_flag
) {
121 PermissionsUpdater::~PermissionsUpdater() {}
123 void PermissionsUpdater::AddPermissions(
124 const Extension
* extension
, const PermissionSet
* permissions
) {
125 scoped_refptr
<const PermissionSet
> existing(
126 extension
->permissions_data()->active_permissions());
127 scoped_refptr
<PermissionSet
> total(
128 PermissionSet::CreateUnion(existing
.get(), permissions
));
129 scoped_refptr
<PermissionSet
> added(
130 PermissionSet::CreateDifference(total
.get(), existing
.get()));
132 SetPermissions(extension
, total
, NULL
);
134 // Update the granted permissions so we don't auto-disable the extension.
135 GrantActivePermissions(extension
);
137 NotifyPermissionsUpdated(ADDED
, extension
, added
.get());
140 void PermissionsUpdater::RemovePermissions(
141 const Extension
* extension
, const PermissionSet
* permissions
) {
142 scoped_refptr
<const PermissionSet
> existing(
143 extension
->permissions_data()->active_permissions());
144 scoped_refptr
<PermissionSet
> total(
145 PermissionSet::CreateDifference(existing
.get(), permissions
));
146 scoped_refptr
<PermissionSet
> removed(
147 PermissionSet::CreateDifference(existing
.get(), total
.get()));
149 // We update the active permissions, and not the granted permissions, because
150 // the extension, not the user, removed the permissions. This allows the
151 // extension to add them again without prompting the user.
152 SetPermissions(extension
, total
, NULL
);
154 NotifyPermissionsUpdated(REMOVED
, extension
, removed
.get());
157 void PermissionsUpdater::GrantActivePermissions(const Extension
* extension
) {
160 ExtensionPrefs::Get(browser_context_
)->AddGrantedPermissions(
162 extension
->permissions_data()->active_permissions().get());
165 void PermissionsUpdater::InitializePermissions(const Extension
* extension
) {
166 scoped_refptr
<const PermissionSet
> active_permissions(NULL
);
167 scoped_refptr
<const PermissionSet
> bounded_active(NULL
);
168 // If |extension| is a transient dummy extension, we do not want to look for
169 // it in preferences.
170 if (init_flag_
& INIT_FLAG_TRANSIENT
) {
171 bounded_active
= active_permissions
=
172 extension
->permissions_data()->active_permissions();
174 active_permissions
= ExtensionPrefs::Get(browser_context_
)
175 ->GetActivePermissions(extension
->id());
176 bounded_active
= GetBoundedActivePermissions(extension
, active_permissions
);
179 // Determine whether or not to withhold host permissions.
180 bool should_withhold_permissions
= false;
181 if (PermissionsData::ScriptsMayRequireActionForExtension(
182 extension
, bounded_active
.get())) {
183 should_withhold_permissions
=
184 init_flag_
& INIT_FLAG_TRANSIENT
?
185 !util::DefaultAllowedScriptingOnAllUrls() :
186 !util::AllowedScriptingOnAllUrls(extension
->id(), browser_context_
);
189 URLPatternSet granted_explicit_hosts
;
190 URLPatternSet withheld_explicit_hosts
;
191 SegregateUrlPermissions(bounded_active
->explicit_hosts(),
192 should_withhold_permissions
,
193 &granted_explicit_hosts
,
194 &withheld_explicit_hosts
);
196 URLPatternSet granted_scriptable_hosts
;
197 URLPatternSet withheld_scriptable_hosts
;
198 SegregateUrlPermissions(bounded_active
->scriptable_hosts(),
199 should_withhold_permissions
,
200 &granted_scriptable_hosts
,
201 &withheld_scriptable_hosts
);
203 // After withholding permissions, add back any origins to the active set that
204 // may have been lost during the set operations that would have dropped them.
205 // For example, the union of <all_urls> and "example.com" is <all_urls>, so
206 // we may lose "example.com". However, "example.com" is important once
207 // <all_urls> is stripped during withholding.
208 if (active_permissions
.get()) {
209 granted_explicit_hosts
.AddPatterns(
210 FilterSingleOriginPermissions(active_permissions
->explicit_hosts(),
211 bounded_active
->explicit_hosts()));
212 granted_scriptable_hosts
.AddPatterns(
213 FilterSingleOriginPermissions(active_permissions
->scriptable_hosts(),
214 bounded_active
->scriptable_hosts()));
217 bounded_active
= new PermissionSet(bounded_active
->apis(),
218 bounded_active
->manifest_permissions(),
219 granted_explicit_hosts
,
220 granted_scriptable_hosts
);
222 scoped_refptr
<const PermissionSet
> withheld
=
223 new PermissionSet(APIPermissionSet(),
224 ManifestPermissionSet(),
225 withheld_explicit_hosts
,
226 withheld_scriptable_hosts
);
227 SetPermissions(extension
, bounded_active
, withheld
);
230 void PermissionsUpdater::WithholdImpliedAllHosts(const Extension
* extension
) {
231 scoped_refptr
<const PermissionSet
> active
=
232 extension
->permissions_data()->active_permissions();
233 scoped_refptr
<const PermissionSet
> withheld
=
234 extension
->permissions_data()->withheld_permissions();
236 URLPatternSet withheld_scriptable
= withheld
->scriptable_hosts();
237 URLPatternSet active_scriptable
;
238 SegregateUrlPermissions(active
->scriptable_hosts(),
239 true, // withhold permissions
241 &withheld_scriptable
);
243 URLPatternSet withheld_explicit
= withheld
->explicit_hosts();
244 URLPatternSet active_explicit
;
245 SegregateUrlPermissions(active
->explicit_hosts(),
246 true, // withhold permissions
250 URLPatternSet delta_explicit
;
251 URLPatternSet::CreateDifference(
252 active
->explicit_hosts(), active_explicit
, &delta_explicit
);
253 URLPatternSet delta_scriptable
;
254 URLPatternSet::CreateDifference(
255 active
->scriptable_hosts(), active_scriptable
, &delta_scriptable
);
257 SetPermissions(extension
,
258 new PermissionSet(active
->apis(),
259 active
->manifest_permissions(),
262 new PermissionSet(withheld
->apis(),
263 withheld
->manifest_permissions(),
265 withheld_scriptable
));
267 scoped_refptr
<const PermissionSet
> delta(new PermissionSet(
269 ManifestPermissionSet(),
272 NotifyPermissionsUpdated(REMOVED
, extension
, delta
.get());
275 void PermissionsUpdater::GrantWithheldImpliedAllHosts(
276 const Extension
* extension
) {
277 scoped_refptr
<const PermissionSet
> active
=
278 extension
->permissions_data()->active_permissions();
279 scoped_refptr
<const PermissionSet
> withheld
=
280 extension
->permissions_data()->withheld_permissions();
282 // Move the all-hosts permission from withheld to active.
283 // We can cheat a bit here since we know that the only host permission we
284 // withhold is allhosts (or something similar enough to it), so we can just
285 // grant all withheld host permissions.
286 URLPatternSet explicit_hosts
;
287 URLPatternSet::CreateUnion(
288 active
->explicit_hosts(), withheld
->explicit_hosts(), &explicit_hosts
);
289 URLPatternSet scriptable_hosts
;
290 URLPatternSet::CreateUnion(active
->scriptable_hosts(),
291 withheld
->scriptable_hosts(),
294 URLPatternSet delta_explicit
;
295 URLPatternSet::CreateDifference(
296 explicit_hosts
, active
->explicit_hosts(), &delta_explicit
);
297 URLPatternSet delta_scriptable
;
298 URLPatternSet::CreateDifference(
299 scriptable_hosts
, active
->scriptable_hosts(), &delta_scriptable
);
301 // Since we only withhold host permissions (so far), we know that withheld
302 // permissions will be empty.
303 SetPermissions(extension
,
304 new PermissionSet(active
->apis(),
305 active
->manifest_permissions(),
308 new PermissionSet());
310 scoped_refptr
<const PermissionSet
> delta(new PermissionSet(
312 ManifestPermissionSet(),
315 NotifyPermissionsUpdated(ADDED
, extension
, delta
.get());
318 void PermissionsUpdater::SetPermissions(
319 const Extension
* extension
,
320 const scoped_refptr
<const PermissionSet
>& active
,
321 scoped_refptr
<const PermissionSet
> withheld
) {
322 withheld
= withheld
.get() ? withheld
323 : extension
->permissions_data()->withheld_permissions();
324 extension
->permissions_data()->SetPermissions(active
, withheld
);
325 if ((init_flag_
& INIT_FLAG_TRANSIENT
) == 0) {
326 ExtensionPrefs::Get(browser_context_
)
327 ->SetActivePermissions(extension
->id(), active
.get());
331 void PermissionsUpdater::DispatchEvent(
332 const std::string
& extension_id
,
333 const char* event_name
,
334 const PermissionSet
* changed_permissions
) {
335 EventRouter
* event_router
= EventRouter::Get(browser_context_
);
339 scoped_ptr
<base::ListValue
> value(new base::ListValue());
340 scoped_ptr
<api::permissions::Permissions
> permissions
=
341 PackPermissionSet(changed_permissions
);
342 value
->Append(permissions
->ToValue().release());
343 scoped_ptr
<Event
> event(new Event(event_name
, value
.Pass()));
344 event
->restrict_to_browser_context
= browser_context_
;
345 event_router
->DispatchEventToExtension(extension_id
, event
.Pass());
348 void PermissionsUpdater::NotifyPermissionsUpdated(
349 EventType event_type
,
350 const Extension
* extension
,
351 const PermissionSet
* changed
) {
352 DCHECK((init_flag_
& INIT_FLAG_TRANSIENT
) == 0);
353 if (!changed
|| changed
->IsEmpty())
356 UpdatedExtensionPermissionsInfo::Reason reason
;
357 const char* event_name
= NULL
;
359 if (event_type
== REMOVED
) {
360 reason
= UpdatedExtensionPermissionsInfo::REMOVED
;
361 event_name
= permissions::OnRemoved::kEventName
;
363 CHECK_EQ(ADDED
, event_type
);
364 reason
= UpdatedExtensionPermissionsInfo::ADDED
;
365 event_name
= permissions::OnAdded::kEventName
;
368 // Notify other APIs or interested parties.
369 UpdatedExtensionPermissionsInfo info
= UpdatedExtensionPermissionsInfo(
370 extension
, changed
, reason
);
371 Profile
* profile
= Profile::FromBrowserContext(browser_context_
);
372 content::NotificationService::current()->Notify(
373 extensions::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED
,
374 content::Source
<Profile
>(profile
),
375 content::Details
<UpdatedExtensionPermissionsInfo
>(&info
));
377 ExtensionMsg_UpdatePermissions_Params params
;
378 params
.extension_id
= extension
->id();
379 params
.active_permissions
= ExtensionMsg_PermissionSetStruct(
380 *extension
->permissions_data()->active_permissions());
381 params
.withheld_permissions
= ExtensionMsg_PermissionSetStruct(
382 *extension
->permissions_data()->withheld_permissions());
384 // Send the new permissions to the renderers.
385 for (RenderProcessHost::iterator
i(RenderProcessHost::AllHostsIterator());
386 !i
.IsAtEnd(); i
.Advance()) {
387 RenderProcessHost
* host
= i
.GetCurrentValue();
388 if (profile
->IsSameProfile(
389 Profile::FromBrowserContext(host
->GetBrowserContext()))) {
390 host
->Send(new ExtensionMsg_UpdatePermissions(params
));
394 // Trigger the onAdded and onRemoved events in the extension.
395 DispatchEvent(extension
->id(), event_name
, changed
);
398 } // namespace extensions