Commit 1320ac55 authored by Maciek Slusarczyk's avatar Maciek Slusarczyk Committed by Commit Bot

SAML sync token check on login screen.

This CL introduces checking the password sync token for SAML users on
the login screen. If the token is already set in local settings we poll
the token API and update login screen to online authentication if the
token is not valid.

Bug: 1090341
Change-Id: I93fed049cf4050d4114143dd971614d3fa22740e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2438493
Commit-Queue: Maciek Slusarczyk <mslus@chromium.org>
Reviewed-by: default avatarMaksim Ivanov <emaxx@chromium.org>
Reviewed-by: default avatarRoman Sorokin [CET] <rsorokin@chromium.org>
Reviewed-by: default avatarDenis Kuznetsov [CET] <antrim@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#818483}
parent 01693ae1
......@@ -1662,8 +1662,12 @@ source_set("chromeos") {
"login/saml/in_session_password_sync_manager_factory.h",
"login/saml/password_expiry_notification.cc",
"login/saml/password_expiry_notification.h",
"login/saml/password_sync_token_checkers_collection.cc",
"login/saml/password_sync_token_checkers_collection.h",
"login/saml/password_sync_token_fetcher.cc",
"login/saml/password_sync_token_fetcher.h",
"login/saml/password_sync_token_login_checker.cc",
"login/saml/password_sync_token_login_checker.h",
"login/saml/password_sync_token_verifier.cc",
"login/saml/password_sync_token_verifier.h",
"login/saml/password_sync_token_verifier_factory.cc",
......@@ -3481,6 +3485,7 @@ source_set("unit_tests") {
"login/saml/mock_lock_handler.cc",
"login/saml/mock_lock_handler.h",
"login/saml/password_expiry_notification_unittest.cc",
"login/saml/password_sync_token_login_checker_unittest.cc",
"login/saml/password_sync_token_verifier_unittest.cc",
"login/saml/saml_offline_signin_limiter_unittest.cc",
"login/screens/encryption_migration_screen_unittest.cc",
......
......@@ -551,6 +551,7 @@ void ExistingUserController::UpdateLoginDisplay(
}
bool show_users_on_signin;
user_manager::UserList filtered_users;
user_manager::UserList saml_users_for_password_sync;
cros_settings_->GetBoolean(kAccountsPrefShowUserNamesOnSignIn,
&show_users_on_signin);
......@@ -573,13 +574,28 @@ void ExistingUserController::UpdateLoginDisplay(
const bool meets_show_users_requirements =
show_users_on_signin ||
user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
if (meets_supervised_requirements && meets_allowlist_requirements &&
meets_show_users_requirements) {
if (meets_supervised_requirements && meets_allowlist_requirements) {
if (meets_show_users_requirements) {
filtered_users.push_back(user);
}
if (user->using_saml()) {
saml_users_for_password_sync.push_back(user);
}
}
}
ForceOnlineFlagChanged(filtered_users);
// ExistingUserController owns PasswordSyncTokenLoginCheckers only if user
// pods are hidden.
if (!show_users_on_signin && !saml_users_for_password_sync.empty()) {
sync_token_checkers_ =
std::make_unique<PasswordSyncTokenCheckersCollection>();
sync_token_checkers_->StartPasswordSyncCheckers(
saml_users_for_password_sync,
/*observer*/ nullptr);
} else {
sync_token_checkers_.reset();
}
// If no user pods are visible, fallback to single new user pod which will
// have guest session link.
bool show_guest = user_manager->IsGuestSessionAllowed();
......
......@@ -23,6 +23,7 @@
#include "base/timer/timer.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_types.h"
#include "chrome/browser/chromeos/login/saml/password_sync_token_checkers_collection.h"
#include "chrome/browser/chromeos/login/screens/encryption_migration_mode.h"
#include "chrome/browser/chromeos/login/session/user_session_manager.h"
#include "chrome/browser/chromeos/login/ui/login_display.h"
......@@ -398,6 +399,10 @@ class ExistingUserController : public LoginDisplay::Delegate,
// online user authentication.
std::unique_ptr<base::OneShotTimer> screen_refresh_timer_;
// Collection of verifiers that check validity of password sync token for SAML
// users.
std::unique_ptr<PasswordSyncTokenCheckersCollection> sync_token_checkers_;
std::unique_ptr<login::NetworkStateHelper> network_state_helper_;
std::unique_ptr<CrosSettings::ObserverSubscription>
......
......@@ -12,6 +12,9 @@
#include "base/values.h"
#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h"
#include "chrome/browser/chromeos/login/existing_user_controller.h"
#include "chrome/browser/chromeos/login/saml/password_sync_token_checkers_collection.h"
#include "chrome/browser/chromeos/login/saml/password_sync_token_fetcher.h"
#include "chrome/browser/chromeos/login/saml/password_sync_token_login_checker.h"
#include "chrome/browser/chromeos/login/ui/mock_login_display.h"
#include "chrome/browser/chromeos/login/ui/mock_login_display_host.h"
#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
......@@ -39,6 +42,9 @@ const char kFirstSAMLUserEmail[] = "bob@corp.example.com";
const char kSecondSAMLUserId[] = "67891";
const char kSecondSAMLUserEmail[] = "alice@corp.example.com";
const char kSamlToken1[] = "saml-token-1";
const char kSamlToken2[] = "saml-token-2";
constexpr base::TimeDelta kSamlOnlineShortDelay =
base::TimeDelta::FromSeconds(10);
constexpr base::TimeDelta kSamlOnlineLongDelay =
......@@ -77,6 +83,7 @@ class ExistingUserControllerForcedOnlineAuthTest : public ::testing::Test {
mock_user_manager_ = new MockUserManager();
scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
std::make_unique<FakeUserManagerWithLocalState>(mock_user_manager_));
settings_helper_.ReplaceDeviceSettingsProviderWithStub();
existing_user_controller_ = std::make_unique<ExistingUserController>();
ON_CALL(*mock_login_display_host_, GetLoginDisplay())
......@@ -101,6 +108,24 @@ class ExistingUserControllerForcedOnlineAuthTest : public ::testing::Test {
MockUserManager* mock_user_manager() { return mock_user_manager_; }
int password_sync_token_checkers_size() {
if (!existing_user_controller()->sync_token_checkers_)
return 0;
return existing_user_controller()
->sync_token_checkers_->sync_token_checkers_.size();
}
PasswordSyncTokenLoginChecker* get_password_sync_token_checker(
std::string token) {
return existing_user_controller()
->sync_token_checkers_->sync_token_checkers_[token]
.get();
}
void set_hide_user_names_on_signin() {
settings_helper_.SetBoolean(kAccountsPrefShowUserNamesOnSignIn, false);
}
const AccountId saml_login_account1_id_ =
AccountId::FromUserEmailGaiaId(kFirstSAMLUserEmail, kFirstSAMLUserId);
......@@ -197,4 +222,70 @@ TEST_F(ExistingUserControllerForcedOnlineAuthTest,
EXPECT_TRUE(is_force_online_flag_set());
}
// Tests creation of password sync token checker for 2 SAML users. Only one of
// them has local copy of password sync token.
TEST_F(ExistingUserControllerForcedOnlineAuthTest,
SyncTokenCheckersCreationWithOneToken) {
user_manager::known_user::SetPasswordSyncToken(saml_login_account1_id_,
kSamlToken1);
set_hide_user_names_on_signin();
mock_user_manager()->AddPublicAccountWithSAML(saml_login_account1_id_);
mock_user_manager()->AddPublicAccountWithSAML(saml_login_account2_id_);
existing_user_controller()->Init(mock_user_manager()->GetUsers());
EXPECT_EQ(password_sync_token_checkers_size(), 1);
get_password_sync_token_checker(kSamlToken1)->OnTokenVerified(true);
task_environment_.FastForwardBy(kSamlOnlineShortDelay);
EXPECT_TRUE(get_password_sync_token_checker(kSamlToken1)->IsCheckPending());
}
// Tests creation of password sync token checker for 2 SAML users with password
// sync tokens.
TEST_F(ExistingUserControllerForcedOnlineAuthTest,
SyncTokenCheckersCreationWithTwoTokens) {
user_manager::known_user::SetPasswordSyncToken(saml_login_account1_id_,
kSamlToken1);
user_manager::known_user::SetPasswordSyncToken(saml_login_account2_id_,
kSamlToken2);
set_hide_user_names_on_signin();
mock_user_manager()->AddPublicAccountWithSAML(saml_login_account1_id_);
mock_user_manager()->AddPublicAccountWithSAML(saml_login_account2_id_);
existing_user_controller()->Init(mock_user_manager()->GetUsers());
EXPECT_EQ(password_sync_token_checkers_size(), 2);
get_password_sync_token_checker(kSamlToken1)
->OnApiCallFailed(PasswordSyncTokenFetcher::ErrorType::kServerError);
task_environment_.FastForwardBy(kSamlOnlineShortDelay);
EXPECT_TRUE(get_password_sync_token_checker(kSamlToken1)->IsCheckPending());
}
// Tests sync token checkers removal in case of failed token validation.
TEST_F(ExistingUserControllerForcedOnlineAuthTest,
SyncTokenCheckersInvalidPasswordForTwoUsers) {
user_manager::known_user::SetPasswordSyncToken(saml_login_account1_id_,
kSamlToken1);
user_manager::known_user::SetPasswordSyncToken(saml_login_account2_id_,
kSamlToken2);
set_hide_user_names_on_signin();
mock_user_manager()->AddPublicAccountWithSAML(saml_login_account1_id_);
mock_user_manager()->AddPublicAccountWithSAML(saml_login_account2_id_);
existing_user_controller()->Init(mock_user_manager()->GetUsers());
EXPECT_EQ(password_sync_token_checkers_size(), 2);
get_password_sync_token_checker(kSamlToken1)->OnTokenVerified(false);
get_password_sync_token_checker(kSamlToken2)->OnTokenVerified(false);
EXPECT_EQ(password_sync_token_checkers_size(), 0);
}
// Sync token checkers are not owned by ExistingUserController if user pods are
// visible.
TEST_F(ExistingUserControllerForcedOnlineAuthTest,
NoSyncTokenCheckersWhenPodsVisible) {
user_manager::known_user::SetPasswordSyncToken(saml_login_account1_id_,
kSamlToken1);
user_manager::known_user::SetPasswordSyncToken(saml_login_account2_id_,
kSamlToken2);
mock_user_manager()->AddPublicAccountWithSAML(saml_login_account1_id_);
mock_user_manager()->AddPublicAccountWithSAML(saml_login_account2_id_);
existing_user_controller()->Init(mock_user_manager()->GetUsers());
EXPECT_EQ(password_sync_token_checkers_size(), 0);
}
} // namespace chromeos
......@@ -49,6 +49,9 @@ enum ReauthReason {
// User cancelled the password change prompt when prompted by Chrome OS.
PASSWORD_UPDATE_SKIPPED = 8,
// SAML password sync token validation failed.
SAML_PASSWORD_SYNC_TOKEN_VALIDATION_FAILED = 9,
// Must be the last value in this list.
NUM_REAUTH_FLOW_REASONS,
};
......
// Copyright 2020 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 "chrome/browser/chromeos/login/saml/password_sync_token_checkers_collection.h"
#include "components/user_manager/known_user.h"
#include "components/user_manager/user_manager.h"
namespace chromeos {
const net::BackoffEntry::Policy
PasswordSyncTokenCheckersCollection::kFetchTokenRetryBackoffPolicy = {
0, // Number of initial errors to ignore.
10 * 1000, // Initial request delay in ms.
2.0, // Factor by which the waiting time will be multiplied.
0.1, // Fuzzing percentage.
60 * 60 * 1000, // Maximum request delay in ms.
-1, // Never discard the entry.
true, // Use initial delay in the first retry.
};
PasswordSyncTokenCheckersCollection::PasswordSyncTokenCheckersCollection()
: sync_token_retry_backoff_(&kFetchTokenRetryBackoffPolicy) {}
PasswordSyncTokenCheckersCollection::~PasswordSyncTokenCheckersCollection() =
default;
void PasswordSyncTokenCheckersCollection::StartPasswordSyncCheckers(
const user_manager::UserList& users,
PasswordSyncTokenLoginChecker::Observer* observer) {
for (auto* user : users) {
// Online login already enforced for the user - no further checks are
// required.
if (!user->using_saml() || user->force_online_signin())
continue;
const std::string sync_token =
user_manager::known_user::GetPasswordSyncToken(user->GetAccountId());
if (!sync_token.empty() &&
!base::Contains(sync_token_checkers_, sync_token)) {
sync_token_checkers_.insert(
{sync_token,
std::make_unique<PasswordSyncTokenLoginChecker>(
user->GetAccountId(), sync_token, &sync_token_retry_backoff_)});
if (observer)
sync_token_checkers_[sync_token]->AddObserver(observer);
sync_token_checkers_[sync_token]->AddObserver(this);
sync_token_checkers_[sync_token]->CheckForPasswordNotInSync();
}
}
}
void PasswordSyncTokenCheckersCollection::OnInvalidSyncToken(
const AccountId& account_id) {
const std::string sync_token =
user_manager::known_user::GetPasswordSyncToken(account_id);
if (base::Contains(sync_token_checkers_, sync_token))
sync_token_checkers_.erase(sync_token);
}
} // namespace chromeos
// Copyright 2020 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_CHROMEOS_LOGIN_SAML_PASSWORD_SYNC_TOKEN_CHECKERS_COLLECTION_H_
#define CHROME_BROWSER_CHROMEOS_LOGIN_SAML_PASSWORD_SYNC_TOKEN_CHECKERS_COLLECTION_H_
#include <map>
#include <memory>
#include <string>
#include "chrome/browser/chromeos/login/saml/password_sync_token_login_checker.h"
#include "components/user_manager/user.h"
#include "net/base/backoff_entry.h"
namespace chromeos {
// Helper class to handle PasswordSyncTokenLoginChecker objects for all users
// on the login screen.
class PasswordSyncTokenCheckersCollection
: PasswordSyncTokenLoginChecker::Observer {
public:
// Backoff policy for token fetch retry attempts in case token fetch failed or
// returned invalid data.
static const net::BackoffEntry::Policy kFetchTokenRetryBackoffPolicy;
PasswordSyncTokenCheckersCollection();
~PasswordSyncTokenCheckersCollection() override;
PasswordSyncTokenCheckersCollection(
const PasswordSyncTokenCheckersCollection&) = delete;
PasswordSyncTokenCheckersCollection& operator=(
const PasswordSyncTokenCheckersCollection&) = delete;
void StartPasswordSyncCheckers(
const user_manager::UserList& users,
PasswordSyncTokenLoginChecker::Observer* observer);
// PasswordSyncTokenLoginChecker::Observer
void OnInvalidSyncToken(const AccountId& account_id) override;
private:
friend class PasswordSyncTokenLoginCheckerTest;
friend class ExistingUserControllerForcedOnlineAuthTest;
std::unordered_map<std::string,
std::unique_ptr<PasswordSyncTokenLoginChecker>>
sync_token_checkers_;
net::BackoffEntry sync_token_retry_backoff_;
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_LOGIN_SAML_PASSWORD_SYNC_TOKEN_CHECKERS_COLLECTION_H_
......@@ -98,7 +98,6 @@ PasswordSyncTokenFetcher::PasswordSyncTokenFetcher(
profile_(profile),
consumer_(consumer),
request_type_(RequestType::kNone) {
DCHECK(profile_);
DCHECK(consumer_);
}
......@@ -124,6 +123,7 @@ void PasswordSyncTokenFetcher::StartTokenVerify(const std::string& sync_token) {
}
void PasswordSyncTokenFetcher::StartAccessTokenFetch() {
DCHECK(profile_);
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile_);
DCHECK(identity_manager);
......
// Copyright 2020 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 "chrome/browser/chromeos/login/saml/password_sync_token_login_checker.h"
#include "base/task/post_task.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/login/existing_user_controller.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "components/user_manager/known_user.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/storage_partition.h"
namespace chromeos {
namespace {
const base::TimeDelta kPollingInterval = base::TimeDelta::FromMinutes(5);
}
PasswordSyncTokenLoginChecker::PasswordSyncTokenLoginChecker(
const AccountId& account_id,
const std::string& sync_token,
net::BackoffEntry* retry_backoff)
: account_id_(account_id),
sync_token_(sync_token),
retry_backoff_(retry_backoff) {
DCHECK(!sync_token_.empty());
}
PasswordSyncTokenLoginChecker::~PasswordSyncTokenLoginChecker() = default;
void PasswordSyncTokenLoginChecker::RecheckAfter(base::TimeDelta delay) {
CancelPendingChecks();
recheck_timer_.Start(
FROM_HERE, delay,
base::BindOnce(&PasswordSyncTokenLoginChecker::CheckForPasswordNotInSync,
weak_ptr_factory_.GetWeakPtr()));
}
void PasswordSyncTokenLoginChecker::CheckForPasswordNotInSync() {
DCHECK(!password_sync_token_fetcher_);
SystemNetworkContextManager* network_context_manager =
g_browser_process->system_network_context_manager();
if (!network_context_manager)
return;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
network_context_manager->GetSharedURLLoaderFactory();
if (!url_loader_factory.get())
return;
password_sync_token_fetcher_ = std::make_unique<PasswordSyncTokenFetcher>(
url_loader_factory, /*primary_profile_ = */ nullptr, this);
password_sync_token_fetcher_->StartTokenVerify(sync_token_);
}
void PasswordSyncTokenLoginChecker::CancelPendingChecks() {
// We should not have any active request at this point. DCHECK makes sure it
// is really the case for the dev build. In a release build InvalidateWeakPtrs
// helps to recover by cancelling potential existing requests.
DCHECK(!IsCheckPending());
weak_ptr_factory_.InvalidateWeakPtrs();
}
bool PasswordSyncTokenLoginChecker::IsCheckPending() {
return recheck_timer_.IsRunning();
}
void PasswordSyncTokenLoginChecker::OnTokenCreated(
const std::string& sync_token) {
/* ignored */
}
void PasswordSyncTokenLoginChecker::OnTokenFetched(
const std::string& sync_token) {
/* ignored */
}
void PasswordSyncTokenLoginChecker::OnTokenVerified(bool is_valid) {
retry_backoff_->InformOfRequest(true);
password_sync_token_fetcher_.reset();
if (is_valid) {
// Schedule next token check after base interval.
RecheckAfter(kPollingInterval);
return;
}
// Set force_online flag and refresh the login screen.
user_manager::UserManager::Get()->SaveForceOnlineSignin(account_id_, true);
NotifyObservers();
}
void PasswordSyncTokenLoginChecker::OnApiCallFailed(
PasswordSyncTokenFetcher::ErrorType error_type) {
retry_backoff_->InformOfRequest(false);
password_sync_token_fetcher_.reset();
// Schedule next token check with interval calculated with exponential
// backoff.
RecheckAfter(retry_backoff_->GetTimeUntilRelease());
}
void PasswordSyncTokenLoginChecker::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void PasswordSyncTokenLoginChecker::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
void PasswordSyncTokenLoginChecker::NotifyObservers() {
for (auto& observer : observer_list_) {
observer.OnInvalidSyncToken(account_id_);
}
}
} // namespace chromeos
// Copyright 2020 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_CHROMEOS_LOGIN_SAML_PASSWORD_SYNC_TOKEN_LOGIN_CHECKER_H_
#define CHROME_BROWSER_CHROMEOS_LOGIN_SAML_PASSWORD_SYNC_TOKEN_LOGIN_CHECKER_H_
#include <memory>
#include <string>
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/chromeos/login/saml/password_sync_token_fetcher.h"
#include "components/account_id/account_id.h"
#include "net/base/backoff_entry.h"
namespace chromeos {
// Verifies local copy of the password sync token by executing API call. If
// token is invalid calls InSessionPasswordSyncManager to request online re-auth
// that will sync the password and update the token.
class PasswordSyncTokenLoginChecker
: public PasswordSyncTokenFetcher::Consumer {
public:
// Observers of PasswordSyncTokenLoginChecker are notified of invalid sync
// token.
class Observer : public base::CheckedObserver {
public:
virtual void OnInvalidSyncToken(const AccountId& account_id) = 0;
};
explicit PasswordSyncTokenLoginChecker(const AccountId& account_id,
const std::string& sync_token,
net::BackoffEntry* retry_backoff);
~PasswordSyncTokenLoginChecker() override;
PasswordSyncTokenLoginChecker(const PasswordSyncTokenLoginChecker&) = delete;
PasswordSyncTokenLoginChecker& operator=(
const PasswordSyncTokenLoginChecker&) = delete;
// Execute verification API call.
void CheckForPasswordNotInSync();
// Cancel all pending check requests.
void CancelPendingChecks();
// Returns true if pending check exists for account_id_.
bool IsCheckPending();
// PasswordSyncTokenFetcher::Consumer
void OnTokenCreated(const std::string& sync_token) override;
void OnTokenFetched(const std::string& sync_token) override;
void OnTokenVerified(bool is_valid) override;
void OnApiCallFailed(PasswordSyncTokenFetcher::ErrorType error_type) override;
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
private:
friend class PasswordSyncTokenLoginCheckerTest;
// Recheck after given |delay|.
void RecheckAfter(base::TimeDelta delay);
void NotifyObservers();
base::ObserverList<Observer> observer_list_;
std::unique_ptr<PasswordSyncTokenFetcher> password_sync_token_fetcher_;
const AccountId account_id_;
const std::string sync_token_;
net::BackoffEntry* retry_backoff_ = nullptr;
base::OneShotTimer recheck_timer_;
base::WeakPtrFactory<PasswordSyncTokenLoginChecker> weak_ptr_factory_{this};
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_LOGIN_SAML_PASSWORD_SYNC_TOKEN_LOGIN_CHECKER_H_
// Copyright 2020 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 "chrome/browser/chromeos/login/saml/password_sync_token_login_checker.h"
#include "base/time/default_clock.h"
#include "chrome/browser/chromeos/login/saml/password_sync_token_checkers_collection.h"
#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
#include "chrome/test/base/testing_browser_process.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/user_manager/user_names.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace chromeos {
namespace {
const char kSAMLUserId[] = "12345";
const char kSAMLUserEmail[] = "alice@corp.example.com";
const char kSyncToken[] = "sync-token-1";
constexpr base::TimeDelta kSamlTokenDelay = base::TimeDelta::FromSeconds(60);
class FakeUserManagerWithLocalState : public chromeos::FakeChromeUserManager {
public:
FakeUserManagerWithLocalState()
: test_local_state_(std::make_unique<TestingPrefServiceSimple>()) {
RegisterPrefs(test_local_state_->registry());
}
~FakeUserManagerWithLocalState() override = default;
PrefService* GetLocalState() const override {
return test_local_state_.get();
}
private:
std::unique_ptr<TestingPrefServiceSimple> test_local_state_;
};
} // namespace
class PasswordSyncTokenLoginCheckerTest : public testing::Test {
protected:
PasswordSyncTokenLoginCheckerTest();
void CreatePasswordSyncTokenLoginChecker();
void DestroyPasswordSyncTokenLoginChecker();
void OnTokenVerified(bool is_verified);
const AccountId saml_login_account_id_ =
AccountId::FromUserEmailGaiaId(kSAMLUserEmail, kSAMLUserId);
content::BrowserTaskEnvironment test_environment_{
base::test::TaskEnvironment::MainThreadType::UI,
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
std::unique_ptr<net::BackoffEntry> sync_token_retry_backoff_;
FakeChromeUserManager* user_manager_ = nullptr;
std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
std::unique_ptr<PasswordSyncTokenLoginChecker> checker_;
};
PasswordSyncTokenLoginCheckerTest::PasswordSyncTokenLoginCheckerTest() {
std::unique_ptr<FakeChromeUserManager> fake_user_manager =
std::make_unique<FakeUserManagerWithLocalState>();
scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
std::move(fake_user_manager));
sync_token_retry_backoff_ = std::make_unique<net::BackoffEntry>(
&PasswordSyncTokenCheckersCollection::kFetchTokenRetryBackoffPolicy);
user_manager_ =
static_cast<FakeChromeUserManager*>(user_manager::UserManager::Get());
user_manager_->AddUser(saml_login_account_id_);
user_manager_->SwitchActiveUser(saml_login_account_id_);
}
void PasswordSyncTokenLoginCheckerTest::CreatePasswordSyncTokenLoginChecker() {
DestroyPasswordSyncTokenLoginChecker();
checker_ = std::make_unique<PasswordSyncTokenLoginChecker>(
saml_login_account_id_, kSyncToken, sync_token_retry_backoff_.get());
}
void PasswordSyncTokenLoginCheckerTest::DestroyPasswordSyncTokenLoginChecker() {
checker_.reset();
}
void PasswordSyncTokenLoginCheckerTest::OnTokenVerified(bool is_verified) {
checker_->OnTokenVerified(is_verified);
}
TEST_F(PasswordSyncTokenLoginCheckerTest, SyncTokenValid) {
CreatePasswordSyncTokenLoginChecker();
checker_->CheckForPasswordNotInSync();
OnTokenVerified(true);
EXPECT_FALSE(
user_manager_->FindUser(saml_login_account_id_)->force_online_signin());
test_environment_.FastForwardBy(kSamlTokenDelay);
EXPECT_TRUE(checker_->IsCheckPending());
}
TEST_F(PasswordSyncTokenLoginCheckerTest, SyncTokenInvalid) {
CreatePasswordSyncTokenLoginChecker();
checker_->CheckForPasswordNotInSync();
OnTokenVerified(false);
EXPECT_TRUE(
user_manager_->FindUser(saml_login_account_id_)->force_online_signin());
test_environment_.FastForwardBy(kSamlTokenDelay);
EXPECT_FALSE(checker_->IsCheckPending());
}
} // namespace chromeos
......@@ -709,6 +709,14 @@ void UserSelectionScreen::Init(const user_manager::UserList& users) {
if (!ime_state_.get())
ime_state_ = input_method::InputMethodManager::Get()->GetActiveIMEState();
if (users.size() > 0) {
sync_token_checkers_ =
std::make_unique<PasswordSyncTokenCheckersCollection>();
sync_token_checkers_->StartPasswordSyncCheckers(users, this);
} else {
sync_token_checkers_.reset();
}
if (tpm_locked_checker_)
return;
......@@ -976,6 +984,13 @@ void UserSelectionScreen::OnSessionStateChanged() {
HandleFocusPod(focused_pod);
}
void UserSelectionScreen::OnInvalidSyncToken(const AccountId& account_id) {
RecordReauthReason(account_id,
ReauthReason::SAML_PASSWORD_SYNC_TOKEN_VALIDATION_FAILED);
SetAuthType(account_id, proximity_auth::mojom::AuthType::ONLINE_SIGN_IN,
base::string16());
}
void UserSelectionScreen::ShowImpl() {}
void UserSelectionScreen::HideImpl() {}
......
......@@ -5,7 +5,6 @@
#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_USER_SELECTION_SCREEN_H_
#define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_USER_SELECTION_SCREEN_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
......@@ -17,6 +16,7 @@
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/values.h"
#include "chrome/browser/chromeos/login/saml/password_sync_token_checkers_collection.h"
#include "chrome/browser/chromeos/login/screens/base_screen.h"
#include "chrome/browser/chromeos/login/signin/token_handle_util.h"
#include "chrome/browser/chromeos/login/ui/login_display.h"
......@@ -44,7 +44,8 @@ class UserSelectionScreen
: public ui::UserActivityObserver,
public proximity_auth::ScreenlockBridge::LockHandler,
public BaseScreen,
public session_manager::SessionManagerObserver {
public session_manager::SessionManagerObserver,
public PasswordSyncTokenLoginChecker::Observer {
public:
explicit UserSelectionScreen(const std::string& display_type);
~UserSelectionScreen() override;
......@@ -107,6 +108,9 @@ class UserSelectionScreen
// session_manager::SessionManagerObserver
void OnSessionStateChanged() override;
// PasswordSyncTokenLoginChecker::Observer
void OnInvalidSyncToken(const AccountId& account_id) override;
// Fills `user_dict` with information about `user`.
static void FillUserDictionary(
const user_manager::User* user,
......@@ -200,6 +204,10 @@ class UserSelectionScreen
std::unique_ptr<CrosSettings::ObserverSubscription>
allowed_input_methods_subscription_;
// Collection of verifiers that check validity of password sync token for SAML
// users corresponding to visible pods.
std::unique_ptr<PasswordSyncTokenCheckersCollection> sync_token_checkers_;
base::WeakPtrFactory<UserSelectionScreen> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(UserSelectionScreen);
......
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