blob: dd59de8287758a3238db4b5d42aed16b6bdf7973 [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
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"
[email protected]76aeb1772012-01-20 22:14:1610#include "chrome/browser/extensions/api/permissions/permissions_api_helpers.h"
[email protected]5a38dfd2012-07-23 23:22:1011#include "chrome/browser/extensions/event_router.h"
[email protected]c333e792012-01-06 16:57:3912#include "chrome/browser/extensions/extension_prefs.h"
13#include "chrome/browser/extensions/extension_service.h"
14#include "chrome/browser/profiles/profile.h"
[email protected]b70a2d92012-06-28 19:51:2115#include "chrome/browser/signin/token_service.h"
16#include "chrome/browser/signin/token_service_factory.h"
[email protected]c333e792012-01-06 16:57:3917#include "chrome/common/chrome_notification_types.h"
[email protected]6386cf52012-09-07 04:26:3718#include "chrome/common/extensions/api/permissions.h"
[email protected]c333e792012-01-06 16:57:3919#include "chrome/common/extensions/extension.h"
20#include "chrome/common/extensions/extension_messages.h"
[email protected]c97496b2012-10-01 16:20:1921#include "content/public/browser/notification_observer.h"
22#include "content/public/browser/notification_registrar.h"
[email protected]c333e792012-01-06 16:57:3923#include "content/public/browser/notification_service.h"
24#include "content/public/browser/render_process_host.h"
[email protected]6386cf52012-09-07 04:26:3725#include "google_apis/gaia/oauth2_mint_token_flow.h"
[email protected]c333e792012-01-06 16:57:3926
27using content::RenderProcessHost;
[email protected]15f08dd2012-01-27 07:29:4828using extensions::permissions_api_helpers::PackPermissionSet;
[email protected]b70a2d92012-06-28 19:51:2129using extensions::PermissionSet;
[email protected]c333e792012-01-06 16:57:3930
31namespace extensions {
32
33namespace {
34
35const char kOnAdded[] = "permissions.onAdded";
36const char kOnRemoved[] = "permissions.onRemoved";
37
[email protected]c97496b2012-10-01 16:20:1938// An object to link the lifetime of an OAuth2MintTokenFlow to a Profile.
39// The flow should not outlive the profile because the request context will
40// become invalid.
41class OAuth2GrantRecorder : public OAuth2MintTokenFlow::Delegate,
42 public content::NotificationObserver {
43 public:
44 OAuth2GrantRecorder(Profile* profile, const Extension* extension)
45 : ALLOW_THIS_IN_INITIALIZER_LIST(flow_(
46 profile->GetRequestContext(),
47 this,
48 OAuth2MintTokenFlow::Parameters(
49 TokenServiceFactory::GetForProfile(profile)->
50 GetOAuth2LoginRefreshToken(),
51 extension->id(),
52 extension->oauth2_info().client_id,
53 extension->oauth2_info().scopes,
54 OAuth2MintTokenFlow::MODE_RECORD_GRANT))) {
55 notification_registrar_.Add(this,
56 chrome::NOTIFICATION_PROFILE_DESTROYED,
57 content::Source<Profile>(profile));
58
59 flow_.Start();
60 }
61
62 // content::NotificationObserver:
63 void Observe(int type,
64 const content::NotificationSource& source,
65 const content::NotificationDetails& details) OVERRIDE {
66 DCHECK_EQ(type, chrome::NOTIFICATION_PROFILE_DESTROYED);
67 delete this;
68 }
69
70 // OAuth2MintTokenFlow::Delegate:
71 virtual void OnMintTokenSuccess(const std::string& access_token) OVERRIDE {
72 delete this;
73 }
74 virtual void OnIssueAdviceSuccess(
75 const IssueAdviceInfo& issue_advice) OVERRIDE {
76 delete this;
77 }
78 virtual void OnMintTokenFailure(
79 const GoogleServiceAuthError& error) OVERRIDE {
80 delete this;
81 }
82
83 private:
84 virtual ~OAuth2GrantRecorder() {}
85
86 OAuth2MintTokenFlow flow_;
87 content::NotificationRegistrar notification_registrar_;
88};
89
90} // namespace
[email protected]c333e792012-01-06 16:57:3991
92PermissionsUpdater::PermissionsUpdater(Profile* profile)
93 : profile_(profile) {}
94
95PermissionsUpdater::~PermissionsUpdater() {}
96
97void PermissionsUpdater::AddPermissions(
[email protected]c2e66e12012-06-27 06:27:0698 const Extension* extension, const PermissionSet* permissions) {
99 scoped_refptr<const PermissionSet> existing(
[email protected]c333e792012-01-06 16:57:39100 extension->GetActivePermissions());
[email protected]c2e66e12012-06-27 06:27:06101 scoped_refptr<PermissionSet> total(
102 PermissionSet::CreateUnion(existing, permissions));
103 scoped_refptr<PermissionSet> added(
104 PermissionSet::CreateDifference(total.get(), existing));
[email protected]c333e792012-01-06 16:57:39105
106 UpdateActivePermissions(extension, total.get());
107
108 // Update the granted permissions so we don't auto-disable the extension.
[email protected]b70a2d92012-06-28 19:51:21109 GrantActivePermissions(extension, false);
[email protected]c333e792012-01-06 16:57:39110
111 NotifyPermissionsUpdated(ADDED, extension, added.get());
112}
113
114void PermissionsUpdater::RemovePermissions(
[email protected]c2e66e12012-06-27 06:27:06115 const Extension* extension, const PermissionSet* permissions) {
116 scoped_refptr<const PermissionSet> existing(
[email protected]c333e792012-01-06 16:57:39117 extension->GetActivePermissions());
[email protected]c2e66e12012-06-27 06:27:06118 scoped_refptr<PermissionSet> total(
119 PermissionSet::CreateDifference(existing, permissions));
120 scoped_refptr<PermissionSet> removed(
121 PermissionSet::CreateDifference(existing, total.get()));
[email protected]c333e792012-01-06 16:57:39122
123 // We update the active permissions, and not the granted permissions, because
124 // the extension, not the user, removed the permissions. This allows the
125 // extension to add them again without prompting the user.
126 UpdateActivePermissions(extension, total.get());
127
128 NotifyPermissionsUpdated(REMOVED, extension, removed.get());
129}
130
[email protected]b70a2d92012-06-28 19:51:21131void PermissionsUpdater::GrantActivePermissions(const Extension* extension,
132 bool record_oauth2_grant) {
[email protected]c333e792012-01-06 16:57:39133 CHECK(extension);
134
[email protected]c24fb292012-02-01 22:52:11135 // We only maintain the granted permissions prefs for INTERNAL and LOAD
136 // extensions.
137 if (extension->location() != Extension::LOAD &&
138 extension->location() != Extension::INTERNAL)
[email protected]c333e792012-01-06 16:57:39139 return;
140
[email protected]c856e9762012-10-12 19:42:38141 if (record_oauth2_grant) {
142 // Only record OAuth grant if:
143 // 1. The extension has client id and scopes.
144 // 2. The user is signed in to Chrome.
145 const Extension::OAuth2Info& oauth2_info = extension->oauth2_info();
146 if (!oauth2_info.client_id.empty() && !oauth2_info.scopes.empty()) {
147 TokenService* token_service = TokenServiceFactory::GetForProfile(
148 profile_);
149 if (token_service && token_service->HasOAuthLoginToken()) {
150 new OAuth2GrantRecorder(profile_, extension);
151 }
152 }
153 }
[email protected]b70a2d92012-06-28 19:51:21154
[email protected]d4a37f1c2012-07-09 21:36:13155 GetExtensionPrefs()->AddGrantedPermissions(extension->id(),
156 extension->GetActivePermissions());
[email protected]f746b3f2012-07-03 17:53:37157}
158
[email protected]c333e792012-01-06 16:57:39159void PermissionsUpdater::UpdateActivePermissions(
[email protected]c2e66e12012-06-27 06:27:06160 const Extension* extension, const PermissionSet* permissions) {
[email protected]c333e792012-01-06 16:57:39161 GetExtensionPrefs()->SetActivePermissions(extension->id(), permissions);
162 extension->SetActivePermissions(permissions);
163}
164
165void PermissionsUpdater::DispatchEvent(
166 const std::string& extension_id,
167 const char* event_name,
[email protected]c2e66e12012-06-27 06:27:06168 const PermissionSet* changed_permissions) {
[email protected]c333e792012-01-06 16:57:39169 if (!profile_ || !profile_->GetExtensionEventRouter())
170 return;
171
[email protected]c9bd90f2012-08-07 23:58:15172 scoped_ptr<ListValue> value(new ListValue());
[email protected]15f08dd2012-01-27 07:29:48173 scoped_ptr<api::permissions::Permissions> permissions =
174 PackPermissionSet(changed_permissions);
[email protected]c9bd90f2012-08-07 23:58:15175 value->Append(permissions->ToValue().release());
[email protected]c333e792012-01-06 16:57:39176 profile_->GetExtensionEventRouter()->DispatchEventToExtension(
[email protected]c9bd90f2012-08-07 23:58:15177 extension_id, event_name, value.Pass(), profile_, GURL());
[email protected]c333e792012-01-06 16:57:39178}
179
180void PermissionsUpdater::NotifyPermissionsUpdated(
181 EventType event_type,
182 const Extension* extension,
[email protected]c2e66e12012-06-27 06:27:06183 const PermissionSet* changed) {
[email protected]c333e792012-01-06 16:57:39184 if (!changed || changed->IsEmpty())
185 return;
186
187 UpdatedExtensionPermissionsInfo::Reason reason;
188 const char* event_name = NULL;
189
190 if (event_type == REMOVED) {
191 reason = UpdatedExtensionPermissionsInfo::REMOVED;
192 event_name = kOnRemoved;
193 } else {
194 CHECK_EQ(ADDED, event_type);
195 reason = UpdatedExtensionPermissionsInfo::ADDED;
196 event_name = kOnAdded;
197 }
198
199 // Notify other APIs or interested parties.
200 UpdatedExtensionPermissionsInfo info = UpdatedExtensionPermissionsInfo(
201 extension, changed, reason);
202 content::NotificationService::current()->Notify(
203 chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
204 content::Source<Profile>(profile_),
205 content::Details<UpdatedExtensionPermissionsInfo>(&info));
206
207 // Send the new permissions to the renderers.
208 for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
209 !i.IsAtEnd(); i.Advance()) {
210 RenderProcessHost* host = i.GetCurrentValue();
211 Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
212 if (profile_->IsSameProfile(profile))
213 host->Send(new ExtensionMsg_UpdatePermissions(
214 static_cast<int>(reason),
215 extension->id(),
216 changed->apis(),
217 changed->explicit_hosts(),
218 changed->scriptable_hosts()));
219 }
220
221 // Trigger the onAdded and onRemoved events in the extension.
222 DispatchEvent(extension->id(), event_name, changed);
223}
224
225ExtensionPrefs* PermissionsUpdater::GetExtensionPrefs() {
226 return profile_->GetExtensionService()->extension_prefs();
227}
228
229} // namespace extensions