Commit cb05af1b authored by Colin Blundell's avatar Colin Blundell Committed by Commit Bot

IdentityManager: Add APIs for querying state of refresh tokens

This CL adds APIs to IdentityManager for obtaining the set of all
accounts with refresh tokens and querying whether the primary account
is available with a refresh token.

The design follows that of IdentityManager's caching of the primary
account information:
- IdentityManager initializes its state with the current state of
  ProfileOAuth2TokenService.
- IdentityManager updates this state in response to notifications from
  PO2TS that an account's refresh token was updated/removed.

TBR=bsazonov@chromium.org

Bug: 806774
Change-Id: Idc4a37a15ced2e7d32f012a50e4e0d1fdb1aecdf
Reviewed-on: https://chromium-review.googlesource.com/1098668
Commit-Queue: Colin Blundell <blundell@chromium.org>
Reviewed-by: default avatarMihai Sardarescu <msarda@chromium.org>
Reviewed-by: default avatarMarc Treib <treib@chromium.org>
Reviewed-by: default avatarSylvain Defresne <sdefresne@chromium.org>
Cr-Commit-Position: refs/heads/master@{#571451}
parent 4437633b
...@@ -156,6 +156,14 @@ public class OAuth2TokenServiceIntegrationTest { ...@@ -156,6 +156,14 @@ public class OAuth2TokenServiceIntegrationTest {
// Adding an observer should not lead to a callback. // Adding an observer should not lead to a callback.
Assert.assertEquals(0, mObserver.getRevokedCallCount()); Assert.assertEquals(0, mObserver.getRevokedCallCount());
// First seed the account state (some native classes make the assumption that
// a notification that a token was revoked for a given account was preceded by a
// notifictation that that account was available).
mOAuth2TokenService.fireRefreshTokenAvailable(TEST_ACCOUNT1);
mOAuth2TokenService.fireRefreshTokenAvailable(TEST_ACCOUNT2);
Assert.assertEquals(2, mObserver.getAvailableCallCount());
// An observer should be called with the correct account. // An observer should be called with the correct account.
mOAuth2TokenService.fireRefreshTokenRevoked(TEST_ACCOUNT1); mOAuth2TokenService.fireRefreshTokenRevoked(TEST_ACCOUNT1);
Assert.assertEquals(1, mObserver.getRevokedCallCount()); Assert.assertEquals(1, mObserver.getRevokedCallCount());
...@@ -166,8 +174,9 @@ public class OAuth2TokenServiceIntegrationTest { ...@@ -166,8 +174,9 @@ public class OAuth2TokenServiceIntegrationTest {
mOAuth2TokenService.fireRefreshTokenRevoked(TEST_ACCOUNT2); mOAuth2TokenService.fireRefreshTokenRevoked(TEST_ACCOUNT2);
Assert.assertEquals(1, mObserver.getRevokedCallCount()); Assert.assertEquals(1, mObserver.getRevokedCallCount());
// No other observer interface method should ever have been called. // No other observer interface method should have been called after the initial seeding
Assert.assertEquals(0, mObserver.getAvailableCallCount()); // of the accounts.
Assert.assertEquals(2, mObserver.getAvailableCallCount());
Assert.assertEquals(0, mObserver.getLoadedCallCount()); Assert.assertEquals(0, mObserver.getLoadedCallCount());
}); });
} }
......
...@@ -14,7 +14,20 @@ IdentityManager::IdentityManager(SigninManagerBase* signin_manager, ...@@ -14,7 +14,20 @@ IdentityManager::IdentityManager(SigninManagerBase* signin_manager,
: signin_manager_(signin_manager), : signin_manager_(signin_manager),
token_service_(token_service), token_service_(token_service),
account_tracker_service_(account_tracker_service) { account_tracker_service_(account_tracker_service) {
// Initialize the state of the primary account.
primary_account_info_ = signin_manager_->GetAuthenticatedAccountInfo(); primary_account_info_ = signin_manager_->GetAuthenticatedAccountInfo();
// Initialize the state of accounts with refresh tokens.
// |account_id| is moved into |accounts_with_refresh_tokens_|.
// Do not change this to "const std::string&".
for (std::string account_id : token_service->GetAccounts()) {
AccountInfo account_info =
account_tracker_service_->GetAccountInfo(account_id);
DCHECK(!account_info.IsEmpty());
accounts_with_refresh_tokens_.emplace(std::move(account_id),
std::move(account_info));
}
signin_manager_->AddObserver(this); signin_manager_->AddObserver(this);
#if !defined(OS_CHROMEOS) #if !defined(OS_CHROMEOS)
SigninManager::FromSigninManagerBase(signin_manager_) SigninManager::FromSigninManagerBase(signin_manager_)
...@@ -83,6 +96,29 @@ bool IdentityManager::HasPrimaryAccount() { ...@@ -83,6 +96,29 @@ bool IdentityManager::HasPrimaryAccount() {
return !primary_account_info_.account_id.empty(); return !primary_account_info_.account_id.empty();
} }
std::vector<AccountInfo> IdentityManager::GetAccountsWithRefreshTokens() {
// TODO(blundell): It seems wasteful to construct this vector every time this
// method is called, but it also seems bad to maintain the vector as an ivar
// along the map.
std::vector<AccountInfo> accounts;
accounts.reserve(accounts_with_refresh_tokens_.size());
for (const auto& pair : accounts_with_refresh_tokens_) {
accounts.push_back(pair.second);
}
return accounts;
}
bool IdentityManager::HasAccountWithRefreshToken(
const std::string& account_id) {
return base::ContainsKey(accounts_with_refresh_tokens_, account_id);
}
bool IdentityManager::HasPrimaryAccountWithRefreshToken() {
return HasAccountWithRefreshToken(GetPrimaryAccountInfo().account_id);
}
std::unique_ptr<AccessTokenFetcher> std::unique_ptr<AccessTokenFetcher>
IdentityManager::CreateAccessTokenFetcherForAccount( IdentityManager::CreateAccessTokenFetcherForAccount(
const std::string& account_id, const std::string& account_id,
...@@ -197,6 +233,20 @@ void IdentityManager::WillFireOnRefreshTokenAvailable( ...@@ -197,6 +233,20 @@ void IdentityManager::WillFireOnRefreshTokenAvailable(
AccountInfo account_info = AccountInfo account_info =
account_tracker_service_->GetAccountInfo(account_id); account_tracker_service_->GetAccountInfo(account_id);
DCHECK(!account_info.IsEmpty()); DCHECK(!account_info.IsEmpty());
// Insert the account into |accounts_with_refresh_tokens_|.
auto insertion_result = accounts_with_refresh_tokens_.emplace(
account_id, std::move(account_info));
// The account might have already been present (e.g., this method can fire on
// updating an invalid token to a valid one or vice versa); in this case we
// sanity-check that the cached account info has the expected values.
if (!insertion_result.second) {
AccountInfo cached_account_info = insertion_result.first->second;
DCHECK_EQ(account_info.gaia, cached_account_info.gaia);
DCHECK_EQ(account_info.email, cached_account_info.email);
}
for (auto& observer : observer_list_) { for (auto& observer : observer_list_) {
observer.OnRefreshTokenUpdatedForAccount(account_info, is_valid); observer.OnRefreshTokenUpdatedForAccount(account_info, is_valid);
} }
...@@ -207,6 +257,11 @@ void IdentityManager::WillFireOnRefreshTokenRevoked( ...@@ -207,6 +257,11 @@ void IdentityManager::WillFireOnRefreshTokenRevoked(
AccountInfo account_info = AccountInfo account_info =
account_tracker_service_->GetAccountInfo(account_id); account_tracker_service_->GetAccountInfo(account_id);
DCHECK(!account_info.IsEmpty()); DCHECK(!account_info.IsEmpty());
auto iterator = accounts_with_refresh_tokens_.find(account_id);
DCHECK(iterator != accounts_with_refresh_tokens_.end());
accounts_with_refresh_tokens_.erase(iterator);
for (auto& observer : observer_list_) { for (auto& observer : observer_list_) {
observer.OnRefreshTokenRemovedForAccount(account_info); observer.OnRefreshTokenRemovedForAccount(account_info);
} }
......
...@@ -120,6 +120,20 @@ class IdentityManager : public SigninManagerBase::Observer, ...@@ -120,6 +120,20 @@ class IdentityManager : public SigninManagerBase::Observer,
// primary account info has a valid account ID. // primary account info has a valid account ID.
bool HasPrimaryAccount(); bool HasPrimaryAccount();
// Provides access to the latest cached information of all accounts that have
// refresh tokens.
// NOTE: The accounts should not be assumed to be in any particular order; in
// particular, they are not guaranteed to be in the order in which the
// refresh tokens were added.
std::vector<AccountInfo> GetAccountsWithRefreshTokens();
// Returns true if a refresh token exists for |account_id|.
bool HasAccountWithRefreshToken(const std::string& account_id);
// Returns true if (a) the primary account exists, and (b) a refresh token
// exists for the primary account.
bool HasPrimaryAccountWithRefreshToken();
// Creates an AccessTokenFetcher given the passed-in information. // Creates an AccessTokenFetcher given the passed-in information.
std::unique_ptr<AccessTokenFetcher> CreateAccessTokenFetcherForAccount( std::unique_ptr<AccessTokenFetcher> CreateAccessTokenFetcherForAccount(
const std::string& account_id, const std::string& account_id,
...@@ -218,6 +232,10 @@ class IdentityManager : public SigninManagerBase::Observer, ...@@ -218,6 +232,10 @@ class IdentityManager : public SigninManagerBase::Observer,
// The latest (cached) value of the primary account. // The latest (cached) value of the primary account.
AccountInfo primary_account_info_; AccountInfo primary_account_info_;
// The latest (cached) value of the accounts with refresh tokens.
using AccountIDToAccountInfoMap = std::map<std::string, AccountInfo>;
AccountIDToAccountInfoMap accounts_with_refresh_tokens_;
// Lists of observers. // Lists of observers.
// Makes sure lists are empty on destruction. // Makes sure lists are empty on destruction.
base::ObserverList<Observer, true> observer_list_; base::ObserverList<Observer, true> observer_list_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment