blob: 5acdee9d89adbae0a3330395d40c1f39c2209d8f [file] [log] [blame]
// Copyright 2013 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.
#include <memory>
#include "base/run_loop.h"
#include "base/strings/sys_string_conversions.h"
#include "components/browser_sync/profile_sync_service_mock.h"
#include "components/keyed_service/core/service_access_type.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/signin/core/browser/profile_oauth2_token_service.h"
#include "components/signin/core/browser/signin_pref_names.h"
#include "components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h"
#include "components/sync_preferences/pref_service_mock_factory.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state_manager.h"
#include "ios/chrome/browser/content_settings/cookie_settings_factory.h"
#include "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "ios/chrome/browser/experimental_flags.h"
#include "ios/chrome/browser/pref_names.h"
#include "ios/chrome/browser/prefs/browser_prefs.h"
#include "ios/chrome/browser/signin/account_tracker_service_factory.h"
#import "ios/chrome/browser/signin/authentication_service.h"
#import "ios/chrome/browser/signin/authentication_service_delegate_fake.h"
#import "ios/chrome/browser/signin/authentication_service_factory.h"
#import "ios/chrome/browser/signin/identity_manager_factory.h"
#import "ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.h"
#include "ios/chrome/browser/signin/ios_chrome_signin_client.h"
#include "ios/chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "ios/chrome/browser/signin/signin_client_factory.h"
#include "ios/chrome/browser/sync/ios_chrome_profile_sync_test_util.h"
#include "ios/chrome/browser/sync/profile_sync_service_factory.h"
#include "ios/chrome/browser/sync/sync_setup_service_factory.h"
#include "ios/chrome/browser/sync/sync_setup_service_mock.h"
#include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_state_manager.h"
#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
#import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
#import "ios/public/provider/chrome/browser/signin/fake_chrome_identity_service.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#import "services/identity/public/cpp/identity_test_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using testing::_;
using testing::Invoke;
using testing::Return;
namespace {
class FakeSigninClient : public IOSChromeSigninClient {
public:
explicit FakeSigninClient(
ios::ChromeBrowserState* browser_state,
scoped_refptr<content_settings::CookieSettings> cookie_settings,
scoped_refptr<HostContentSettingsMap> host_content_settings_map)
: IOSChromeSigninClient(browser_state,
cookie_settings,
host_content_settings_map) {}
~FakeSigninClient() override {}
base::Time GetInstallDate() override { return base::Time::Now(); }
};
std::unique_ptr<KeyedService> BuildFakeTestSigninClient(
web::BrowserState* context) {
ios::ChromeBrowserState* chrome_browser_state =
ios::ChromeBrowserState::FromBrowserState(context);
return std::make_unique<FakeSigninClient>(
chrome_browser_state,
ios::CookieSettingsFactory::GetForBrowserState(chrome_browser_state),
ios::HostContentSettingsMapFactory::GetForBrowserState(
chrome_browser_state));
}
std::unique_ptr<KeyedService> BuildMockSyncSetupService(
web::BrowserState* context) {
ios::ChromeBrowserState* browser_state =
ios::ChromeBrowserState::FromBrowserState(context);
return std::make_unique<SyncSetupServiceMock>(
ProfileSyncServiceFactory::GetForBrowserState(browser_state));
}
} // namespace
class AuthenticationServiceTest : public PlatformTest,
public identity::IdentityManager::Observer {
protected:
AuthenticationServiceTest()
: scoped_browser_state_manager_(
std::make_unique<TestChromeBrowserStateManager>(base::FilePath())),
refresh_token_available_count_(0) {}
void SetUp() override {
PlatformTest::SetUp();
identity_service_ =
ios::FakeChromeIdentityService::GetInstanceFromChromeProvider();
identity_service_->AddIdentities(@[ @"foo", @"foo2" ]);
identity_ =
[identity_service_->GetAllIdentitiesSortedForDisplay() objectAtIndex:0];
identity2_ =
[identity_service_->GetAllIdentitiesSortedForDisplay() objectAtIndex:1];
TestChromeBrowserState::Builder builder;
builder.SetPrefService(CreatePrefService());
builder.AddTestingFactory(SigninClientFactory::GetInstance(),
base::BindRepeating(&BuildFakeTestSigninClient));
builder.AddTestingFactory(
ProfileSyncServiceFactory::GetInstance(),
base::BindRepeating(&BuildMockProfileSyncService));
builder.AddTestingFactory(SyncSetupServiceFactory::GetInstance(),
base::BindRepeating(&BuildMockSyncSetupService));
builder.SetPrefService(CreatePrefService());
browser_state_ = IdentityTestEnvironmentChromeBrowserStateAdaptor::
CreateChromeBrowserStateForIdentityTestEnvironment(builder);
identity_test_environment_adaptor_ =
std::make_unique<IdentityTestEnvironmentChromeBrowserStateAdaptor>(
browser_state_.get());
profile_sync_service_mock_ =
static_cast<browser_sync::ProfileSyncServiceMock*>(
ProfileSyncServiceFactory::GetForBrowserState(
browser_state_.get()));
sync_setup_service_mock_ = static_cast<SyncSetupServiceMock*>(
SyncSetupServiceFactory::GetForBrowserState(browser_state_.get()));
CreateAuthenticationService();
identity_manager()->AddObserver(this);
}
std::unique_ptr<sync_preferences::PrefServiceSyncable> CreatePrefService() {
sync_preferences::PrefServiceMockFactory factory;
scoped_refptr<user_prefs::PrefRegistrySyncable> registry(
new user_prefs::PrefRegistrySyncable);
std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs =
factory.CreateSyncable(registry.get());
RegisterBrowserStatePrefs(registry.get());
return prefs;
}
void TearDown() override {
identity_manager()->RemoveObserver(this);
identity_test_environment_adaptor_.reset();
authentication_service_->Shutdown();
authentication_service_.reset();
browser_state_.reset();
PlatformTest::TearDown();
}
void SetExpectationsForSignIn() {
EXPECT_CALL(*profile_sync_service_mock_->GetUserSettingsMock(),
SetSyncRequested(true));
EXPECT_CALL(*sync_setup_service_mock_, PrepareForFirstSyncSetup());
}
void CreateAuthenticationService() {
if (authentication_service_.get()) {
authentication_service_->Shutdown();
}
authentication_service_ = std::make_unique<AuthenticationService>(
browser_state_->GetPrefs(),
ProfileOAuth2TokenServiceFactory::GetForBrowserState(
browser_state_.get()),
SyncSetupServiceFactory::GetForBrowserState(browser_state_.get()),
ios::AccountTrackerServiceFactory::GetForBrowserState(
browser_state_.get()),
IdentityManagerFactory::GetForBrowserState(browser_state_.get()),
ProfileSyncServiceFactory::GetForBrowserState(browser_state_.get()));
authentication_service_->Initialize(
std::make_unique<AuthenticationServiceDelegateFake>());
}
void StoreAccountsInPrefs() {
authentication_service_->StoreAccountsInPrefs();
}
void MigrateAccountsStoredInPrefsIfNeeded() {
authentication_service_->MigrateAccountsStoredInPrefsIfNeeded();
}
std::vector<std::string> GetAccountsInPrefs() {
return authentication_service_->GetAccountsInPrefs();
}
void SetAuthenticationServiceInForeground(bool is_in_foreground) {
authentication_service_->is_in_foreground_ = is_in_foreground;
}
void FireApplicationEnterForeground() {
authentication_service_->OnApplicationEnterForeground();
}
void FireApplicationEnterBackground() {
authentication_service_->OnApplicationEnterBackground();
}
void FireAccessTokenRefreshFailed(ChromeIdentity* identity,
NSDictionary* user_info) {
authentication_service_->OnAccessTokenRefreshFailed(identity, user_info);
}
void FireIdentityListChanged() {
authentication_service_->OnIdentityListChanged();
}
void SetCachedMDMInfo(ChromeIdentity* identity, NSDictionary* user_info) {
authentication_service_
->cached_mdm_infos_[base::SysNSStringToUTF8([identity gaiaID])] =
user_info;
}
bool HasCachedMDMInfo(ChromeIdentity* identity) {
return authentication_service_->cached_mdm_infos_.count(
base::SysNSStringToUTF8([identity gaiaID])) > 0;
}
// IdentityManager::Observer
void OnRefreshTokenUpdatedForAccount(
const AccountInfo& account_info) override {
refresh_token_available_count_++;
}
identity::IdentityManager* identity_manager() {
return identity_test_environment_adaptor_->identity_test_env()
->identity_manager();
}
web::TestWebThreadBundle thread_bundle_;
IOSChromeScopedTestingChromeBrowserStateManager scoped_browser_state_manager_;
std::unique_ptr<TestChromeBrowserState> browser_state_;
ios::FakeChromeIdentityService* identity_service_;
browser_sync::ProfileSyncServiceMock* profile_sync_service_mock_;
SyncSetupServiceMock* sync_setup_service_mock_;
std::unique_ptr<AuthenticationService> authentication_service_;
std::unique_ptr<IdentityTestEnvironmentChromeBrowserStateAdaptor>
identity_test_environment_adaptor_;
ChromeIdentity* identity_;
ChromeIdentity* identity2_;
int refresh_token_available_count_;
};
TEST_F(AuthenticationServiceTest, TestDefaultGetAuthenticatedIdentity) {
EXPECT_FALSE(authentication_service_->GetAuthenticatedIdentity());
}
TEST_F(AuthenticationServiceTest, TestSignInAndGetAuthenticatedIdentity) {
// Sign in.
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
EXPECT_NSEQ(identity_, authentication_service_->GetAuthenticatedIdentity());
ProfileOAuth2TokenService* token_service =
ProfileOAuth2TokenServiceFactory::GetForBrowserState(
browser_state_.get());
AccountTrackerService* account_tracker =
ios::AccountTrackerServiceFactory::GetForBrowserState(
browser_state_.get());
std::string user_email = base::SysNSStringToUTF8([identity_ userEmail]);
AccountInfo account_info =
account_tracker->FindAccountInfoByEmail(user_email);
EXPECT_EQ(user_email, account_info.email);
EXPECT_EQ(base::SysNSStringToUTF8([identity_ gaiaID]), account_info.gaia);
EXPECT_TRUE(token_service->RefreshTokenIsAvailable(account_info.account_id));
}
TEST_F(AuthenticationServiceTest, TestSetPromptForSignIn) {
// Verify that the default value of this flag is off.
EXPECT_FALSE(authentication_service_->ShouldPromptForSignIn());
// Verify that prompt-flag setter and getter functions are working correctly.
authentication_service_->SetPromptForSignIn(true);
EXPECT_TRUE(authentication_service_->ShouldPromptForSignIn());
authentication_service_->SetPromptForSignIn(false);
EXPECT_FALSE(authentication_service_->ShouldPromptForSignIn());
}
TEST_F(AuthenticationServiceTest, OnAppEnterForegroundWithSyncSetupCompleted) {
// Sign in.
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
EXPECT_CALL(*sync_setup_service_mock_, HasFinishedInitialSetup())
.WillOnce(Return(true));
CreateAuthenticationService();
EXPECT_EQ(base::SysNSStringToUTF8([identity_ userEmail]),
identity_manager()->GetPrimaryAccountInfo().email);
EXPECT_NSEQ([identity_ userEmail],
authentication_service_->GetAuthenticatedUserEmail());
EXPECT_EQ(identity_, authentication_service_->GetAuthenticatedIdentity());
}
TEST_F(AuthenticationServiceTest, OnAppEnterForegroundWithSyncDisabled) {
// Sign in.
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
EXPECT_CALL(*sync_setup_service_mock_, HasFinishedInitialSetup())
.WillOnce(Invoke(
sync_setup_service_mock_,
&SyncSetupServiceMock::SyncSetupServiceHasFinishedInitialSetup));
EXPECT_CALL(*profile_sync_service_mock_, GetDisableReasons())
.WillOnce(Return(syncer::SyncService::DISABLE_REASON_USER_CHOICE));
CreateAuthenticationService();
EXPECT_EQ(base::SysNSStringToUTF8([identity_ userEmail]),
identity_manager()->GetPrimaryAccountInfo().email);
EXPECT_NSEQ([identity_ userEmail],
authentication_service_->GetAuthenticatedUserEmail());
EXPECT_EQ(identity_, authentication_service_->GetAuthenticatedIdentity());
}
TEST_F(AuthenticationServiceTest, OnAppEnterForegroundWithSyncNotConfigured) {
// Sign in.
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
// User is signed out if sync initial setup isn't completed.
EXPECT_CALL(*sync_setup_service_mock_, HasFinishedInitialSetup())
.WillOnce(Return(false));
// Expect a call to disable sync as part of the sign out process.
EXPECT_CALL(*profile_sync_service_mock_, StopAndClear());
CreateAuthenticationService();
EXPECT_EQ("", identity_manager()->GetPrimaryAccountInfo().email);
EXPECT_NSEQ(nil, authentication_service_->GetAuthenticatedUserEmail());
EXPECT_FALSE(authentication_service_->GetAuthenticatedIdentity());
}
TEST_F(AuthenticationServiceTest, TestHandleForgottenIdentityNoPromptSignIn) {
// Sign in.
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
// Set the authentication service as "In Foreground", remove identity and run
// the loop.
SetAuthenticationServiceInForeground(false);
FireApplicationEnterForeground();
identity_service_->ForgetIdentity(identity_, nil);
base::RunLoop().RunUntilIdle();
// User is signed out (no corresponding identity), but not prompted for sign
// in (as the action was user initiated).
EXPECT_EQ("", identity_manager()->GetPrimaryAccountInfo().email);
EXPECT_FALSE(authentication_service_->GetAuthenticatedIdentity());
EXPECT_FALSE(authentication_service_->ShouldPromptForSignIn());
}
TEST_F(AuthenticationServiceTest, TestHandleForgottenIdentityPromptSignIn) {
// Sign in.
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
// Set the authentication service as "In Background", remove identity and run
// the loop.
FireApplicationEnterBackground();
identity_service_->ForgetIdentity(identity_, nil);
base::RunLoop().RunUntilIdle();
// User is signed out (no corresponding identity), but not prompted for sign
// in (as the action was user initiated).
EXPECT_EQ("", identity_manager()->GetPrimaryAccountInfo().email);
EXPECT_FALSE(authentication_service_->GetAuthenticatedIdentity());
EXPECT_TRUE(authentication_service_->ShouldPromptForSignIn());
}
TEST_F(AuthenticationServiceTest, StoreAndGetAccountsInPrefs) {
// Profile starts empty.
std::vector<std::string> accounts = GetAccountsInPrefs();
EXPECT_TRUE(accounts.empty());
// Sign in.
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
// Store the accounts and get them back from the prefs. They should be the
// same as the token service accounts.
StoreAccountsInPrefs();
accounts = GetAccountsInPrefs();
ASSERT_EQ(2u, accounts.size());
AccountTrackerService* account_tracker =
ios::AccountTrackerServiceFactory::GetForBrowserState(
browser_state_.get());
switch (account_tracker->GetMigrationState()) {
case AccountTrackerService::MIGRATION_NOT_STARTED:
EXPECT_EQ("foo2@foo.com", accounts[0]);
EXPECT_EQ("foo@foo.com", accounts[1]);
break;
case AccountTrackerService::MIGRATION_IN_PROGRESS:
case AccountTrackerService::MIGRATION_DONE:
EXPECT_EQ("foo2ID", accounts[0]);
EXPECT_EQ("fooID", accounts[1]);
break;
case AccountTrackerService::NUM_MIGRATION_STATES:
FAIL() << "NUM_MIGRATION_STATES is not a real migration state.";
break;
}
}
TEST_F(AuthenticationServiceTest,
OnApplicationEnterForegroundReloadCredentials) {
// Sign in.
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
identity_service_->AddIdentities(@[ @"foo3" ]);
auto account_compare_func = [](const AccountInfo& first,
const AccountInfo& second) {
return first.account_id < second.account_id;
};
std::vector<AccountInfo> accounts =
identity_manager()->GetAccountsWithRefreshTokens();
std::sort(accounts.begin(), accounts.end(), account_compare_func);
ASSERT_EQ(2u, accounts.size());
AccountTrackerService* account_tracker =
ios::AccountTrackerServiceFactory::GetForBrowserState(
browser_state_.get());
switch (account_tracker->GetMigrationState()) {
case AccountTrackerService::MIGRATION_NOT_STARTED:
EXPECT_EQ("foo2@foo.com", accounts[0].account_id);
EXPECT_EQ("foo@foo.com", accounts[1].account_id);
break;
case AccountTrackerService::MIGRATION_IN_PROGRESS:
case AccountTrackerService::MIGRATION_DONE:
EXPECT_EQ("foo2ID", accounts[0].account_id);
EXPECT_EQ("fooID", accounts[1].account_id);
break;
case AccountTrackerService::NUM_MIGRATION_STATES:
FAIL() << "NUM_MIGRATION_STATES is not a real migration state.";
break;
}
// Simulate a switching to background and back to foreground, triggering a
// credentials reload.
FireApplicationEnterBackground();
FireApplicationEnterForeground();
// Accounts are reloaded, "foo3@foo.com" is added as it is now in
// ChromeIdentityService.
accounts = identity_manager()->GetAccountsWithRefreshTokens();
std::sort(accounts.begin(), accounts.end(), account_compare_func);
ASSERT_EQ(3u, accounts.size());
switch (account_tracker->GetMigrationState()) {
case AccountTrackerService::MIGRATION_NOT_STARTED:
EXPECT_EQ("foo2@foo.com", accounts[0].account_id);
EXPECT_EQ("foo3@foo.com", accounts[1].account_id);
EXPECT_EQ("foo@foo.com", accounts[2].account_id);
break;
case AccountTrackerService::MIGRATION_IN_PROGRESS:
case AccountTrackerService::MIGRATION_DONE:
EXPECT_EQ("foo2ID", accounts[0].account_id);
EXPECT_EQ("foo3ID", accounts[1].account_id);
EXPECT_EQ("fooID", accounts[2].account_id);
break;
case AccountTrackerService::NUM_MIGRATION_STATES:
FAIL() << "NUM_MIGRATION_STATES is not a real migration state.";
break;
}
}
TEST_F(AuthenticationServiceTest, HaveAccountsNotChangedDefault) {
EXPECT_FALSE(authentication_service_->HaveAccountsChanged());
}
TEST_F(AuthenticationServiceTest, HaveAccountsNotChanged) {
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
identity_service_->AddIdentities(@[ @"foo3" ]);
FireIdentityListChanged();
base::RunLoop().RunUntilIdle();
// Simulate a switching to background and back to foreground.
FireApplicationEnterBackground();
FireApplicationEnterForeground();
EXPECT_FALSE(authentication_service_->HaveAccountsChanged());
}
TEST_F(AuthenticationServiceTest, HaveAccountsChanged) {
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
identity_service_->AddIdentities(@[ @"foo3" ]);
FireIdentityListChanged();
base::RunLoop().RunUntilIdle();
// Simulate a switching to background and back to foreground, changing the
// accounts while in background.
FireApplicationEnterBackground();
identity_service_->AddIdentities(@[ @"foo4" ]);
FireApplicationEnterForeground();
EXPECT_TRUE(authentication_service_->HaveAccountsChanged());
}
TEST_F(AuthenticationServiceTest, HaveAccountsChangedBackground) {
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
identity_service_->AddIdentities(@[ @"foo3" ]);
FireIdentityListChanged();
base::RunLoop().RunUntilIdle();
// Simulate a switching to background, changing the accounts while in
// background.
FireApplicationEnterBackground();
identity_service_->AddIdentities(@[ @"foo4" ]);
EXPECT_TRUE(authentication_service_->HaveAccountsChanged());
}
TEST_F(AuthenticationServiceTest, IsAuthenticatedBackground) {
// Sign in.
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
EXPECT_TRUE(authentication_service_->IsAuthenticated());
// Remove the signed in identity while in background, and check that
// IsAuthenticated is up-to-date.
FireApplicationEnterBackground();
identity_service_->ForgetIdentity(identity_, nil);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(authentication_service_->IsAuthenticated());
}
TEST_F(AuthenticationServiceTest, MigrateAccountsStoredInPref) {
AccountTrackerService* account_tracker =
ios::AccountTrackerServiceFactory::GetForBrowserState(
browser_state_.get());
if (account_tracker->GetMigrationState() ==
AccountTrackerService::MIGRATION_NOT_STARTED) {
// The account tracker is not migratable. Skip the test as the accounts
// cannot be migrated.
return;
}
// Force the migration state to MIGRATION_NOT_STARTED before signing in.
browser_state_->GetPrefs()->SetInteger(
prefs::kAccountIdMigrationState,
AccountTrackerService::MIGRATION_NOT_STARTED);
browser_state_->GetPrefs()->SetBoolean(prefs::kSigninLastAccountsMigrated,
false);
// Sign in user emails as account ids.
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
std::vector<std::string> accounts_in_prefs = GetAccountsInPrefs();
ASSERT_EQ(2U, accounts_in_prefs.size());
EXPECT_EQ("foo2@foo.com", accounts_in_prefs[0]);
EXPECT_EQ("foo@foo.com", accounts_in_prefs[1]);
// Migrate the accounts (this actually requires a shutdown and re-initialize
// of the account tracker).
account_tracker->Shutdown();
account_tracker->Initialize(browser_state_->GetPrefs(), base::FilePath());
account_tracker->SetMigrationDone();
// Actually migrate the accounts in prefs.
MigrateAccountsStoredInPrefsIfNeeded();
std::vector<std::string> migrated_accounts_in_prefs = GetAccountsInPrefs();
ASSERT_EQ(2U, migrated_accounts_in_prefs.size());
EXPECT_EQ("foo2ID", migrated_accounts_in_prefs[0]);
EXPECT_EQ("fooID", migrated_accounts_in_prefs[1]);
EXPECT_TRUE(browser_state_->GetPrefs()->GetBoolean(
prefs::kSigninLastAccountsMigrated));
// Calling migrate after the migration is done is a no-op.
MigrateAccountsStoredInPrefsIfNeeded();
EXPECT_EQ(migrated_accounts_in_prefs, GetAccountsInPrefs());
}
// Tests that MDM errors are correctly cleared on foregrounding, sending
// refresh token available notifications.
TEST_F(AuthenticationServiceTest, MDMErrorsClearedOnForeground) {
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
EXPECT_EQ(2, refresh_token_available_count_);
NSDictionary* user_info = [NSDictionary dictionary];
SetCachedMDMInfo(identity_, user_info);
GoogleServiceAuthError error(
GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
ProfileOAuth2TokenServiceFactory::GetForBrowserState(browser_state_.get())
->GetDelegate()
->UpdateAuthError(base::SysNSStringToUTF8([identity_ gaiaID]), error);
EXPECT_EQ(2, refresh_token_available_count_);
// MDM error for |identity_| is being cleared, refresh token available
// notification will be sent.
FireApplicationEnterBackground();
FireApplicationEnterForeground();
EXPECT_EQ(3, refresh_token_available_count_);
// MDM error has already been cleared, no notification will be sent.
FireApplicationEnterBackground();
FireApplicationEnterForeground();
EXPECT_EQ(3, refresh_token_available_count_);
}
// Tests that MDM errors are correctly cleared when signing out, without sending
// refresh token available notifications.
TEST_F(AuthenticationServiceTest, MDMErrorsClearedOnSignout) {
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
NSDictionary* user_info = [NSDictionary dictionary];
SetCachedMDMInfo(identity_, user_info);
int refresh_token_available_count_before_signout =
refresh_token_available_count_;
authentication_service_->SignOut(signin_metrics::ABORT_SIGNIN, nil);
EXPECT_FALSE(HasCachedMDMInfo(identity_));
EXPECT_EQ(refresh_token_available_count_before_signout,
refresh_token_available_count_);
}
// Tests that potential MDM notifications are correctly handled and dispatched
// to MDM service when necessary.
TEST_F(AuthenticationServiceTest, HandleMDMNotification) {
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
GoogleServiceAuthError error(
GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
ProfileOAuth2TokenServiceFactory::GetForBrowserState(browser_state_.get())
->GetDelegate()
->UpdateAuthError(base::SysNSStringToUTF8([identity_ gaiaID]), error);
NSDictionary* user_info1 = @{ @"foo" : @1 };
ON_CALL(*identity_service_, GetMDMDeviceStatus(user_info1))
.WillByDefault(Return(1));
NSDictionary* user_info2 = @{ @"foo" : @2 };
ON_CALL(*identity_service_, GetMDMDeviceStatus(user_info2))
.WillByDefault(Return(2));
// Notification will show the MDM dialog the first time.
EXPECT_CALL(*identity_service_,
HandleMDMNotification(identity_, user_info1, _))
.WillOnce(Return(true));
FireAccessTokenRefreshFailed(identity_, user_info1);
// Same notification won't show the MDM dialog the second time.
EXPECT_CALL(*identity_service_,
HandleMDMNotification(identity_, user_info1, _))
.Times(0);
FireAccessTokenRefreshFailed(identity_, user_info1);
// New notification will show the MDM dialog on the same identity.
EXPECT_CALL(*identity_service_,
HandleMDMNotification(identity_, user_info2, _))
.WillOnce(Return(true));
FireAccessTokenRefreshFailed(identity_, user_info2);
}
// Tests that MDM blocked notifications are correctly signing out the user if
// the primary account is blocked.
TEST_F(AuthenticationServiceTest, HandleMDMBlockedNotification) {
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
GoogleServiceAuthError error(
GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
ProfileOAuth2TokenServiceFactory::GetForBrowserState(browser_state_.get())
->GetDelegate()
->UpdateAuthError(base::SysNSStringToUTF8([identity_ gaiaID]), error);
NSDictionary* user_info1 = @{ @"foo" : @1 };
ON_CALL(*identity_service_, GetMDMDeviceStatus(user_info1))
.WillByDefault(Return(1));
auto handle_mdm_notification_callback = [](ChromeIdentity*, NSDictionary*,
ios::MDMStatusCallback callback) {
callback(true /* is_blocked */);
return true;
};
// User not signed out as |identity2_| isn't the primary account.
EXPECT_CALL(*identity_service_,
HandleMDMNotification(identity2_, user_info1, _))
.WillOnce(Invoke(handle_mdm_notification_callback));
FireAccessTokenRefreshFailed(identity2_, user_info1);
EXPECT_TRUE(authentication_service_->IsAuthenticated());
// User signed out as |identity_| is the primary account.
EXPECT_CALL(*identity_service_,
HandleMDMNotification(identity_, user_info1, _))
.WillOnce(Invoke(handle_mdm_notification_callback));
FireAccessTokenRefreshFailed(identity_, user_info1);
EXPECT_FALSE(authentication_service_->IsAuthenticated());
}
// Tests that MDM dialog isn't shown when there is no cached MDM error.
TEST_F(AuthenticationServiceTest, ShowMDMErrorDialogNoCachedError) {
EXPECT_CALL(*identity_service_, HandleMDMNotification(identity_, _, _))
.Times(0);
EXPECT_FALSE(
authentication_service_->ShowMDMErrorDialogForIdentity(identity_));
}
// Tests that MDM dialog isn't shown when there is a cached MDM error but no
// corresponding error for the account.
TEST_F(AuthenticationServiceTest, ShowMDMErrorDialogInvalidCachedError) {
NSDictionary* user_info = [NSDictionary dictionary];
SetCachedMDMInfo(identity_, user_info);
EXPECT_CALL(*identity_service_,
HandleMDMNotification(identity_, user_info, _))
.Times(0);
EXPECT_FALSE(
authentication_service_->ShowMDMErrorDialogForIdentity(identity_));
}
// Tests that MDM dialog is shown when there is a cached error and a
// corresponding error for the account.
TEST_F(AuthenticationServiceTest, ShowMDMErrorDialog) {
SetExpectationsForSignIn();
authentication_service_->SignIn(identity_, std::string());
GoogleServiceAuthError error(
GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
ProfileOAuth2TokenServiceFactory::GetForBrowserState(browser_state_.get())
->GetDelegate()
->UpdateAuthError(base::SysNSStringToUTF8([identity_ gaiaID]), error);
NSDictionary* user_info = [NSDictionary dictionary];
SetCachedMDMInfo(identity_, user_info);
EXPECT_CALL(*identity_service_,
HandleMDMNotification(identity_, user_info, _))
.WillOnce(Return(true));
EXPECT_TRUE(
authentication_service_->ShowMDMErrorDialogForIdentity(identity_));
}