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/api/push_messaging/push_messaging_api.h" 6 7 #include <set> 8 9 #include "base/bind.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" 38 39 using content::BrowserThread; 40 41 namespace extensions { 42 43 namespace { 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; 51 const char kDeprecationMessage[] = 52 "The chrome.pushMessaging API is deprecated. Use chrome.gcm API instead."; 53 } // namespace 54 55 namespace glue = api::push_messaging; 56 57 PushMessagingEventRouter::PushMessagingEventRouter( 58 content::BrowserContext* context) 59 : browser_context_(context) { 60 } 61 62 PushMessagingEventRouter::~PushMessagingEventRouter() {} 63 64 void PushMessagingEventRouter::TriggerMessageForTest( 65 const std::string& extension_id, 66 int subchannel, 67 const std::string& payload) { 68 OnMessage(extension_id, subchannel, payload); 69 } 70 71 void PushMessagingEventRouter::OnMessage(const std::string& extension_id, 72 int subchannel, 73 const std::string& payload) { 74 glue::Message message; 75 message.subchannel_id = subchannel; 76 message.payload = payload; 77 78 DVLOG(2) << "PushMessagingEventRouter::OnMessage" 79 << " payload = '" << payload 80 << "' subchannel = '" << subchannel 81 << "' extension = '" << extension_id << "'"; 82 83 scoped_ptr<base::ListValue> args(glue::OnMessage::Create(message)); 84 scoped_ptr<Event> event(new Event(glue::OnMessage::kEventName, args.Pass())); 85 event->restrict_to_browser_context = browser_context_; 86 EventRouter::Get(browser_context_) 87 ->DispatchEventToExtension(extension_id, event.Pass()); 88 } 89 90 // GetChannelId class functions 91 92 PushMessagingGetChannelIdFunction::PushMessagingGetChannelIdFunction() 93 : OAuth2TokenService::Consumer("push_messaging"), 94 interactive_(false) {} 95 96 PushMessagingGetChannelIdFunction::~PushMessagingGetChannelIdFunction() {} 97 98 bool PushMessagingGetChannelIdFunction::RunAsync() { 99 // Issue a deprecation message. 100 WriteToConsole(content::CONSOLE_MESSAGE_LEVEL_WARNING, kDeprecationMessage); 101 102 // Fetch the function arguments. 103 scoped_ptr<glue::GetChannelId::Params> params( 104 glue::GetChannelId::Params::Create(*args_)); 105 EXTENSION_FUNCTION_VALIDATE(params.get()); 106 107 if (params && params->interactive) { 108 interactive_ = *params->interactive; 109 } 110 111 // Balanced in ReportResult() 112 AddRef(); 113 114 invalidation::ProfileInvalidationProvider* invalidation_provider = 115 invalidation::ProfileInvalidationProviderFactory::GetForProfile( 116 GetProfile()); 117 if (!invalidation_provider) { 118 error_ = kAPINotAvailableForUser; 119 ReportResult(std::string(), error_); 120 return false; 121 } 122 123 IdentityProvider* identity_provider = 124 invalidation_provider->GetInvalidationService()->GetIdentityProvider(); 125 if (!identity_provider->GetTokenService()->RefreshTokenIsAvailable( 126 identity_provider->GetActiveAccountId())) { 127 if (interactive_ && identity_provider->RequestLogin()) { 128 identity_provider->AddActiveAccountRefreshTokenObserver(this); 129 return true; 130 } else { 131 error_ = kUserNotSignedIn; 132 ReportResult(std::string(), error_); 133 return false; 134 } 135 } 136 137 DVLOG(2) << "Logged in profile name: " << GetProfile()->GetProfileName(); 138 139 StartAccessTokenFetch(); 140 return true; 141 } 142 143 void PushMessagingGetChannelIdFunction::StartAccessTokenFetch() { 144 invalidation::ProfileInvalidationProvider* invalidation_provider = 145 invalidation::ProfileInvalidationProviderFactory::GetForProfile( 146 GetProfile()); 147 CHECK(invalidation_provider); 148 IdentityProvider* identity_provider = 149 invalidation_provider->GetInvalidationService()->GetIdentityProvider(); 150 151 std::vector<std::string> scope_vector = ObfuscatedGaiaIdFetcher::GetScopes(); 152 OAuth2TokenService::ScopeSet scopes(scope_vector.begin(), scope_vector.end()); 153 fetcher_access_token_request_ = 154 identity_provider->GetTokenService()->StartRequest( 155 identity_provider->GetActiveAccountId(), scopes, this); 156 } 157 158 void PushMessagingGetChannelIdFunction::OnRefreshTokenAvailable( 159 const std::string& account_id) { 160 invalidation::ProfileInvalidationProvider* invalidation_provider = 161 invalidation::ProfileInvalidationProviderFactory::GetForProfile( 162 GetProfile()); 163 CHECK(invalidation_provider); 164 invalidation_provider->GetInvalidationService()->GetIdentityProvider()-> 165 RemoveActiveAccountRefreshTokenObserver(this); 166 DVLOG(2) << "Newly logged in: " << GetProfile()->GetProfileName(); 167 StartAccessTokenFetch(); 168 } 169 170 void PushMessagingGetChannelIdFunction::OnGetTokenSuccess( 171 const OAuth2TokenService::Request* request, 172 const std::string& access_token, 173 const base::Time& expiration_time) { 174 DCHECK_EQ(fetcher_access_token_request_.get(), request); 175 fetcher_access_token_request_.reset(); 176 177 StartGaiaIdFetch(access_token); 178 } 179 180 void PushMessagingGetChannelIdFunction::OnGetTokenFailure( 181 const OAuth2TokenService::Request* request, 182 const GoogleServiceAuthError& error) { 183 DCHECK_EQ(fetcher_access_token_request_.get(), request); 184 fetcher_access_token_request_.reset(); 185 186 // TODO(fgorski): We are currently ignoring the error passed in upon failure. 187 // It should be revisited when we are working on improving general error 188 // handling for the identity related code. 189 DVLOG(1) << "Cannot obtain access token for this user " 190 << error.error_message() << " " << error.state(); 191 error_ = kUserAccessTokenFailure; 192 ReportResult(std::string(), error_); 193 } 194 195 void PushMessagingGetChannelIdFunction::StartGaiaIdFetch( 196 const std::string& access_token) { 197 // Start the async fetch of the Gaia Id. 198 DCHECK_CURRENTLY_ON(BrowserThread::UI); 199 net::URLRequestContextGetter* context = GetProfile()->GetRequestContext(); 200 fetcher_.reset(new ObfuscatedGaiaIdFetcher(context, this, access_token)); 201 202 // Get the token cache and see if we have already cached a Gaia Id. 203 TokenCacheService* token_cache = 204 TokenCacheServiceFactory::GetForProfile(GetProfile()); 205 206 // Check the cache, if we already have a Gaia ID, use it instead of 207 // fetching the ID over the network. 208 const std::string& gaia_id = 209 token_cache->RetrieveToken(GaiaConstants::kObfuscatedGaiaId); 210 if (!gaia_id.empty()) { 211 ReportResult(gaia_id, std::string()); 212 return; 213 } 214 215 fetcher_->Start(); 216 } 217 218 void PushMessagingGetChannelIdFunction::ReportResult( 219 const std::string& gaia_id, const std::string& error_string) { 220 DCHECK_CURRENTLY_ON(BrowserThread::UI); 221 222 BuildAndSendResult(gaia_id, error_string); 223 224 // Cache the obfuscated ID locally. It never changes for this user, 225 // and if we call the web API too often, we get errors due to rate limiting. 226 if (!gaia_id.empty()) { 227 base::TimeDelta timeout = 228 base::TimeDelta::FromDays(kObfuscatedGaiaIdTimeoutInDays); 229 TokenCacheService* token_cache = 230 TokenCacheServiceFactory::GetForProfile(GetProfile()); 231 token_cache->StoreToken(GaiaConstants::kObfuscatedGaiaId, gaia_id, 232 timeout); 233 } 234 235 // Balanced in RunAsync. 236 Release(); 237 } 238 239 void PushMessagingGetChannelIdFunction::BuildAndSendResult( 240 const std::string& gaia_id, const std::string& error_message) { 241 std::string channel_id; 242 if (!gaia_id.empty()) { 243 channel_id = gaia_id; 244 channel_id += kChannelIdSeparator; 245 channel_id += extension_id(); 246 } 247 248 // TODO(petewil): It may be a good idea to further 249 // obfuscate the channel ID to prevent the user's obfuscated Gaia Id 250 // from being readily obtained. Security review will tell us if we need to. 251 252 // Create a ChannelId results object and set the fields. 253 glue::ChannelIdResult result; 254 result.channel_id = channel_id; 255 SetError(error_message); 256 results_ = glue::GetChannelId::Results::Create(result); 257 258 bool success = error_message.empty() && !gaia_id.empty(); 259 SendResponse(success); 260 } 261 262 void PushMessagingGetChannelIdFunction::OnObfuscatedGaiaIdFetchSuccess( 263 const std::string& gaia_id) { 264 ReportResult(gaia_id, std::string()); 265 } 266 267 void PushMessagingGetChannelIdFunction::OnObfuscatedGaiaIdFetchFailure( 268 const GoogleServiceAuthError& error) { 269 std::string error_text = error.error_message(); 270 // If the error message is blank, see if we can set it from the state. 271 if (error_text.empty() && 272 (0 != error.state())) { 273 error_text = base::IntToString(error.state()); 274 } 275 276 DVLOG(1) << "GetChannelId status: '" << error_text << "'"; 277 278 // If we had bad credentials, try the logon again. 279 switch (error.state()) { 280 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: 281 case GoogleServiceAuthError::ACCOUNT_DELETED: 282 case GoogleServiceAuthError::ACCOUNT_DISABLED: { 283 invalidation::ProfileInvalidationProvider* invalidation_provider = 284 invalidation::ProfileInvalidationProviderFactory::GetForProfile( 285 GetProfile()); 286 CHECK(invalidation_provider); 287 if (!interactive_ || !invalidation_provider->GetInvalidationService()-> 288 GetIdentityProvider()->RequestLogin()) { 289 ReportResult(std::string(), error_text); 290 } 291 return; 292 } 293 default: 294 // Return error to caller. 295 ReportResult(std::string(), error_text); 296 return; 297 } 298 } 299 300 PushMessagingAPI::PushMessagingAPI(content::BrowserContext* context) 301 : extension_registry_observer_(this), browser_context_(context) { 302 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); 303 } 304 305 PushMessagingAPI::~PushMessagingAPI() { 306 } 307 308 // static 309 PushMessagingAPI* PushMessagingAPI::Get(content::BrowserContext* context) { 310 return BrowserContextKeyedAPIFactory<PushMessagingAPI>::Get(context); 311 } 312 313 void PushMessagingAPI::Shutdown() { 314 event_router_.reset(); 315 handler_.reset(); 316 } 317 318 static base::LazyInstance<BrowserContextKeyedAPIFactory<PushMessagingAPI> > 319 g_factory = LAZY_INSTANCE_INITIALIZER; 320 321 // static 322 BrowserContextKeyedAPIFactory<PushMessagingAPI>* 323 PushMessagingAPI::GetFactoryInstance() { 324 return g_factory.Pointer(); 325 } 326 327 bool PushMessagingAPI::InitEventRouterAndHandler() { 328 invalidation::ProfileInvalidationProvider* invalidation_provider = 329 invalidation::ProfileInvalidationProviderFactory::GetForProfile( 330 Profile::FromBrowserContext(browser_context_)); 331 if (!invalidation_provider) 332 return false; 333 334 if (!event_router_) 335 event_router_.reset(new PushMessagingEventRouter(browser_context_)); 336 if (!handler_) { 337 handler_.reset(new PushMessagingInvalidationHandler( 338 invalidation_provider->GetInvalidationService(), 339 event_router_.get())); 340 } 341 342 return true; 343 } 344 345 void PushMessagingAPI::OnExtensionLoaded( 346 content::BrowserContext* browser_context, 347 const Extension* extension) { 348 if (!InitEventRouterAndHandler()) 349 return; 350 351 if (extension->permissions_data()->HasAPIPermission( 352 APIPermission::kPushMessaging)) { 353 handler_->RegisterExtension(extension->id()); 354 } 355 } 356 357 void PushMessagingAPI::OnExtensionUnloaded( 358 content::BrowserContext* browser_context, 359 const Extension* extension, 360 UnloadedExtensionInfo::Reason reason) { 361 if (!InitEventRouterAndHandler()) 362 return; 363 364 if (extension->permissions_data()->HasAPIPermission( 365 APIPermission::kPushMessaging)) { 366 handler_->UnregisterExtension(extension->id()); 367 } 368 } 369 370 void PushMessagingAPI::OnExtensionWillBeInstalled( 371 content::BrowserContext* browser_context, 372 const Extension* extension, 373 bool is_update, 374 bool from_ephemeral, 375 const std::string& old_name) { 376 if (InitEventRouterAndHandler() && 377 extension->permissions_data()->HasAPIPermission( 378 APIPermission::kPushMessaging)) { 379 handler_->SuppressInitialInvalidationsForExtension(extension->id()); 380 } 381 } 382 383 void PushMessagingAPI::SetMapperForTest( 384 scoped_ptr<PushMessagingInvalidationMapper> mapper) { 385 handler_ = mapper.Pass(); 386 } 387 388 template <> 389 void 390 BrowserContextKeyedAPIFactory<PushMessagingAPI>::DeclareFactoryDependencies() { 391 DependsOn(ExtensionRegistryFactory::GetInstance()); 392 DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory()); 393 DependsOn(invalidation::ProfileInvalidationProviderFactory::GetInstance()); 394 } 395 396 } // namespace extensions 397