| // Copyright 2018 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 <windows.h> |
| |
| #include <sddl.h> // For ConvertSidToStringSid() |
| #include <wrl/client.h> |
| #include <algorithm> |
| #include <vector> |
| |
| #include "base/base_paths_win.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/json/json_writer.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/scoped_path_override.h" |
| #include "base/time/time_override.h" |
| |
| #include "chrome/browser/ui/startup/credential_provider_signin_dialog_win_test_data.h" |
| #include "chrome/credential_provider/common/gcp_strings.h" |
| #include "chrome/credential_provider/gaiacp/gaia_credential_base.h" |
| #include "chrome/credential_provider/gaiacp/gaia_resources.h" |
| #include "chrome/credential_provider/gaiacp/gcpw_strings.h" |
| #include "chrome/credential_provider/gaiacp/mdm_utils.h" |
| #include "chrome/credential_provider/gaiacp/password_recovery_manager.h" |
| #include "chrome/credential_provider/gaiacp/reg_utils.h" |
| #include "chrome/credential_provider/gaiacp/user_policies_manager.h" |
| #include "chrome/credential_provider/test/gls_runner_test_base.h" |
| #include "chrome/credential_provider/test/test_credential.h" |
| #include "google_apis/gaia/gaia_urls.h" |
| #include "net/base/escape.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace credential_provider { |
| |
| namespace testing { |
| |
| constexpr char kFakeResourceId[] = "fake_resource_id"; |
| |
| // DER-encoded, PKIX public key. |
| constexpr char kTestPublicKey[] = |
| "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlFxKse4DGwIDQKLN/4Su\n" |
| "TvF6+J5Juv/Ywwovws+UV7UmXDCRPaaFj36u9LpIqzja2/KG+17Ob7L4KDLLIe6g\n" |
| "mJ2wP9ioawBDJ1JWryNkHcVUcc/bbTgpyD6N0RcpvsbM8YpccYJ1aDAsdKy0593s\n" |
| "ozMUBZ9Y7Z3Yb1Xvoq965At6ihD7s0FMNzehCuwrfJ+A47ChIho0IMxpa2NhrQUo\n" |
| "1Sjm7NEh5u9xTzH+5VtGLJnF5FJ6fWy2YEUfMUM9TxrPPDt795UQj5MyVjph0Ssp\n" |
| "vXuLQ1Ub7zonhhRcfXi/iCC42n+lpW9TeECKXxj/4xAP4Gqq/VoF1Sr1M6+aZTK5\n" |
| "qwIDAQAB"; |
| |
| // DER-encoded, PKCS#8 private key. |
| const char kTestPrivateKey[] = |
| "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCUXEqx7gMbAgNAos3/hK5O8" |
| "Xr4nkm6/9jDCi/Cz5RXtSZcMJE9poWPfq70ukirONrb8ob7Xs5vsvgoMssh7qCYnbA/2KhrAE" |
| "MnUlavI2QdxVRxz9ttOCnIPo3RFym+xszxilxxgnVoMCx0rLTn3eyjMxQFn1jtndhvVe+ir3r" |
| "kC3qKEPuzQUw3N6EK7Ct8n4DjsKEiGjQgzGlrY2GtBSjVKObs0SHm73FPMf7lW0YsmcXkUnp9" |
| "bLZgRR8xQz1PGs88O3v3lRCPkzJWOmHRKym9e4tDVRvvOieGFFx9eL+IILjaf6Wlb1N4QIpfG" |
| "P/jEA/gaqr9WgXVKvUzr5plMrmrAgMBAAECggEACK3liMc900So4A0mM/6VG/Uwln7cHV5+Vd" |
| "qwtJrkOMVWOyp0NMEbKyvkHFkRi0LGOvvTPb1sIki8D8346EFHj+YZu4J3R9s6EoDUpWZSoxM" |
| "6P3ZDhf41I4vVTBgozwpeTvsjMVjKeY/n6eN4qd/nyhxg3XtW/n+ve8PxQvk1HUYfxokJBkjs" |
| "5IF/Nka18Ia/nEjaItnix+tdYPH/e074QorvXR+VYH+YKiOEfVCFH98HyLjsd2g7TOwEzQnzh" |
| "ECSR7tAa7Q4EsrwmpPfQ9TJy476CY/RcVe5waLRfpj8medkVEDgqmds+KI/qI/TMJL2aCTfax" |
| "1g4yBzzf/ADgyBYQKBgQDRbtChbtTM4srMqsIwO/g2kKzv3b/c3fAKW8HbkHdMRAVswJbBMJ/" |
| "OrPO6cxLbpy7CtJzH8A7DSZuVH7oyUTI4xVQRT53MF+dmeDyAdwN8pPeS9pb1o2qCXTBKigKD" |
| "pFUccq2T3dm9wHLdIwysa5PziOUoRGrgHFoyijcazLN5OQKBgQC1WSklS+fPwpI9fQOj949gj" |
| "osTcK/3QeqS2so5xZaSFUPvJtK3PezFGvyF05FM+3VzpS3wfl0Z30msuAMQL7a9tKGykkoUDs" |
| "XgmS+Rg4yoqmzk5nWRuE2AenJZs7rtkyLujrv5QYCG5A7TX5rU+c/GquZbmG4lSZ58hbYOxCC" |
| "+AwKBgE+u8PQq/g5CT9TVN3MwrfzcyN+uqDw5uQXH6ZdHfQxoaQP6tqEkhfkVttn+xHMMRe9Q" |
| "1sH/pS5KSEbRvn88g3Y0Jgs8Fpa7lZBYOPTL02jOP2AMMF2fYnvdRu1lWxWJJdTgEQjMhPb8T" |
| "Pe0STMk7zLeqAnNFjjUsMC/871fmv2JAoGAb7mWl9vD3UPKRQeYDpSeSKaJGFj8kCCUHBWfMS" |
| "iCM03WpKgOecY08NpHaUuG4R6qpazGOLwhL6dZBIf5mydKNmXqmNF3whO35T97BvM83Uzh+cP" |
| "h+vzJArZtbMZGC8fyZXaaaF3qiTBH0gG8qimd0I/Ji/TFJ0PL2HuoRkCey3ECgYAJh9HYMbVe" |
| "9+Sxa1/UL+HSC/AgA8ueMNxzFZ4fI8haab16xefDXwdrHm3PSxt0pn1E1kmTQyP2KPuoOLYas" |
| "q6BRf4WzsjBrS1kPrlCwZNZkPqz3QnV4oVT3tW6q9kWyY+WKz0s7byT0AiriRrCLcQbYYYog7" |
| "OaEw4i7JOShaPsLQ=="; |
| |
| class GcpGaiaCredentialBaseTest : public GlsRunnerTestBase {}; |
| |
| TEST_F(GcpGaiaCredentialBaseTest, Advise) { |
| // Create provider with credentials. This should Advise the credential. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| // Release ref count so the credential can be deleted by the call to |
| // ReleaseProvider. |
| cred.Reset(); |
| |
| // Release the provider. This should unadvise the credential. |
| ASSERT_EQ(S_OK, ReleaseProvider()); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, SetSelected) { |
| // Create provider and credential only. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| // A credential that has not attempted to sign in a user yet should return |
| // false for |auto_login|. |
| BOOL auto_login; |
| ASSERT_EQ(S_OK, cred->SetSelected(&auto_login)); |
| ASSERT_FALSE(auto_login); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_NoInternet) { |
| FakeInternetAvailabilityChecker internet_checker( |
| FakeInternetAvailabilityChecker::kHicForceNo); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcess(/*succeeds=*/false, IDS_NO_NETWORK_BASE)); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_GlsLoadingFailed) { |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| // Fail loading the gls logon UI. |
| test->FailLoadingGaiaLogonStub(); |
| |
| ASSERT_EQ(S_OK, StartLogonProcess( |
| /*succeeds=*/false, IDS_FAILED_CREATE_LOGON_STUB_BASE)); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_Start) { |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| } |
| |
| // Tests the GetSerialization Finish scenario. |
| // 1. Is gem features enabled. If enabled, tos should be tested out. |
| // Otherwise, ToS shouldn't be set irrespective of the |kAcceptTos| |
| // registry entry. |
| class GcpGaiaCredentialGetSerializationBaseTest |
| : public GcpGaiaCredentialBaseTest, |
| public ::testing::WithParamInterface<bool> {}; |
| |
| TEST_P(GcpGaiaCredentialGetSerializationBaseTest, Finish) { |
| bool is_gem_features_enabled = GetParam(); |
| |
| if (is_gem_features_enabled) { |
| // Set |kKeyEnableGemFeatures| registry entry to 1. |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kKeyEnableGemFeatures, 1u)); |
| } else { |
| // Set |kKeyEnableGemFeatures| registry entry to 0. |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kKeyEnableGemFeatures, 0u)); |
| } |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); |
| |
| // Make sure a "foo" user was created. |
| PSID sid; |
| EXPECT_EQ(S_OK, fake_os_user_manager()->GetUserSID( |
| OSUserManager::GetLocalDomain().c_str(), kDefaultUsername, |
| &sid)); |
| |
| // New user should be created. |
| EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| |
| // Finishing logon process should trigger credential changed and trigger |
| // GetSerialization. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| |
| // Make sure ToS acceptance when is_gem_features_enabled isn't enabled. |
| DWORD accept_tos = 0u; |
| wchar_t* user_sid_string = nullptr; |
| ASSERT_TRUE(ConvertSidToStringSid(sid, &user_sid_string)); |
| HRESULT hr = GetUserProperty(user_sid_string, kKeyAcceptTos, &accept_tos); |
| if (is_gem_features_enabled) { |
| ASSERT_EQ(S_OK, hr); |
| ASSERT_EQ(1u, accept_tos); |
| } else { |
| ASSERT_TRUE(FAILED(hr)); |
| ASSERT_EQ(0u, accept_tos); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| GcpGaiaCredentialGetSerializationBaseTest, |
| ::testing::Values(true, false)); |
| |
| // This test emulates the scenario where SetDeselected is triggered by the |
| // Windows Login UI process after GetSerialization prior to invocation of |
| // ReportResult. Note: This currently happens only for OtherUser credential |
| // workflow. |
| TEST_F(GcpGaiaCredentialBaseTest, |
| GetSerialization_SetDeselectedBeforeReportResult) { |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); |
| |
| // Make sure a "foo" user was created. |
| PSID sid; |
| EXPECT_EQ(S_OK, fake_os_user_manager()->GetUserSID( |
| OSUserManager::GetLocalDomain().c_str(), kDefaultUsername, |
| &sid)); |
| |
| // New user should be created. |
| EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| |
| // Finishing logon process should trigger credential changed and trigger |
| // GetSerialization. |
| ASSERT_EQ(S_OK, FinishLogonProcessWithCred(true, true, 0, cred)); |
| |
| // Trigger SetDeselected prior to ReportResult is invoked. |
| cred->SetDeselected(); |
| |
| // Verify that the authentication results dictionary is not empty. |
| ASSERT_FALSE(test->IsAuthenticationResultsEmpty()); |
| ASSERT_FALSE(test->IsAdJoinedUser()); |
| |
| // Trigger ReportResult and verify that the authentication results are saved |
| // into registry and ResetInternalState is triggered. |
| ReportLogonProcessResult(cred); |
| |
| // Verify that the registry entry for the user was created. |
| wchar_t gaia_id[256]; |
| ULONG length = base::size(gaia_id); |
| wchar_t* sidstr = nullptr; |
| ::ConvertSidToStringSid(sid, &sidstr); |
| ::LocalFree(sid); |
| |
| HRESULT gaia_id_hr = GetUserProperty(sidstr, kUserId, gaia_id, &length); |
| ASSERT_EQ(S_OK, gaia_id_hr); |
| ASSERT_TRUE(gaia_id[0]); |
| |
| // Verify that the authentication results dictionary is now empty. |
| ASSERT_TRUE(test->IsAuthenticationResultsEmpty()); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_Abort) { |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| ASSERT_EQ(S_OK, test->SetDefaultExitCode(kUiecAbort)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // Logon process should not signal credentials change or raise an error |
| // message. |
| ASSERT_EQ(S_OK, FinishLogonProcess(false, false, 0)); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, |
| GetSerialization_AssociateToMatchingAssociatedUser) { |
| USES_CONVERSION; |
| // Create a fake user that has the same gaia id as the test gaia id. |
| CComBSTR first_sid; |
| base::string16 username(L"foo"); |
| ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( |
| username, L"password", L"name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), |
| &first_sid)); |
| ASSERT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // User should have been associated. |
| EXPECT_EQ(test->GetFinalUsername(), username); |
| // Email should be the same as the default one. |
| EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); |
| |
| // No new user should be created. |
| EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_MultipleCalls) { |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| constexpr wchar_t kStartGlsEventName[] = |
| L"GetSerialization_MultipleCalls_Wait"; |
| base::win::ScopedHandle start_event_handle( |
| ::CreateEvent(nullptr, false, false, kStartGlsEventName)); |
| ASSERT_TRUE(start_event_handle.IsValid()); |
| ASSERT_EQ(S_OK, test->SetStartGlsEventName(kStartGlsEventName)); |
| base::WaitableEvent start_event(std::move(start_event_handle)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcess(/*succeeds=*/true)); |
| |
| // Calling GetSerialization again while the credential is waiting for the |
| // logon process should yield CPGSR_NO_CREDENTIAL_NOT_FINISHED as a |
| // response. |
| CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr; |
| CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION cpcs; |
| wchar_t* status_text; |
| CREDENTIAL_PROVIDER_STATUS_ICON status_icon; |
| EXPECT_EQ(S_OK, |
| cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); |
| EXPECT_EQ(nullptr, status_text); |
| EXPECT_EQ(CPSI_NONE, status_icon); |
| EXPECT_EQ(CPGSR_NO_CREDENTIAL_NOT_FINISHED, cpgsr); |
| |
| // Signal that the gls process can finish. |
| start_event.Signal(); |
| |
| ASSERT_EQ(S_OK, WaitForLogonProcess()); |
| } |
| |
| // Test disabling force reset password field. If provided gaia password |
| // isn't valid, credential goes into password recovery flow. If the force reset |
| // password is disabled through registry, force reset password field should be |
| // in CPFS_HIDDEN state. |
| // 0 - Disable force reset pasword link. |
| // 1 - Enable force reset password link. |
| // 2 - Test default value of registry. By default force reset password link |
| // should be enabled. |
| class GcpGaiaCredentialBaseForceResetRegistryTest |
| : public GcpGaiaCredentialBaseTest, |
| public ::testing::WithParamInterface<int> {}; |
| |
| TEST_P(GcpGaiaCredentialBaseForceResetRegistryTest, |
| ForceResetPasswordRegistry) { |
| int enable_forgot_password_registry_value = GetParam(); |
| |
| if (enable_forgot_password_registry_value < 2) |
| ASSERT_EQ(S_OK, |
| SetGlobalFlagForTesting(kRegMdmEnableForcePasswordReset, |
| enable_forgot_password_registry_value)); |
| |
| // Create a fake user for which the windows password does not match the gaia |
| // password supplied by the test gls process. |
| CComBSTR sid; |
| CComBSTR windows_password = L"password2"; |
| ASSERT_EQ(S_OK, |
| fake_os_user_manager()->CreateTestOSUser( |
| L"foo", (BSTR)windows_password, L"Full Name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid)); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| ASSERT_EQ(CPFS_HIDDEN, |
| fake_credential_provider_credential_events()->GetFieldState( |
| cred.Get(), FID_FORGOT_PASSWORD_LINK)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| EXPECT_TRUE(test->CanAttemptWindowsLogon()); |
| EXPECT_FALSE(test->IsWindowsPasswordValidForStoredUser()); |
| |
| if (!enable_forgot_password_registry_value) { |
| ASSERT_EQ(CPFS_HIDDEN, |
| fake_credential_provider_credential_events()->GetFieldState( |
| cred.Get(), FID_FORGOT_PASSWORD_LINK)); |
| } else { |
| ASSERT_EQ(CPFS_DISPLAY_IN_SELECTED_TILE, |
| fake_credential_provider_credential_events()->GetFieldState( |
| cred.Get(), FID_FORGOT_PASSWORD_LINK)); |
| } |
| |
| // Update the Windows password to be the real password created for the user. |
| cred->SetStringValue(FID_CURRENT_PASSWORD_FIELD, windows_password); |
| // Sign in information should still be available. |
| EXPECT_TRUE(test->GetFinalEmail().length()); |
| |
| // Both Windows and Gaia credentials should be valid now. |
| EXPECT_TRUE(test->CanAttemptWindowsLogon()); |
| EXPECT_TRUE(test->IsWindowsPasswordValidForStoredUser()); |
| |
| // Finish logon successfully but with no credential changed event. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, false, 0)); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| GcpGaiaCredentialBaseForceResetRegistryTest, |
| ::testing::Values(0, 1, 2)); |
| |
| TEST_F(GcpGaiaCredentialBaseTest, |
| GetSerialization_PasswordChangedForAssociatedUser) { |
| USES_CONVERSION; |
| |
| // Create a fake user for which the windows password does not match the gaia |
| // password supplied by the test gls process. |
| CComBSTR sid; |
| CComBSTR windows_password = L"password2"; |
| ASSERT_EQ(S_OK, |
| fake_os_user_manager()->CreateTestOSUser( |
| L"foo", (BSTR)windows_password, L"Full Name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid)); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| ASSERT_EQ(CPFS_HIDDEN, |
| fake_credential_provider_credential_events()->GetFieldState( |
| cred.Get(), FID_FORGOT_PASSWORD_LINK)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| EXPECT_TRUE(test->CanAttemptWindowsLogon()); |
| EXPECT_FALSE(test->IsWindowsPasswordValidForStoredUser()); |
| |
| ASSERT_EQ(CPFS_DISPLAY_IN_SELECTED_TILE, |
| fake_credential_provider_credential_events()->GetFieldState( |
| cred.Get(), FID_FORGOT_PASSWORD_LINK)); |
| |
| // Check that the process has not finished yet. |
| CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr; |
| CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION cpcs; |
| wchar_t* status_text; |
| CREDENTIAL_PROVIDER_STATUS_ICON status_icon; |
| ASSERT_EQ(S_OK, |
| cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); |
| EXPECT_EQ(nullptr, status_text); |
| EXPECT_EQ(CPGSR_NO_CREDENTIAL_NOT_FINISHED, cpgsr); |
| |
| // Credentials should still be available. |
| EXPECT_TRUE(test->CanAttemptWindowsLogon()); |
| EXPECT_FALSE(test->IsWindowsPasswordValidForStoredUser()); |
| |
| // Set an invalid password and try to get serialization again. Credentials |
| // should still be valid but serialization is not complete. |
| CComBSTR invalid_windows_password = L"a"; |
| cred->SetStringValue(FID_CURRENT_PASSWORD_FIELD, invalid_windows_password); |
| EXPECT_EQ(nullptr, status_text); |
| ASSERT_EQ(S_OK, |
| cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); |
| EXPECT_EQ(CPGSR_NO_CREDENTIAL_NOT_FINISHED, cpgsr); |
| |
| // Update the Windows password to be the real password created for the user. |
| cred->SetStringValue(FID_CURRENT_PASSWORD_FIELD, windows_password); |
| // Sign in information should still be available. |
| EXPECT_TRUE(test->GetFinalEmail().length()); |
| |
| // Both Windows and Gaia credentials should be valid now |
| EXPECT_TRUE(test->CanAttemptWindowsLogon()); |
| EXPECT_TRUE(test->IsWindowsPasswordValidForStoredUser()); |
| |
| // Finish logon successfully but with no credential changed event. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, false, 0)); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, |
| GetSerialization_ForgotPasswordForAssociatedUser) { |
| USES_CONVERSION; |
| |
| // Create a fake user for which the windows password does not match the gaia |
| // password supplied by the test gls process. |
| CComBSTR sid; |
| CComBSTR windows_password = L"password2"; |
| ASSERT_EQ(S_OK, |
| fake_os_user_manager()->CreateTestOSUser( |
| L"foo", (BSTR)windows_password, L"Full Name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid)); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| EXPECT_TRUE(test->CanAttemptWindowsLogon()); |
| EXPECT_FALSE(test->IsWindowsPasswordValidForStoredUser()); |
| |
| // Check that the process has not finished yet. |
| CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr; |
| CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION cpcs; |
| wchar_t* status_text; |
| CREDENTIAL_PROVIDER_STATUS_ICON status_icon; |
| ASSERT_EQ(S_OK, |
| cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); |
| EXPECT_EQ(nullptr, status_text); |
| EXPECT_EQ(CPGSR_NO_CREDENTIAL_NOT_FINISHED, cpgsr); |
| |
| // Credentials should still be available. |
| EXPECT_TRUE(test->CanAttemptWindowsLogon()); |
| EXPECT_FALSE(test->IsWindowsPasswordValidForStoredUser()); |
| |
| // Simulate a click on the "Forgot Password" link. |
| cred->CommandLinkClicked(FID_FORGOT_PASSWORD_LINK); |
| |
| // Finish logon successfully but with no credential changed event. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, false, 0)); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, |
| GetSerialization_AlternateForgotPasswordAssociatedUser) { |
| USES_CONVERSION; |
| |
| // Create a fake user for which the windows password does not match the gaia |
| // password supplied by the test gls process. |
| CComBSTR sid; |
| CComBSTR windows_password = L"password2"; |
| ASSERT_EQ(S_OK, |
| fake_os_user_manager()->CreateTestOSUser( |
| L"foo", (BSTR)windows_password, L"Full Name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid)); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| EXPECT_TRUE(test->CanAttemptWindowsLogon()); |
| EXPECT_FALSE(test->IsWindowsPasswordValidForStoredUser()); |
| |
| // Check that the process has not finished yet. |
| CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr; |
| CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION cpcs; |
| wchar_t* status_text; |
| CREDENTIAL_PROVIDER_STATUS_ICON status_icon; |
| ASSERT_EQ(S_OK, |
| cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); |
| EXPECT_EQ(nullptr, status_text); |
| EXPECT_EQ(CPGSR_NO_CREDENTIAL_NOT_FINISHED, cpgsr); |
| |
| // Credentials should still be available. |
| EXPECT_TRUE(test->CanAttemptWindowsLogon()); |
| EXPECT_FALSE(test->IsWindowsPasswordValidForStoredUser()); |
| |
| // Simulate a click on the "Forgot Password" link. |
| cred->CommandLinkClicked(FID_FORGOT_PASSWORD_LINK); |
| |
| // Go back to windows password entry. |
| cred->CommandLinkClicked(FID_FORGOT_PASSWORD_LINK); |
| |
| // Set an invalid password and try to get serialization again. Credentials |
| // should still be valid but serialization is not complete. |
| CComBSTR invalid_windows_password = L"a"; |
| cred->SetStringValue(FID_CURRENT_PASSWORD_FIELD, invalid_windows_password); |
| EXPECT_EQ(nullptr, status_text); |
| ASSERT_EQ(S_OK, |
| cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); |
| EXPECT_EQ(CPGSR_NO_CREDENTIAL_NOT_FINISHED, cpgsr); |
| |
| // Update the Windows password to be the real password created for the user. |
| cred->SetStringValue(FID_CURRENT_PASSWORD_FIELD, windows_password); |
| // Sign in information should still be available. |
| EXPECT_TRUE(test->GetFinalEmail().length()); |
| |
| // Both Windows and Gaia credentials should be valid now |
| EXPECT_TRUE(test->CanAttemptWindowsLogon()); |
| EXPECT_TRUE(test->IsWindowsPasswordValidForStoredUser()); |
| |
| // Finish logon successfully but with no credential changed event. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, false, 0)); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_Cancel) { |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| // This event is merely used to keep the gls running while it is cancelled |
| // through SetDeselected(). |
| constexpr wchar_t kStartGlsEventName[] = L"GetSerialization_Cancel_Signal"; |
| base::win::ScopedHandle start_event_handle( |
| ::CreateEvent(nullptr, false, false, kStartGlsEventName)); |
| ASSERT_TRUE(start_event_handle.IsValid()); |
| ASSERT_EQ(S_OK, test->SetStartGlsEventName(kStartGlsEventName)); |
| base::WaitableEvent start_event(std::move(start_event_handle)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcess(/*succeeds=*/true)); |
| |
| // Deselect the credential provider so that it cancels the GLS process and |
| // returns. |
| ASSERT_EQ(S_OK, cred->SetDeselected()); |
| |
| ASSERT_EQ(S_OK, WaitForLogonProcess()); |
| |
| // Logon process should not signal credentials change or raise an error |
| // message. |
| ASSERT_EQ(S_OK, FinishLogonProcess(false, false, 0)); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, FailedUserCreation) { |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| // Fail user creation. |
| fake_os_user_manager()->SetFailureReason(FAILEDOPERATIONS::ADD_USER, E_FAIL); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // Logon process should fail with an internal error. |
| ASSERT_EQ(S_OK, FinishLogonProcess(false, false, IDS_INTERNAL_ERROR_BASE)); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, FailedUserCreation_PasswordTooShort) { |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| // Fail user creation. |
| fake_os_user_manager()->SetFailureReason( |
| FAILEDOPERATIONS::ADD_USER, HRESULT_FROM_WIN32(NERR_PasswordTooShort)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // Logon process should fail with an internal error. |
| ASSERT_EQ(S_OK, FinishLogonProcess(false, false, |
| IDS_CREATE_USER_PASSWORD_TOO_SHORT_BASE)); |
| } |
| |
| class GcpGaiaCredentialBaseInvalidDomainTest |
| : public GcpGaiaCredentialBaseTest, |
| public ::testing::WithParamInterface< |
| std::tuple<const wchar_t*, const wchar_t*>> { |
| }; |
| |
| TEST_P(GcpGaiaCredentialBaseInvalidDomainTest, Fail) { |
| // Setting those registry keys to empty string effectively deletes them. |
| SetGlobalFlagForTesting(L"ed", L""); |
| SetGlobalFlagForTesting(L"domains_allowed_to_login", L""); |
| |
| const wchar_t* allow_domains_key = std::get<0>(GetParam()); |
| const base::string16 allowed_email_domains = std::get<1>(GetParam()); |
| ASSERT_EQ(S_OK, |
| SetGlobalFlagForTesting(allow_domains_key, allowed_email_domains)); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| if (allowed_email_domains.empty()) { |
| // Fails due to missing registry key for allowed domains. |
| ASSERT_EQ(S_OK, |
| StartLogonProcess(/*succeeds=*/false, IDS_EMAIL_MISMATCH_BASE)); |
| } else { |
| // Fail due to invalid domain. |
| ASSERT_EQ(S_OK, test->SetDefaultExitCode(kUiecInvalidEmailDomain)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| base::string16 expected_error_msg = |
| GetStringResource(IDS_INVALID_EMAIL_DOMAIN_BASE); |
| |
| // Logon process should fail with the specified error message. |
| ASSERT_EQ(S_OK, FinishLogonProcess(false, false, expected_error_msg)); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| GcpGaiaCredentialBaseInvalidDomainTest, |
| ::testing::Combine(::testing::Values(L"ed", L"domains_allowed_to_login"), |
| ::testing::Values(L"acme.com,acme2.com,acme3.com", |
| L""))); |
| |
| class GcpGaiaCredentialBasePermittedAccountTest |
| : public GcpGaiaCredentialBaseTest, |
| public ::testing::WithParamInterface< |
| std::tuple<const wchar_t*, const wchar_t*>> { |
| }; |
| |
| TEST_P(GcpGaiaCredentialBasePermittedAccountTest, PermittedAccounts) { |
| const base::string16 permitted_acounts = std::get<0>(GetParam()); |
| const base::string16 restricted_domains = std::get<1>(GetParam()); |
| |
| ASSERT_EQ(S_OK, |
| SetGlobalFlagForTesting(L"permitted_accounts", permitted_acounts)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(L"domains_allowed_to_login", |
| restricted_domains)); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| base::string16 email = L"user@test.com"; |
| base::string16 email_domain = email.substr(email.find(L"@") + 1); |
| |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(base::UTF16ToUTF8(email))); |
| |
| bool allowed_email = permitted_acounts.empty() || |
| permitted_acounts.find(email) != base::string16::npos; |
| bool found_domain = |
| restricted_domains.find(email_domain) != base::string16::npos; |
| |
| if (!found_domain) |
| ASSERT_EQ(S_OK, test->SetDefaultExitCode(kUiecInvalidEmailDomain)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| if (allowed_email && found_domain) { |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| } else { |
| base::string16 expected_error_msg; |
| if (!found_domain) { |
| expected_error_msg = GetStringResource(IDS_INVALID_EMAIL_DOMAIN_BASE); |
| } else { |
| expected_error_msg = GetStringResource(IDS_EMAIL_MISMATCH_BASE); |
| } |
| // Logon process should fail with the specified error message. |
| ASSERT_EQ(S_OK, FinishLogonProcess(false, false, expected_error_msg)); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| GcpGaiaCredentialBasePermittedAccountTest, |
| ::testing::Combine( |
| ::testing::Values(L"", |
| L"user@test.com", |
| L"other@test.com", |
| L"other@test.com,user@test.com"), |
| ::testing::Values(L"test.com", L"best.com", L"test.com,best.com"))); |
| |
| TEST_F(GcpGaiaCredentialBaseTest, StripEmailTLD) { |
| USES_CONVERSION; |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| constexpr char email[] = "foo@imfl.info"; |
| |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_STREQ(W2COLE(L"foo_imfl"), test->GetFinalUsername()); |
| EXPECT_EQ(test->GetFinalEmail(), email); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, TrimPeriodAtTheEnd) { |
| USES_CONVERSION; |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| // The top level domain("info" in this example) is removed and the rest is |
| // truncated to be 20 characters. However, in this example, this will result |
| // with "abcdefghijklmn_abcd." which isn't valid per Microsoft documentation. |
| // The rule says there shouldn't be a '.' at the end. Thus it needs to be |
| // removed. |
| constexpr char email[] = "abcdefghijklmn@abcd.ef.info"; |
| |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_STREQ(W2COLE(L"abcdefghijklmn_abcd"), test->GetFinalUsername()); |
| EXPECT_EQ(test->GetFinalEmail(), email); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, UseShorterFormForAccountName) { |
| USES_CONVERSION; |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegUseShorterAccountName, 1)); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| constexpr char email[] = "abc@def.com"; |
| |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_STREQ(W2COLE(L"abc"), test->GetFinalUsername()); |
| EXPECT_EQ(test->GetFinalEmail(), email); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, UseShorterFormForAccountNameWithConflict) { |
| USES_CONVERSION; |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegUseShorterAccountName, 1)); |
| |
| const wchar_t user_name[] = L"abc"; |
| const wchar_t password[] = L"password"; |
| |
| CComBSTR local_sid; |
| DWORD error; |
| HRESULT hr = fake_os_user_manager()->AddUser( |
| user_name, password, L"fullname", L"comment", true, &local_sid, &error); |
| ASSERT_EQ(S_OK, hr); |
| ASSERT_EQ(0u, error); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| constexpr char email[] = "abc@def.com"; |
| |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_STREQ(W2COLE(L"abc2"), test->GetFinalUsername()); |
| EXPECT_EQ(test->GetFinalEmail(), email); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, NewUserDisabledThroughUsageScenario) { |
| USES_CONVERSION; |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| // Set the other user tile so that we can get the anonymous credential |
| // that may try create a new user. |
| fake_user_array()->SetAccountOptions(CPAO_EMPTY_LOCAL); |
| |
| SetUsageScenario(CPUS_UNLOCK_WORKSTATION); |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // Sign in should fail with an error stating that no new users can be created. |
| ASSERT_EQ(S_OK, FinishLogonProcess(false, false, |
| IDS_INVALID_UNLOCK_WORKSTATION_USER_BASE)); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, NewUserDisabledThroughMdm) { |
| USES_CONVERSION; |
| // Enforce single user mode for MDM. |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegEnableDmEnrollment, 1)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmAllowConsumerAccounts, 1)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmSupportsMultiUser, 0)); |
| GoogleMdmEnrolledStatusForTesting force_success(true); |
| |
| // Create a fake user that is already associated so when the user tries to |
| // sign on and create a new user, it fails. |
| CComBSTR sid; |
| ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( |
| L"foo_registered", L"password", L"name", L"comment", |
| L"gaia-id-registered", base::string16(), &sid)); |
| |
| // Populate the associated users list. The created user's token handle |
| // should be valid so that no reauth credential is created. |
| fake_associated_user_validator()->StartRefreshingTokenHandleValidity(); |
| |
| // Set the other user tile so that we can get the anonymous credential |
| // that may try to sign in a user. |
| fake_user_array()->SetAccountOptions(CPAO_EMPTY_LOCAL); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // Sign in should fail with an error stating that no new users can be created. |
| ASSERT_EQ(S_OK, |
| FinishLogonProcess(false, false, IDS_ADD_USER_DISALLOWED_BASE)); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, InvalidUserUnlockedAfterSignin) { |
| // Enforce token handle verification with user locking when the token handle |
| // is not valid. |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegEnableDmEnrollment, 1)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmAllowConsumerAccounts, 1)); |
| GoogleMdmEnrollmentStatusForTesting force_success(true); |
| |
| USES_CONVERSION; |
| // Create a fake user that has the same gaia id as the test gaia id. |
| CComBSTR sid; |
| base::string16 username(L"foo"); |
| ASSERT_EQ(S_OK, |
| fake_os_user_manager()->CreateTestOSUser( |
| username, L"password", L"name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid)); |
| ASSERT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| // Create with invalid token handle response. |
| SetDefaultTokenHandleResponse(kDefaultInvalidTokenHandleResponse); |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| // User should have invalid token handle and be locked. |
| EXPECT_TRUE( |
| fake_associated_user_validator()->IsAuthEnforcedForUser(OLE2W(sid))); |
| EXPECT_EQ(true, |
| fake_associated_user_validator()->IsUserAccessBlockedForTesting( |
| OLE2W(sid))); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // User should have been associated. |
| EXPECT_EQ(test->GetFinalUsername(), username); |
| // Email should be the same as the default one. |
| EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); |
| |
| // Now finish the logon, this should unlock the user. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| |
| EXPECT_EQ(false, |
| fake_associated_user_validator()->IsUserAccessBlockedForTesting( |
| OLE2W(sid))); |
| |
| // No new user should be created. |
| EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, SigninNotBlockedWhenValidChromeNotFound) { |
| // Enforce token handle verification with user locking when the token handle |
| // is not valid. |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegEnableDmEnrollment, 1)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmAllowConsumerAccounts, 1)); |
| GoogleMdmEnrollmentStatusForTesting force_success(true); |
| |
| // Simulate a valid Chrome installation not being found. |
| fake_chrome_checker()->SetHasSupportedChrome( |
| FakeChromeAvailabilityChecker::kChromeForceNo); |
| |
| USES_CONVERSION; |
| // Create a fake user that has the same gaia id as the test gaia id. |
| CComBSTR sid; |
| base::string16 username(L"foo"); |
| ASSERT_EQ(S_OK, |
| fake_os_user_manager()->CreateTestOSUser( |
| username, L"password", L"name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid)); |
| ASSERT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| // Create with invalid token handle response. |
| SetDefaultTokenHandleResponse(kDefaultInvalidTokenHandleResponse); |
| ASSERT_EQ(E_FAIL, InitializeProviderAndGetCredential(0, &cred)); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, DenySigninBlockedDuringSignin) { |
| USES_CONVERSION; |
| |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegEnableDmEnrollment, 1)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmSupportsMultiUser, 1)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmAllowConsumerAccounts, 1)); |
| GoogleMdmEnrolledStatusForTesting force_success(true); |
| GoogleUploadDeviceDetailsNeededForTesting upload_device_details_needed(false); |
| |
| // Create a fake user that has the same gaia id as the test gaia id. |
| CComBSTR first_sid; |
| base::string16 username(L"foo"); |
| ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( |
| username, L"password", L"name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), |
| &first_sid)); |
| ASSERT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| |
| std::vector<base::string16> reauth_sids; |
| reauth_sids.push_back((BSTR)first_sid); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| // Create with valid token handle response and sign in the anonymous |
| // credential with the user that should still be valid. |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // Change token response to an invalid one. |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(AssociatedUserValidator::kTokenInfoUrl), |
| FakeWinHttpUrlFetcher::Headers(), "{}"); |
| |
| // Force refresh of all token handles on the next query. |
| fake_associated_user_validator()->ForceRefreshTokenHandlesForTesting(); |
| |
| // Signin process has already started. User should not be locked even if their |
| // token handle is invalid. |
| EXPECT_FALSE( |
| fake_associated_user_validator() |
| ->DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON, reauth_sids)); |
| EXPECT_FALSE(fake_associated_user_validator()->IsUserAccessBlockedForTesting( |
| OLE2W(first_sid))); |
| |
| // Now finish the logon. |
| ASSERT_EQ(S_OK, FinishLogonProcessWithCred(true, true, 0, cred)); |
| |
| // User should have been associated. |
| EXPECT_EQ(test->GetFinalUsername(), username); |
| // Email should be the same as the default one. |
| EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); |
| |
| // Result has not been reported yet, user signin should still not be denied. |
| EXPECT_FALSE( |
| fake_associated_user_validator() |
| ->DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON, reauth_sids)); |
| EXPECT_FALSE(fake_associated_user_validator()->IsUserAccessBlockedForTesting( |
| OLE2W(first_sid))); |
| |
| ReportLogonProcessResult(cred); |
| |
| // Now signin can be denied for the user if their token handle is invalid. |
| EXPECT_TRUE( |
| fake_associated_user_validator() |
| ->DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON, reauth_sids)); |
| EXPECT_TRUE(fake_associated_user_validator()->IsUserAccessBlockedForTesting( |
| OLE2W(first_sid))); |
| |
| // No new user should be created. |
| EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| } |
| |
| class BaseTimeClockOverrideValue { |
| public: |
| static base::Time NowOverride() { return current_time_; } |
| static base::Time current_time_; |
| }; |
| |
| base::Time BaseTimeClockOverrideValue::current_time_; |
| TEST_F(GcpGaiaCredentialBaseTest, |
| DenySigninBlockedDuringSignin_StaleOnlineLogin) { |
| USES_CONVERSION; |
| |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegEnableDmEnrollment, 1)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmSupportsMultiUser, 1)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmAllowConsumerAccounts, 1)); |
| GoogleUploadDeviceDetailsNeededForTesting upload_device_details_needed(false); |
| |
| // Create a fake user that has the same gaia id as the test gaia id. |
| CComBSTR first_sid; |
| base::string16 username(L"foo"); |
| ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( |
| username, L"password", L"name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), |
| &first_sid)); |
| ASSERT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| |
| std::vector<base::string16> reauth_sids; |
| reauth_sids.push_back((BSTR)first_sid); |
| |
| // Set the current time same as last token valid timestamp. |
| base::Time last_token_valid = base::Time::Now(); |
| base::string16 last_token_valid_millis = base::NumberToString16( |
| last_token_valid.ToDeltaSinceWindowsEpoch().InMilliseconds()); |
| int validity_period_in_days = 10; |
| DWORD validity_period_in_days_dword = |
| static_cast<DWORD>(validity_period_in_days); |
| ASSERT_EQ(S_OK, |
| SetUserProperty((BSTR)first_sid, |
| base::UTF8ToUTF16(std::string(kKeyLastTokenValid)), |
| last_token_valid_millis)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting( |
| base::UTF8ToUTF16(std::string(kKeyValidityPeriodInDays)), |
| validity_period_in_days_dword)); |
| |
| GoogleMdmEnrolledStatusForTesting force_success(true); |
| fake_internet_checker()->SetHasInternetConnection( |
| FakeInternetAvailabilityChecker::kHicForceYes); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| SetDefaultTokenHandleResponse(kDefaultValidTokenHandleResponse); |
| |
| // Create with valid token handle response and sign in the anonymous |
| // credential with the user that should still be valid. |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| // User access shouldn't be blocked before login starts. |
| EXPECT_FALSE( |
| fake_associated_user_validator() |
| ->DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON, reauth_sids)); |
| EXPECT_FALSE(fake_associated_user_validator()->IsUserAccessBlockedForTesting( |
| OLE2W(first_sid))); |
| |
| // Internet should be disabled for stale online login verifications to be |
| // considered. |
| SetDefaultTokenHandleResponse(kDefaultInvalidTokenHandleResponse); |
| fake_internet_checker()->SetHasInternetConnection( |
| FakeInternetAvailabilityChecker::kHicForceNo); |
| // Advance the time that is more than the offline validity period. |
| BaseTimeClockOverrideValue::current_time_ = |
| base::Time::Now() + base::TimeDelta::FromDays(validity_period_in_days) + |
| base::TimeDelta::FromMilliseconds(1); |
| base::subtle::ScopedTimeClockOverrides time_override( |
| &BaseTimeClockOverrideValue::NowOverride, nullptr, nullptr); |
| |
| // User access should be blocked now that the time has been moved. |
| ASSERT_TRUE( |
| fake_associated_user_validator() |
| ->DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON, reauth_sids)); |
| EXPECT_TRUE(fake_associated_user_validator()->IsUserAccessBlockedForTesting( |
| OLE2W(first_sid))); |
| |
| // Reset the internet back to being on. |
| fake_internet_checker()->SetHasInternetConnection( |
| FakeInternetAvailabilityChecker::kHicForceYes); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // Now finish the logon. |
| ASSERT_EQ(S_OK, FinishLogonProcessWithCred(true, true, 0, cred)); |
| |
| // User should have been associated. |
| EXPECT_EQ(test->GetFinalUsername(), username); |
| // Email should be the same as the default one. |
| EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); |
| |
| ReportLogonProcessResult(cred); |
| |
| // User access shouldn't be blocked after login completes. |
| EXPECT_FALSE( |
| fake_associated_user_validator() |
| ->DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON, reauth_sids)); |
| EXPECT_FALSE(fake_associated_user_validator()->IsUserAccessBlockedForTesting( |
| OLE2W(first_sid))); |
| |
| wchar_t latest_token_valid_millis[512]; |
| ULONG latest_token_valid_size = base::size(latest_token_valid_millis); |
| ASSERT_EQ(S_OK, GetUserProperty( |
| OLE2W(first_sid), base::UTF8ToUTF16(kKeyLastTokenValid), |
| latest_token_valid_millis, &latest_token_valid_size)); |
| int64_t latest_token_valid_millis_int64; |
| base::StringToInt64(latest_token_valid_millis, |
| &latest_token_valid_millis_int64); |
| |
| long difference = |
| latest_token_valid_millis_int64 - |
| BaseTimeClockOverrideValue::current_time_.ToDeltaSinceWindowsEpoch() |
| .InMilliseconds(); |
| ASSERT_EQ(0, difference); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, StripEmailTLD_Gmail) { |
| USES_CONVERSION; |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| constexpr char email[] = "bar@gmail.com"; |
| |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_STREQ(W2COLE(L"bar"), test->GetFinalUsername()); |
| EXPECT_EQ(test->GetFinalEmail(), email); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, StripEmailTLD_Googlemail) { |
| USES_CONVERSION; |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| constexpr char email[] = "toto@googlemail.com"; |
| |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_STREQ(W2COLE(L"toto"), test->GetFinalUsername()); |
| EXPECT_EQ(test->GetFinalEmail(), email); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, InvalidUsernameCharacters) { |
| USES_CONVERSION; |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| constexpr char email[] = "a\\[]:|<>+=;?*z@gmail.com"; |
| |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_STREQ(W2COLE(L"a____________z"), test->GetFinalUsername()); |
| EXPECT_EQ(test->GetFinalEmail(), email); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, EmailTooLong) { |
| USES_CONVERSION; |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| constexpr char email[] = "areallylongemailadressdude@gmail.com"; |
| |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_STREQ(W2COLE(L"areallylongemailadre"), test->GetFinalUsername()); |
| EXPECT_EQ(test->GetFinalEmail(), email); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, EmailTooLong2) { |
| USES_CONVERSION; |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| constexpr char email[] = "foo@areallylongdomaindude.com"; |
| |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_STREQ(W2COLE(L"foo_areallylongdomai"), test->GetFinalUsername()); |
| EXPECT_EQ(test->GetFinalEmail(), email); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, EmailIsNoAt) { |
| USES_CONVERSION; |
| constexpr char email[] = "foo"; |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_STREQ(W2COLE(L"foo_gmail"), test->GetFinalUsername()); |
| EXPECT_EQ(test->GetFinalEmail(), email); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, EmailIsAtCom) { |
| USES_CONVERSION; |
| |
| constexpr char email[] = "@com"; |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_STREQ(W2COLE(L"_com"), test->GetFinalUsername()); |
| EXPECT_EQ(test->GetFinalEmail(), email); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseTest, EmailIsAtDotCom) { |
| USES_CONVERSION; |
| |
| constexpr char email[] = "@.com"; |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_STREQ(W2COLE(L"_.com"), test->GetFinalUsername()); |
| EXPECT_EQ(test->GetFinalEmail(), email); |
| } |
| |
| // Test various existing local account mapping or active directory account |
| // mapping in cloud sign-in scenarios. |
| class GcpGaiaCredentialBaseCloudMappingTest |
| : public GcpGaiaCredentialBaseTest, |
| public ::testing::WithParamInterface<bool> { |
| protected: |
| void SetUp() override; |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred_; |
| // The admin sdk users directory get URL. |
| std::string get_cd_user_url_ = base::StringPrintf( |
| "https://www.googleapis.com/admin/directory/v1/users/" |
| "%s?projection=full&viewType=domain_public", |
| net::EscapeUrlEncodedData(kDefaultEmail, true).c_str()); |
| GaiaUrls* gaia_urls_ = GaiaUrls::GetInstance(); |
| bool is_ad_user = GetParam(); |
| }; |
| |
| void GcpGaiaCredentialBaseCloudMappingTest::SetUp() { |
| GcpGaiaCredentialBaseTest::SetUp(); |
| if (is_ad_user) { |
| // Set the device as a domain joined machine. |
| fake_os_user_manager()->SetIsDeviceDomainJoined(true); |
| } |
| |
| // Override registry to enable cloud association with google. |
| constexpr wchar_t kRegCloudAssociation[] = L"enable_cloud_association"; |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegCloudAssociation, 1)); |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred_)); |
| } |
| |
| // Fetching downscoped access token required for calling admin sdk failed. |
| // The login attempt would fail in this scenario. |
| TEST_P(GcpGaiaCredentialBaseCloudMappingTest, |
| GetSerialization_CallToFetchDownscopedAccessTokenFailed) { |
| // Attempt to fetch the token from gaia fails. |
| fake_http_url_fetcher_factory()->SetFakeFailedResponse( |
| GURL(gaia_urls_->oauth2_token_url().spec().c_str()), E_FAIL); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred_.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_TRUE(base::size(test->GetFinalEmail()) == 0); |
| |
| // Make sure no user was created and the login attempt failed. |
| PSID sid = nullptr; |
| EXPECT_EQ( |
| HRESULT_FROM_WIN32(NERR_UserNotFound), |
| fake_os_user_manager()->GetUserSID( |
| OSUserManager::GetLocalDomain().c_str(), kDefaultUsername, &sid)); |
| ASSERT_EQ(nullptr, sid); |
| |
| // No new user is created. |
| EXPECT_EQ(1ul, fake_os_user_manager()->GetUserCount()); |
| |
| // TODO(crbug.com/976406): Set the error message appropriately for failure |
| // scenarios. |
| ASSERT_EQ(S_OK, FinishLogonProcess( |
| /*expected_success=*/false, |
| /*expected_credentials_change_fired=*/false, |
| IDS_INTERNAL_ERROR_BASE)); |
| } |
| |
| // Empty access token returned. |
| TEST_P(GcpGaiaCredentialBaseCloudMappingTest, |
| GetSerialization_EmptyAccessTokenReturned) { |
| // Set token result to not contain any access token. |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(gaia_urls_->oauth2_token_url().spec().c_str()), |
| FakeWinHttpUrlFetcher::Headers(), "{}"); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred_.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_TRUE(base::size(test->GetFinalEmail()) == 0); |
| |
| // Make sure no user was created and the login attempt failed. |
| PSID sid = nullptr; |
| EXPECT_EQ( |
| HRESULT_FROM_WIN32(NERR_UserNotFound), |
| fake_os_user_manager()->GetUserSID( |
| OSUserManager::GetLocalDomain().c_str(), kDefaultUsername, &sid)); |
| ASSERT_EQ(nullptr, sid); |
| |
| // No new user is created. |
| EXPECT_EQ(1ul, fake_os_user_manager()->GetUserCount()); |
| |
| ASSERT_EQ(S_OK, FinishLogonProcess( |
| /*expected_success=*/false, |
| /*expected_credentials_change_fired=*/false, |
| IDS_EMPTY_ACCESS_TOKEN_BASE)); |
| } |
| |
| // Empty AD_accounts or Local_Windows_accounts is returned via admin sdk. |
| TEST_P(GcpGaiaCredentialBaseCloudMappingTest, |
| GetSerialization_NoUserNameFoundFromAdminSdk) { |
| // Set token result a valid access token. |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(gaia_urls_->oauth2_token_url().spec().c_str()), |
| FakeWinHttpUrlFetcher::Headers(), "{\"access_token\": \"dummy_token\"}"); |
| |
| // Set empty response from admin sdk. |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(get_cd_user_url_.c_str()), FakeWinHttpUrlFetcher::Headers(), "{}"); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred_.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); |
| |
| // Make sure a "foo" user was created. |
| PSID sid; |
| EXPECT_EQ(S_OK, fake_os_user_manager()->GetUserSID( |
| OSUserManager::GetLocalDomain().c_str(), kDefaultUsername, |
| &sid)); |
| ::LocalFree(sid); |
| |
| // New user should be created. |
| EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| } |
| |
| // Call to the admin sdk to fetch AD_accounts or Local_Windows_accounts failed. |
| TEST_P(GcpGaiaCredentialBaseCloudMappingTest, |
| GetSerialization_CallToAdminSdkFailed) { |
| // Set token result a valid access token. |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(gaia_urls_->oauth2_token_url().spec().c_str()), |
| FakeWinHttpUrlFetcher::Headers(), "{\"access_token\": \"dummy_token\"}"); |
| |
| // Fail the call from admin sdk. |
| fake_http_url_fetcher_factory()->SetFakeFailedResponse( |
| GURL(get_cd_user_url_.c_str()), E_FAIL); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred_.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_TRUE(base::size(test->GetFinalEmail()) == 0); |
| |
| // Make sure no user was created and the login attempt failed. |
| PSID sid = nullptr; |
| EXPECT_EQ( |
| HRESULT_FROM_WIN32(NERR_UserNotFound), |
| fake_os_user_manager()->GetUserSID( |
| OSUserManager::GetLocalDomain().c_str(), kDefaultUsername, &sid)); |
| ASSERT_EQ(nullptr, sid); |
| |
| // No new user is created. |
| EXPECT_EQ(1ul, fake_os_user_manager()->GetUserCount()); |
| |
| ASSERT_EQ(S_OK, FinishLogonProcess( |
| /*expected_success=*/false, |
| /*expected_credentials_change_fired=*/false, |
| IDS_INTERNAL_ERROR_BASE)); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| GcpGaiaCredentialBaseCloudMappingTest, |
| ::testing::Values(true, false)); |
| |
| // Test various active directory specific sign in scenarios. |
| class GcpGaiaCredentialBaseAdScenariosTest : public GcpGaiaCredentialBaseTest { |
| protected: |
| void SetUp() override; |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred_; |
| // The admin sdk users directory get URL. |
| std::string get_cd_user_url_ = base::StringPrintf( |
| "https://www.googleapis.com/admin/directory/v1/users/" |
| "%s?projection=full&viewType=domain_public", |
| net::EscapeUrlEncodedData(kDefaultEmail, true).c_str()); |
| GaiaUrls* gaia_urls_ = GaiaUrls::GetInstance(); |
| }; |
| |
| void GcpGaiaCredentialBaseAdScenariosTest::SetUp() { |
| GcpGaiaCredentialBaseTest::SetUp(); |
| |
| // Set the device as a domain joined machine. |
| fake_os_user_manager()->SetIsDeviceDomainJoined(true); |
| |
| // Override registry to enable cloud association with google. |
| constexpr wchar_t kRegCloudAssociation[] = L"enable_cloud_association"; |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegCloudAssociation, 1)); |
| // Set |kKeyEnableGemFeatures| registry entry |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kKeyEnableGemFeatures, 1u)); |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred_)); |
| } |
| |
| // Customer configured invalid AD_accounts. |
| TEST_F(GcpGaiaCredentialBaseAdScenariosTest, |
| GetSerialization_WithAD_InvalidADUPNConfigured) { |
| // Add the user as a domain joined user. |
| const wchar_t user_name[] = L"ad_user"; |
| const wchar_t password[] = L"password"; |
| |
| const wchar_t domain_name[] = L"ad_domain"; |
| CComBSTR existing_user_sid; |
| DWORD error; |
| HRESULT add_domain_user_hr = fake_os_user_manager()->AddUser( |
| user_name, password, L"fullname", L"comment", true, domain_name, |
| &existing_user_sid, &error); |
| ASSERT_EQ(S_OK, add_domain_user_hr); |
| ASSERT_EQ(0u, error); |
| |
| // Set token result a valid access token. |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(gaia_urls_->oauth2_token_url().spec().c_str()), |
| FakeWinHttpUrlFetcher::Headers(), "{\"access_token\": \"dummy_token\"}"); |
| |
| // Invalid configuration in admin sdk. Don't set the username. |
| std::string admin_sdk_response = base::StringPrintf( |
| "{\"customSchemas\": {\"Enhanced_desktop_security\": {\"AD_accounts\":" |
| "[{ \"value\": \"%ls\\\\\" }]}}}", |
| domain_name); |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(get_cd_user_url_.c_str()), FakeWinHttpUrlFetcher::Headers(), |
| admin_sdk_response); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred_.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_TRUE(base::size(test->GetFinalEmail()) == 0); |
| |
| // Make sure no user was created and the login attempt failed. |
| PSID sid = nullptr; |
| EXPECT_EQ( |
| HRESULT_FROM_WIN32(NERR_UserNotFound), |
| fake_os_user_manager()->GetUserSID( |
| OSUserManager::GetLocalDomain().c_str(), kDefaultUsername, &sid)); |
| ASSERT_EQ(nullptr, sid); |
| |
| // No new user is created. |
| EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| |
| ASSERT_EQ(S_OK, FinishLogonProcess( |
| /*expected_success=*/false, |
| /*expected_credentials_change_fired=*/false, |
| IDS_INVALID_AD_UPN_BASE)); |
| } |
| |
| // Customer configured a valid AD UPN but user is trying to a |
| // machine that is joined to different AD domain forest. |
| TEST_F(GcpGaiaCredentialBaseAdScenariosTest, |
| GetSerialization_WithAD_InvalidDomainForest) { |
| // Add the user as a domain joined user. |
| const wchar_t user_name[] = L"ad_user"; |
| const wchar_t password[] = L"password"; |
| |
| const wchar_t domain_name[] = L"ad_domain"; |
| CComBSTR existing_user_sid; |
| DWORD error; |
| HRESULT add_domain_user_hr = fake_os_user_manager()->AddUser( |
| user_name, password, L"fullname", L"comment", true, domain_name, |
| &existing_user_sid, &error); |
| ASSERT_EQ(S_OK, add_domain_user_hr); |
| ASSERT_EQ(0u, error); |
| |
| // Set token result a valid access token. |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(gaia_urls_->oauth2_token_url().spec().c_str()), |
| FakeWinHttpUrlFetcher::Headers(), "{\"access_token\": \"dummy_token\"}"); |
| |
| const wchar_t another_domain_name[] = L"ad_another_domain"; |
| // Invalid configuration in admin sdk. Don't set the username. |
| std::string admin_sdk_response = base::StringPrintf( |
| "{\"customSchemas\": {\"Enhanced_desktop_security\": {\"AD_accounts\":" |
| "[{ \"value\": \"%ls\\\\%ls\" }]}}}", |
| another_domain_name, user_name); |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(get_cd_user_url_.c_str()), FakeWinHttpUrlFetcher::Headers(), |
| admin_sdk_response); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred_.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| ASSERT_TRUE(base::size(test->GetFinalEmail()) == 0); |
| |
| // Make sure no user was created and the login attempt failed. |
| PSID sid = nullptr; |
| EXPECT_EQ( |
| HRESULT_FROM_WIN32(NERR_UserNotFound), |
| fake_os_user_manager()->GetUserSID( |
| OSUserManager::GetLocalDomain().c_str(), kDefaultUsername, &sid)); |
| ASSERT_EQ(nullptr, sid); |
| |
| // No new user is created. |
| EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| |
| ASSERT_EQ(S_OK, FinishLogonProcess( |
| /*expected_success=*/false, |
| /*expected_credentials_change_fired=*/false, |
| IDS_INVALID_AD_UPN_BASE)); |
| } |
| |
| // This is the success scenario where all preconditions are met in the |
| // AD login scenario. The user is successfully logged in. |
| TEST_F(GcpGaiaCredentialBaseAdScenariosTest, |
| GetSerialization_WithADSuccessScenario) { |
| // Add the user as a domain joined user. |
| const wchar_t user_name[] = L"ad_user"; |
| const wchar_t domain_name[] = L"ad_domain"; |
| const wchar_t password[] = L"password"; |
| |
| CComBSTR ad_sid; |
| DWORD error; |
| HRESULT add_domain_user_hr = fake_os_user_manager()->AddUser( |
| user_name, password, L"fullname", L"comment", true, domain_name, &ad_sid, |
| &error); |
| ASSERT_EQ(S_OK, add_domain_user_hr); |
| ASSERT_EQ(0u, error); |
| |
| // Set token result as a valid access token. |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(gaia_urls_->oauth2_token_url().spec().c_str()), |
| FakeWinHttpUrlFetcher::Headers(), "{\"access_token\": \"dummy_token\"}"); |
| |
| // Set valid response from admin sdk. |
| std::string admin_sdk_response = base::StringPrintf( |
| "{\"customSchemas\": {\"Enhanced_desktop_security\": {\"AD_accounts\":" |
| "[{ \"value\": \"%ls\\\\%ls\" }]}}}", |
| domain_name, user_name); |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(get_cd_user_url_.c_str()), FakeWinHttpUrlFetcher::Headers(), |
| admin_sdk_response); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred_.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); |
| ASSERT_TRUE(test->IsAdJoinedUser()); |
| |
| // Make sure no user was created and the login happens on the |
| // existing user instead. |
| PSID sid = nullptr; |
| EXPECT_EQ( |
| HRESULT_FROM_WIN32(NERR_UserNotFound), |
| fake_os_user_manager()->GetUserSID( |
| OSUserManager::GetLocalDomain().c_str(), kDefaultUsername, &sid)); |
| ASSERT_EQ(nullptr, sid); |
| |
| // Finishing logon process should trigger credential changed and trigger |
| // GetSerialization. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| |
| // Verify that the registry entry for the user was created. |
| wchar_t gaia_id[256]; |
| ULONG length = base::size(gaia_id); |
| std::wstring sid_str(ad_sid, SysStringLen(ad_sid)); |
| ::SysFreeString(ad_sid); |
| |
| HRESULT gaia_id_hr = |
| GetUserProperty(sid_str.c_str(), kUserId, gaia_id, &length); |
| ASSERT_EQ(S_OK, gaia_id_hr); |
| ASSERT_TRUE(gaia_id[0]); |
| |
| // Make sure ToS acceptance was recorded. |
| DWORD accept_tos; |
| HRESULT hr = GetUserProperty(sid_str.c_str(), kKeyAcceptTos, &accept_tos); |
| ASSERT_EQ(S_OK, hr); |
| ASSERT_EQ(1u, accept_tos); |
| |
| // Verify that the authentication results dictionary is now empty. |
| ASSERT_TRUE(test->IsAuthenticationResultsEmpty()); |
| } |
| |
| // Test various existing local account mapping specific in cloud sign in |
| // scenarios. |
| class GcpGaiaCredentialBaseCloudLocalAccountTest |
| : public GcpGaiaCredentialBaseTest { |
| protected: |
| void SetUp() override; |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred_; |
| // The admin sdk users directory get URL. |
| std::string get_cd_user_url_ = base::StringPrintf( |
| "https://www.googleapis.com/admin/directory/v1/users/" |
| "%s?projection=full&viewType=domain_public", |
| net::EscapeUrlEncodedData(kDefaultEmail, true).c_str()); |
| GaiaUrls* gaia_urls_ = GaiaUrls::GetInstance(); |
| }; |
| |
| void GcpGaiaCredentialBaseCloudLocalAccountTest::SetUp() { |
| GcpGaiaCredentialBaseTest::SetUp(); |
| |
| // Override registry to enable cloud association with google. |
| constexpr wchar_t kRegCloudAssociation[] = L"enable_cloud_association"; |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegCloudAssociation, 1)); |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred_)); |
| } |
| |
| // Customer configured invalid local account info. |
| TEST_F(GcpGaiaCredentialBaseCloudLocalAccountTest, |
| GetSerialization_InvalidLocalAccountInfoConfigured) { |
| // Add the user as a local user. |
| const wchar_t user_name[] = L"local_user"; |
| const wchar_t password[] = L"password"; |
| |
| CComBSTR local_sid; |
| DWORD error; |
| HRESULT hr = fake_os_user_manager()->AddUser( |
| user_name, password, L"fullname", L"comment", true, &local_sid, &error); |
| ASSERT_EQ(S_OK, hr); |
| ASSERT_EQ(0u, error); |
| |
| // Set token result a valid access token. |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(gaia_urls_->oauth2_token_url().spec().c_str()), |
| FakeWinHttpUrlFetcher::Headers(), "{\"access_token\": \"dummy_token\"}"); |
| |
| // Invalid configuration in admin sdk. Don't set the username. |
| std::string admin_sdk_response = base::StringPrintf( |
| "{\"customSchemas\": {\"Enhanced_desktop_security\": " |
| "{\"Local_Windows_accounts\":" |
| " \"un:abcd\"}}}"); |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(get_cd_user_url_.c_str()), FakeWinHttpUrlFetcher::Headers(), |
| admin_sdk_response); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred_.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // Make sure new user was created since no valid mapping was found. |
| PSID sid = nullptr; |
| fake_os_user_manager()->GetUserSID(OSUserManager::GetLocalDomain().c_str(), |
| kDefaultUsername, &sid); |
| ASSERT_NE(nullptr, sid); |
| |
| // New user is created. |
| EXPECT_EQ(3ul, fake_os_user_manager()->GetUserCount()); |
| |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseCloudLocalAccountTest, MultipleLocalAccountInfo) { |
| // Add the user as a local user. |
| const wchar_t user_name[] = L"local_user"; |
| const wchar_t password[] = L"password"; |
| |
| CComBSTR local_sid; |
| DWORD error; |
| HRESULT hr = fake_os_user_manager()->AddUser( |
| user_name, password, L"fullname", L"comment", true, &local_sid, &error); |
| ASSERT_EQ(S_OK, hr); |
| ASSERT_EQ(0u, error); |
| |
| // Set token result as a valid access token. |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(gaia_urls_->oauth2_token_url().spec().c_str()), |
| FakeWinHttpUrlFetcher::Headers(), "{\"access_token\": \"dummy_token\"}"); |
| |
| std::string admin_sdk_response; |
| // Set a fake serial number. |
| base::string16 serial_number = L"1234"; |
| GoogleRegistrationDataForTesting g_registration_data(serial_number); |
| |
| const wchar_t another_user_name[] = L"another_local_user"; |
| |
| // Set valid response from admin sdk with Local_Windows_accounts containing |
| // one mapping with "serial_number" in it and another one without |
| // serial number. |
| admin_sdk_response = base::StringPrintf( |
| "{\"customSchemas\": {\"Enhanced_desktop_security\": " |
| "{\"Local_Windows_accounts\":" |
| "[{ \"value\": \"un:%ls,sn:%ls\" },{ \"value\": \"un:%ls\"}]}}}", |
| user_name, serial_number.c_str(), another_user_name); |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(get_cd_user_url_.c_str()), FakeWinHttpUrlFetcher::Headers(), |
| admin_sdk_response); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred_.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); |
| |
| // Make sure no user was created and the login happens on the |
| // existing user instead. |
| PSID sid = nullptr; |
| EXPECT_EQ( |
| HRESULT_FROM_WIN32(NERR_UserNotFound), |
| fake_os_user_manager()->GetUserSID( |
| OSUserManager::GetLocalDomain().c_str(), kDefaultUsername, &sid)); |
| ASSERT_EQ(nullptr, sid); |
| |
| // Finishing logon process should trigger credential changed and trigger |
| // GetSerialization. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| |
| // Verify that the registry entry for the user was created. |
| std::wstring sid_str(local_sid, SysStringLen(local_sid)); |
| |
| wchar_t gaia_id[256]; |
| ULONG length = base::size(gaia_id); |
| HRESULT gaia_id_hr = |
| GetUserProperty(sid_str.c_str(), kUserId, gaia_id, &length); |
| ASSERT_EQ(S_OK, gaia_id_hr); |
| ASSERT_TRUE(gaia_id[0]); |
| |
| // Verify that the authentication results dictionary is now empty. |
| ASSERT_TRUE(test->IsAuthenticationResultsEmpty()); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseCloudLocalAccountTest, |
| InvalidUserToSerialNumberMapping) { |
| // Add the user as a local user. |
| const wchar_t user_name[] = L"local_user"; |
| const wchar_t password[] = L"password"; |
| |
| CComBSTR local_sid; |
| DWORD error; |
| HRESULT hr = fake_os_user_manager()->AddUser( |
| user_name, password, L"fullname", L"comment", true, &local_sid, &error); |
| ASSERT_EQ(S_OK, hr); |
| ASSERT_EQ(0u, error); |
| |
| // Set token result as a valid access token. |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(gaia_urls_->oauth2_token_url().spec().c_str()), |
| FakeWinHttpUrlFetcher::Headers(), "{\"access_token\": \"dummy_token\"}"); |
| |
| std::string admin_sdk_response; |
| // Set a fake serial number. |
| base::string16 serial_number = L"1234"; |
| GoogleRegistrationDataForTesting g_registration_data(serial_number); |
| |
| const wchar_t another_user_name1[] = L"another_local_user_1"; |
| const wchar_t another_user_name2[] = L"another_local_user_2"; |
| |
| // Set valid response from admin sdk with Local_Windows_accounts containing |
| // multiple mappings with matching "serial_number" in it and another |
| // one without serial number. |
| admin_sdk_response = base::StringPrintf( |
| "{\"customSchemas\": {\"Enhanced_desktop_security\": " |
| "{\"Local_Windows_accounts\":" |
| "[{ \"value\": \"un:%ls,sn:%ls\" },{ \"value\": \"un:%ls,sn:%ls\" },{ " |
| " \"value\": \"un:%ls\" }]}}}", |
| another_user_name1, serial_number.c_str(), another_user_name2, |
| serial_number.c_str(), user_name); |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(get_cd_user_url_.c_str()), FakeWinHttpUrlFetcher::Headers(), |
| admin_sdk_response); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred_.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); |
| |
| // Make sure no user was created and the login happens on the |
| // existing user instead. |
| PSID sid = nullptr; |
| EXPECT_EQ( |
| HRESULT_FROM_WIN32(NERR_UserNotFound), |
| fake_os_user_manager()->GetUserSID( |
| OSUserManager::GetLocalDomain().c_str(), kDefaultUsername, &sid)); |
| ASSERT_EQ(nullptr, sid); |
| |
| // Finishing logon process should trigger credential changed and trigger |
| // GetSerialization. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| |
| // Verify that the registry entry for the user was created. |
| std::wstring sid_str(local_sid, SysStringLen(local_sid)); |
| |
| wchar_t gaia_id[256]; |
| ULONG length = base::size(gaia_id); |
| HRESULT gaia_id_hr = |
| GetUserProperty(sid_str.c_str(), kUserId, gaia_id, &length); |
| ASSERT_EQ(S_OK, gaia_id_hr); |
| ASSERT_TRUE(gaia_id[0]); |
| |
| // Verify that the authentication results dictionary is now empty. |
| ASSERT_TRUE(test->IsAuthenticationResultsEmpty()); |
| } |
| |
| TEST_F(GcpGaiaCredentialBaseCloudLocalAccountTest, |
| MultipleValidLocalAccountInfoMapping) { |
| // Set token result as a valid access token. |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(gaia_urls_->oauth2_token_url().spec().c_str()), |
| FakeWinHttpUrlFetcher::Headers(), "{\"access_token\": \"dummy_token\"}"); |
| |
| std::string admin_sdk_response; |
| // Set a fake serial number. |
| base::string16 serial_number = L"1234"; |
| GoogleRegistrationDataForTesting g_registration_data(serial_number); |
| |
| const wchar_t another_user_name1[] = L"another_local_user_1"; |
| const wchar_t another_user_name2[] = L"another_local_user_2"; |
| |
| // Set valid response from admin sdk with Local_Windows_accounts containing |
| // multiple mappings with matching "serial_number" in it and multiple |
| // mappings without serial number. |
| admin_sdk_response = base::StringPrintf( |
| "{\"customSchemas\": {\"Enhanced_desktop_security\": " |
| "{\"Local_Windows_accounts\":" |
| "[{ \"value\": \"un:%ls,sn:%ls\" },{ \"value\": \"un:%ls,sn:%ls\" },{ " |
| " \"value\": \"un:%ls\" },{ \"value\": \"un:%ls\"}]}}}", |
| another_user_name1, serial_number.c_str(), another_user_name2, |
| serial_number.c_str(), another_user_name1, another_user_name2); |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(get_cd_user_url_.c_str()), FakeWinHttpUrlFetcher::Headers(), |
| admin_sdk_response); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred_.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // Make sure new user was created since no valid mapping was found. |
| PSID sid = nullptr; |
| fake_os_user_manager()->GetUserSID(OSUserManager::GetLocalDomain().c_str(), |
| kDefaultUsername, &sid); |
| ASSERT_NE(nullptr, sid); |
| |
| // New user is created. |
| EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| } |
| |
| // This is the success scenario where all preconditions are met in the |
| // existing cloud local account login scenario. The user is successfully |
| // logged in. |
| class GaiaCredentialBaseCloudLocalAccountSuccessTest |
| : public GcpGaiaCredentialBaseCloudLocalAccountTest, |
| public ::testing::WithParamInterface<std::tuple<bool, const wchar_t*>> {}; |
| |
| TEST_P(GaiaCredentialBaseCloudLocalAccountSuccessTest, SerialNumber) { |
| bool set_serial_number = std::get<0>(GetParam()); |
| const wchar_t* serial_number = std::get<1>(GetParam()); |
| |
| // Add the user as a local user. |
| const wchar_t user_name[] = L"local_user"; |
| const wchar_t password[] = L"password"; |
| |
| CComBSTR local_sid; |
| DWORD error; |
| HRESULT hr = fake_os_user_manager()->AddUser( |
| user_name, password, L"fullname", L"comment", true, &local_sid, &error); |
| ASSERT_EQ(S_OK, hr); |
| ASSERT_EQ(0u, error); |
| |
| // Set token result as a valid access token. |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(gaia_urls_->oauth2_token_url().spec().c_str()), |
| FakeWinHttpUrlFetcher::Headers(), "{\"access_token\": \"dummy_token\"}"); |
| |
| std::string admin_sdk_response; |
| // Set a fake serial number. |
| GoogleRegistrationDataForTesting g_registration_data(serial_number); |
| |
| if (set_serial_number) { |
| // Set valid response from admin sdk. |
| admin_sdk_response = base::StringPrintf( |
| "{\"customSchemas\": {\"Enhanced_desktop_security\": " |
| "{\"Local_Windows_accounts\":" |
| "[{ \"value\": \"un:%ls,sn:%ls\"}]}}}", |
| user_name, serial_number); |
| } else { |
| // Set valid response from admin sdk. |
| admin_sdk_response = base::StringPrintf( |
| "{\"customSchemas\": {\"Enhanced_desktop_security\": " |
| "{\"Local_Windows_accounts\":" |
| "[{ \"value\": \"un:%ls\"}]}}}", |
| user_name); |
| } |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(get_cd_user_url_.c_str()), FakeWinHttpUrlFetcher::Headers(), |
| admin_sdk_response); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred_.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); |
| |
| // Make sure no user was created and the login happens on the |
| // existing user instead. |
| PSID sid = nullptr; |
| EXPECT_EQ( |
| HRESULT_FROM_WIN32(NERR_UserNotFound), |
| fake_os_user_manager()->GetUserSID( |
| OSUserManager::GetLocalDomain().c_str(), kDefaultUsername, &sid)); |
| ASSERT_EQ(nullptr, sid); |
| |
| // Finishing logon process should trigger credential changed and trigger |
| // GetSerialization. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| |
| // Verify that the registry entry for the user was created. |
| std::wstring sid_str(local_sid, SysStringLen(local_sid)); |
| |
| wchar_t gaia_id[256]; |
| ULONG length = base::size(gaia_id); |
| HRESULT gaia_id_hr = |
| GetUserProperty(sid_str.c_str(), kUserId, gaia_id, &length); |
| ASSERT_EQ(S_OK, gaia_id_hr); |
| ASSERT_TRUE(gaia_id[0]); |
| |
| // Verify that the authentication results dictionary is now empty. |
| ASSERT_TRUE(test->IsAuthenticationResultsEmpty()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| GaiaCredentialBaseCloudLocalAccountSuccessTest, |
| ::testing::Combine( |
| ::testing::Bool(), |
| ::testing::Values(L"!@#!", // All non alphanumeric characters |
| L"serial#123", // Contains non-alphanumeric chars. |
| L"serial123!" // Ends with non alphanumeric chars. |
| ))); |
| |
| // Existing cloud local account login scenario that was configured incorrectly. |
| class GaiaCredentialBaseCDUsernameSuccessTest |
| : public GcpGaiaCredentialBaseCloudLocalAccountTest, |
| public ::testing::WithParamInterface<const wchar_t*> {}; |
| |
| TEST_P(GaiaCredentialBaseCDUsernameSuccessTest, AnyUsername) { |
| const wchar_t* user_name = GetParam(); |
| |
| // Add the user as a local user. |
| const wchar_t password[] = L"password"; |
| |
| CComBSTR local_sid; |
| DWORD error; |
| HRESULT hr = fake_os_user_manager()->AddUser( |
| user_name, password, L"fullname", L"comment", true, &local_sid, &error); |
| ASSERT_EQ(S_OK, hr); |
| ASSERT_EQ(0u, error); |
| |
| // Set token result as a valid access token. |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(gaia_urls_->oauth2_token_url().spec().c_str()), |
| FakeWinHttpUrlFetcher::Headers(), "{\"access_token\": \"dummy_token\"}"); |
| |
| // Set valid response from admin sdk. |
| std::string admin_sdk_response = base::StringPrintf( |
| "{\"customSchemas\": {\"Enhanced_desktop_security\": " |
| "{\"Local_Windows_accounts\":" |
| "[{ \"value\": \"un:%ls\"}]}}}", |
| user_name); |
| |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(get_cd_user_url_.c_str()), FakeWinHttpUrlFetcher::Headers(), |
| admin_sdk_response); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred_.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // New user is not created. |
| EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| |
| // Verify that the registry entry for the user was created. |
| std::wstring sid_str(local_sid, SysStringLen(local_sid)); |
| |
| wchar_t gaia_id[256]; |
| ULONG length = base::size(gaia_id); |
| HRESULT gaia_id_hr = |
| GetUserProperty(sid_str.c_str(), kUserId, gaia_id, &length); |
| ASSERT_EQ(S_OK, gaia_id_hr); |
| ASSERT_TRUE(gaia_id[0]); |
| |
| // Verify that the authentication results dictionary is now empty. |
| ASSERT_TRUE(test->IsAuthenticationResultsEmpty()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| GaiaCredentialBaseCDUsernameSuccessTest, |
| ::testing::Values(L"!@#!", // All non alphanumeric characters |
| L"user#123", // Contains non-alphanumeric chars. |
| L"user123!")); // Ends with non alphanumeric chars. |
| |
| // Existing cloud local account login scenario that was configured incorrectly. |
| class GaiaCredentialBaseCDSerialNumberFailureTest |
| : public GcpGaiaCredentialBaseCloudLocalAccountTest, |
| public ::testing::WithParamInterface<const wchar_t*> {}; |
| |
| TEST_P(GaiaCredentialBaseCDSerialNumberFailureTest, InvalidSerialNumber) { |
| const wchar_t* serial_number = GetParam(); |
| |
| // Add the user as a local user. |
| const wchar_t user_name[] = L"local_user"; |
| const wchar_t password[] = L"password"; |
| |
| CComBSTR local_sid; |
| DWORD error; |
| HRESULT hr = fake_os_user_manager()->AddUser( |
| user_name, password, L"fullname", L"comment", true, &local_sid, &error); |
| ASSERT_EQ(S_OK, hr); |
| ASSERT_EQ(0u, error); |
| |
| // Set fake serial number. |
| GoogleRegistrationDataForTesting g_registration_data(serial_number); |
| |
| // Set token result as a valid access token. |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(gaia_urls_->oauth2_token_url().spec().c_str()), |
| FakeWinHttpUrlFetcher::Headers(), "{\"access_token\": \"dummy_token\"}"); |
| |
| // Set valid response from admin sdk. |
| std::string admin_sdk_response = base::StringPrintf( |
| "{\"customSchemas\": {\"Enhanced_desktop_security\": " |
| "{\"Local_Windows_accounts\":" |
| "[{ \"value\": \"un:%ls,sn:%ls\"}]}}}", |
| user_name, serial_number); |
| |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| GURL(get_cd_user_url_.c_str()), FakeWinHttpUrlFetcher::Headers(), |
| admin_sdk_response); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred_.As(&test)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // Make sure new user was created since no valid mapping was found. |
| PSID sid = nullptr; |
| fake_os_user_manager()->GetUserSID(OSUserManager::GetLocalDomain().c_str(), |
| kDefaultUsername, &sid); |
| ASSERT_NE(nullptr, sid); |
| |
| // New user is created. |
| EXPECT_EQ(3ul, fake_os_user_manager()->GetUserCount()); |
| |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| GaiaCredentialBaseCDSerialNumberFailureTest, |
| ::testing::Values( |
| L"" // Except for empty string all other characters are allowed chars. |
| )); |
| |
| // Tests various sign in scenarios with consumer and non-consumer domains. |
| // Parameters are: |
| // 1. bool : Is mdm enrollment enabled. |
| // 2. int : The mdm_aca reg key setting: |
| // - 0: Set reg key to 0. |
| // - 1: Set reg key to 1. |
| // - 2: Don't set reg key. |
| // 3. bool : Whether an existing associated user is already present. |
| // 4. bool : Whether the user being created (or existing) uses a consumer |
| // account. |
| // 5. bool : Whether cloud policies are enabled. |
| class GcpGaiaCredentialBaseConsumerEmailTest |
| : public GcpGaiaCredentialBaseTest, |
| public ::testing::WithParamInterface< |
| std::tuple<bool, int, bool, bool, bool>> {}; |
| |
| TEST_P(GcpGaiaCredentialBaseConsumerEmailTest, ConsumerEmailSignin) { |
| USES_CONVERSION; |
| const bool mdm_enabled = std::get<0>(GetParam()); |
| const int mdm_consumer_accounts_reg_key_setting = std::get<1>(GetParam()); |
| const bool user_created = std::get<2>(GetParam()); |
| const bool user_is_consumer = std::get<3>(GetParam()); |
| const bool cloud_policies_enabled = std::get<4>(GetParam()); |
| |
| FakeAssociatedUserValidator validator; |
| FakeInternetAvailabilityChecker internet_checker; |
| GoogleMdmEnrollmentStatusForTesting force_success(true); |
| FakeDevicePoliciesManager fake_device_policies_manager( |
| cloud_policies_enabled); |
| |
| if (cloud_policies_enabled) { |
| DevicePolicies policies; |
| policies.enable_dm_enrollment = mdm_enabled; |
| fake_device_policies_manager.SetDevicePolicies(policies); |
| } else { |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegEnableDmEnrollment, |
| mdm_enabled ? 1 : 0)); |
| } |
| |
| const bool mdm_consumer_accounts_reg_key_set = |
| mdm_consumer_accounts_reg_key_setting >= 0 && |
| mdm_consumer_accounts_reg_key_setting < 2; |
| if (mdm_consumer_accounts_reg_key_set) { |
| ASSERT_EQ(S_OK, |
| SetGlobalFlagForTesting(kRegMdmAllowConsumerAccounts, |
| mdm_consumer_accounts_reg_key_setting)); |
| } |
| |
| std::string user_email = user_is_consumer ? kDefaultEmail : "foo@imfl.info"; |
| |
| CComBSTR sid; |
| base::string16 username(user_is_consumer ? L"foo" : L"foo_imfl"); |
| |
| // Create a fake user that has the same gaia id as the test gaia id. |
| if (user_created) { |
| ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( |
| username, L"password", L"name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), |
| base::UTF8ToUTF16(user_email), &sid)); |
| ASSERT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| } |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| test->SetGlsEmailAddress(user_email); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| bool should_signin_succeed = !mdm_enabled || |
| (mdm_consumer_accounts_reg_key_set && |
| mdm_consumer_accounts_reg_key_setting) || |
| !user_is_consumer; |
| |
| // Sign in success. |
| if (should_signin_succeed) { |
| // User should have been associated. |
| EXPECT_EQ(test->GetFinalUsername(), username); |
| // Email should be the same as the default one. |
| EXPECT_EQ(test->GetFinalEmail(), user_email); |
| |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| } else { |
| // Error message concerning invalid domain is sent. |
| ASSERT_EQ(S_OK, FinishLogonProcess(false, false, |
| IDS_DISALLOWED_CONSUMER_EMAIL_BASE)); |
| } |
| |
| if (user_created) { |
| // No new user should be created. |
| EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| } else { |
| // New user created only if their domain is valid for the sign in. |
| EXPECT_EQ(1ul + (should_signin_succeed ? 1ul : 0ul), |
| fake_os_user_manager()->GetUserCount()); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| GcpGaiaCredentialBaseConsumerEmailTest, |
| ::testing::Combine(::testing::Bool(), |
| ::testing::Values(0, 1, 2), |
| ::testing::Bool(), |
| ::testing::Bool(), |
| ::testing::Bool())); |
| |
| // Test password recovery system for various failure success cases. |
| // Parameters are: |
| // 1. int - The expected result of the initial public key retrieval for storing |
| // the password. Values are 0 - success, 1 - failure, 2 - timeout. |
| // 2. int - The expected result of the initial public private retrieval for |
| // decrypting the password. Values are 0 - success, 1 - failure, |
| // 2 - timeout. |
| // 3. int - The expected result of the initial public private retrieval for |
| // decrypting the password. Values are 0 - success, 1 - failure, |
| // 2 - timeout. |
| class GcpGaiaCredentialBasePasswordRecoveryTest |
| : public GcpGaiaCredentialBaseTest, |
| public ::testing::WithParamInterface<std::tuple<int, int, int>> {}; |
| |
| TEST_P(GcpGaiaCredentialBasePasswordRecoveryTest, PasswordRecovery) { |
| USES_CONVERSION; |
| |
| int generate_public_key_result = std::get<0>(GetParam()); |
| int get_private_key_result = std::get<1>(GetParam()); |
| int generate_public_key_again_result = std::get<2>(GetParam()); |
| |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegEnableDmEnrollment, 1)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegDisablePasswordSync, 0)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmAllowConsumerAccounts, 1)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmSupportsMultiUser, 0)); |
| |
| GoogleMdmEnrolledStatusForTesting force_success(true); |
| |
| // Create a fake user associated to a gaia id. |
| CComBSTR sid; |
| constexpr wchar_t kOldPassword[] = L"password"; |
| ASSERT_EQ(S_OK, |
| fake_os_user_manager()->CreateTestOSUser( |
| kDefaultUsername, kOldPassword, L"Full Name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid)); |
| |
| // Change token response to an invalid one. |
| SetDefaultTokenHandleResponse(kDefaultInvalidTokenHandleResponse); |
| |
| // Make a dummy response for successful public key generation and private key |
| // retrieval. |
| std::string generate_success_response = |
| fake_password_recovery_manager()->MakeGenerateKeyPairResponseForTesting( |
| kTestPublicKey, kFakeResourceId); |
| |
| std::string get_key_success_response = |
| fake_password_recovery_manager()->MakeGetPrivateKeyResponseForTesting( |
| kTestPrivateKey); |
| |
| // Make timeout events for the various escrow service requests if needed. |
| std::unique_ptr<base::WaitableEvent> get_key_event; |
| std::unique_ptr<base::WaitableEvent> generate_key_event; |
| |
| if (generate_public_key_result == 2) |
| get_key_event.reset(new base::WaitableEvent()); |
| |
| if (get_private_key_result == 2) |
| generate_key_event.reset(new base::WaitableEvent()); |
| |
| if (get_key_event || generate_key_event) { |
| fake_password_recovery_manager()->SetRequestTimeoutForTesting( |
| base::TimeDelta::FromMilliseconds(50)); |
| } |
| |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| fake_password_recovery_manager()->GetEscrowServiceGenerateKeyPairUrl(), |
| FakeWinHttpUrlFetcher::Headers(), |
| generate_public_key_result != 1 ? generate_success_response : "{}", |
| generate_key_event ? generate_key_event->handle() : INVALID_HANDLE_VALUE); |
| |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| fake_password_recovery_manager()->GetEscrowServiceGetPrivateKeyUrl( |
| kFakeResourceId), |
| FakeWinHttpUrlFetcher::Headers(), |
| get_private_key_result != 1 ? get_key_success_response : "{}", |
| get_key_event ? get_key_event->handle() : INVALID_HANDLE_VALUE); |
| |
| bool should_store_succeed = generate_public_key_result == 0; |
| bool should_recover_succeed = get_private_key_result == 0; |
| |
| // Sign on once to store the password in the LSA |
| { |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // Finish logon successfully to propagate password recovery information to |
| // LSA. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| |
| ASSERT_EQ(S_OK, ReleaseProvider()); |
| } |
| |
| // If there was a timeout for the generation of the public key, signal it now |
| // so that the request thread can complete. Also delete the event in case it |
| // needs to be used again on the sign in after the password was retrieved. |
| if (generate_key_event) { |
| generate_key_event->Signal(); |
| generate_key_event.reset(); |
| } |
| |
| if (generate_public_key_again_result == 2) |
| generate_key_event.reset(new base::WaitableEvent()); |
| |
| if (generate_key_event) { |
| fake_password_recovery_manager()->SetRequestTimeoutForTesting( |
| base::TimeDelta::FromMilliseconds(50)); |
| } |
| |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| fake_password_recovery_manager()->GetEscrowServiceGenerateKeyPairUrl(), |
| FakeWinHttpUrlFetcher::Headers(), |
| generate_public_key_again_result != 1 ? generate_success_response : "{}", |
| generate_key_event ? generate_key_event->handle() : INVALID_HANDLE_VALUE); |
| |
| constexpr char kNewPassword[] = "password2"; |
| |
| // Sign in a second time with a different password and see if it is updated |
| // automatically. |
| { |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| // Send back a different gaia password to force a password update. |
| ASSERT_EQ(S_OK, test->SetGlsGaiaPassword(kNewPassword)); |
| |
| // Don't send a forced e-mail. It will be sent from the user that was |
| // updated during the last sign in. |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(std::string())); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| Microsoft::WRL::ComPtr<ITestCredentialProvider> test_provider; |
| ASSERT_EQ(S_OK, created_provider().As(&test_provider)); |
| |
| // If either password storage or recovery failed then the user will need to |
| // enter their old Windows password. |
| if (!should_store_succeed || !should_recover_succeed) { |
| // Logon should not complete but there is no error message. |
| EXPECT_EQ(test_provider->credentials_changed_fired(), false); |
| |
| // Make sure password textbox is shown if the recovery of the password |
| // through escros service fails. |
| ASSERT_EQ(CPFS_DISPLAY_IN_SELECTED_TILE, |
| fake_credential_provider_credential_events()->GetFieldState( |
| cred.Get(), FID_CURRENT_PASSWORD_FIELD)); |
| |
| // Set the correct old password so that the user can sign in. |
| ASSERT_EQ(S_OK, |
| cred->SetStringValue(FID_CURRENT_PASSWORD_FIELD, kOldPassword)); |
| |
| // Finish logon successfully now which should update the password. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, false, 0)); |
| } else { |
| // Make sure password textbox isn't shown if the recovery of the password |
| // through escrow service succeeds. |
| ASSERT_EQ(CPFS_HIDDEN, |
| fake_credential_provider_credential_events()->GetFieldState( |
| cred.Get(), FID_CURRENT_PASSWORD_FIELD)); |
| |
| // Make sure the new password is sent to the provider. |
| EXPECT_STREQ(A2OLE(kNewPassword), OLE2CW(test_provider->password())); |
| |
| // Finish logon successfully but with no credential changed event. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| } |
| |
| // Make sure the user has the new password internally. |
| EXPECT_EQ(S_OK, fake_os_user_manager()->IsWindowsPasswordValid( |
| OSUserManager::GetLocalDomain().c_str(), |
| kDefaultUsername, A2OLE(kNewPassword))); |
| |
| ASSERT_EQ(S_OK, ReleaseProvider()); |
| } |
| |
| // Complete the private key retrieval request if it was waiting. |
| if (get_key_event) |
| get_key_event->Signal(); |
| |
| // If generate of the second public key failed, the next sign in would |
| // need to re-enter their password |
| if (generate_public_key_again_result != 0) { |
| constexpr char kNewPassword2[] = "password3"; |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| // Send back a different gaia password to force a password update. |
| ASSERT_EQ(S_OK, test->SetGlsGaiaPassword(kNewPassword2)); |
| |
| // Don't send a forced e-mail. It will be sent from the user that was |
| // updated during the last sign in. |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(std::string())); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| Microsoft::WRL::ComPtr<ITestCredentialProvider> test_provider; |
| ASSERT_EQ(S_OK, created_provider().As(&test_provider)); |
| |
| // Logon should not complete but there is no error message. |
| EXPECT_EQ(test_provider->credentials_changed_fired(), false); |
| |
| // Set the correct old password so that the user can sign in. |
| ASSERT_EQ(S_OK, |
| cred->SetStringValue(FID_CURRENT_PASSWORD_FIELD, |
| base::UTF8ToUTF16(kNewPassword).c_str())); |
| |
| // Finish logon successfully now which should update the password. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, false, 0)); |
| |
| // Make sure the user has the new password internally. |
| EXPECT_EQ(S_OK, fake_os_user_manager()->IsWindowsPasswordValid( |
| OSUserManager::GetLocalDomain().c_str(), |
| kDefaultUsername, A2OLE(kNewPassword2))); |
| |
| ASSERT_EQ(S_OK, ReleaseProvider()); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| GcpGaiaCredentialBasePasswordRecoveryTest, |
| ::testing::Combine(::testing::Values(0, 1, 2), |
| ::testing::Values(0, 1, 2), |
| ::testing::Values(0, 1, 2))); |
| |
| // Tests failures in NetUserChangePassword attempt after password is |
| // successfully retrieved. |
| // 1. int - Password change attempt fails due to multiple reasons. Values are |
| // 0 - ERROR_INVALID_PASSWORD, 1 - NERR_InvalidComputer, |
| // 2 - NERR_NotPrimary, 3 - NERR_UserNotFound, 4 - |
| // NERR_PasswordTooShort, 5 - UnknownStatus |
| class GcpGaiaCredentialBasePasswordChangeFailureTest |
| : public GcpGaiaCredentialBaseTest, |
| public ::testing::WithParamInterface<int> {}; |
| |
| TEST_P(GcpGaiaCredentialBasePasswordChangeFailureTest, Fail) { |
| USES_CONVERSION; |
| |
| int failure_reason = GetParam(); |
| |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegEnableDmEnrollment, 1)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegDisablePasswordSync, 0)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmAllowConsumerAccounts, 1)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmSupportsMultiUser, 0)); |
| |
| GoogleMdmEnrolledStatusForTesting force_success(true); |
| |
| // Create a fake user associated to a gaia id. |
| CComBSTR sid; |
| constexpr wchar_t kOldPassword[] = L"password"; |
| ASSERT_EQ(S_OK, |
| fake_os_user_manager()->CreateTestOSUser( |
| kDefaultUsername, kOldPassword, L"Full Name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid)); |
| |
| // Change token response to an invalid one. |
| SetDefaultTokenHandleResponse(kDefaultInvalidTokenHandleResponse); |
| |
| // Make a dummy response for successful public key generation and private key |
| // retrieval. |
| std::string generate_success_response = |
| fake_password_recovery_manager()->MakeGenerateKeyPairResponseForTesting( |
| kTestPublicKey, kFakeResourceId); |
| |
| std::string get_key_success_response = |
| fake_password_recovery_manager()->MakeGetPrivateKeyResponseForTesting( |
| kTestPrivateKey); |
| |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| fake_password_recovery_manager()->GetEscrowServiceGenerateKeyPairUrl(), |
| FakeWinHttpUrlFetcher::Headers(), generate_success_response, |
| INVALID_HANDLE_VALUE); |
| |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| fake_password_recovery_manager()->GetEscrowServiceGetPrivateKeyUrl( |
| kFakeResourceId), |
| FakeWinHttpUrlFetcher::Headers(), get_key_success_response, |
| INVALID_HANDLE_VALUE); |
| |
| // Sign on once to store the password in the LSA |
| { |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // Finish logon successfully to propagate password recovery information to |
| // LSA. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| |
| ASSERT_EQ(S_OK, ReleaseProvider()); |
| } |
| |
| constexpr char kNewPassword[] = "password2"; |
| |
| // Sign in a second time with a different password and see if it is updated |
| // automatically. |
| { |
| HRESULT net_api_status; |
| UINT message_id; |
| switch (failure_reason) { |
| case 0: |
| net_api_status = HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD); |
| message_id = IDS_INVALID_PASSWORD_BASE; |
| break; |
| case 1: |
| net_api_status = HRESULT_FROM_WIN32(NERR_InvalidComputer); |
| message_id = IDS_INVALID_COMPUTER_NAME_ERROR_BASE; |
| break; |
| case 2: |
| net_api_status = HRESULT_FROM_WIN32(NERR_NotPrimary); |
| message_id = IDS_AD_PASSWORD_CHANGE_DENIED_BASE; |
| break; |
| case 3: |
| net_api_status = HRESULT_FROM_WIN32(NERR_UserNotFound); |
| message_id = IDS_USER_NOT_FOUND_PASSWORD_ERROR_BASE; |
| break; |
| case 4: |
| net_api_status = HRESULT_FROM_WIN32(NERR_PasswordTooShort); |
| message_id = IDS_PASSWORD_COMPLEXITY_ERROR_BASE; |
| break; |
| default: |
| net_api_status = E_FAIL; |
| message_id = IDS_UNKNOWN_PASSWORD_ERROR_BASE; |
| break; |
| } |
| |
| base::string16 expected_error_msg = GetStringResource(message_id); |
| |
| // Set reason for failing the password change attempt. |
| fake_os_user_manager()->SetFailureReason(FAILEDOPERATIONS::CHANGE_PASSWORD, |
| net_api_status); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| // Send back a different gaia password to force a password update. |
| ASSERT_EQ(S_OK, test->SetGlsGaiaPassword(kNewPassword)); |
| |
| // Don't send a forced e-mail. It will be sent from the user that was |
| // updated during the last sign in. |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(std::string())); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| Microsoft::WRL::ComPtr<ITestCredentialProvider> test_provider; |
| ASSERT_EQ(S_OK, created_provider().As(&test_provider)); |
| |
| ASSERT_EQ(net_api_status, FinishLogonProcess(true, true, 0)); |
| |
| CREDENTIAL_PROVIDER_FIELD_STATE cpfs = CPFS_DISPLAY_IN_SELECTED_TILE; |
| if (message_id == IDS_PASSWORD_COMPLEXITY_ERROR_BASE || |
| message_id == IDS_USER_NOT_FOUND_PASSWORD_ERROR_BASE || |
| message_id == IDS_AD_PASSWORD_CHANGE_DENIED_BASE) { |
| cpfs = CPFS_HIDDEN; |
| } |
| |
| // Make sure password textbox is shown due to password change failure. |
| ASSERT_EQ(cpfs, fake_credential_provider_credential_events()->GetFieldState( |
| cred.Get(), FID_CURRENT_PASSWORD_FIELD)); |
| |
| EXPECT_STREQ(expected_error_msg.c_str(), |
| fake_credential_provider_credential_events()->GetFieldString( |
| cred.Get(), FID_DESCRIPTION)); |
| |
| ASSERT_EQ(S_OK, ReleaseProvider()); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| GcpGaiaCredentialBasePasswordChangeFailureTest, |
| ::testing::Values(0, 1, 2, 3, 4, 5)); |
| |
| // Test password recovery system being disabled by registry settings. |
| // Parameter is a pointer to an escrow service url. Can be empty or nullptr. |
| class GcpGaiaCredentialBasePasswordRecoveryDisablingTest |
| : public GcpGaiaCredentialBaseTest, |
| public ::testing::WithParamInterface<int> {}; |
| |
| TEST_P(GcpGaiaCredentialBasePasswordRecoveryDisablingTest, |
| PasswordRecovery_Disabled) { |
| USES_CONVERSION; |
| int disable_escrow_service = GetParam(); |
| |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegEnableDmEnrollment, 1)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmAllowConsumerAccounts, 1)); |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmSupportsMultiUser, 0)); |
| // SetGlobalFlagForTesting effectively deletes the registry when the provided |
| // registry value is empty. That implicitly enables escrow service without a |
| // registry override. |
| ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegDisablePasswordSync, |
| disable_escrow_service)); |
| |
| GoogleMdmEnrolledStatusForTesting force_success(true); |
| |
| // Create a fake user associated to a gaia id. |
| CComBSTR sid; |
| constexpr wchar_t kOldPassword[] = L"password"; |
| ASSERT_EQ(S_OK, |
| fake_os_user_manager()->CreateTestOSUser( |
| kDefaultUsername, kOldPassword, L"Full Name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid)); |
| |
| // Change token response to an invalid one. |
| SetDefaultTokenHandleResponse(kDefaultInvalidTokenHandleResponse); |
| |
| // Make a dummy response for successful public key generation and private key |
| // retrieval. |
| std::string generate_success_response = |
| fake_password_recovery_manager()->MakeGenerateKeyPairResponseForTesting( |
| kTestPublicKey, kFakeResourceId); |
| |
| std::string get_key_success_response = |
| fake_password_recovery_manager()->MakeGetPrivateKeyResponseForTesting( |
| kTestPrivateKey); |
| |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| fake_password_recovery_manager()->GetEscrowServiceGenerateKeyPairUrl(), |
| FakeWinHttpUrlFetcher::Headers(), generate_success_response); |
| |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| fake_password_recovery_manager()->GetEscrowServiceGetPrivateKeyUrl( |
| kFakeResourceId), |
| FakeWinHttpUrlFetcher::Headers(), get_key_success_response); |
| |
| // Sign on once to store the password in the LSA |
| { |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // Finish logon successfully to propagate password recovery information to |
| // LSA. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| |
| ASSERT_EQ(S_OK, ReleaseProvider()); |
| } |
| |
| // Sign in a second time with a different password and see if it is updated |
| // automatically. |
| { |
| constexpr char kNewPassword[] = "password2"; |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| // Send back a different gaia password to force a password update. |
| ASSERT_EQ(S_OK, test->SetGlsGaiaPassword(kNewPassword)); |
| |
| // Don't send a forced e-mail. It will be sent from the user that was |
| // updated during the last sign in. |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(std::string())); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| Microsoft::WRL::ComPtr<ITestCredentialProvider> test_provider; |
| ASSERT_EQ(S_OK, created_provider().As(&test_provider)); |
| |
| // Disable password recovery and force the user to enter their password. |
| if (disable_escrow_service) { |
| // Logon should not complete but there is no error message. |
| EXPECT_EQ(test_provider->credentials_changed_fired(), false); |
| |
| // Set the correct old password so that the user can sign in. |
| ASSERT_EQ(S_OK, |
| cred->SetStringValue(FID_CURRENT_PASSWORD_FIELD, kOldPassword)); |
| |
| // Finish logon successfully now which should update the password. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, false, 0)); |
| } else { |
| // Make sure the new password is sent to the provider. |
| EXPECT_STREQ(A2OLE(kNewPassword), OLE2CW(test_provider->password())); |
| |
| // Finish logon successfully but with no credential changed event. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| } |
| |
| // Make sure the user has the new password internally. |
| EXPECT_EQ(S_OK, fake_os_user_manager()->IsWindowsPasswordValid( |
| OSUserManager::GetLocalDomain().c_str(), |
| kDefaultUsername, A2OLE(kNewPassword))); |
| |
| ASSERT_EQ(S_OK, ReleaseProvider()); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| GcpGaiaCredentialBasePasswordRecoveryDisablingTest, |
| ::testing::Values(0, 1)); |
| |
| // Test Upload device details to GEM service with different failure scenarios. |
| // Parameters are: |
| // int - 0. Successfully uploaded device details. |
| // 1. Fails the upload device details call due to network timeout. |
| // 2. Fails the upload device details call due to invalid response |
| // from the GEM http server. |
| // 3. A previously saved device resource ID is present on the device. |
| // int - number of previously failed upload device details attempts. |
| class GcpGaiaCredentialBaseUploadDeviceDetailsTest |
| : public GcpGaiaCredentialBaseTest, |
| public ::testing::WithParamInterface<std::tuple<int, int>> {}; |
| |
| TEST_P(GcpGaiaCredentialBaseUploadDeviceDetailsTest, UploadDeviceDetails) { |
| bool fail_upload_device_details_timeout = (std::get<0>(GetParam()) == 1); |
| bool fail_upload_device_details_invalid_response = |
| (std::get<0>(GetParam()) == 2); |
| bool registry_has_device_resource_id = (std::get<0>(GetParam()) == 3); |
| const DWORD num_previous_failures = std::get<1>(GetParam()); |
| |
| GoogleMdmEnrolledStatusForTesting force_success(true); |
| // Set a fake serial number. |
| base::string16 serial_number = L"1234"; |
| GoogleRegistrationDataForTesting g_registration_data(serial_number); |
| base::string16 domain = L"domain"; |
| base::string16 machine_guid = L"machine_guid"; |
| SetMachineGuidForTesting(machine_guid); |
| |
| std::vector<std::string> mac_addresses; |
| mac_addresses.push_back("mac_address_1"); |
| mac_addresses.push_back("mac_address_2"); |
| std::string os_version = "10.1.17134"; |
| GemDeviceDetailsForTesting g_device_details(mac_addresses, os_version); |
| |
| // Create a fake user associated to a gaia id. |
| CComBSTR sid; |
| ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( |
| kDefaultUsername, L"password", L"Full Name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), |
| domain, &sid)); |
| |
| // Change token response to an invalid one. |
| SetDefaultTokenHandleResponse(kDefaultValidTokenHandleResponse); |
| |
| // Make timeout events for the upload device details request if needed. |
| std::unique_ptr<base::WaitableEvent> upload_device_details_key_event; |
| |
| if (fail_upload_device_details_timeout) { |
| upload_device_details_key_event.reset(new base::WaitableEvent()); |
| |
| fake_gem_device_details_manager()->SetRequestTimeoutForTesting( |
| base::TimeDelta::FromMilliseconds(50)); |
| } |
| const std::string device_resource_id = "test-device-resource-id"; |
| const std::string valid_server_response = |
| "{\"deviceResourceId\": \"" + device_resource_id + "\"}"; |
| |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| fake_gem_device_details_manager()->GetGemServiceUploadDeviceDetailsUrl(), |
| FakeWinHttpUrlFetcher::Headers(), |
| fail_upload_device_details_invalid_response ? "Invalid json response" |
| : valid_server_response, |
| upload_device_details_key_event |
| ? upload_device_details_key_event->handle() |
| : INVALID_HANDLE_VALUE); |
| |
| if (registry_has_device_resource_id) { |
| HRESULT hr = SetUserProperty(sid.Copy(), kRegUserDeviceResourceId, |
| base::UTF8ToUTF16(device_resource_id)); |
| EXPECT_TRUE(SUCCEEDED(hr)); |
| } |
| |
| // Set status and num failures from previous attempts. |
| HRESULT hr = SetUserProperty(sid.Copy(), kRegDeviceDetailsUploadStatus, |
| num_previous_failures ? 0 : 1); |
| EXPECT_TRUE(SUCCEEDED(hr)); |
| |
| hr = SetUserProperty(sid.Copy(), kRegDeviceDetailsUploadFailures, |
| num_previous_failures); |
| EXPECT_TRUE(SUCCEEDED(hr)); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // Finish logon successfully. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| |
| // Verify that upload device details http call returned back with appropriate |
| // status code. Since the login process doesn't get affected by the status of |
| // the upload device details process, the login attempt would always succeed |
| // irrespective of the upload status. |
| hr = fake_gem_device_details_manager()->GetUploadStatusForTesting(); |
| bool has_upload_failed = (fail_upload_device_details_timeout || |
| fail_upload_device_details_invalid_response); |
| ASSERT_TRUE(has_upload_failed ? FAILED(hr) : SUCCEEDED(hr)); |
| |
| // Assert on the request parameters sent in the UploadDeviceDetails rpc. |
| const base::Value& request_dict = |
| fake_gem_device_details_manager()->GetRequestDictForTesting(); |
| ASSERT_NE(nullptr, request_dict.FindStringKey("machine_guid")); |
| ASSERT_EQ(*request_dict.FindStringKey("machine_guid"), |
| base::UTF16ToUTF8(machine_guid)); |
| ASSERT_NE(nullptr, request_dict.FindStringKey("device_serial_number")); |
| ASSERT_EQ(*request_dict.FindStringKey("device_serial_number"), |
| base::UTF16ToUTF8(serial_number)); |
| ASSERT_NE(nullptr, request_dict.FindStringKey("device_domain")); |
| ASSERT_EQ(*request_dict.FindStringKey("device_domain"), |
| base::UTF16ToUTF8(domain)); |
| ASSERT_NE(nullptr, request_dict.FindStringKey("account_username")); |
| ASSERT_EQ(*request_dict.FindStringKey("account_username"), |
| base::UTF16ToUTF8(kDefaultUsername)); |
| ASSERT_NE(nullptr, request_dict.FindStringKey("user_sid")); |
| ASSERT_EQ(*request_dict.FindStringKey("user_sid"), |
| base::UTF16ToUTF8((BSTR)sid)); |
| ASSERT_NE(nullptr, request_dict.FindStringKey("os_edition")); |
| ASSERT_EQ(*request_dict.FindStringKey("os_edition"), os_version); |
| ASSERT_TRUE(request_dict.FindBoolKey("is_ad_joined_user").has_value()); |
| ASSERT_EQ(request_dict.FindBoolKey("is_ad_joined_user").value(), true); |
| ASSERT_TRUE(request_dict.FindKey("wlan_mac_addr")->is_list()); |
| |
| std::vector<std::string> actual_mac_address_list; |
| for (const base::Value& value : |
| request_dict.FindKey("wlan_mac_addr")->GetList()) { |
| ASSERT_TRUE(value.is_string()); |
| actual_mac_address_list.push_back(value.GetString()); |
| } |
| |
| ASSERT_TRUE(std::equal(actual_mac_address_list.begin(), |
| actual_mac_address_list.end(), mac_addresses.begin())); |
| |
| if (registry_has_device_resource_id) { |
| ASSERT_EQ(*request_dict.FindStringKey("device_resource_id"), |
| device_resource_id); |
| } |
| |
| DWORD device_upload_status = 0; |
| hr = GetUserProperty(sid.Copy(), kRegDeviceDetailsUploadStatus, |
| &device_upload_status); |
| DWORD device_upload_failures = 0; |
| hr = GetUserProperty(sid.Copy(), kRegDeviceDetailsUploadFailures, |
| &device_upload_failures); |
| |
| if (!fail_upload_device_details_timeout && |
| !fail_upload_device_details_invalid_response) { |
| ASSERT_EQ(1UL, device_upload_status); |
| ASSERT_EQ(0UL, device_upload_failures); |
| |
| wchar_t resource_id[512]; |
| ULONG resource_id_size = base::size(resource_id); |
| hr = GetUserProperty(sid.Copy(), kRegUserDeviceResourceId, resource_id, |
| &resource_id_size); |
| ASSERT_TRUE(SUCCEEDED(hr)); |
| ASSERT_TRUE(resource_id_size > 0); |
| ASSERT_EQ(device_resource_id, base::UTF16ToUTF8(resource_id)); |
| } else { |
| ASSERT_EQ(0UL, device_upload_status); |
| ASSERT_EQ(num_previous_failures + 1, device_upload_failures); |
| } |
| |
| ASSERT_EQ(S_OK, ReleaseProvider()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| GcpGaiaCredentialBaseUploadDeviceDetailsTest, |
| ::testing::Combine(::testing::Values(0, 1, 2, 3), |
| ::testing::Values(0, 1, 2))); |
| |
| class GcpGaiaCredentialBaseFullNameUpdateTest |
| : public GcpGaiaCredentialBaseTest, |
| public ::testing::WithParamInterface<std::tuple<HRESULT, HRESULT>> {}; |
| |
| TEST_P(GcpGaiaCredentialBaseFullNameUpdateTest, FullNameUpdated) { |
| USES_CONVERSION; |
| |
| CredentialProviderSigninDialogTestDataStorage test_data_storage; |
| |
| CComBSTR username = L"foo_bar"; |
| CComBSTR full_name = A2COLE(test_data_storage.GetSuccessFullName().c_str()); |
| CComBSTR password = A2COLE(test_data_storage.GetSuccessPassword().c_str()); |
| CComBSTR email = A2COLE(test_data_storage.GetSuccessEmail().c_str()); |
| |
| // Create a fake user to reauth. |
| CComBSTR sid; |
| ASSERT_EQ(S_OK, |
| fake_os_user_manager()->CreateTestOSUser( |
| OLE2CW(username), OLE2CW(password), OLE2CW(full_name), |
| L"comment", base::UTF8ToUTF16(test_data_storage.GetSuccessId()), |
| OLE2CW(email), &sid)); |
| |
| base::string16 current_full_name; |
| ASSERT_EQ(S_OK, fake_os_user_manager()->GetUserFullname( |
| fake_os_user_manager()->GetLocalDomain().c_str(), |
| username, ¤t_full_name)); |
| ASSERT_EQ(current_full_name, (BSTR)full_name); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| // Create with invalid token handle response so that a reauth occurs. |
| SetDefaultTokenHandleResponse(kDefaultInvalidTokenHandleResponse); |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(1, &cred)); |
| |
| Microsoft::WRL::ComPtr<ITestCredential> test; |
| ASSERT_EQ(S_OK, cred.As(&test)); |
| |
| ASSERT_EQ(S_OK, test->SetGlsEmailAddress(std::string())); |
| |
| // Override the full name in the gls command line. |
| std::string new_full_name = "New Name"; |
| ASSERT_EQ(S_OK, test->SetGaiaFullNameOverride(new_full_name)); |
| |
| HRESULT get_fullname_hr = std::get<0>(GetParam()); |
| HRESULT set_fullname_hr = std::get<1>(GetParam()); |
| if (FAILED(get_fullname_hr)) { |
| fake_os_user_manager()->SetFailureReason( |
| FAILEDOPERATIONS::GET_USER_FULLNAME, get_fullname_hr); |
| } |
| if (FAILED(set_fullname_hr)) { |
| fake_os_user_manager()->SetFailureReason( |
| FAILEDOPERATIONS::SET_USER_FULLNAME, set_fullname_hr); |
| } |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| fake_os_user_manager()->RestoreOperation(FAILEDOPERATIONS::GET_USER_FULLNAME); |
| fake_os_user_manager()->RestoreOperation(FAILEDOPERATIONS::SET_USER_FULLNAME); |
| |
| base::string16 updated_full_name; |
| ASSERT_EQ(S_OK, fake_os_user_manager()->GetUserFullname( |
| fake_os_user_manager()->GetLocalDomain().c_str(), |
| username, &updated_full_name)); |
| if (FAILED(get_fullname_hr) || FAILED(set_fullname_hr)) { |
| ASSERT_NE(updated_full_name, base::UTF8ToUTF16(new_full_name)); |
| } else { |
| ASSERT_EQ(updated_full_name, base::UTF8ToUTF16(new_full_name)); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| GcpGaiaCredentialBaseFullNameUpdateTest, |
| ::testing::Combine(::testing::Values(S_OK, E_FAIL), |
| ::testing::Values(S_OK, E_FAIL))); |
| |
| // Test event logs upload to GEM service with different failure scenarios. |
| // Parameters are: |
| // 1. bool true: HTTP call to upload logs succeeds. |
| // false: Fails the upload call due to invalid response from the GEM |
| // http server. |
| // 2. int - The number of fake events to seed the fake event log with. |
| class GcpGaiaCredentialBaseUploadEventLogsTest |
| : public GcpGaiaCredentialBaseTest, |
| public ::testing::WithParamInterface<std::tuple<bool, int>> {}; |
| |
| TEST_P(GcpGaiaCredentialBaseUploadEventLogsTest, UploadEventViewerLogs) { |
| bool fail_upload_event_logs_invalid_response = std::get<0>(GetParam()); |
| uint64_t num_events_in_log = std::get<1>(GetParam()); |
| |
| // The number of events in the fake log that we consider too many to upload |
| // in a single request. The fake log events are all 1KB so this would be about |
| // 2.5MB of payload. |
| const uint64_t max_number_of_events_handled_per_invocation = 2500; |
| |
| GoogleMdmEnrolledStatusForTesting force_success(true); |
| |
| // Create some fake logs. |
| std::vector<FakeEventLogsUploadManager::EventLogEntry> logs; |
| for (size_t i = 0; i < num_events_in_log; i++) { |
| base::string16 data(1024, '0'); // 1KB payload. |
| logs.push_back({i + 1, {1000 + i, 200 + i}, data, 1 + i % 4}); |
| } |
| |
| FakeEventLogsUploadManager fake_event_logs_upload_manager(logs); |
| |
| // Create a fake user associated to a gaia id. |
| CComBSTR sid; |
| ASSERT_EQ(S_OK, |
| fake_os_user_manager()->CreateTestOSUser( |
| kDefaultUsername, L"password", L"Full Name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid)); |
| |
| // Change token response to an valid one. |
| SetDefaultTokenHandleResponse(kDefaultValidTokenHandleResponse); |
| |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| fake_event_logs_upload_manager.GetGcpwServiceUploadEventViewerLogsUrl(), |
| FakeWinHttpUrlFetcher::Headers(), |
| fail_upload_event_logs_invalid_response ? "Invalid json response" : "{}"); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // Finish logon successfully. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| |
| // Verify that the upload call returned an appropriate status code. |
| // Upload should fail if the server didn't respond appropriately |
| // unless there were no event logs to upload at all. |
| HRESULT hr = fake_event_logs_upload_manager.GetUploadStatus(); |
| ASSERT_TRUE((fail_upload_event_logs_invalid_response && num_events_in_log > 0) |
| ? FAILED(hr) |
| : SUCCEEDED(hr)); |
| |
| if (!fail_upload_event_logs_invalid_response) { |
| if (num_events_in_log > max_number_of_events_handled_per_invocation) { |
| // In this case we don't expect that all the events are uploaded. |
| ASSERT_TRUE(fake_event_logs_upload_manager.GetNumLogsUploaded() > 0); |
| ASSERT_TRUE(fake_event_logs_upload_manager.GetNumLogsUploaded() <= |
| num_events_in_log); |
| } else { |
| ASSERT_EQ(num_events_in_log, |
| fake_event_logs_upload_manager.GetNumLogsUploaded()); |
| } |
| } |
| |
| ASSERT_EQ(S_OK, ReleaseProvider()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| GcpGaiaCredentialBaseUploadEventLogsTest, |
| ::testing::Combine(::testing::Values(true, false), |
| ::testing::Values(0, 2, 1000, 3000))); |
| |
| // Test if the credential can be created successfully depending on whether a |
| // Chrome path is found. |
| // Parameters are: |
| // 1. bool true: A Chrome path is set. |
| // false: No Chrome path set. |
| class GcpGaiaCredentialBaseChromeAvailabilityTest |
| : public GcpGaiaCredentialBaseTest, |
| public ::testing::WithParamInterface<bool> {}; |
| |
| TEST_P(GcpGaiaCredentialBaseChromeAvailabilityTest, CustomChromeSpecified) { |
| // Simulate a custom Chrome path being set. |
| fake_chrome_checker()->SetHasSupportedChrome( |
| FakeChromeAvailabilityChecker::kChromeDontForce); |
| |
| bool custom_path_set = GetParam(); |
| base::ScopedTempDir temp_chrome_path; |
| |
| // Set system Chrome path to empty so that we are not influenced by the |
| // runtime environment. |
| GoogleChromePathForTesting google_chrome_path_for_testing( |
| base::FilePath(L"")); |
| |
| if (custom_path_set) { |
| ASSERT_TRUE(temp_chrome_path.CreateUniqueTempDir()); |
| ASSERT_EQ(S_OK, |
| SetGlobalFlagForTesting( |
| kRegGlsPath, temp_chrome_path.GetPath().AsUTF16Unsafe())); |
| } |
| |
| USES_CONVERSION; |
| // Create a fake user that has the same gaia id as the test gaia id. |
| CComBSTR sid; |
| base::string16 username(L"foo"); |
| ASSERT_EQ(S_OK, |
| fake_os_user_manager()->CreateTestOSUser( |
| username, L"password", L"name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid)); |
| ASSERT_EQ(2ul, fake_os_user_manager()->GetUserCount()); |
| |
| // Create provider. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| if (custom_path_set) { |
| // Don't fail to create the credential. |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| } else { |
| // Credential creation should fail as no chrome will be found. |
| ASSERT_EQ(E_FAIL, InitializeProviderAndGetCredential(0, &cred)); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| GcpGaiaCredentialBaseChromeAvailabilityTest, |
| ::testing::Values(true, false)); |
| |
| // Test fetching of user cloud policies from the GEM service with different |
| // failure scenarios. |
| // Parameters are: |
| // 1. bool true: HTTP call to fetch policies succeeds. |
| // false: Fails the upload call due to invalid response from the GEM |
| // http server. |
| // 2. bool true: Policies were fetched recently and don't need refreshing. |
| // false: Policies were never fetched or are very old. |
| // 3. bool : Whether cloud policies feature is enabled. |
| class GcpGaiaCredentialBaseFetchCloudPoliciesTest |
| : public GcpGaiaCredentialBaseTest, |
| public ::testing::WithParamInterface<std::tuple<bool, bool, bool>> {}; |
| |
| TEST_P(GcpGaiaCredentialBaseFetchCloudPoliciesTest, FetchAndStore) { |
| bool fail_fetch_policies = std::get<0>(GetParam()); |
| bool policy_refreshed_recently = std::get<1>(GetParam()); |
| bool cloud_policies_enabled = std::get<2>(GetParam()); |
| |
| FakeUserPoliciesManager fake_user_policies_manager(cloud_policies_enabled); |
| GoogleMdmEnrolledStatusForTesting force_success(true); |
| |
| // Create a fake user associated to a gaia id. |
| CComBSTR sid_str; |
| ASSERT_EQ(S_OK, |
| fake_os_user_manager()->CreateTestOSUser( |
| kDefaultUsername, L"password", L"Full Name", L"comment", |
| base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid_str)); |
| base::string16 sid = OLE2W(sid_str); |
| |
| if (cloud_policies_enabled) { |
| base::string16 fetch_time_millis = L"0"; |
| if (policy_refreshed_recently) { |
| fetch_time_millis = base::NumberToString16( |
| base::Time::Now().ToDeltaSinceWindowsEpoch().InMilliseconds()); |
| } |
| ASSERT_EQ(S_OK, SetUserProperty(sid, L"last_policy_refresh_time", |
| fetch_time_millis)); |
| |
| std::string expected_response; |
| if (fail_fetch_policies) { |
| expected_response = "Invalid json response"; |
| } else { |
| UserPolicies policies; |
| base::Value policies_value = policies.ToValue(); |
| base::JSONWriter::Write(policies_value, &expected_response); |
| } |
| |
| fake_http_url_fetcher_factory()->SetFakeResponse( |
| UserPoliciesManager::Get()->GetGcpwServiceUserPoliciesUrl(sid), |
| FakeWinHttpUrlFetcher::Headers(), expected_response); |
| } |
| |
| // Change token response to an valid one. |
| SetDefaultTokenHandleResponse(kDefaultValidTokenHandleResponse); |
| |
| // Create provider and start logon. |
| Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred; |
| |
| ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); |
| |
| ASSERT_EQ(S_OK, StartLogonProcessAndWait()); |
| |
| // Finish logon successfully. |
| ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); |
| |
| base::TimeDelta time_since_last_fetch = |
| UserPoliciesManager::Get()->GetTimeDeltaSinceLastPolicyFetch(sid); |
| |
| if (cloud_policies_enabled && !policy_refreshed_recently) { |
| ASSERT_EQ(1, fake_user_policies_manager.GetNumTimesFetchAndStoreCalled()); |
| } else { |
| ASSERT_EQ(0, fake_user_policies_manager.GetNumTimesFetchAndStoreCalled()); |
| } |
| |
| // Expected number of HTTP calls when not fetching user policies since upload |
| // device details is always called. |
| const size_t base_num_http_requests = 1; |
| const size_t requests_created = |
| fake_http_url_fetcher_factory()->requests_created(); |
| if (!cloud_policies_enabled || policy_refreshed_recently) { |
| // No new requests for fetching policies. |
| ASSERT_EQ(base_num_http_requests, requests_created); |
| } else { |
| // Verify the fetch status matches expected value. |
| HRESULT hr = UserPoliciesManager::Get()->GetLastFetchStatusForTesting(); |
| |
| if (!fail_fetch_policies) { |
| ASSERT_TRUE(SUCCEEDED(hr)); |
| // One additional request for fetching policies. |
| ASSERT_EQ(1 + base_num_http_requests, requests_created); |
| ASSERT_TRUE(time_since_last_fetch < |
| kMaxTimeDeltaSinceLastUserPolicyRefresh); |
| } else { |
| ASSERT_TRUE(FAILED(hr)); |
| // Two additional requests since we retry on failure. |
| ASSERT_EQ(2 + base_num_http_requests, requests_created); |
| ASSERT_TRUE(time_since_last_fetch > |
| kMaxTimeDeltaSinceLastUserPolicyRefresh); |
| } |
| } |
| |
| ASSERT_EQ(S_OK, ReleaseProvider()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| GcpGaiaCredentialBaseFetchCloudPoliciesTest, |
| ::testing::Combine(::testing::Bool(), |
| ::testing::Bool(), |
| ::testing::Bool())); |
| |
| } // namespace testing |
| } // namespace credential_provider |