Commit bb99954d authored by Yusuf Sengul's avatar Yusuf Sengul Committed by Commit Bot

Control force reset password option through registry

Bug: 1018350
Change-Id: I8c0091f1925d0ddcfba806b1adf09b18c86d5cdb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1880806
Commit-Queue: Yusuf Sengul <yusufsn@google.com>
Reviewed-by: default avatarTien Mai <tienmai@chromium.org>
Cr-Commit-Position: refs/heads/master@{#710840}
parent 38ebe59c
......@@ -2223,10 +2223,10 @@ void CGaiaCredentialBase::DisplayPasswordField(int password_message) {
GetStringResource(password_message).c_str());
events_->SetFieldState(this, FID_CURRENT_PASSWORD_FIELD,
CPFS_DISPLAY_IN_SELECTED_TILE);
// Request force password change wouldn't work on a domain joined
// machine as it requires domain admin role privileges to communicate
// with the domain controller whereas GCPW only has SYSTEM privilege.
if (!OSUserManager::Get()->IsUserDomainJoined(get_sid().m_str)) {
// Force password link won't be displayed if the machine is domain joined
// or force reset password is disabled through registry.
if (!OSUserManager::Get()->IsUserDomainJoined(get_sid().m_str) &&
GetGlobalFlagOrDefault(kRegMdmEnableForcePasswordReset, 1)) {
events_->SetFieldState(this, FID_FORGOT_PASSWORD_LINK,
CPFS_DISPLAY_IN_SELECTED_TILE);
events_->SetFieldString(
......
......@@ -293,6 +293,80 @@ TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_MultipleCalls) {
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.
CComPtr<ICredentialProviderCredential> cred;
ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred));
CComPtr<ITestCredential> test;
ASSERT_EQ(S_OK, cred.QueryInterface(&test));
ASSERT_EQ(CPFS_HIDDEN,
fake_credential_provider_credential_events()->GetFieldState(
cred, 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, FID_FORGOT_PASSWORD_LINK));
} else {
ASSERT_EQ(CPFS_DISPLAY_IN_SELECTED_TILE,
fake_credential_provider_credential_events()->GetFieldState(
cred, 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(,
GcpGaiaCredentialBaseForceResetRegistryTest,
::testing::Values(0, 1, 2));
TEST_F(GcpGaiaCredentialBaseTest,
GetSerialization_PasswordChangedForAssociatedUser) {
USES_CONVERSION;
......@@ -314,10 +388,18 @@ TEST_F(GcpGaiaCredentialBaseTest,
CComPtr<ITestCredential> test;
ASSERT_EQ(S_OK, cred.QueryInterface(&test));
ASSERT_EQ(CPFS_HIDDEN,
fake_credential_provider_credential_events()->GetFieldState(
cred, FID_FORGOT_PASSWORD_LINK));
ASSERT_EQ(S_OK, StartLogonProcessAndWait());
EXPECT_TRUE(test->CanAttemptWindowsLogon());
EXPECT_EQ(S_OK, test->IsWindowsPasswordValidForStoredUser());
EXPECT_FALSE(test->IsWindowsPasswordValidForStoredUser());
ASSERT_EQ(CPFS_DISPLAY_IN_SELECTED_TILE,
fake_credential_provider_credential_events()->GetFieldState(
cred, FID_FORGOT_PASSWORD_LINK));
// Check that the process has not finished yet.
CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr;
......@@ -331,7 +413,7 @@ TEST_F(GcpGaiaCredentialBaseTest,
// Credentials should still be available.
EXPECT_TRUE(test->CanAttemptWindowsLogon());
EXPECT_EQ(S_OK, test->IsWindowsPasswordValidForStoredUser());
EXPECT_FALSE(test->IsWindowsPasswordValidForStoredUser());
// Set an invalid password and try to get serialization again. Credentials
// should still be valid but serialization is not complete.
......@@ -349,7 +431,7 @@ TEST_F(GcpGaiaCredentialBaseTest,
// Both Windows and Gaia credentials should be valid now
EXPECT_TRUE(test->CanAttemptWindowsLogon());
EXPECT_EQ(S_FALSE, test->IsWindowsPasswordValidForStoredUser());
EXPECT_TRUE(test->IsWindowsPasswordValidForStoredUser());
// Finish logon successfully but with no credential changed event.
ASSERT_EQ(S_OK, FinishLogonProcess(true, false, 0));
......@@ -379,7 +461,7 @@ TEST_F(GcpGaiaCredentialBaseTest,
ASSERT_EQ(S_OK, StartLogonProcessAndWait());
EXPECT_TRUE(test->CanAttemptWindowsLogon());
EXPECT_EQ(S_OK, test->IsWindowsPasswordValidForStoredUser());
EXPECT_FALSE(test->IsWindowsPasswordValidForStoredUser());
// Check that the process has not finished yet.
CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr;
......@@ -393,7 +475,7 @@ TEST_F(GcpGaiaCredentialBaseTest,
// Credentials should still be available.
EXPECT_TRUE(test->CanAttemptWindowsLogon());
EXPECT_EQ(S_OK, test->IsWindowsPasswordValidForStoredUser());
EXPECT_FALSE(test->IsWindowsPasswordValidForStoredUser());
// Simulate a click on the "Forgot Password" link.
cred->CommandLinkClicked(FID_FORGOT_PASSWORD_LINK);
......@@ -426,7 +508,7 @@ TEST_F(GcpGaiaCredentialBaseTest,
ASSERT_EQ(S_OK, StartLogonProcessAndWait());
EXPECT_TRUE(test->CanAttemptWindowsLogon());
EXPECT_EQ(S_OK, test->IsWindowsPasswordValidForStoredUser());
EXPECT_FALSE(test->IsWindowsPasswordValidForStoredUser());
// Check that the process has not finished yet.
CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr;
......@@ -440,7 +522,7 @@ TEST_F(GcpGaiaCredentialBaseTest,
// Credentials should still be available.
EXPECT_TRUE(test->CanAttemptWindowsLogon());
EXPECT_EQ(S_OK, test->IsWindowsPasswordValidForStoredUser());
EXPECT_FALSE(test->IsWindowsPasswordValidForStoredUser());
// Simulate a click on the "Forgot Password" link.
cred->CommandLinkClicked(FID_FORGOT_PASSWORD_LINK);
......@@ -464,7 +546,7 @@ TEST_F(GcpGaiaCredentialBaseTest,
// Both Windows and Gaia credentials should be valid now
EXPECT_TRUE(test->CanAttemptWindowsLogon());
EXPECT_EQ(S_FALSE, test->IsWindowsPasswordValidForStoredUser());
EXPECT_TRUE(test->IsWindowsPasswordValidForStoredUser());
// Finish logon successfully but with no credential changed event.
ASSERT_EQ(S_OK, FinishLogonProcess(true, false, 0));
......@@ -1412,6 +1494,12 @@ TEST_P(GcpGaiaCredentialBasePasswordRecoveryTest, PasswordRecovery) {
// 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, 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));
......@@ -1419,6 +1507,12 @@ TEST_P(GcpGaiaCredentialBasePasswordRecoveryTest, PasswordRecovery) {
// 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, FID_CURRENT_PASSWORD_FIELD));
// Make sure the new password is sent to the provider.
EXPECT_STREQ(A2OLE(kNewPassword), OLE2CW(test_provider->password()));
......
......@@ -30,6 +30,8 @@
namespace credential_provider {
constexpr wchar_t kRegMdmUrl[] = L"mdm";
constexpr wchar_t kRegMdmEnableForcePasswordReset[] =
L"mdm_enable_force_password";
constexpr wchar_t kRegMdmEscrowServiceServerUrl[] = L"mdm_ess_url";
constexpr wchar_t kRegMdmSupportsMultiUser[] = L"mdm_mu";
constexpr wchar_t kRegMdmAllowConsumerAccounts[] = L"mdm_aca";
......
......@@ -30,6 +30,9 @@ extern const wchar_t kRegMdmSupportsMultiUser[];
// Allow sign in using normal consumer accounts.
extern const wchar_t kRegMdmAllowConsumerAccounts[];
// Enables force password reset option in forgot password flow.
extern const wchar_t kRegMdmEnableForcePasswordReset[];
// Password lsa store key prefix.
extern const wchar_t kUserPasswordLsaStoreKeyPrefix[];
......
......@@ -166,6 +166,96 @@ IMPL_IUNKOWN_NOQI_WITH_REF(FakeCredentialProviderEvents)
///////////////////////////////////////////////////////////////////////////////
FakeCredentialProviderCredentialEvents::
FakeCredentialProviderCredentialEvents() {}
FakeCredentialProviderCredentialEvents::
~FakeCredentialProviderCredentialEvents() {}
HRESULT FakeCredentialProviderCredentialEvents::AppendFieldComboBoxItem(
ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
LPCWSTR pszItem) {
return S_OK;
}
HRESULT FakeCredentialProviderCredentialEvents::DeleteFieldComboBoxItem(
ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
DWORD dwItem) {
return S_OK;
}
HRESULT FakeCredentialProviderCredentialEvents::OnCreatingWindow(
HWND* phwndOwner) {
return S_OK;
}
HRESULT FakeCredentialProviderCredentialEvents::SetFieldBitmap(
ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
HBITMAP hbmp) {
return S_OK;
}
HRESULT FakeCredentialProviderCredentialEvents::SetFieldCheckbox(
ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
BOOL bChecked,
LPCWSTR pszLabel) {
return S_OK;
}
HRESULT FakeCredentialProviderCredentialEvents::SetFieldComboBoxSelectedItem(
ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
DWORD dwSelectedItem) {
return S_OK;
}
HRESULT FakeCredentialProviderCredentialEvents::SetFieldInteractiveState(
ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE cpfis) {
return S_OK;
}
HRESULT FakeCredentialProviderCredentialEvents::SetFieldState(
ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
CREDENTIAL_PROVIDER_FIELD_STATE cpfs) {
field_states_[pcpc][dwFieldID] = cpfs;
return S_OK;
}
HRESULT FakeCredentialProviderCredentialEvents::SetFieldString(
ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
LPCWSTR psz) {
return S_OK;
}
HRESULT FakeCredentialProviderCredentialEvents::SetFieldSubmitButton(
ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
DWORD dwAdjacentTo) {
return S_OK;
}
CREDENTIAL_PROVIDER_FIELD_STATE
FakeCredentialProviderCredentialEvents::GetFieldState(
ICredentialProviderCredential* pcpc,
DWORD dwFieldID) {
DCHECK(field_states_.count(pcpc));
DCHECK(field_states_[pcpc].count(dwFieldID));
return field_states_[pcpc][dwFieldID];
}
IMPL_IUNKOWN_NOQI_WITH_REF(FakeCredentialProviderCredentialEvents)
///////////////////////////////////////////////////////////////////////////////
CTestGaiaCredentialProvider::CTestGaiaCredentialProvider() {
// Set functions for creating test credentials of all types.
SetCredentialCreatorFunctionsForTesting(
......
......@@ -98,6 +98,58 @@ class FakeCredentialProviderEvents : public ICredentialProviderEvents {
///////////////////////////////////////////////////////////////////////////////
// Fake OS imlpementation of ICredentialProviderEvents.
class FakeCredentialProviderCredentialEvents
: public ICredentialProviderCredentialEvents {
public:
FakeCredentialProviderCredentialEvents();
virtual ~FakeCredentialProviderCredentialEvents();
// ICredentialProviderCredentialEvents
DECLARE_IUNKOWN_NOQI_WITH_REF()
IFACEMETHODIMP AppendFieldComboBoxItem(ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
LPCWSTR pszItem) override;
IFACEMETHODIMP DeleteFieldComboBoxItem(ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
DWORD dwItem) override;
IFACEMETHODIMP OnCreatingWindow(HWND* phwndOwner) override;
IFACEMETHODIMP SetFieldBitmap(ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
HBITMAP hbmp) override;
IFACEMETHODIMP SetFieldCheckbox(ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
BOOL bChecked,
LPCWSTR pszLabel) override;
IFACEMETHODIMP SetFieldComboBoxSelectedItem(
ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
DWORD dwSelectedItem) override;
IFACEMETHODIMP SetFieldInteractiveState(
ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE cpfis) override;
IFACEMETHODIMP SetFieldState(ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
CREDENTIAL_PROVIDER_FIELD_STATE cpfs) override;
IFACEMETHODIMP SetFieldString(ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
LPCWSTR psz) override;
IFACEMETHODIMP SetFieldSubmitButton(ICredentialProviderCredential* pcpc,
DWORD dwFieldID,
DWORD dwAdjacentTo) override;
CREDENTIAL_PROVIDER_FIELD_STATE GetFieldState(
ICredentialProviderCredential* pcpc,
DWORD dwFieldID);
private:
std::unordered_map<ICredentialProviderCredential*,
std::unordered_map<DWORD, CREDENTIAL_PROVIDER_FIELD_STATE>>
field_states_;
};
///////////////////////////////////////////////////////////////////////////////
// Test implementation of GaiaCredentialProvider that stores information from
// OnUserAuthenticatedImpl.
class CTestGaiaCredentialProvider : public CGaiaCredentialProvider,
......
......@@ -314,8 +314,7 @@ HRESULT GlsRunnerTestBase::InternalInitializeProvider(
return hr;
// Activate the CP.
FakeCredentialProviderEvents events;
hr = provider->Advise(&fake_provider_events_, 0);
hr = provider->Advise(fake_provider_events(), 0);
if (FAILED(hr))
return hr;
......@@ -340,11 +339,12 @@ HRESULT GlsRunnerTestBase::InternalInitializeProvider(
CComPtr<ICredentialProviderCredential> current_credential;
hr = gaia_provider_->GetCredentialAt(i, &current_credential);
if (FAILED(hr))
break;
return hr;
hr = current_credential->Advise(nullptr);
hr = current_credential->Advise(
fake_credential_provider_credential_events());
if (FAILED(hr))
break;
return hr;
}
}
......@@ -361,6 +361,28 @@ HRESULT GlsRunnerTestBase::InternalInitializeProvider(
return hr;
}
// Initialize the default field states by calling GetFieldState of
// ICredentialProviderCredential.
for (DWORD i = 0; count && i < *count; ++i) {
CComPtr<ICredentialProviderCredential> current_credential;
hr = gaia_provider_->GetCredentialAt(i, &current_credential);
if (FAILED(hr))
return hr;
for (DWORD fieldID = 0; fieldID < field_count; fieldID++) {
CREDENTIAL_PROVIDER_FIELD_STATE cpfs;
CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE cpfis;
hr = current_credential->GetFieldState(fieldID, &cpfs, &cpfis);
if (FAILED(hr))
return hr;
hr = fake_credential_provider_credential_events()->SetFieldState(
current_credential, fieldID, cpfs);
if (FAILED(hr))
return hr;
}
}
return S_OK;
}
......
......@@ -65,6 +65,10 @@ class GlsRunnerTestBase : public ::testing::Test {
FakeCredentialProviderEvents* fake_provider_events() {
return &fake_provider_events_;
}
FakeCredentialProviderCredentialEvents*
fake_credential_provider_credential_events() {
return &fake_credential_provider_credential_events_;
}
FakeInternetAvailabilityChecker* fake_internet_checker() {
return &fake_internet_checker_;
}
......@@ -179,6 +183,8 @@ class GlsRunnerTestBase : public ::testing::Test {
FakePasswordRecoveryManager fake_password_recovery_manager_;
FakeWinHttpUrlFetcherFactory fake_http_url_fetcher_factory_;
FakeCredentialProviderEvents fake_provider_events_;
FakeCredentialProviderCredentialEvents
fake_credential_provider_credential_events_;
FakeCredentialProviderUserArray fake_user_array_;
// SID of the user that is considered to be locking the workstation. This is
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment