// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CHROME_BROWSER_EXTENSIONS_APP_NOTIFY_CHANNEL_SETUP_H_ #define CHROME_BROWSER_EXTENSIONS_APP_NOTIFY_CHANNEL_SETUP_H_ #include #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/extensions/app_notify_channel_ui.h" #include "google_apis/gaia/oauth2_access_token_consumer.h" #include "google_apis/gaia/oauth2_access_token_fetcher.h" #include "googleurl/src/gurl.h" #include "net/url_request/url_fetcher.h" #include "net/url_request/url_fetcher_delegate.h" class Profile; namespace extensions { class AppNotifyChannelSetupTest; // This class uses the browser login credentials to setup app notifications // for a given app. // // Performs the following steps when Start() is called: // 1. If the user is not logged in, prompt the user to login. // 2. OAuth2: Record a notifications grant for the user and the given app. // 3. Get notifications channel id for the current user. // 4. Call the delegate passed in to the constructor with the results of // the above steps. class AppNotifyChannelSetup : public net::URLFetcherDelegate, public AppNotifyChannelUI::Delegate, public OAuth2AccessTokenConsumer, public base::RefCountedThreadSafe { public: // These are the various error conditions, made public for use by the test // interceptor. enum SetupError { NONE, AUTH_ERROR, INTERNAL_ERROR, USER_CANCELLED, // This is used for histograms, and should always be the last value. SETUP_ERROR_BOUNDARY }; class Delegate { public: // If successful, |channel_id| will be non-empty. On failure, |channel_id| // will be empty and |error| will contain an error to report to the JS // callback. virtual void AppNotifyChannelSetupComplete( const std::string& channel_id, const std::string& error, const AppNotifyChannelSetup* setup) = 0; }; // For tests, we allow intercepting the request to setup the channel and // forcing the return of a certain result to the delegate. class InterceptorForTests { public: virtual void DoIntercept( const AppNotifyChannelSetup* setup, std::string* result_channel_id, AppNotifyChannelSetup::SetupError* result_error) = 0; }; static void SetInterceptorForTests(InterceptorForTests* interceptor); // Ownership of |ui| is transferred to this object. AppNotifyChannelSetup(Profile* profile, const std::string& extension_id, const std::string& client_id, const GURL& requestor_url, int return_route_id, int callback_id, AppNotifyChannelUI* ui, base::WeakPtr delegate); AppNotifyChannelUI* ui() { return ui_.get(); } // This begins the process of fetching the channel id using the browser login // credentials (or using |ui_| to prompt for login if needed). void Start(); // OAuth2AccessTokenConsumer implementation. virtual void OnGetTokenSuccess(const std::string& access_token, const base::Time& expiration_time) OVERRIDE; virtual void OnGetTokenFailure(const GoogleServiceAuthError& error) OVERRIDE; // Getters for various members. const std::string& extension_id() const { return extension_id_; } const std::string& client_id() const { return client_id_; } int return_route_id() const { return return_route_id_; } int callback_id() const { return callback_id_; } protected: // net::URLFetcherDelegate. virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; // AppNotifyChannelUI::Delegate. virtual void OnSyncSetupResult(bool enabled) OVERRIDE; private: enum State { INITIAL, LOGIN_STARTED, LOGIN_DONE, FETCH_ACCESS_TOKEN_STARTED, FETCH_ACCESS_TOKEN_DONE, RECORD_GRANT_STARTED, RECORD_GRANT_DONE, CHANNEL_ID_SETUP_STARTED, CHANNEL_ID_SETUP_DONE, ERROR_STATE }; friend class base::RefCountedThreadSafe; friend class AppNotifyChannelSetupTest; virtual ~AppNotifyChannelSetup(); // Creates an instance of URLFetcher that does not send or save cookies. // The URLFether's method will be GET if body is empty, POST otherwise. // Caller owns the returned instance. net::URLFetcher* CreateURLFetcher( const GURL& url, const std::string& body, const std::string& auth_token); void BeginLogin(); void EndLogin(bool success); void BeginGetAccessToken(); void EndGetAccessToken(bool success); void BeginRecordGrant(); void EndRecordGrant(const net::URLFetcher* source); void BeginGetChannelId(); void EndGetChannelId(const net::URLFetcher* source); void ReportResult(const std::string& channel_id, SetupError error); static std::string GetErrorString(SetupError error); static GURL GetCWSChannelServiceURL(); static GURL GetOAuth2IssueTokenURL(); static std::string MakeOAuth2IssueTokenBody( const std::string& oauth_client_id, const std::string& extension_id); static std::string MakeAuthorizationHeader(const std::string& auth_token); static bool ParseCWSChannelServiceResponse( const std::string& data, std::string* result); // Checks if the user needs to be prompted for login. bool ShouldPromptForLogin() const; void RegisterForTokenServiceNotifications(); void UnregisterForTokenServiceNotifications(); Profile* profile_; std::string extension_id_; std::string client_id_; GURL requestor_url_; int return_route_id_; int callback_id_; base::WeakPtr delegate_; scoped_ptr url_fetcher_; scoped_ptr oauth2_fetcher_; scoped_ptr ui_; State state_; std::string oauth2_access_token_; // Keeps track of whether we have encountered failure in OAuth2 access // token generation already. We use this to prevent us from doing an // infinite loop of trying to generate access token, if that fails, try // to login the user and generate access token, etc. bool oauth2_access_token_failure_; DISALLOW_COPY_AND_ASSIGN(AppNotifyChannelSetup); }; } // namespace extensions #endif // CHROME_BROWSER_EXTENSIONS_APP_NOTIFY_CHANNEL_SETUP_H_