diff options
author | chron@chromium.org <chron@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-28 21:44:43 +0000 |
---|---|---|
committer | chron@chromium.org <chron@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-28 21:44:43 +0000 |
commit | 5de3c6834b77d8b3eeec7b5d7175a2ab246ea234 (patch) | |
tree | d07b8ff32badac7913311f67bf1354403fea0f35 /chrome/browser | |
parent | c9206275ca3f57888fcf9da96c647d8fcf67ff3f (diff) | |
download | chromium_src-5de3c6834b77d8b3eeec7b5d7175a2ab246ea234.zip chromium_src-5de3c6834b77d8b3eeec7b5d7175a2ab246ea234.tar.gz chromium_src-5de3c6834b77d8b3eeec7b5d7175a2ab246ea234.tar.bz2 |
The token service should now support issuing auth tokens.
The service will fetch tokens in the background and broadcast notifications after it's done.
Move some common constants out into a new file.
Modify TestNotificationTracker to support subclassing as it doesn't properly make a deep copy of notifications on the stack.
TEST=unit tests included
BUG=47093
Review URL: http://codereview.chromium.org/3024002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@54028 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/chromeos/login/google_authenticator.cc | 5 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/google_authenticator_unittest.cc | 1 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/login_utils.cc | 5 | ||||
-rw-r--r-- | chrome/browser/net/gaia/token_service.cc | 67 | ||||
-rw-r--r-- | chrome/browser/net/gaia/token_service.h | 87 | ||||
-rw-r--r-- | chrome/browser/net/gaia/token_service_unittest.cc | 172 | ||||
-rw-r--r-- | chrome/browser/sync/profile_sync_service_startup_unittest.cc | 5 |
7 files changed, 324 insertions, 18 deletions
diff --git a/chrome/browser/chromeos/login/google_authenticator.cc b/chrome/browser/chromeos/login/google_authenticator.cc index fe8de56..bd4c027 100644 --- a/chrome/browser/chromeos/login/google_authenticator.cc +++ b/chrome/browser/chromeos/login/google_authenticator.cc @@ -26,6 +26,7 @@ #include "chrome/browser/profile_manager.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/net/gaia/gaia_authenticator2.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/notification_service.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" @@ -79,7 +80,7 @@ void GoogleAuthenticator::CancelClientLogin() { void GoogleAuthenticator::TryClientLogin() { gaia_authenticator_->StartClientLogin(username_, password_, - GaiaAuthenticator2::kContactsService, + GaiaConstants::kContactsService, login_token_, login_captcha_); ChromeThread::PostDelayedTask( @@ -122,7 +123,7 @@ bool GoogleAuthenticator::AuthenticateToLogin( gaia_authenticator_.reset( new GaiaAuthenticator2(this, - GaiaAuthenticator2::kChromeOSSource, + GaiaConstants::kChromeOSSource, profile->GetRequestContext())); // Will be used for retries. PrepareClientLoginAttempt(password, login_token, login_captcha); diff --git a/chrome/browser/chromeos/login/google_authenticator_unittest.cc b/chrome/browser/chromeos/login/google_authenticator_unittest.cc index 4676ff0..9f372b6 100644 --- a/chrome/browser/chromeos/login/google_authenticator_unittest.cc +++ b/chrome/browser/chromeos/login/google_authenticator_unittest.cc @@ -56,6 +56,7 @@ class ExpectCanceledFetcher : public URLFetcher { public: ExpectCanceledFetcher(bool success, const GURL& url, + const std::string& results, URLFetcher::RequestType request_type, URLFetcher::Delegate* d) : URLFetcher(url, request_type, d) { diff --git a/chrome/browser/chromeos/login/login_utils.cc b/chrome/browser/chromeos/login/login_utils.cc index 6e80423..5b0b284 100644 --- a/chrome/browser/chromeos/login/login_utils.cc +++ b/chrome/browser/chromeos/login/login_utils.cc @@ -29,6 +29,7 @@ #include "chrome/common/logging_chrome.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/net/url_request_context_getter.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" @@ -153,7 +154,9 @@ void LoginUtilsImpl::CompleteLogin(const std::string& username, logging::DELETE_OLD_LOG_FILE); // Supply credentials for sync and others to use - profile->GetTokenService()->SetClientLoginResult(credentials); + profile->GetTokenService()->Initialize(GaiaConstants::kChromeOSSource, + profile->GetRequestContext(), + credentials); // Take the credentials passed in and try to exchange them for // full-fledged Google authentication cookies. This is diff --git a/chrome/browser/net/gaia/token_service.cc b/chrome/browser/net/gaia/token_service.cc index f514bf7..8420fb2 100644 --- a/chrome/browser/net/gaia/token_service.cc +++ b/chrome/browser/net/gaia/token_service.cc @@ -4,15 +4,76 @@ #include "chrome/browser/net/gaia/token_service.h" -void TokenService::SetClientLoginResult( +#include "base/string_util.h" +#include "chrome/common/net/gaia/gaia_authenticator2.h" +#include "chrome/common/net/gaia/gaia_constants.h" +#include "chrome/common/notification_service.h" + +void TokenService::Initialize( + const char* const source, + URLRequestContextGetter* getter, const GaiaAuthConsumer::ClientLoginResult& credentials) { + credentials_ = credentials; + source_ = std::string(source); + sync_token_fetcher_.reset(new GaiaAuthenticator2(this, source_, getter)); + talk_token_fetcher_.reset(new GaiaAuthenticator2(this, source_, getter)); +} + +const bool TokenService::AreCredentialsValid() const { + return !credentials_.lsid.empty() && !credentials_.sid.empty(); } -bool TokenService::HasLsid() { +const bool TokenService::HasLsid() const { return !credentials_.lsid.empty(); } -const std::string& TokenService::GetLsid() { +const std::string& TokenService::GetLsid() const { return credentials_.lsid; } + +void TokenService::StartFetchingTokens() { + DCHECK(AreCredentialsValid()); + sync_token_fetcher_->StartIssueAuthToken(credentials_.sid, + credentials_.lsid, + GaiaConstants::kSyncService); + talk_token_fetcher_->StartIssueAuthToken(credentials_.sid, + credentials_.lsid, + GaiaConstants::kTalkService); +} + +// Services dependent on a token will check if a token is available. +// If it isn't, they'll go to sleep until they get a token event. +const bool TokenService::HasTokenForService(const char* const service) const { + return token_map_.count(service) > 0; +} + +const std::string& TokenService::GetTokenForService( + const char* const service) const { + + if (token_map_.count(service) > 0) { + // map[key] is not const + return (*token_map_.find(service)).second; + } + return EmptyString(); +} + +void TokenService::OnIssueAuthTokenSuccess(const std::string& service, + const std::string& auth_token) { + LOG(INFO) << "Got an authorization token for " << service; + token_map_[service] = auth_token; + TokenAvailableDetails details(service, auth_token); + NotificationService::current()->Notify( + NotificationType::TOKEN_AVAILABLE, + Source<TokenService>(this), + Details<const TokenAvailableDetails>(&details)); +} +void TokenService::OnIssueAuthTokenFailure(const std::string& service, + const GaiaAuthError& error) { + LOG(WARNING) << "Auth token issuing failed for service:" << service; + TokenRequestFailedDetails details(service, error); + NotificationService::current()->Notify( + NotificationType::TOKEN_REQUEST_FAILED, + Source<TokenService>(this), + Details<const TokenRequestFailedDetails>(&details)); +} diff --git a/chrome/browser/net/gaia/token_service.h b/chrome/browser/net/gaia/token_service.h index 4705e69..bad80f5 100644 --- a/chrome/browser/net/gaia/token_service.h +++ b/chrome/browser/net/gaia/token_service.h @@ -3,30 +3,95 @@ // found in the LICENSE file. // // The TokenService will supply authentication tokens for any service that -// needs it. One such service is Sync. -// For the time being, the Token Service just supplies the LSID from the -// ChromiumOS login. In the future, it'll have a bit more logic and supply -// only AuthTokens from Gaia, and not LSIDs. +// needs it, such as sync. #ifndef CHROME_BROWSER_NET_GAIA_TOKEN_SERVICE_H_ #define CHROME_BROWSER_NET_GAIA_TOKEN_SERVICE_H_ #pragma once +#include <map> +#include <string> +#include "base/scoped_ptr.h" #include "chrome/common/net/gaia/gaia_auth_consumer.h" +#include "chrome/common/net/gaia/gaia_authenticator2.h" +#include "chrome/test/testing_profile.h" -class TokenService { +class URLRequestContextGetter; + +// The TokenService is a Profile member, so all calls are expected +// from the UI thread. +class TokenService : public GaiaAuthConsumer { public: - void SetClientLoginResult( - const GaiaAuthConsumer::ClientLoginResult& credentials); + TokenService() {} + + // Notification classes + class TokenAvailableDetails { + public: + TokenAvailableDetails() {} + TokenAvailableDetails(const std::string& service, + const std::string& token) + : service_(service), token_(token) {} + const std::string& service() const { return service_; } + const std::string& token() const { return token_; } + private: + std::string service_; + std::string token_; + }; + + class TokenRequestFailedDetails { + public: + TokenRequestFailedDetails() {} + TokenRequestFailedDetails(const std::string& service, + const GaiaAuthError& error) + : service_(service), error_(error) {} + const std::string& service() const { return service_; } + const GaiaAuthError& error() const { return error_; } + private: + std::string service_; + GaiaAuthError error_; + }; + + // Initialize this token service with a request source + // (usually from a GaiaAuthConsumer constant), a getter and + // results from ClientLogin. + void Initialize( + const char* const source, + URLRequestContextGetter* getter, + const GaiaAuthConsumer::ClientLoginResult& credentials); - bool HasLsid(); - const std::string& GetLsid(); + // For legacy services with their own auth routines, they can just read + // the LSID out directly. Deprecated. + const bool HasLsid() const; + const std::string& GetLsid() const; - // TODO(chron): Token broadcast will require removing a lot of auth code - // from sync. For the time being we'll start with LSID passing. + // On login, StartFetchingTokens() should be called to kick off token + // fetching. + // Tokens will be fetched for all services(sync, talk) in the background. + // Results come back via event channel. Services can also poll. + void StartFetchingTokens(); + const bool HasTokenForService(const char* const service) const; + const std::string& GetTokenForService(const char* const service) const; + + // Callbacks from the fetchers. + void OnIssueAuthTokenSuccess(const std::string& service, + const std::string& auth_token); + void OnIssueAuthTokenFailure(const std::string& service, + const GaiaAuthError& error); private: + // Did we get a proper LSID? + const bool AreCredentialsValid() const; + + // Gaia request source for Gaia accounting. + std::string source_; + // Credentials from ClientLogin for Issuing auth tokens. GaiaAuthConsumer::ClientLoginResult credentials_; + scoped_ptr<GaiaAuthenticator2> sync_token_fetcher_; + scoped_ptr<GaiaAuthenticator2> talk_token_fetcher_; + // Map from service to token. + std::map<std::string, std::string> token_map_; + + DISALLOW_COPY_AND_ASSIGN(TokenService); }; #endif // CHROME_BROWSER_NET_GAIA_TOKEN_SERVICE_H_ diff --git a/chrome/browser/net/gaia/token_service_unittest.cc b/chrome/browser/net/gaia/token_service_unittest.cc new file mode 100644 index 0000000..0bd7e56 --- /dev/null +++ b/chrome/browser/net/gaia/token_service_unittest.cc @@ -0,0 +1,172 @@ +// Copyright (c) 2010 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. +// +// This file defines a unit test for the profile's token service. + +#include "chrome/browser/net/gaia/token_service.h" +#include "chrome/common/net/gaia/gaia_authenticator2_unittest.h" +#include "chrome/common/net/gaia/gaia_auth_consumer.h" +#include "chrome/common/net/gaia/gaia_constants.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "chrome/test/test_notification_tracker.h" + +// TestNotificationTracker doesn't do a deep copy on the notification details. +// We have to in order to read it out, or we have a bad ptr, since the details +// are a reference on the stack. +class TokenAvailableTracker : public TestNotificationTracker { + public: + const TokenService::TokenAvailableDetails& get_last_token_details() { + return details_; + } + + private: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + TestNotificationTracker::Observe(type, source, details); + if (type == NotificationType::TOKEN_AVAILABLE) { + Details<const TokenService::TokenAvailableDetails> full = details; + details_ = *full.ptr(); + } + } + + TokenService::TokenAvailableDetails details_; +}; + +class TokenFailedTracker : public TestNotificationTracker { + public: + const TokenService::TokenRequestFailedDetails& get_last_token_details() { + return details_; + } + + private: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + TestNotificationTracker::Observe(type, source, details); + if (type == NotificationType::TOKEN_REQUEST_FAILED) { + Details<const TokenService::TokenRequestFailedDetails> full = details; + details_ = *full.ptr(); + } + } + + TokenService::TokenRequestFailedDetails details_; +}; + +class TokenServiceTest : public testing::Test { + public: + TokenServiceTest() { + credentials_.sid = "sid"; + credentials_.lsid = "lsid"; + credentials_.token = "token"; + credentials_.data = "data"; + + success_tracker_.ListenFor(NotificationType::TOKEN_AVAILABLE, + Source<TokenService>(&service_)); + failure_tracker_.ListenFor(NotificationType::TOKEN_REQUEST_FAILED, + Source<TokenService>(&service_)); + + service_.Initialize("test", profile_.GetRequestContext(), credentials_); + } + + TokenService service_; + TokenAvailableTracker success_tracker_; + TokenFailedTracker failure_tracker_; + GaiaAuthConsumer::ClientLoginResult credentials_; + + private: + TestingProfile profile_; +}; + +TEST_F(TokenServiceTest, SanityCheck) { + EXPECT_TRUE(service_.HasLsid()); + EXPECT_EQ(service_.GetLsid(), "lsid"); + EXPECT_FALSE(service_.HasTokenForService("nonexistant service")); +} + +TEST_F(TokenServiceTest, NoToken) { + EXPECT_FALSE(service_.HasTokenForService("nonexistant service")); + EXPECT_EQ(service_.GetTokenForService("nonexistant service"), std::string()); +} + +TEST_F(TokenServiceTest, NotificationSuccess) { + EXPECT_EQ(0U, success_tracker_.size()); + EXPECT_EQ(0U, failure_tracker_.size()); + service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "token"); + EXPECT_EQ(1U, success_tracker_.size()); + EXPECT_EQ(0U, failure_tracker_.size()); + + TokenService::TokenAvailableDetails details = + success_tracker_.get_last_token_details(); + // MSVC doesn't like this comparison as EQ + EXPECT_TRUE(details.service() == GaiaConstants::kSyncService); + EXPECT_EQ(details.token(), "token"); +} + +TEST_F(TokenServiceTest, NotificationFailed) { + EXPECT_EQ(0U, success_tracker_.size()); + EXPECT_EQ(0U, failure_tracker_.size()); + GaiaAuthConsumer::GaiaAuthError error; + error.code = GaiaAuthConsumer::REQUEST_CANCELED; + service_.OnIssueAuthTokenFailure(GaiaConstants::kSyncService, error); + EXPECT_EQ(0U, success_tracker_.size()); + EXPECT_EQ(1U, failure_tracker_.size()); + + TokenService::TokenRequestFailedDetails details = + failure_tracker_.get_last_token_details(); + + // MSVC doesn't like this comparison as EQ + EXPECT_TRUE(details.service() == GaiaConstants::kSyncService); + EXPECT_TRUE(details.error() == error); // Struct has no print function +} + +TEST_F(TokenServiceTest, OnTokenSuccessUpdate) { + service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "token"); + EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService)); + EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), "token"); + + service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "token2"); + EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService)); + EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), "token2"); + + service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, ""); + EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService)); + EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), ""); +} + +TEST_F(TokenServiceTest, OnTokenSuccess) { + // Don't "start fetching", just go ahead and issue the callback. + service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "token"); + EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService)); + EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kTalkService)); + // Gaia returns the entire result as the token so while this is a shared + // result with ClientLogin, it doesn't matter, we should still get it back. + EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), "token"); + + // Check 2nd service + service_.OnIssueAuthTokenSuccess(GaiaConstants::kTalkService, "token2"); + EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kTalkService)); + EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kTalkService), "token2"); + + // Didn't change + EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), "token"); +} + +TEST_F(TokenServiceTest, FullIntegration) { + MockFactory<MockFetcher> factory; + std::string result = "SID=sid\nLSID=lsid\nAuth=auth\n"; + factory.set_results(result); + URLFetcher::set_factory(&factory); + EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kSyncService)); + EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kTalkService)); + service_.StartFetchingTokens(); + URLFetcher::set_factory(NULL); + + EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService)); + EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kTalkService)); + // Gaia returns the entire result as the token so while this is a shared + // result with ClientLogin, it doesn't matter, we should still get it back. + EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), result); + EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kTalkService), result); +} diff --git a/chrome/browser/sync/profile_sync_service_startup_unittest.cc b/chrome/browser/sync/profile_sync_service_startup_unittest.cc index a9d7944..f49354d 100644 --- a/chrome/browser/sync/profile_sync_service_startup_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_startup_unittest.cc @@ -203,7 +203,10 @@ TEST_F(ProfileSyncServiceStartupBootstrapTest, SKIP_MACOSX(StartFirstTime)) { GaiaAuthConsumer::ClientLoginResult result; result.sid = "sid"; result.lsid = "lsid"; - profile_.GetTokenService()->SetClientLoginResult(result); + profile_.GetTokenService()->Initialize("test", + profile_.GetRequestContext(), + result); + // Will start sync even though setup hasn't been completed (since // setup is bypassed when bootstrapping is enabled). service_->Initialize(); |