blob: 070d500e4f724f8b8145325c889d98087a57f393 [file] [log] [blame]
// Copyright 2019 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 <string>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/scoped_observer.h"
#include "chrome/browser/chromeos/login/existing_user_controller.h"
#include "chrome/browser/chromeos/login/login_manager_test.h"
#include "chrome/browser/chromeos/login/session/user_session_manager.h"
#include "chrome/browser/chromeos/login/session/user_session_manager_test_api.h"
#include "chrome/browser/chromeos/login/signin_specifics.h"
#include "chrome/browser/chromeos/login/startup_utils.h"
#include "chrome/browser/chromeos/login/test/js_checker.h"
#include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chromeos/login/auth/stub_authenticator.h"
#include "chromeos/login/auth/stub_authenticator_builder.h"
#include "chromeos/login/auth/user_context.h"
#include "components/account_id/account_id.h"
#include "components/session_manager/core/session_manager.h"
#include "components/session_manager/core/session_manager_observer.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/web_contents.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
#include "ui/gfx/native_widget_types.h"
namespace chromeos {
namespace {
constexpr char kTestUser[] = "test-user1@gmail.com";
constexpr char kTestUserGaiaId[] = "test-user1@gmail.com";
constexpr char kPassword[] = "test user password";
// Waits for the window that hosts OOBE UI changes visibility to target value.
// When waiting for the OOBE UI window to be hidden, it handles the window
// getting destroyed. Window getting destroyed while waiting for the window
// to become visible will stop the waiter, but will cause a test failure.
class OobeUiWindowVisibilityWaiter : public aura::WindowObserver {
public:
explicit OobeUiWindowVisibilityWaiter(bool target_visibilty)
: target_visibility_(target_visibilty) {}
~OobeUiWindowVisibilityWaiter() override = default;
void Wait() {
aura::Window* window = GetWindow();
if (!window && !target_visibility_)
return;
DCHECK(window);
if (target_visibility_ == window->IsVisible())
return;
base::RunLoop run_loop;
wait_stop_closure_ = run_loop.QuitClosure();
window_observer_.Add(window);
run_loop.Run();
}
// aura::WindowObserver:
void OnWindowVisibilityChanged(aura::Window* window, bool visible) override {
if (visible != target_visibility_)
return;
window_observer_.RemoveAll();
std::move(wait_stop_closure_).Run();
}
void OnWindowDestroyed(aura::Window* window) override {
EXPECT_FALSE(target_visibility_);
window_observer_.RemoveAll();
std::move(wait_stop_closure_).Run();
}
private:
aura::Window* GetWindow() {
LoginDisplayHost* host = LoginDisplayHost::default_host();
if (!host || !host->GetOobeWebContents())
return nullptr;
return host->GetOobeWebContents()->GetTopLevelNativeWindow();
}
const bool target_visibility_;
base::OnceClosure wait_stop_closure_;
ScopedObserver<aura::Window, OobeUiWindowVisibilityWaiter> window_observer_{
this};
};
// Used to wait for session manager to become active.
class SessionStartWaiter : public session_manager::SessionManagerObserver {
public:
SessionStartWaiter() = default;
~SessionStartWaiter() override = default;
void Wait() {
if (!session_manager::SessionManager::Get()->IsUserSessionBlocked())
return;
session_observer_.Add(session_manager::SessionManager::Get());
base::RunLoop run_loop;
session_active_callback_ = run_loop.QuitClosure();
run_loop.Run();
session_observer_.RemoveAll();
}
// session_manager::SessionManagerObserver:
void OnSessionStateChanged() override {
if (!session_manager::SessionManager::Get()->IsUserSessionBlocked() &&
session_active_callback_) {
std::move(session_active_callback_).Run();
}
}
private:
base::OnceClosure session_active_callback_;
ScopedObserver<session_manager::SessionManager, SessionStartWaiter>
session_observer_{this};
DISALLOW_COPY_AND_ASSIGN(SessionStartWaiter);
};
} // namespace
class PasswordChangeTest : public LoginManagerTest {
public:
PasswordChangeTest()
: LoginManagerTest(false, false),
test_account_id_(
AccountId::FromUserEmailGaiaId(kTestUser, kTestUserGaiaId)) {
set_force_webui_login(false);
}
~PasswordChangeTest() override = default;
UserContext GetTestUserContext() {
const user_manager::User* user =
user_manager::UserManager::Get()->FindUser(test_account_id_);
UserContext user_context(*user);
user_context.SetKey(Key(kPassword));
user_context.SetUserIDHash(test_account_id_.GetUserEmail());
return user_context;
}
// Sets up UserSessionManager to use stub authenticator that reports a
// password change, and attempts login.
// Password changed OOBE dialog is expected to show up after calling this.
void SetUpStubAuthentcatorAndAttemptLogin(const std::string& old_password) {
UserContext user_context = GetTestUserContext();
auto authenticator_builder =
std::make_unique<StubAuthenticatorBuilder>(user_context);
authenticator_builder->SetUpPasswordChange(
old_password,
base::BindRepeating(&PasswordChangeTest::HandleDataRecoveryStatusChange,
base::Unretained(this)));
test::UserSessionManagerTestApi(UserSessionManager::GetInstance())
.InjectAuthenticatorBuilder(std::move(authenticator_builder));
ExistingUserController::current_controller()->SetDisplayEmail(
test_account_id_.GetUserEmail());
ExistingUserController::current_controller()->Login(user_context,
SigninSpecifics());
}
protected:
AccountId test_account_id_;
StubAuthenticator::DataRecoveryStatus data_recovery_status_ =
StubAuthenticator::DataRecoveryStatus::kNone;
private:
void HandleDataRecoveryStatusChange(
StubAuthenticator::DataRecoveryStatus status) {
EXPECT_EQ(StubAuthenticator::DataRecoveryStatus::kNone,
data_recovery_status_);
data_recovery_status_ = status;
}
};
IN_PROC_BROWSER_TEST_F(PasswordChangeTest, PRE_MigrateOldCryptohome) {
RegisterUser(test_account_id_);
StartupUtils::MarkOobeCompleted();
}
IN_PROC_BROWSER_TEST_F(PasswordChangeTest, MigrateOldCryptohome) {
SetUpStubAuthentcatorAndAttemptLogin("old user password");
OobeScreenWaiter(OobeScreen::SCREEN_PASSWORD_CHANGED).Wait();
OobeUiWindowVisibilityWaiter(true).Wait();
test::OobeJS().CreateVisibilityWaiter(
true, {"gaia-password-changed", "oldPasswordCard"});
// Fill out and submit the old password passed to the stub authenticator.
test::OobeJS().TypeIntoPath("old user password",
{"gaia-password-changed", "oldPasswordInput"});
test::OobeJS().TapOnPath(
{"gaia-password-changed", "oldPasswordInputForm", "button"});
// User session should start, and whole OOBE screen is expected to be hidden,
OobeUiWindowVisibilityWaiter(false).Wait();
EXPECT_EQ(StubAuthenticator::DataRecoveryStatus::kRecovered,
data_recovery_status_);
SessionStartWaiter().Wait();
}
IN_PROC_BROWSER_TEST_F(PasswordChangeTest, PRE_RetryOnWrongPassword) {
RegisterUser(test_account_id_);
StartupUtils::MarkOobeCompleted();
}
IN_PROC_BROWSER_TEST_F(PasswordChangeTest, RetryOnWrongPassword) {
SetUpStubAuthentcatorAndAttemptLogin("old user password");
OobeScreenWaiter(OobeScreen::SCREEN_PASSWORD_CHANGED).Wait();
OobeUiWindowVisibilityWaiter(true).Wait();
test::OobeJS().CreateVisibilityWaiter(
true, {"gaia-password-changed", "oldPasswordCard"});
// Fill out and submit the old password passed to the stub authenticator.
test::OobeJS().TypeIntoPath("incorrect old user password",
{"gaia-password-changed", "oldPasswordInput"});
test::OobeJS().TapOnPath(
{"gaia-password-changed", "oldPasswordInputForm", "button"});
// Expect the UI to report failure.
test::OobeJS()
.CreateWaiter(test::GetOobeElementPath(
{"gaia-password-changed", "oldPasswordInput"}) +
".isInvalid")
->Wait();
test::OobeJS().ExpectEnabledPath(
{"gaia-password-changed", "oldPasswordCard"});
EXPECT_EQ(StubAuthenticator::DataRecoveryStatus::kRecoveryFailed,
data_recovery_status_);
data_recovery_status_ = StubAuthenticator::DataRecoveryStatus::kNone;
// Submit the correct password.
test::OobeJS().TypeIntoPath("old user password",
{"gaia-password-changed", "oldPasswordInput"});
test::OobeJS().TapOnPath(
{"gaia-password-changed", "oldPasswordInputForm", "button"});
// User session should start, and whole OOBE screen is expected to be hidden,
OobeUiWindowVisibilityWaiter(false).Wait();
EXPECT_EQ(StubAuthenticator::DataRecoveryStatus::kRecovered,
data_recovery_status_);
SessionStartWaiter().Wait();
}
IN_PROC_BROWSER_TEST_F(PasswordChangeTest, PRE_SkipDataRecovery) {
RegisterUser(test_account_id_);
StartupUtils::MarkOobeCompleted();
}
IN_PROC_BROWSER_TEST_F(PasswordChangeTest, SkipDataRecovery) {
SetUpStubAuthentcatorAndAttemptLogin("old user password");
OobeScreenWaiter(OobeScreen::SCREEN_PASSWORD_CHANGED).Wait();
OobeUiWindowVisibilityWaiter(true).Wait();
test::OobeJS().CreateVisibilityWaiter(
true, {"gaia-password-changed", "oldPasswordCard"});
// Click forgot password link.
test::OobeJS().TapOnPath({"gaia-password-changed", "forgot-password-link"});
test::OobeJS().CreateVisibilityWaiter(
false, {"gaia-password-changed", "oldPasswordCard"});
test::OobeJS().ExpectVisiblePath({"gaia-password-changed", "try-again-link"});
test::OobeJS().ExpectVisiblePath(
{"gaia-password-changed", "proceedAnywayBtn"});
// Click "Proceed anyway".
test::OobeJS().TapOnPath({"gaia-password-changed", "proceedAnywayBtn"});
// User session should start, and whole OOBE screen is expected to be hidden,
OobeUiWindowVisibilityWaiter(false).Wait();
EXPECT_EQ(StubAuthenticator::DataRecoveryStatus::kResynced,
data_recovery_status_);
SessionStartWaiter().Wait();
}
IN_PROC_BROWSER_TEST_F(PasswordChangeTest, PRE_TryAgainAfterForgetLinkClick) {
RegisterUser(test_account_id_);
StartupUtils::MarkOobeCompleted();
}
IN_PROC_BROWSER_TEST_F(PasswordChangeTest, TryAgainAfterForgetLinkClick) {
SetUpStubAuthentcatorAndAttemptLogin("old user password");
OobeScreenWaiter(OobeScreen::SCREEN_PASSWORD_CHANGED).Wait();
OobeUiWindowVisibilityWaiter(true).Wait();
test::OobeJS().CreateVisibilityWaiter(
true, {"gaia-password-changed", "oldPasswordCard"});
// Click forgot password link.
test::OobeJS().TapOnPath({"gaia-password-changed", "forgot-password-link"});
test::OobeJS().CreateVisibilityWaiter(
false, {"gaia-password-changed", "oldPasswordCard"});
test::OobeJS().ExpectVisiblePath({"gaia-password-changed", "try-again-link"});
test::OobeJS().ExpectVisiblePath(
{"gaia-password-changed", "proceedAnywayBtn"});
// Go back to old password input by clicking Try Again.
test::OobeJS().TapOnPath({"gaia-password-changed", "try-again-link"});
test::OobeJS().CreateVisibilityWaiter(
true, {"gaia-password-changed", "oldPasswordCard"});
// Enter and submit the correct password.
test::OobeJS().TypeIntoPath("old user password",
{"gaia-password-changed", "oldPasswordInput"});
test::OobeJS().TapOnPath(
{"gaia-password-changed", "oldPasswordInputForm", "button"});
// User session should start, and whole OOBE screen is expected to be hidden,
OobeUiWindowVisibilityWaiter(false).Wait();
EXPECT_EQ(StubAuthenticator::DataRecoveryStatus::kRecovered,
data_recovery_status_);
SessionStartWaiter().Wait();
}
IN_PROC_BROWSER_TEST_F(PasswordChangeTest, PRE_ClosePasswordChangedDialog) {
RegisterUser(test_account_id_);
StartupUtils::MarkOobeCompleted();
}
IN_PROC_BROWSER_TEST_F(PasswordChangeTest, ClosePasswordChangedDialog) {
SetUpStubAuthentcatorAndAttemptLogin("old user password");
OobeScreenWaiter(OobeScreen::SCREEN_PASSWORD_CHANGED).Wait();
test::OobeJS().CreateVisibilityWaiter(
true, {"gaia-password-changed", "oldPasswordCard"});
test::OobeJS().TypeIntoPath("old user password",
{"gaia-password-changed", "oldPasswordInput"});
// Click the close button.
test::OobeJS().TapOnPath(
{"gaia-password-changed", "navigation", "closeButton"});
OobeUiWindowVisibilityWaiter(false).Wait();
EXPECT_EQ(StubAuthenticator::DataRecoveryStatus::kNone,
data_recovery_status_);
ExistingUserController::current_controller()->Login(GetTestUserContext(),
SigninSpecifics());
OobeUiWindowVisibilityWaiter(true).Wait();
OobeScreenWaiter(OobeScreen::SCREEN_PASSWORD_CHANGED).Wait();
}
} // namespace chromeos