| // Copyright 2016 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 tests the chromeos.quickUnlockPrivate extension API. |
| |
| #include "chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h" |
| |
| #include <memory> |
| |
| #include "ash/public/cpp/ash_pref_names.h" |
| #include "base/bind.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/no_destructor.h" |
| #include "base/run_loop.h" |
| #include "base/stl_util.h" |
| #include "base/test/bind_test_util.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_factory.h" |
| #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.h" |
| #include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h" |
| #include "chrome/browser/chromeos/login/quick_unlock/pin_storage_prefs.h" |
| #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h" |
| #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h" |
| #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h" |
| #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" |
| #include "chrome/browser/chromeos/profiles/profile_helper.h" |
| #include "chrome/browser/extensions/extension_api_unittest.h" |
| #include "chrome/common/pref_names.h" |
| #include "chromeos/cryptohome/mock_homedir_methods.h" |
| #include "chromeos/cryptohome/system_salt_getter.h" |
| #include "chromeos/dbus/dbus_thread_manager.h" |
| #include "chromeos/dbus/fake_cryptohome_client.h" |
| #include "chromeos/login/auth/fake_extended_authenticator.h" |
| #include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h" |
| #include "chromeos/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h" |
| #include "chromeos/services/secure_channel/public/cpp/client/fake_secure_channel_client.h" |
| #include "components/user_manager/scoped_user_manager.h" |
| #include "content/public/test/test_utils.h" |
| #include "extensions/browser/api_test_utils.h" |
| #include "extensions/browser/extension_function_dispatcher.h" |
| |
| using namespace extensions; |
| namespace quick_unlock_private = extensions::api::quick_unlock_private; |
| using CredentialCheck = quick_unlock_private::CredentialCheck; |
| using CredentialProblem = quick_unlock_private::CredentialProblem; |
| using CredentialRequirements = quick_unlock_private::CredentialRequirements; |
| using QuickUnlockMode = quick_unlock_private::QuickUnlockMode; |
| using QuickUnlockModeList = std::vector<QuickUnlockMode>; |
| using CredentialList = std::vector<std::string>; |
| |
| using ::testing::_; |
| using ::testing::Invoke; |
| using ::testing::WithArgs; |
| |
| namespace chromeos { |
| namespace { |
| |
| enum class TestType { kPrefs, kCryptohome }; |
| |
| constexpr char kTestUserEmail[] = "testuser@gmail.com"; |
| constexpr char kTestUserGaiaId[] = "9876543210"; |
| constexpr char kTestUserEmailHash[] = "testuser@gmail.com-hash"; |
| constexpr char kInvalidToken[] = "invalid"; |
| constexpr char kValidPassword[] = "valid"; |
| constexpr char kInvalidPassword[] = "invalid"; |
| |
| class FakeEasyUnlockService : public EasyUnlockServiceRegular { |
| public: |
| FakeEasyUnlockService( |
| Profile* profile, |
| chromeos::device_sync::FakeDeviceSyncClient* fake_device_sync_client, |
| chromeos::secure_channel::FakeSecureChannelClient* |
| fake_secure_channel_client, |
| chromeos::multidevice_setup::FakeMultiDeviceSetupClient* |
| fake_multidevice_setup_client) |
| : EasyUnlockServiceRegular(profile, |
| fake_secure_channel_client, |
| fake_device_sync_client, |
| fake_multidevice_setup_client) {} |
| ~FakeEasyUnlockService() override {} |
| |
| // EasyUnlockServiceRegular: |
| void InitializeInternal() override {} |
| void ShutdownInternal() override {} |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(FakeEasyUnlockService); |
| }; |
| |
| std::unique_ptr<KeyedService> CreateEasyUnlockServiceForTest( |
| content::BrowserContext* context) { |
| static base::NoDestructor<chromeos::device_sync::FakeDeviceSyncClient> |
| fake_device_sync_client; |
| static base::NoDestructor<chromeos::secure_channel::FakeSecureChannelClient> |
| fake_secure_channel_client; |
| static base::NoDestructor< |
| chromeos::multidevice_setup::FakeMultiDeviceSetupClient> |
| fake_multidevice_setup_client; |
| |
| return std::make_unique<FakeEasyUnlockService>( |
| Profile::FromBrowserContext(context), fake_device_sync_client.get(), |
| fake_secure_channel_client.get(), fake_multidevice_setup_client.get()); |
| } |
| |
| ExtendedAuthenticator* CreateFakeAuthenticator( |
| AuthStatusConsumer* auth_status_consumer) { |
| const AccountId account_id = |
| AccountId::FromUserEmailGaiaId(kTestUserEmail, kTestUserGaiaId); |
| UserContext expected_context(user_manager::USER_TYPE_REGULAR, account_id); |
| expected_context.SetKey(Key(kValidPassword)); |
| |
| ExtendedAuthenticator* authenticator = |
| new FakeExtendedAuthenticator(auth_status_consumer, expected_context); |
| return authenticator; |
| } |
| |
| void FailIfCalled(const QuickUnlockModeList& modes) { |
| FAIL(); |
| } |
| |
| enum ExpectedPinState { |
| PIN_GOOD = 1 << 0, |
| PIN_TOO_SHORT = 1 << 1, |
| PIN_TOO_LONG = 1 << 2, |
| PIN_WEAK_ERROR = 1 << 3, |
| PIN_WEAK_WARNING = 1 << 4, |
| PIN_CONTAINS_NONDIGIT = 1 << 5 |
| }; |
| |
| } // namespace |
| |
| class QuickUnlockPrivateUnitTest |
| : public ExtensionApiUnittest, |
| public ::testing::WithParamInterface<TestType> { |
| public: |
| QuickUnlockPrivateUnitTest() |
| : fake_user_manager_(new FakeChromeUserManager()), |
| scoped_user_manager_(base::WrapUnique(fake_user_manager_)) {} |
| |
| protected: |
| void SetUp() override { |
| // Setup DBusThreadManager before calling ExtensionApiUnittest::SetUp() |
| // since that will set dbus up with the default fake configuration. |
| auto cryptohome_client = std::make_unique<chromeos::FakeCryptohomeClient>(); |
| if (GetParam() == TestType::kCryptohome) { |
| cryptohome_client->set_supports_low_entropy_credentials(true); |
| cryptohome_client->set_enable_auth_check(true); |
| } |
| DBusThreadManager::GetSetterForTesting()->SetCryptohomeClient( |
| std::move(cryptohome_client)); |
| |
| ExtensionApiUnittest::SetUp(); |
| |
| SystemSaltGetter::Get()->SetRawSaltForTesting({1, 2, 3, 4, 5, 6, 7, 8}); |
| cryptohome::HomedirMethods::Initialize(); |
| fake_user_manager_->CreateLocalState(); |
| |
| // Rebuild quick unlock state. |
| quick_unlock::EnableForTesting(); |
| quick_unlock::PinBackend::ResetForTesting(); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| // Setup a primary user. |
| auto test_account = |
| AccountId::FromUserEmailGaiaId(kTestUserEmail, kTestUserGaiaId); |
| fake_user_manager_->AddUser(test_account); |
| fake_user_manager_->UserLoggedIn(test_account, kTestUserEmailHash, false, |
| false); |
| ProfileHelper::Get()->SetUserToProfileMappingForTesting( |
| fake_user_manager_->GetPrimaryUser(), GetProfile()); |
| |
| // Generate an auth token. |
| auth_token_user_context_.SetAccountId(test_account); |
| auth_token_user_context_.SetUserIDHash(kTestUserEmailHash); |
| token_ = quick_unlock::QuickUnlockFactory::GetForProfile(profile()) |
| ->CreateAuthToken(auth_token_user_context_); |
| |
| modes_changed_handler_ = base::DoNothing(); |
| |
| // Ensure that quick unlock is turned off. |
| RunSetModes(QuickUnlockModeList{}, CredentialList{}); |
| } |
| |
| void TearDown() override { |
| quick_unlock::DisablePinByPolicyForTesting(false); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| fake_user_manager_ = nullptr; |
| |
| ExtensionApiUnittest::TearDown(); |
| cryptohome::HomedirMethods::Shutdown(); |
| } |
| |
| TestingProfile::TestingFactories GetTestingFactories() override { |
| return {{EasyUnlockServiceFactory::GetInstance(), |
| base::BindRepeating(&CreateEasyUnlockServiceForTest)}}; |
| } |
| |
| // If a mode change event is raised, fail the test. |
| void FailIfModesChanged() { |
| modes_changed_handler_ = base::Bind(&FailIfCalled); |
| } |
| |
| // If a mode change event is raised, expect the given |modes|. |
| void ExpectModesChanged(const QuickUnlockModeList& modes) { |
| modes_changed_handler_ = |
| base::Bind(&QuickUnlockPrivateUnitTest::ExpectModeList, |
| base::Unretained(this), modes); |
| expect_modes_changed_ = true; |
| } |
| |
| // Wrapper for chrome.quickUnlockPrivate.getAuthToken. Expects the function |
| // to succeed and returns the result. |
| std::unique_ptr<quick_unlock_private::TokenInfo> GetAuthToken( |
| const std::string& password) { |
| // Setup a fake authenticator to avoid calling cryptohome methods. |
| auto* func = new QuickUnlockPrivateGetAuthTokenFunction(); |
| func->SetAuthenticatorAllocatorForTesting( |
| base::Bind(&CreateFakeAuthenticator)); |
| |
| auto params = std::make_unique<base::ListValue>(); |
| params->GetList().push_back(base::Value(password)); |
| std::unique_ptr<base::Value> result = RunFunction(func, std::move(params)); |
| EXPECT_TRUE(result); |
| auto token_info = quick_unlock_private::TokenInfo::FromValue(*result); |
| EXPECT_TRUE(token_info); |
| return token_info; |
| } |
| |
| // Wrapper for chrome.quickUnlockPrivate.getAuthToken with an invalid |
| // password. Expects the function to fail and returns the error. |
| std::string RunAuthTokenWithInvalidPassword() { |
| // Setup a fake authenticator to avoid calling cryptohome methods. |
| auto* func = new QuickUnlockPrivateGetAuthTokenFunction(); |
| func->SetAuthenticatorAllocatorForTesting( |
| base::Bind(&CreateFakeAuthenticator)); |
| |
| auto params = std::make_unique<base::ListValue>(); |
| params->GetList().push_back(base::Value(kInvalidPassword)); |
| return RunFunctionAndReturnError(func, std::move(params)); |
| } |
| |
| // Wrapper for chrome.quickUnlockPrivate.setLockScreenEnabled. |
| void SetLockScreenEnabled(const std::string& token, bool enabled) { |
| auto params = std::make_unique<base::ListValue>(); |
| params->AppendString(token); |
| params->AppendBoolean(enabled); |
| RunFunction(new QuickUnlockPrivateSetLockScreenEnabledFunction(), |
| std::move(params)); |
| } |
| |
| // Wrapper for chrome.quickUnlockPrivate.setLockScreenEnabled. |
| std::string SetLockScreenEnabledWithInvalidToken(bool enabled) { |
| auto params = std::make_unique<base::ListValue>(); |
| params->AppendString(kInvalidToken); |
| params->AppendBoolean(enabled); |
| return RunFunctionAndReturnError( |
| new QuickUnlockPrivateSetLockScreenEnabledFunction(), |
| std::move(params)); |
| } |
| |
| // Wrapper for chrome.quickUnlockPrivate.getAvailableModes. |
| QuickUnlockModeList GetAvailableModes() { |
| // Run the function. |
| std::unique_ptr<base::Value> result = |
| RunFunction(new QuickUnlockPrivateGetAvailableModesFunction(), |
| std::make_unique<base::ListValue>()); |
| |
| // Extract the results. |
| QuickUnlockModeList modes; |
| |
| base::ListValue* list = nullptr; |
| EXPECT_TRUE(result->GetAsList(&list)); |
| for (const base::Value& value : *list) { |
| std::string mode; |
| EXPECT_TRUE(value.GetAsString(&mode)); |
| modes.push_back(quick_unlock_private::ParseQuickUnlockMode(mode)); |
| } |
| |
| return modes; |
| } |
| |
| // Wrapper for chrome.quickUnlockPrivate.getActiveModes. |
| QuickUnlockModeList GetActiveModes() { |
| std::unique_ptr<base::Value> result = |
| RunFunction(new QuickUnlockPrivateGetActiveModesFunction(), |
| std::make_unique<base::ListValue>()); |
| |
| QuickUnlockModeList modes; |
| |
| base::ListValue* list = nullptr; |
| EXPECT_TRUE(result->GetAsList(&list)); |
| for (const base::Value& value : *list) { |
| std::string mode; |
| EXPECT_TRUE(value.GetAsString(&mode)); |
| modes.push_back(quick_unlock_private::ParseQuickUnlockMode(mode)); |
| } |
| |
| return modes; |
| } |
| |
| bool HasFlag(int outcome, int flag) { return (outcome & flag) != 0; } |
| |
| // Helper function for checking whether |IsCredentialUsableUsingPin| will |
| // return the right message given a pin. |
| void CheckPin(int expected_outcome, const std::string& pin) { |
| CredentialCheck result = CheckCredentialUsingPin(pin); |
| const std::vector<CredentialProblem> errors(result.errors); |
| const std::vector<CredentialProblem> warnings(result.warnings); |
| |
| // A pin is considered good if it emits no errors or warnings. |
| EXPECT_EQ(HasFlag(expected_outcome, PIN_GOOD), |
| errors.empty() && warnings.empty()); |
| EXPECT_EQ(HasFlag(expected_outcome, PIN_TOO_SHORT), |
| base::ContainsValue( |
| errors, CredentialProblem::CREDENTIAL_PROBLEM_TOO_SHORT)); |
| EXPECT_EQ(HasFlag(expected_outcome, PIN_TOO_LONG), |
| base::ContainsValue( |
| errors, CredentialProblem::CREDENTIAL_PROBLEM_TOO_LONG)); |
| EXPECT_EQ(HasFlag(expected_outcome, PIN_WEAK_WARNING), |
| base::ContainsValue( |
| warnings, CredentialProblem::CREDENTIAL_PROBLEM_TOO_WEAK)); |
| EXPECT_EQ(HasFlag(expected_outcome, PIN_WEAK_ERROR), |
| base::ContainsValue( |
| errors, CredentialProblem::CREDENTIAL_PROBLEM_TOO_WEAK)); |
| EXPECT_EQ( |
| HasFlag(expected_outcome, PIN_CONTAINS_NONDIGIT), |
| base::ContainsValue( |
| errors, CredentialProblem::CREDENTIAL_PROBLEM_CONTAINS_NONDIGIT)); |
| } |
| |
| CredentialCheck CheckCredentialUsingPin(const std::string& pin) { |
| auto params = std::make_unique<base::ListValue>(); |
| params->AppendString(ToString(QuickUnlockMode::QUICK_UNLOCK_MODE_PIN)); |
| params->AppendString(pin); |
| |
| std::unique_ptr<base::Value> result = RunFunction( |
| new QuickUnlockPrivateCheckCredentialFunction(), std::move(params)); |
| |
| CredentialCheck function_result; |
| EXPECT_TRUE(CredentialCheck::Populate(*result, &function_result)); |
| return function_result; |
| } |
| |
| void CheckGetCredentialRequirements(int expected_pin_min_length, |
| int expected_pin_max_length) { |
| auto params = std::make_unique<base::ListValue>(); |
| params->AppendString(ToString(QuickUnlockMode::QUICK_UNLOCK_MODE_PIN)); |
| |
| std::unique_ptr<base::Value> result = |
| RunFunction(new QuickUnlockPrivateGetCredentialRequirementsFunction(), |
| std::move(params)); |
| |
| CredentialRequirements function_result; |
| EXPECT_TRUE(CredentialRequirements::Populate(*result, &function_result)); |
| |
| EXPECT_EQ(function_result.min_length, expected_pin_min_length); |
| EXPECT_EQ(function_result.max_length, expected_pin_max_length); |
| } |
| |
| std::unique_ptr<base::ListValue> GetSetModesParams( |
| const std::string& token, |
| const QuickUnlockModeList& modes, |
| const CredentialList& passwords) { |
| auto params = std::make_unique<base::ListValue>(); |
| params->AppendString(token); |
| |
| auto serialized_modes = std::make_unique<base::ListValue>(); |
| for (QuickUnlockMode mode : modes) |
| serialized_modes->AppendString(quick_unlock_private::ToString(mode)); |
| params->Append(std::move(serialized_modes)); |
| |
| auto serialized_passwords = std::make_unique<base::ListValue>(); |
| for (const std::string& password : passwords) |
| serialized_passwords->AppendString(password); |
| params->Append(std::move(serialized_passwords)); |
| |
| return params; |
| } |
| |
| // Runs chrome.quickUnlockPrivate.setModes using a valid token. Expects the |
| // function to succeed. |
| void RunSetModes(const QuickUnlockModeList& modes, |
| const CredentialList& passwords) { |
| std::unique_ptr<base::ListValue> params = |
| GetSetModesParams(token_, modes, passwords); |
| auto* func = new QuickUnlockPrivateSetModesFunction(); |
| |
| // Stub out event handling since we are not setting up an event router. |
| func->SetModesChangedEventHandlerForTesting(modes_changed_handler_); |
| |
| // Run the function. Expect a non null result. |
| RunFunction(func, std::move(params)); |
| |
| // Verify that the mode change event handler was run if it was registered. |
| // ExpectModesChanged will set expect_modes_changed_ to true and the event |
| // handler will set it to false; so if the handler never runs, |
| // expect_modes_changed_ will still be true. |
| EXPECT_FALSE(expect_modes_changed_) << "Mode change event was not raised"; |
| } |
| |
| // Runs chrome.quickUnlockPrivate.setModes using an invalid token. Expects the |
| // function to fail and returns the error. |
| std::string RunSetModesWithInvalidToken() { |
| std::unique_ptr<base::ListValue> params = GetSetModesParams( |
| kInvalidToken, {QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"111111"}); |
| auto* func = new QuickUnlockPrivateSetModesFunction(); |
| |
| // Stub out event handling since we are not setting up an event router. |
| func->SetModesChangedEventHandlerForTesting(modes_changed_handler_); |
| |
| // Run function, expecting it to fail. |
| return RunFunctionAndReturnError(func, std::move(params)); |
| } |
| |
| std::string SetModesWithError(const std::string& args) { |
| auto* func = new QuickUnlockPrivateSetModesFunction(); |
| func->SetModesChangedEventHandlerForTesting(base::DoNothing()); |
| |
| return api_test_utils::RunFunctionAndReturnError(func, args, profile()); |
| } |
| |
| std::string token() { return token_; } |
| |
| // Returns if the pin is set in the backend. |
| bool IsPinSetInBackend() { |
| const AccountId account_id = |
| AccountId::FromUserEmailGaiaId(kTestUserEmail, kTestUserGaiaId); |
| |
| bool called = false; |
| bool is_set = false; |
| quick_unlock::PinBackend::GetInstance()->IsSet( |
| account_id, base::BindOnce( |
| [](bool* out_called, bool* out_is_set, bool is_set) { |
| *out_called = true; |
| *out_is_set = is_set; |
| }, |
| &called, &is_set)); |
| base::RunLoop().RunUntilIdle(); |
| CHECK(called); |
| return is_set; |
| } |
| |
| // Run an authentication attempt with the plain-text |password|. |
| bool TryAuthenticate(const std::string& password) { |
| const AccountId account_id = |
| AccountId::FromUserEmailGaiaId(kTestUserEmail, kTestUserGaiaId); |
| bool called = false; |
| bool success = false; |
| quick_unlock::PinBackend::GetInstance()->TryAuthenticate( |
| account_id, Key(password), |
| base::BindLambdaForTesting([&](bool auth_success) { |
| called = true; |
| success = auth_success; |
| })); |
| base::RunLoop().RunUntilIdle(); |
| CHECK(called); |
| return success; |
| } |
| |
| private: |
| // Runs the given |func| with the given |params|. |
| std::unique_ptr<base::Value> RunFunction( |
| scoped_refptr<UIThreadExtensionFunction> func, |
| std::unique_ptr<base::ListValue> params) { |
| base::RunLoop().RunUntilIdle(); |
| std::unique_ptr<base::Value> result = |
| api_test_utils::RunFunctionWithDelegateAndReturnSingleResult( |
| func, std::move(params), profile(), |
| std::make_unique<ExtensionFunctionDispatcher>(profile()), |
| api_test_utils::NONE); |
| base::RunLoop().RunUntilIdle(); |
| return result; |
| } |
| |
| // Runs |func| with |params|. Expects and returns an error result. |
| std::string RunFunctionAndReturnError( |
| scoped_refptr<UIThreadExtensionFunction> func, |
| std::unique_ptr<base::ListValue> params) { |
| base::RunLoop().RunUntilIdle(); |
| std::unique_ptr<ExtensionFunctionDispatcher> dispatcher( |
| new ExtensionFunctionDispatcher(profile())); |
| api_test_utils::RunFunction(func.get(), std::move(params), profile(), |
| std::move(dispatcher), api_test_utils::NONE); |
| EXPECT_TRUE(func->GetResultList()->empty()); |
| base::RunLoop().RunUntilIdle(); |
| return func->GetError(); |
| } |
| |
| // Verifies a mode change event is raised and that |expected| is now the |
| // active set of quick unlock modes. |
| void ExpectModeList(const QuickUnlockModeList& expected, |
| const QuickUnlockModeList& actual) { |
| EXPECT_EQ(expected, actual); |
| expect_modes_changed_ = false; |
| } |
| |
| FakeChromeUserManager* fake_user_manager_; |
| user_manager::ScopedUserManager scoped_user_manager_; |
| QuickUnlockPrivateSetModesFunction::ModesChangedEventHandler |
| modes_changed_handler_; |
| bool expect_modes_changed_ = false; |
| chromeos::UserContext auth_token_user_context_; |
| std::string token_; |
| |
| DISALLOW_COPY_AND_ASSIGN(QuickUnlockPrivateUnitTest); |
| }; |
| |
| // Verifies that GetAuthTokenValid succeeds when a valid password is provided. |
| TEST_P(QuickUnlockPrivateUnitTest, GetAuthTokenValid) { |
| std::unique_ptr<quick_unlock_private::TokenInfo> token_info = |
| GetAuthToken(kValidPassword); |
| |
| quick_unlock::QuickUnlockStorage* quick_unlock_storage = |
| quick_unlock::QuickUnlockFactory::GetForProfile(profile()); |
| EXPECT_EQ(token_info->token, quick_unlock_storage->GetAuthToken()); |
| EXPECT_EQ(token_info->lifetime_seconds, |
| quick_unlock::AuthToken::kTokenExpirationSeconds); |
| } |
| |
| // Verifies that GetAuthTokenValid fails when an invalid password is provided. |
| TEST_P(QuickUnlockPrivateUnitTest, GetAuthTokenInvalid) { |
| std::string error = RunAuthTokenWithInvalidPassword(); |
| EXPECT_FALSE(error.empty()); |
| } |
| |
| // Verifies that setting lock screen enabled modifies the setting. |
| TEST_P(QuickUnlockPrivateUnitTest, SetLockScreenEnabled) { |
| PrefService* pref_service = profile()->GetPrefs(); |
| bool lock_screen_enabled = |
| pref_service->GetBoolean(ash::prefs::kEnableAutoScreenLock); |
| |
| SetLockScreenEnabled(token(), !lock_screen_enabled); |
| |
| EXPECT_EQ(!lock_screen_enabled, |
| pref_service->GetBoolean(ash::prefs::kEnableAutoScreenLock)); |
| } |
| |
| // Verifies that setting lock screen enabled fails to modify the setting with |
| // an invalid token. |
| TEST_P(QuickUnlockPrivateUnitTest, SetLockScreenEnabledFailsWithInvalidToken) { |
| PrefService* pref_service = profile()->GetPrefs(); |
| bool lock_screen_enabled = |
| pref_service->GetBoolean(ash::prefs::kEnableAutoScreenLock); |
| |
| std::string error = |
| SetLockScreenEnabledWithInvalidToken(!lock_screen_enabled); |
| EXPECT_FALSE(error.empty()); |
| |
| EXPECT_EQ(lock_screen_enabled, |
| pref_service->GetBoolean(ash::prefs::kEnableAutoScreenLock)); |
| } |
| |
| // Verifies that this returns PIN for GetAvailableModes, unless it is blocked by |
| // policy. |
| TEST_P(QuickUnlockPrivateUnitTest, GetAvailableModes) { |
| EXPECT_EQ(GetAvailableModes(), |
| QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}); |
| |
| quick_unlock::DisablePinByPolicyForTesting(true); |
| EXPECT_TRUE(GetAvailableModes().empty()); |
| } |
| |
| // Verfies that trying to set modes with a valid PIN failes when PIN is blocked |
| // by policy. |
| TEST_P(QuickUnlockPrivateUnitTest, SetModesForPinFailsWhenPinDisabledByPolicy) { |
| quick_unlock::DisablePinByPolicyForTesting(true); |
| EXPECT_FALSE(SetModesWithError("[\"valid\", [\"PIN\"], [\"111\"]]").empty()); |
| } |
| |
| // Verifies that SetModes succeeds with a valid token. |
| TEST_P(QuickUnlockPrivateUnitTest, SetModes) { |
| // Verify there is no active mode. |
| EXPECT_EQ(GetActiveModes(), QuickUnlockModeList{}); |
| |
| RunSetModes(QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, |
| {"111111"}); |
| EXPECT_EQ(GetActiveModes(), |
| QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}); |
| } |
| |
| // Verifies that an invalid password cannot be used to update the mode list. |
| TEST_P(QuickUnlockPrivateUnitTest, SetModesFailsWithInvalidPassword) { |
| // Verify there is no active mode. |
| EXPECT_EQ(GetActiveModes(), QuickUnlockModeList{}); |
| |
| // Try to enable PIN, but use an invalid password. Verify that no event is |
| // raised and GetActiveModes still returns an empty set. |
| FailIfModesChanged(); |
| std::string error = RunSetModesWithInvalidToken(); |
| EXPECT_FALSE(error.empty()); |
| EXPECT_EQ(GetActiveModes(), QuickUnlockModeList{}); |
| } |
| |
| // Verifies that the quickUnlockPrivate.onActiveModesChanged is only raised when |
| // the active set of modes changes. |
| TEST_P(QuickUnlockPrivateUnitTest, ModeChangeEventOnlyRaisedWhenModesChange) { |
| // Make sure quick unlock is turned off, and then verify that turning it off |
| // again does not trigger an event. |
| RunSetModes(QuickUnlockModeList{}, CredentialList{}); |
| FailIfModesChanged(); |
| RunSetModes(QuickUnlockModeList{}, CredentialList{}); |
| |
| // Turn on PIN unlock, and then verify turning it on again and also changing |
| // the password does not trigger an event. |
| ExpectModesChanged( |
| QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}); |
| RunSetModes(QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, |
| {"111111"}); |
| FailIfModesChanged(); |
| RunSetModes(QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, |
| {"222222"}); |
| RunSetModes(QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, |
| {""}); |
| } |
| |
| // Ensures that quick unlock can be enabled and disabled by checking the result |
| // of quickUnlockPrivate.GetActiveModes and PinStoragePrefs::IsPinSet. |
| TEST_P(QuickUnlockPrivateUnitTest, SetModesAndGetActiveModes) { |
| // Update mode to PIN raises an event and updates GetActiveModes. |
| ExpectModesChanged( |
| QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}); |
| RunSetModes(QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, |
| {"111111"}); |
| EXPECT_EQ(GetActiveModes(), |
| QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}); |
| EXPECT_TRUE(IsPinSetInBackend()); |
| |
| // SetModes can be used to turn off a quick unlock mode. |
| ExpectModesChanged(QuickUnlockModeList{}); |
| RunSetModes(QuickUnlockModeList{}, CredentialList{}); |
| EXPECT_EQ(GetActiveModes(), QuickUnlockModeList{}); |
| EXPECT_FALSE(IsPinSetInBackend()); |
| } |
| |
| // Verifies that enabling PIN quick unlock actually talks to the PIN subsystem. |
| TEST_P(QuickUnlockPrivateUnitTest, VerifyAuthenticationAgainstPIN) { |
| RunSetModes(QuickUnlockModeList{}, CredentialList{}); |
| EXPECT_FALSE(IsPinSetInBackend()); |
| |
| RunSetModes(QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, |
| {"111111"}); |
| EXPECT_TRUE(IsPinSetInBackend()); |
| |
| EXPECT_FALSE(TryAuthenticate("000000")); |
| EXPECT_TRUE(TryAuthenticate("111111")); |
| EXPECT_FALSE(TryAuthenticate("000000")); |
| } |
| |
| // Verifies that the number of modes and the number of passwords given must be |
| // the same. |
| TEST_P(QuickUnlockPrivateUnitTest, ThrowErrorOnMismatchedParameterCount) { |
| EXPECT_FALSE(SetModesWithError("[\"valid\", [\"PIN\"], []]").empty()); |
| EXPECT_FALSE(SetModesWithError("[\"valid\", [], [\"11\"]]").empty()); |
| } |
| |
| // Validates PIN error checking in conjuction with policy-related prefs. |
| TEST_P(QuickUnlockPrivateUnitTest, CheckCredentialProblemReporting) { |
| PrefService* pref_service = profile()->GetPrefs(); |
| |
| // Verify the pin checks work with the default preferences which are minimum |
| // length of 6, maximum length of 0 (no maximum) and no easy to guess check. |
| CheckPin(PIN_GOOD, "111112"); |
| CheckPin(PIN_GOOD, "1111112"); |
| CheckPin(PIN_GOOD, "1111111111111112"); |
| CheckPin(PIN_WEAK_WARNING, "111111"); |
| CheckPin(PIN_TOO_SHORT, "1"); |
| CheckPin(PIN_TOO_SHORT, "11"); |
| CheckPin(PIN_TOO_SHORT | PIN_WEAK_WARNING, "111"); |
| CheckPin(PIN_TOO_SHORT | PIN_CONTAINS_NONDIGIT, "a"); |
| CheckPin(PIN_CONTAINS_NONDIGIT, "aaaaab"); |
| CheckPin(PIN_CONTAINS_NONDIGIT | PIN_WEAK_WARNING, "aaaaaa"); |
| CheckPin(PIN_CONTAINS_NONDIGIT | PIN_WEAK_WARNING, "abcdef"); |
| |
| // Verify that now if the minimum length is set to 3, PINs of length 3 are |
| // accepted. |
| pref_service->SetInteger(prefs::kPinUnlockMinimumLength, 3); |
| CheckPin(PIN_WEAK_WARNING, "111"); |
| |
| // Verify setting a nonzero maximum length that is less than the minimum |
| // length results in the pin only accepting PINs of length minimum length. |
| pref_service->SetInteger(prefs::kPinUnlockMaximumLength, 2); |
| pref_service->SetInteger(prefs::kPinUnlockMinimumLength, 4); |
| CheckPin(PIN_GOOD, "1112"); |
| CheckPin(PIN_TOO_SHORT, "112"); |
| CheckPin(PIN_TOO_LONG, "11112"); |
| |
| // Verify that now if the maximum length is set to 5, PINs longer than 5 are |
| // considered too long and cannot be used. |
| pref_service->SetInteger(prefs::kPinUnlockMaximumLength, 5); |
| CheckPin(PIN_TOO_LONG | PIN_WEAK_WARNING, "111111"); |
| CheckPin(PIN_TOO_LONG | PIN_WEAK_WARNING, "1111111"); |
| |
| // Verify that if both the minimum length and maximum length is set to 4, only |
| // 4 digit PINs can be used. |
| pref_service->SetInteger(prefs::kPinUnlockMinimumLength, 4); |
| pref_service->SetInteger(prefs::kPinUnlockMaximumLength, 4); |
| CheckPin(PIN_TOO_SHORT, "122"); |
| CheckPin(PIN_TOO_LONG, "12222"); |
| CheckPin(PIN_GOOD, "1222"); |
| |
| // Set the PINs minimum/maximum lengths back to their defaults. |
| pref_service->SetInteger(prefs::kPinUnlockMinimumLength, 4); |
| pref_service->SetInteger(prefs::kPinUnlockMaximumLength, 0); |
| |
| // Verify that PINs that are weak are flagged as such. See |
| // IsPinDifficultEnough in quick_unlock_private_api.cc for the description of |
| // a weak pin. |
| pref_service->SetBoolean(prefs::kPinUnlockWeakPinsAllowed, false); |
| // Good. |
| CheckPin(PIN_GOOD, "1112"); |
| CheckPin(PIN_GOOD, "7890"); |
| CheckPin(PIN_GOOD, "0987"); |
| // Same digits. |
| CheckPin(PIN_WEAK_ERROR, "1111"); |
| // Increasing. |
| CheckPin(PIN_WEAK_ERROR, "0123"); |
| CheckPin(PIN_WEAK_ERROR, "3456789"); |
| // Decreasing. |
| CheckPin(PIN_WEAK_ERROR, "3210"); |
| CheckPin(PIN_WEAK_ERROR, "987654"); |
| // Too common. |
| CheckPin(PIN_WEAK_ERROR, "1212"); |
| |
| // Verify that if a PIN has more than one error, both are returned. |
| CheckPin(PIN_TOO_SHORT | PIN_WEAK_ERROR, "111"); |
| CheckPin(PIN_TOO_SHORT | PIN_WEAK_ERROR, "234"); |
| } |
| |
| TEST_P(QuickUnlockPrivateUnitTest, GetCredentialRequirements) { |
| PrefService* pref_service = profile()->GetPrefs(); |
| |
| // Verify that trying out PINs under the minimum/maximum lengths will send the |
| // minimum/maximum lengths as additional information for display purposes. |
| pref_service->SetInteger(prefs::kPinUnlockMinimumLength, 6); |
| pref_service->SetInteger(prefs::kPinUnlockMaximumLength, 8); |
| CheckGetCredentialRequirements(6, 8); |
| |
| // Verify that by setting a maximum length to be nonzero and smaller than the |
| // minimum length, the resulting maxium length will be equal to the minimum |
| // length pref. |
| pref_service->SetInteger(prefs::kPinUnlockMaximumLength, 4); |
| CheckGetCredentialRequirements(6, 6); |
| |
| // Verify that the values received from policy are sanitized. |
| pref_service->SetInteger(prefs::kPinUnlockMinimumLength, -3); |
| pref_service->SetInteger(prefs::kPinUnlockMaximumLength, -3); |
| CheckGetCredentialRequirements(1, 0); |
| } |
| |
| INSTANTIATE_TEST_CASE_P(StorageProviders, |
| QuickUnlockPrivateUnitTest, |
| ::testing::Values(TestType::kPrefs, |
| TestType::kCryptohome)); |
| } // namespace chromeos |