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.
4
5#include "chrome/browser/extensions/permissions_updater.h"
6
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/profiles/profile.h"
13#include "chrome/common/extensions/api/permissions.h"
14#include "content/public/browser/notification_observer.h"
15#include "content/public/browser/notification_registrar.h"
16#include "content/public/browser/notification_service.h"
17#include "content/public/browser/render_process_host.h"
18#include "extensions/browser/event_router.h"
19#include "extensions/browser/extension_prefs.h"
20#include "extensions/common/extension.h"
21#include "extensions/common/extension_messages.h"
22#include "extensions/common/manifest_handlers/permissions_parser.h"
23#include "extensions/common/permissions/permission_set.h"
24#include "extensions/common/permissions/permissions_data.h"
25
26using content::RenderProcessHost;
27using extensions::permissions_api_helpers::PackPermissionSet;
28
29namespace extensions {
30
31namespace permissions = api::permissions;
32
33PermissionsUpdater::PermissionsUpdater(content::BrowserContext* browser_context)
34    : browser_context_(browser_context) {
35}
36
37PermissionsUpdater::~PermissionsUpdater() {}
38
39void PermissionsUpdater::AddPermissions(
40    const Extension* extension, const PermissionSet* permissions) {
41  scoped_refptr<const PermissionSet> existing(
42      extension->permissions_data()->active_permissions());
43  scoped_refptr<PermissionSet> total(
44      PermissionSet::CreateUnion(existing.get(), permissions));
45  scoped_refptr<PermissionSet> added(
46      PermissionSet::CreateDifference(total.get(), existing.get()));
47
48  SetActivePermissions(extension, total.get());
49
50  // Update the granted permissions so we don't auto-disable the extension.
51  GrantActivePermissions(extension);
52
53  NotifyPermissionsUpdated(ADDED, extension, added.get());
54}
55
56void PermissionsUpdater::RemovePermissions(
57    const Extension* extension, const PermissionSet* permissions) {
58  scoped_refptr<const PermissionSet> existing(
59      extension->permissions_data()->active_permissions());
60  scoped_refptr<PermissionSet> total(
61      PermissionSet::CreateDifference(existing.get(), permissions));
62  scoped_refptr<PermissionSet> removed(
63      PermissionSet::CreateDifference(existing.get(), total.get()));
64
65  // We update the active permissions, and not the granted permissions, because
66  // the extension, not the user, removed the permissions. This allows the
67  // extension to add them again without prompting the user.
68  SetActivePermissions(extension, total.get());
69
70  NotifyPermissionsUpdated(REMOVED, extension, removed.get());
71}
72
73void PermissionsUpdater::GrantActivePermissions(const Extension* extension) {
74  CHECK(extension);
75
76  // We only maintain the granted permissions prefs for INTERNAL and LOAD
77  // extensions.
78  if (!Manifest::IsUnpackedLocation(extension->location()) &&
79      extension->location() != Manifest::INTERNAL)
80    return;
81
82  ExtensionPrefs::Get(browser_context_)->AddGrantedPermissions(
83      extension->id(),
84      extension->permissions_data()->active_permissions().get());
85}
86
87void PermissionsUpdater::InitializeActivePermissions(
88    const Extension* extension) {
89  // If the extension has used the optional permissions API, it will have a
90  // custom set of active permissions defined in the extension prefs. Here,
91  // we update the extension's active permissions based on the prefs.
92  scoped_refptr<PermissionSet> active_permissions =
93      ExtensionPrefs::Get(browser_context_)->GetActivePermissions(
94          extension->id());
95  if (!active_permissions)
96    return;
97
98  // We restrict the active permissions to be within the bounds defined in the
99  // extension's manifest.
100  //  a) active permissions must be a subset of optional + default permissions
101  //  b) active permissions must contains all default permissions
102  scoped_refptr<PermissionSet> total_permissions = PermissionSet::CreateUnion(
103      PermissionsParser::GetRequiredPermissions(extension),
104      PermissionsParser::GetOptionalPermissions(extension));
105
106  // Make sure the active permissions contain no more than optional + default.
107  scoped_refptr<PermissionSet> adjusted_active =
108      PermissionSet::CreateIntersection(total_permissions, active_permissions);
109
110  // Make sure the active permissions contain the default permissions.
111  adjusted_active = PermissionSet::CreateUnion(
112      PermissionsParser::GetRequiredPermissions(extension),
113      adjusted_active);
114
115  SetActivePermissions(extension, adjusted_active);
116}
117
118void PermissionsUpdater::SetActivePermissions(
119    const Extension* extension, const PermissionSet* permissions) {
120  ExtensionPrefs::Get(browser_context_)->SetActivePermissions(
121      extension->id(), permissions);
122  extension->permissions_data()->SetActivePermissions(permissions);
123}
124
125void PermissionsUpdater::DispatchEvent(
126    const std::string& extension_id,
127    const char* event_name,
128    const PermissionSet* changed_permissions) {
129  EventRouter* event_router = EventRouter::Get(browser_context_);
130  if (!event_router)
131    return;
132
133  scoped_ptr<base::ListValue> value(new base::ListValue());
134  scoped_ptr<api::permissions::Permissions> permissions =
135      PackPermissionSet(changed_permissions);
136  value->Append(permissions->ToValue().release());
137  scoped_ptr<Event> event(new Event(event_name, value.Pass()));
138  event->restrict_to_browser_context = browser_context_;
139  event_router->DispatchEventToExtension(extension_id, event.Pass());
140}
141
142void PermissionsUpdater::NotifyPermissionsUpdated(
143    EventType event_type,
144    const Extension* extension,
145    const PermissionSet* changed) {
146  if (!changed || changed->IsEmpty())
147    return;
148
149  UpdatedExtensionPermissionsInfo::Reason reason;
150  const char* event_name = NULL;
151
152  if (event_type == REMOVED) {
153    reason = UpdatedExtensionPermissionsInfo::REMOVED;
154    event_name = permissions::OnRemoved::kEventName;
155  } else {
156    CHECK_EQ(ADDED, event_type);
157    reason = UpdatedExtensionPermissionsInfo::ADDED;
158    event_name = permissions::OnAdded::kEventName;
159  }
160
161  // Notify other APIs or interested parties.
162  UpdatedExtensionPermissionsInfo info = UpdatedExtensionPermissionsInfo(
163      extension, changed, reason);
164  Profile* profile = Profile::FromBrowserContext(browser_context_);
165  content::NotificationService::current()->Notify(
166      chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
167      content::Source<Profile>(profile),
168      content::Details<UpdatedExtensionPermissionsInfo>(&info));
169
170  ExtensionMsg_UpdatePermissions_Params params;
171  params.reason_id = static_cast<int>(reason);
172  params.extension_id = extension->id();
173  params.apis = changed->apis();
174  params.manifest_permissions = changed->manifest_permissions();
175  params.explicit_hosts = changed->explicit_hosts();
176  params.scriptable_hosts = changed->scriptable_hosts();
177
178  // Send the new permissions to the renderers.
179  for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
180       !i.IsAtEnd(); i.Advance()) {
181    RenderProcessHost* host = i.GetCurrentValue();
182    if (profile->IsSameProfile(
183            Profile::FromBrowserContext(host->GetBrowserContext()))) {
184      host->Send(new ExtensionMsg_UpdatePermissions(params));
185    }
186  }
187
188  // Trigger the onAdded and onRemoved events in the extension.
189  DispatchEvent(extension->id(), event_name, changed);
190}
191
192}  // namespace extensions
193