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/api/push_messaging/push_messaging_api.h"
10 #include "base/lazy_instance.h"
11 #include "base/logging.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/values.h"
14 #include "chrome/browser/extensions/api/push_messaging/push_messaging_invalidation_handler.h"
15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/extensions/token_cache/token_cache_service.h"
17 #include "chrome/browser/extensions/token_cache/token_cache_service_factory.h"
18 #include "chrome/browser/invalidation/profile_invalidation_provider_factory.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
21 #include "chrome/browser/signin/signin_manager_factory.h"
22 #include "chrome/common/extensions/api/push_messaging.h"
23 #include "components/invalidation/invalidation_service.h"
24 #include "components/invalidation/profile_invalidation_provider.h"
25 #include "components/signin/core/browser/profile_oauth2_token_service.h"
26 #include "components/signin/core/browser/signin_manager.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "extensions/browser/event_router.h"
29 #include "extensions/browser/extension_registry.h"
30 #include "extensions/browser/extension_registry_factory.h"
31 #include "extensions/browser/extension_system_provider.h"
32 #include "extensions/browser/extensions_browser_client.h"
33 #include "extensions/common/extension.h"
34 #include "extensions/common/permissions/api_permission.h"
35 #include "extensions/common/permissions/permissions_data.h"
36 #include "google_apis/gaia/gaia_constants.h"
37 #include "google_apis/gaia/identity_provider.h"
39 using content::BrowserThread;
41 namespace extensions {
44 const char kChannelIdSeparator[] = "/";
45 const char kUserNotSignedIn[] = "The user is not signed in.";
46 const char kUserAccessTokenFailure[] =
47 "Cannot obtain access token for the user.";
48 const char kAPINotAvailableForUser[] =
49 "The API is not available for this user.";
50 const int kObfuscatedGaiaIdTimeoutInDays = 30;
53 namespace glue = api::push_messaging;
55 PushMessagingEventRouter::PushMessagingEventRouter(
56 content::BrowserContext* context)
57 : browser_context_(context) {
60 PushMessagingEventRouter::~PushMessagingEventRouter() {}
62 void PushMessagingEventRouter::TriggerMessageForTest(
63 const std::string& extension_id,
65 const std::string& payload) {
66 OnMessage(extension_id, subchannel, payload);
69 void PushMessagingEventRouter::OnMessage(const std::string& extension_id,
71 const std::string& payload) {
72 glue::Message message;
73 message.subchannel_id = subchannel;
74 message.payload = payload;
76 DVLOG(2) << "PushMessagingEventRouter::OnMessage"
77 << " payload = '" << payload
78 << "' subchannel = '" << subchannel
79 << "' extension = '" << extension_id << "'";
81 scoped_ptr<base::ListValue> args(glue::OnMessage::Create(message));
82 scoped_ptr<Event> event(new Event(glue::OnMessage::kEventName, args.Pass()));
83 event->restrict_to_browser_context = browser_context_;
84 EventRouter::Get(browser_context_)
85 ->DispatchEventToExtension(extension_id, event.Pass());
88 // GetChannelId class functions
90 PushMessagingGetChannelIdFunction::PushMessagingGetChannelIdFunction()
91 : OAuth2TokenService::Consumer("push_messaging"),
92 interactive_(false) {}
94 PushMessagingGetChannelIdFunction::~PushMessagingGetChannelIdFunction() {}
96 bool PushMessagingGetChannelIdFunction::RunAsync() {
97 // Fetch the function arguments.
98 scoped_ptr<glue::GetChannelId::Params> params(
99 glue::GetChannelId::Params::Create(*args_));
100 EXTENSION_FUNCTION_VALIDATE(params.get());
102 if (params && params->interactive) {
103 interactive_ = *params->interactive;
106 // Balanced in ReportResult()
109 invalidation::ProfileInvalidationProvider* invalidation_provider =
110 invalidation::ProfileInvalidationProviderFactory::GetForProfile(
112 if (!invalidation_provider) {
113 error_ = kAPINotAvailableForUser;
114 ReportResult(std::string(), error_);
118 IdentityProvider* identity_provider =
119 invalidation_provider->GetInvalidationService()->GetIdentityProvider();
120 if (!identity_provider->GetTokenService()->RefreshTokenIsAvailable(
121 identity_provider->GetActiveAccountId())) {
122 if (interactive_ && identity_provider->RequestLogin()) {
123 identity_provider->AddActiveAccountRefreshTokenObserver(this);
126 error_ = kUserNotSignedIn;
127 ReportResult(std::string(), error_);
132 DVLOG(2) << "Logged in profile name: " << GetProfile()->GetProfileName();
134 StartAccessTokenFetch();
138 void PushMessagingGetChannelIdFunction::StartAccessTokenFetch() {
139 invalidation::ProfileInvalidationProvider* invalidation_provider =
140 invalidation::ProfileInvalidationProviderFactory::GetForProfile(
142 CHECK(invalidation_provider);
143 IdentityProvider* identity_provider =
144 invalidation_provider->GetInvalidationService()->GetIdentityProvider();
146 std::vector<std::string> scope_vector = ObfuscatedGaiaIdFetcher::GetScopes();
147 OAuth2TokenService::ScopeSet scopes(scope_vector.begin(), scope_vector.end());
148 fetcher_access_token_request_ =
149 identity_provider->GetTokenService()->StartRequest(
150 identity_provider->GetActiveAccountId(), scopes, this);
153 void PushMessagingGetChannelIdFunction::OnRefreshTokenAvailable(
154 const std::string& account_id) {
155 invalidation::ProfileInvalidationProvider* invalidation_provider =
156 invalidation::ProfileInvalidationProviderFactory::GetForProfile(
158 CHECK(invalidation_provider);
159 invalidation_provider->GetInvalidationService()->GetIdentityProvider()->
160 RemoveActiveAccountRefreshTokenObserver(this);
161 DVLOG(2) << "Newly logged in: " << GetProfile()->GetProfileName();
162 StartAccessTokenFetch();
165 void PushMessagingGetChannelIdFunction::OnGetTokenSuccess(
166 const OAuth2TokenService::Request* request,
167 const std::string& access_token,
168 const base::Time& expiration_time) {
169 DCHECK_EQ(fetcher_access_token_request_.get(), request);
170 fetcher_access_token_request_.reset();
172 StartGaiaIdFetch(access_token);
175 void PushMessagingGetChannelIdFunction::OnGetTokenFailure(
176 const OAuth2TokenService::Request* request,
177 const GoogleServiceAuthError& error) {
178 DCHECK_EQ(fetcher_access_token_request_.get(), request);
179 fetcher_access_token_request_.reset();
181 // TODO(fgorski): We are currently ignoring the error passed in upon failure.
182 // It should be revisited when we are working on improving general error
183 // handling for the identity related code.
184 DVLOG(1) << "Cannot obtain access token for this user "
185 << error.error_message() << " " << error.state();
186 error_ = kUserAccessTokenFailure;
187 ReportResult(std::string(), error_);
190 void PushMessagingGetChannelIdFunction::StartGaiaIdFetch(
191 const std::string& access_token) {
192 // Start the async fetch of the Gaia Id.
193 DCHECK_CURRENTLY_ON(BrowserThread::UI);
194 net::URLRequestContextGetter* context = GetProfile()->GetRequestContext();
195 fetcher_.reset(new ObfuscatedGaiaIdFetcher(context, this, access_token));
197 // Get the token cache and see if we have already cached a Gaia Id.
198 TokenCacheService* token_cache =
199 TokenCacheServiceFactory::GetForProfile(GetProfile());
201 // Check the cache, if we already have a Gaia ID, use it instead of
202 // fetching the ID over the network.
203 const std::string& gaia_id =
204 token_cache->RetrieveToken(GaiaConstants::kObfuscatedGaiaId);
205 if (!gaia_id.empty()) {
206 ReportResult(gaia_id, std::string());
213 void PushMessagingGetChannelIdFunction::ReportResult(
214 const std::string& gaia_id, const std::string& error_string) {
215 DCHECK_CURRENTLY_ON(BrowserThread::UI);
217 BuildAndSendResult(gaia_id, error_string);
219 // Cache the obfuscated ID locally. It never changes for this user,
220 // and if we call the web API too often, we get errors due to rate limiting.
221 if (!gaia_id.empty()) {
222 base::TimeDelta timeout =
223 base::TimeDelta::FromDays(kObfuscatedGaiaIdTimeoutInDays);
224 TokenCacheService* token_cache =
225 TokenCacheServiceFactory::GetForProfile(GetProfile());
226 token_cache->StoreToken(GaiaConstants::kObfuscatedGaiaId, gaia_id,
230 // Balanced in RunAsync.
234 void PushMessagingGetChannelIdFunction::BuildAndSendResult(
235 const std::string& gaia_id, const std::string& error_message) {
236 std::string channel_id;
237 if (!gaia_id.empty()) {
238 channel_id = gaia_id;
239 channel_id += kChannelIdSeparator;
240 channel_id += extension_id();
243 // TODO(petewil): It may be a good idea to further
244 // obfuscate the channel ID to prevent the user's obfuscated Gaia Id
245 // from being readily obtained. Security review will tell us if we need to.
247 // Create a ChannelId results object and set the fields.
248 glue::ChannelIdResult result;
249 result.channel_id = channel_id;
250 SetError(error_message);
251 results_ = glue::GetChannelId::Results::Create(result);
253 bool success = error_message.empty() && !gaia_id.empty();
254 SendResponse(success);
257 void PushMessagingGetChannelIdFunction::OnObfuscatedGaiaIdFetchSuccess(
258 const std::string& gaia_id) {
259 ReportResult(gaia_id, std::string());
262 void PushMessagingGetChannelIdFunction::OnObfuscatedGaiaIdFetchFailure(
263 const GoogleServiceAuthError& error) {
264 std::string error_text = error.error_message();
265 // If the error message is blank, see if we can set it from the state.
266 if (error_text.empty() &&
267 (0 != error.state())) {
268 error_text = base::IntToString(error.state());
271 DVLOG(1) << "GetChannelId status: '" << error_text << "'";
273 // If we had bad credentials, try the logon again.
274 switch (error.state()) {
275 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
276 case GoogleServiceAuthError::ACCOUNT_DELETED:
277 case GoogleServiceAuthError::ACCOUNT_DISABLED: {
278 invalidation::ProfileInvalidationProvider* invalidation_provider =
279 invalidation::ProfileInvalidationProviderFactory::GetForProfile(
281 CHECK(invalidation_provider);
282 if (!interactive_ || !invalidation_provider->GetInvalidationService()->
283 GetIdentityProvider()->RequestLogin()) {
284 ReportResult(std::string(), error_text);
289 // Return error to caller.
290 ReportResult(std::string(), error_text);
295 PushMessagingAPI::PushMessagingAPI(content::BrowserContext* context)
296 : extension_registry_observer_(this), browser_context_(context) {
297 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
300 PushMessagingAPI::~PushMessagingAPI() {
304 PushMessagingAPI* PushMessagingAPI::Get(content::BrowserContext* context) {
305 return BrowserContextKeyedAPIFactory<PushMessagingAPI>::Get(context);
308 void PushMessagingAPI::Shutdown() {
309 event_router_.reset();
313 static base::LazyInstance<BrowserContextKeyedAPIFactory<PushMessagingAPI> >
314 g_factory = LAZY_INSTANCE_INITIALIZER;
317 BrowserContextKeyedAPIFactory<PushMessagingAPI>*
318 PushMessagingAPI::GetFactoryInstance() {
319 return g_factory.Pointer();
322 bool PushMessagingAPI::InitEventRouterAndHandler() {
323 invalidation::ProfileInvalidationProvider* invalidation_provider =
324 invalidation::ProfileInvalidationProviderFactory::GetForProfile(
325 Profile::FromBrowserContext(browser_context_));
326 if (!invalidation_provider)
330 event_router_.reset(new PushMessagingEventRouter(browser_context_));
332 handler_.reset(new PushMessagingInvalidationHandler(
333 invalidation_provider->GetInvalidationService(),
334 event_router_.get()));
340 void PushMessagingAPI::OnExtensionLoaded(
341 content::BrowserContext* browser_context,
342 const Extension* extension) {
343 if (!InitEventRouterAndHandler())
346 if (extension->permissions_data()->HasAPIPermission(
347 APIPermission::kPushMessaging)) {
348 handler_->RegisterExtension(extension->id());
352 void PushMessagingAPI::OnExtensionUnloaded(
353 content::BrowserContext* browser_context,
354 const Extension* extension,
355 UnloadedExtensionInfo::Reason reason) {
356 if (!InitEventRouterAndHandler())
359 if (extension->permissions_data()->HasAPIPermission(
360 APIPermission::kPushMessaging)) {
361 handler_->UnregisterExtension(extension->id());
365 void PushMessagingAPI::OnExtensionWillBeInstalled(
366 content::BrowserContext* browser_context,
367 const Extension* extension,
370 const std::string& old_name) {
371 if (InitEventRouterAndHandler() &&
372 extension->permissions_data()->HasAPIPermission(
373 APIPermission::kPushMessaging)) {
374 handler_->SuppressInitialInvalidationsForExtension(extension->id());
378 void PushMessagingAPI::SetMapperForTest(
379 scoped_ptr<PushMessagingInvalidationMapper> mapper) {
380 handler_ = mapper.Pass();
385 BrowserContextKeyedAPIFactory<PushMessagingAPI>::DeclareFactoryDependencies() {
386 DependsOn(ExtensionRegistryFactory::GetInstance());
387 DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
388 DependsOn(invalidation::ProfileInvalidationProviderFactory::GetInstance());
391 } // namespace extensions