Commit b74492d3 authored by Irina Fedorova's avatar Irina Fedorova Committed by Commit Bot

Update ChangeSavedPassword function.

In the previous version, this function allowed changing the username
and optionally changing the password.
In the new version, this function allows to change only the password
and returns true if the password was successfully changed.


Bug: 377410
Change-Id: I0b555b404b781d8fa35c9eda1aff442141ab9219
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2302638
Commit-Queue: Irina Fedorova <irfedorova@google.com>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Reviewed-by: default avatarJan Wilken Dörrie <jdoerrie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#791170}
parent 787b18d6
...@@ -62,12 +62,13 @@ ResponseAction PasswordsPrivateChangeSavedPasswordFunction::Run() { ...@@ -62,12 +62,13 @@ ResponseAction PasswordsPrivateChangeSavedPasswordFunction::Run() {
api::passwords_private::ChangeSavedPassword::Params::Create(*args_); api::passwords_private::ChangeSavedPassword::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(parameters); EXTENSION_FUNCTION_VALIDATE(parameters);
GetDelegate(browser_context()) if (!GetDelegate(browser_context())
->ChangeSavedPassword( ->ChangeSavedPassword(parameters->id,
parameters->id, base::UTF8ToUTF16(parameters->new_username), base::UTF8ToUTF16(parameters->new_password))) {
parameters->new_password ? base::make_optional(base::UTF8ToUTF16( return RespondNow(Error(
*parameters->new_password)) "Could not change the password. Either the password is empty, the user "
: base::nullopt); "is not authenticated or no matching password could be found."));
}
return RespondNow(NoArguments()); return RespondNow(NoArguments());
} }
......
...@@ -120,8 +120,18 @@ class PasswordsPrivateApiTest : public ExtensionApiTest { ...@@ -120,8 +120,18 @@ class PasswordsPrivateApiTest : public ExtensionApiTest {
} // namespace } // namespace
IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, ChangeSavedPassword) { IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, ChangeSavedPasswordSucceeds) {
EXPECT_TRUE(RunPasswordsSubtest("changeSavedPassword")) << message_; EXPECT_TRUE(RunPasswordsSubtest("changeSavedPasswordSucceeds")) << message_;
}
IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, ChangeSavedPasswordFails) {
EXPECT_TRUE(RunPasswordsSubtest("changeSavedPasswordFails")) << message_;
}
IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest,
ChangeSavedPasswordWithEmptyPasswordFails) {
EXPECT_TRUE(RunPasswordsSubtest("changeSavedPasswordWithEmptyPasswordFails"))
<< message_;
} }
IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest,
......
...@@ -56,14 +56,11 @@ class PasswordsPrivateDelegate : public KeyedService { ...@@ -56,14 +56,11 @@ class PasswordsPrivateDelegate : public KeyedService {
base::OnceCallback<void(const ExceptionEntries&)>; base::OnceCallback<void(const ExceptionEntries&)>;
virtual void GetPasswordExceptionsList(ExceptionEntriesCallback callback) = 0; virtual void GetPasswordExceptionsList(ExceptionEntriesCallback callback) = 0;
// Changes the username and password corresponding to |id|. // Changes the password corresponding to |id|.
// |id|: The id for the password entry being updated. // |id|: The id for the password entry being updated.
// |new_username|: The new username.
// |new_password|: The new password. // |new_password|: The new password.
virtual void ChangeSavedPassword( // Returns whether the change succeeded.
int id, virtual bool ChangeSavedPassword(int id, base::string16 new_password) = 0;
base::string16 new_username,
base::Optional<base::string16> new_password) = 0;
// Removes the saved password entries corresponding to the |ids| generated for // Removes the saved password entries corresponding to the |ids| generated for
// each entry of the password list. Any invalid id will be ignored. // each entry of the password list. Any invalid id will be ignored.
......
...@@ -202,14 +202,14 @@ void PasswordsPrivateDelegateImpl::GetPasswordExceptionsList( ...@@ -202,14 +202,14 @@ void PasswordsPrivateDelegateImpl::GetPasswordExceptionsList(
get_password_exception_list_callbacks_.push_back(std::move(callback)); get_password_exception_list_callbacks_.push_back(std::move(callback));
} }
void PasswordsPrivateDelegateImpl::ChangeSavedPassword( bool PasswordsPrivateDelegateImpl::ChangeSavedPassword(
int id, int id,
base::string16 new_username, base::string16 new_password) {
base::Optional<base::string16> new_password) {
const std::string* sort_key = password_id_generator_.TryGetKey(id); const std::string* sort_key = password_id_generator_.TryGetKey(id);
DCHECK(sort_key);
password_manager_presenter_->ChangeSavedPassword( return sort_key != nullptr &&
*sort_key, std::move(new_username), std::move(new_password)); password_manager_presenter_->ChangeSavedPassword(
*sort_key, std::move(new_password));
} }
void PasswordsPrivateDelegateImpl::RemoveSavedPasswords( void PasswordsPrivateDelegateImpl::RemoveSavedPasswords(
......
...@@ -49,10 +49,7 @@ class PasswordsPrivateDelegateImpl : public PasswordsPrivateDelegate, ...@@ -49,10 +49,7 @@ class PasswordsPrivateDelegateImpl : public PasswordsPrivateDelegate,
// PasswordsPrivateDelegate implementation. // PasswordsPrivateDelegate implementation.
void GetSavedPasswordsList(UiEntriesCallback callback) override; void GetSavedPasswordsList(UiEntriesCallback callback) override;
void GetPasswordExceptionsList(ExceptionEntriesCallback callback) override; void GetPasswordExceptionsList(ExceptionEntriesCallback callback) override;
void ChangeSavedPassword( bool ChangeSavedPassword(int id, base::string16 new_password) override;
int id,
base::string16 new_username,
base::Optional<base::string16> new_password) override;
void RemoveSavedPasswords(const std::vector<int>& ids) override; void RemoveSavedPasswords(const std::vector<int>& ids) override;
void RemovePasswordExceptions(const std::vector<int>& ids) override; void RemovePasswordExceptions(const std::vector<int>& ids) override;
void UndoRemoveSavedPasswordOrException() override; void UndoRemoveSavedPasswordOrException() override;
......
...@@ -352,8 +352,8 @@ TEST_F(PasswordsPrivateDelegateImplTest, ChangeSavedPassword) { ...@@ -352,8 +352,8 @@ TEST_F(PasswordsPrivateDelegateImplTest, ChangeSavedPassword) {
int sample_form_id = delegate.GetPasswordIdGeneratorForTesting().GenerateId( int sample_form_id = delegate.GetPasswordIdGeneratorForTesting().GenerateId(
password_manager::CreateSortKey(sample_form)); password_manager::CreateSortKey(sample_form));
delegate.ChangeSavedPassword(sample_form_id, base::ASCIIToUTF16("new_user"), EXPECT_TRUE(delegate.ChangeSavedPassword(sample_form_id,
base::ASCIIToUTF16("new_pass")); base::ASCIIToUTF16("new_pass")));
// Spin the loop to allow PasswordStore tasks posted when changing the // Spin the loop to allow PasswordStore tasks posted when changing the
// password to be completed. // password to be completed.
...@@ -365,8 +365,6 @@ TEST_F(PasswordsPrivateDelegateImplTest, ChangeSavedPassword) { ...@@ -365,8 +365,6 @@ TEST_F(PasswordsPrivateDelegateImplTest, ChangeSavedPassword) {
[&](const PasswordsPrivateDelegate::UiEntries& password_list) { [&](const PasswordsPrivateDelegate::UiEntries& password_list) {
got_passwords = true; got_passwords = true;
ASSERT_EQ(1u, password_list.size()); ASSERT_EQ(1u, password_list.size());
EXPECT_EQ(base::ASCIIToUTF16("new_user"),
base::UTF8ToUTF16(password_list[0].username));
})); }));
EXPECT_TRUE(got_passwords); EXPECT_TRUE(got_passwords);
} }
......
...@@ -61,17 +61,14 @@ void TestPasswordsPrivateDelegate::GetPasswordExceptionsList( ...@@ -61,17 +61,14 @@ void TestPasswordsPrivateDelegate::GetPasswordExceptionsList(
std::move(callback).Run(current_exceptions_); std::move(callback).Run(current_exceptions_);
} }
void TestPasswordsPrivateDelegate::ChangeSavedPassword( bool TestPasswordsPrivateDelegate::ChangeSavedPassword(
int id, int id,
base::string16 username, base::string16 new_password) {
base::Optional<base::string16> password) { if ((static_cast<size_t>(id) >= current_entries_.size()) ||
if (static_cast<size_t>(id) >= current_entries_.size()) new_password.empty())
return; return false;
// PasswordUiEntry does not contain a password. Thus we are only updating return true;
// the username and the length of the password.
current_entries_[id].username = base::UTF16ToUTF8(username);
SendSavedPasswordsList();
} }
void TestPasswordsPrivateDelegate::RemoveSavedPasswords( void TestPasswordsPrivateDelegate::RemoveSavedPasswords(
......
...@@ -23,9 +23,9 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate { ...@@ -23,9 +23,9 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate {
// PasswordsPrivateDelegate implementation. // PasswordsPrivateDelegate implementation.
void GetSavedPasswordsList(UiEntriesCallback callback) override; void GetSavedPasswordsList(UiEntriesCallback callback) override;
void GetPasswordExceptionsList(ExceptionEntriesCallback callback) override; void GetPasswordExceptionsList(ExceptionEntriesCallback callback) override;
void ChangeSavedPassword(int id, // Fake implementation of ChangeSavedPassword. This succeeds if the
base::string16 username, // current list of entries has the id and if the new password isn't empty.
base::Optional<base::string16> password) override; bool ChangeSavedPassword(int id, base::string16 new_password) override;
void RemoveSavedPasswords(const std::vector<int>& id) override; void RemoveSavedPasswords(const std::vector<int>& id) override;
void RemovePasswordExceptions(const std::vector<int>& ids) override; void RemovePasswordExceptions(const std::vector<int>& ids) override;
// Simplified version of undo logic, only use for testing. // Simplified version of undo logic, only use for testing.
......
...@@ -42,6 +42,15 @@ export class PasswordManagerProxy { ...@@ -42,6 +42,15 @@ export class PasswordManagerProxy {
*/ */
recordPasswordsPageAccessInSettings() {} recordPasswordsPageAccessInSettings() {}
/**
* Changes the password corresponding to |id|.
* @param {number} id The id for the password entry being updated.
* @param {string} new_password
* @return {!Promise<void>} A promise that resolves when the password is
* updated.
*/
changeSavedPassword(id, new_password) {}
/** /**
* Should remove the saved password and notify that the list has changed. * Should remove the saved password and notify that the list has changed.
* @param {number} id The id for the password entry being removed. * @param {number} id The id for the password entry being removed.
...@@ -362,6 +371,13 @@ export class PasswordManagerImpl { ...@@ -362,6 +371,13 @@ export class PasswordManagerImpl {
chrome.passwordsPrivate.recordPasswordsPageAccessInSettings(); chrome.passwordsPrivate.recordPasswordsPageAccessInSettings();
} }
/** override */
changeSavedPassword(id, new_password) {
return new Promise(resolve => {
chrome.passwordsPrivate.changeSavedPassword(id, new_password, resolve);
});
}
/** @override */ /** @override */
removeSavedPassword(id) { removeSavedPassword(id) {
chrome.passwordsPrivate.removeSavedPassword(id); chrome.passwordsPrivate.removeSavedPassword(id);
......
...@@ -321,48 +321,28 @@ const autofill::PasswordForm* PasswordManagerPresenter::GetPasswordException( ...@@ -321,48 +321,28 @@ const autofill::PasswordForm* PasswordManagerPresenter::GetPasswordException(
return TryGetPasswordForm(exception_map_, index); return TryGetPasswordForm(exception_map_, index);
} }
void PasswordManagerPresenter::ChangeSavedPassword( bool PasswordManagerPresenter::ChangeSavedPassword(
const std::string& sort_key, const std::string& sort_key,
const base::string16& new_username, base::string16 new_password) {
const base::Optional<base::string16>& new_password) {
// Find the equivalence class that needs to be updated. // Find the equivalence class that needs to be updated.
auto it = password_map_.find(sort_key); auto it = password_map_.find(sort_key);
if (it == password_map_.end()) if (it == password_map_.end())
return; return false;
const FormVector& old_forms = it->second;
// If a password was provided, make sure it is not empty. // Make sure new_password is not empty.
if (new_password && new_password->empty()) { if (new_password.empty()) {
DLOG(ERROR) << "The password is empty."; DLOG(ERROR) << "The password is empty.";
return; return false;
} }
const std::string& signon_realm = old_forms[0]->signon_realm; const FormVector& old_forms = it->second;
const base::string16& old_username = old_forms[0]->username_value;
// TODO(crbug.com/377410): Clean up this check for duplicates because a
// very similar one is in password_store_utils in EditSavedPasswords already.
// In case the username
// changed, make sure that there exists no other credential with the same
// signon_realm and username.
const bool username_changed = old_username != new_username;
if (username_changed) {
for (const auto& sort_key_passwords_pair : password_map_) {
for (const auto& password : sort_key_passwords_pair.second) {
if (password->signon_realm == signon_realm &&
password->username_value == new_username) {
DLOG(ERROR) << "A credential with the same signon_realm and username "
"already exists.";
return;
}
}
}
}
EditSavedPasswords(password_view_->GetProfile(), old_forms, new_username, DCHECK(!old_forms.empty());
new_password); const base::string16& username = old_forms[0]->username_value;
EditSavedPasswords(password_view_->GetProfile(), old_forms, username,
std::move(new_password));
return true;
} }
void PasswordManagerPresenter::RemoveSavedPassword(size_t index) { void PasswordManagerPresenter::RemoveSavedPassword(size_t index) {
......
...@@ -76,10 +76,9 @@ class PasswordManagerPresenter ...@@ -76,10 +76,9 @@ class PasswordManagerPresenter
// Gets the password exception entry at |index|. // Gets the password exception entry at |index|.
const autofill::PasswordForm* GetPasswordException(size_t index) const; const autofill::PasswordForm* GetPasswordException(size_t index) const;
// Changes the username and password corresponding to |sort_key|. // Changes the password corresponding to |sort_key|.
void ChangeSavedPassword(const std::string& sort_key, bool ChangeSavedPassword(const std::string& sort_key,
const base::string16& new_username, base::string16 new_password);
const base::Optional<base::string16>& new_password);
// Removes the saved password entries at |index|, or corresponding to // Removes the saved password entries at |index|, or corresponding to
// |sort_key|, respectively. // |sort_key|, respectively.
......
...@@ -60,7 +60,6 @@ namespace { ...@@ -60,7 +60,6 @@ namespace {
constexpr char kExampleCom[] = "https://example.com/"; constexpr char kExampleCom[] = "https://example.com/";
constexpr char kExampleOrg[] = "https://example.org/"; constexpr char kExampleOrg[] = "https://example.org/";
constexpr char kNewPass[] = "new_pass"; constexpr char kNewPass[] = "new_pass";
constexpr char kNewUser[] = "new_user";
constexpr char kPassword[] = "pass"; constexpr char kPassword[] = "pass";
constexpr char kPassword2[] = "pass2"; constexpr char kPassword2[] = "pass2";
constexpr char kUsername[] = "user"; constexpr char kUsername[] = "user";
...@@ -233,28 +232,26 @@ class PasswordManagerPresenterTest : public testing::Test { ...@@ -233,28 +232,26 @@ class PasswordManagerPresenterTest : public testing::Test {
return form; return form;
} }
void ChangeSavedPasswordBySortKey( bool ChangeSavedPasswordBySortKey(base::StringPiece url,
base::StringPiece url, base::StringPiece username,
base::StringPiece old_username, base::StringPiece old_password,
base::StringPiece old_password, base::StringPiece new_password) {
base::StringPiece new_username,
base::Optional<base::StringPiece> new_password) {
autofill::PasswordForm temp_form; autofill::PasswordForm temp_form;
temp_form.url = GURL(url); temp_form.url = GURL(url);
temp_form.signon_realm = temp_form.url.GetOrigin().spec(); temp_form.signon_realm = temp_form.url.GetOrigin().spec();
temp_form.username_element = base::ASCIIToUTF16("username"); temp_form.username_element = base::ASCIIToUTF16("username");
temp_form.password_element = base::ASCIIToUTF16("password"); temp_form.password_element = base::ASCIIToUTF16("password");
temp_form.username_value = base::ASCIIToUTF16(old_username); temp_form.username_value = base::ASCIIToUTF16(username);
temp_form.password_value = base::ASCIIToUTF16(old_password); temp_form.password_value = base::ASCIIToUTF16(old_password);
mock_controller_.GetPasswordManagerPresenter()->ChangeSavedPassword( bool result =
password_manager::CreateSortKey(temp_form), mock_controller_.GetPasswordManagerPresenter()->ChangeSavedPassword(
base::ASCIIToUTF16(new_username), password_manager::CreateSortKey(temp_form),
new_password ? base::make_optional(base::ASCIIToUTF16(*new_password)) base::ASCIIToUTF16(std::move(new_password)));
: base::nullopt);
// The password store posts mutation tasks to a background thread, thus we // The password store posts mutation tasks to a background thread, thus we
// need to spin the message loop here. // need to spin the message loop here.
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
return result;
} }
void UpdatePasswordLists() { void UpdatePasswordLists() {
...@@ -299,13 +296,14 @@ TEST_F(PasswordManagerPresenterTest, ...@@ -299,13 +296,14 @@ TEST_F(PasswordManagerPresenterTest,
ElementsAre(Pair(kUsername, kPassword))); ElementsAre(Pair(kUsername, kPassword)));
testing::Mock::VerifyAndClearExpectations(&GetUIController()); testing::Mock::VerifyAndClearExpectations(&GetUIController());
ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kNewUser, ""); EXPECT_FALSE(
ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, ""));
EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)), EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
ElementsAre(Pair(kUsername, kPassword))); ElementsAre(Pair(kUsername, kPassword)));
} }
TEST_F(PasswordManagerPresenterTest, TEST_F(PasswordManagerPresenterTest,
ChangeSavedPasswordBySortKey_ChangeUsername) { ChangeSavedPasswordBySortKey_DontRejectSamePassword) {
AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword); AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(1))); EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(1)));
EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty())); EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty()));
...@@ -314,14 +312,14 @@ TEST_F(PasswordManagerPresenterTest, ...@@ -314,14 +312,14 @@ TEST_F(PasswordManagerPresenterTest,
ElementsAre(Pair(kUsername, kPassword))); ElementsAre(Pair(kUsername, kPassword)));
testing::Mock::VerifyAndClearExpectations(&GetUIController()); testing::Mock::VerifyAndClearExpectations(&GetUIController());
ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kNewUser, EXPECT_TRUE(ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword,
base::nullopt); kPassword));
EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)), EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
ElementsAre(Pair(kNewUser, kPassword))); ElementsAre(Pair(kUsername, kPassword)));
} }
TEST_F(PasswordManagerPresenterTest, TEST_F(PasswordManagerPresenterTest,
ChangeSavedPasswordBySortKey_ChangeUsernameAndPassword) { ChangeSavedPasswordBySortKey_ChangePassword) {
AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword); AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(1))); EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(1)));
EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty())); EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty()));
...@@ -330,14 +328,14 @@ TEST_F(PasswordManagerPresenterTest, ...@@ -330,14 +328,14 @@ TEST_F(PasswordManagerPresenterTest,
ElementsAre(Pair(kUsername, kPassword))); ElementsAre(Pair(kUsername, kPassword)));
testing::Mock::VerifyAndClearExpectations(&GetUIController()); testing::Mock::VerifyAndClearExpectations(&GetUIController());
ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kNewUser, EXPECT_TRUE(ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword,
kNewPass); kNewPass));
EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)), EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
ElementsAre(Pair(kNewUser, kNewPass))); ElementsAre(Pair(kUsername, kNewPass)));
} }
TEST_F(PasswordManagerPresenterTest, TEST_F(PasswordManagerPresenterTest,
ChangeSavedPasswordBySortKey_RejectSameUsernameForSameRealm) { ChangeSavedPasswordBySortKey_DontRejectSamePasswordForSameRealm) {
AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword); AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
AddPasswordEntry(GURL(kExampleCom), kUsername2, kPassword2); AddPasswordEntry(GURL(kExampleCom), kUsername2, kPassword2);
EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(2))); EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(2)));
...@@ -348,15 +346,15 @@ TEST_F(PasswordManagerPresenterTest, ...@@ -348,15 +346,15 @@ TEST_F(PasswordManagerPresenterTest,
Pair(kUsername2, kPassword2))); Pair(kUsername2, kPassword2)));
testing::Mock::VerifyAndClearExpectations(&GetUIController()); testing::Mock::VerifyAndClearExpectations(&GetUIController());
ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kUsername2, EXPECT_TRUE(ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword,
base::nullopt); kPassword2));
EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)), EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
UnorderedElementsAre(Pair(kUsername, kPassword), UnorderedElementsAre(Pair(kUsername, kPassword2),
Pair(kUsername2, kPassword2))); Pair(kUsername2, kPassword2)));
} }
TEST_F(PasswordManagerPresenterTest, TEST_F(PasswordManagerPresenterTest,
ChangeSavedPasswordBySortKey_DontRejectSameUsernameForDifferentRealm) { ChangeSavedPasswordBySortKey_DontRejectSamePasswordForDifferentRealm) {
AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword); AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
AddPasswordEntry(GURL(kExampleOrg), kUsername2, kPassword2); AddPasswordEntry(GURL(kExampleOrg), kUsername2, kPassword2);
EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(2))); EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(2)));
...@@ -368,10 +366,10 @@ TEST_F(PasswordManagerPresenterTest, ...@@ -368,10 +366,10 @@ TEST_F(PasswordManagerPresenterTest,
ElementsAre(Pair(kUsername2, kPassword2))); ElementsAre(Pair(kUsername2, kPassword2)));
testing::Mock::VerifyAndClearExpectations(&GetUIController()); testing::Mock::VerifyAndClearExpectations(&GetUIController());
ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kUsername2, EXPECT_TRUE(ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword,
base::nullopt); kPassword2));
EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)), EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
ElementsAre(Pair(kUsername2, kPassword))); ElementsAre(Pair(kUsername, kPassword2)));
EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleOrg)), EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleOrg)),
ElementsAre(Pair(kUsername2, kPassword2))); ElementsAre(Pair(kUsername2, kPassword2)));
} }
...@@ -390,38 +388,11 @@ TEST_F(PasswordManagerPresenterTest, ...@@ -390,38 +388,11 @@ TEST_F(PasswordManagerPresenterTest,
Pair(kUsername, kPassword))); Pair(kUsername, kPassword)));
testing::Mock::VerifyAndClearExpectations(&GetUIController()); testing::Mock::VerifyAndClearExpectations(&GetUIController());
ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kNewUser, EXPECT_TRUE(ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword,
kNewPass); kNewPass));
EXPECT_THAT(
GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
UnorderedElementsAre(Pair(kNewUser, kNewPass), Pair(kNewUser, kNewPass)));
}
TEST_F(PasswordManagerPresenterTest,
ChangeSavedPasswordBySortKey_EditUsernameForTheRightCredential) {
AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
AddPasswordEntry(GURL(kExampleCom), kUsername2, kPassword);
AddPasswordEntry(GURL(kExampleOrg), kUsername, kPassword);
AddPasswordEntry(GURL(kExampleOrg), kUsername2, kPassword);
EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(4)));
EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty()));
UpdatePasswordLists();
EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
UnorderedElementsAre(Pair(kUsername, kPassword),
Pair(kUsername2, kPassword)));
EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleOrg)),
UnorderedElementsAre(Pair(kUsername, kPassword),
Pair(kUsername2, kPassword)));
testing::Mock::VerifyAndClearExpectations(&GetUIController());
ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kNewUser,
kPassword);
EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)), EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
UnorderedElementsAre(Pair(kNewUser, kPassword), UnorderedElementsAre(Pair(kUsername, kNewPass),
Pair(kUsername2, kPassword))); Pair(kUsername, kNewPass)));
EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleOrg)),
UnorderedElementsAre(Pair(kUsername, kPassword),
Pair(kUsername2, kPassword)));
} }
TEST_F(PasswordManagerPresenterTest, TEST_F(PasswordManagerPresenterTest,
...@@ -441,8 +412,8 @@ TEST_F(PasswordManagerPresenterTest, ...@@ -441,8 +412,8 @@ TEST_F(PasswordManagerPresenterTest,
Pair(kUsername2, kPassword))); Pair(kUsername2, kPassword)));
testing::Mock::VerifyAndClearExpectations(&GetUIController()); testing::Mock::VerifyAndClearExpectations(&GetUIController());
ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kUsername, EXPECT_TRUE(ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword,
kNewPass); kNewPass));
EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)), EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
UnorderedElementsAre(Pair(kUsername, kNewPass), UnorderedElementsAre(Pair(kUsername, kNewPass),
Pair(kUsername2, kPassword))); Pair(kUsername2, kPassword)));
......
...@@ -202,12 +202,14 @@ namespace passwordsPrivate { ...@@ -202,12 +202,14 @@ namespace passwordsPrivate {
// Settings WebUI. // Settings WebUI.
static void recordPasswordsPageAccessInSettings(); static void recordPasswordsPageAccessInSettings();
// Changes the username and password corresponding to |id|. // Changes the password corresponding to |id|.
// Invokes |callback| or raises an error depending on whether the operation
// succeeded.
// |id|: The id for the password entry being updated. // |id|: The id for the password entry being updated.
// |new_username|: The new username.
// |new_password|: The new password. // |new_password|: The new password.
static void changeSavedPassword(long id, DOMString new_username, // |callback|: The callback that gets invoked in the end.
optional DOMString new_password); static void changeSavedPassword(long id, DOMString new_password,
optional VoidCallback callback);
// Removes the saved password corresponding to |id|. If no saved password // Removes the saved password corresponding to |id|. If no saved password
// for this pair exists, this function is a no-op. |id|: The id for the // for this pair exists, this function is a no-op. |id|: The id for the
......
...@@ -10,26 +10,31 @@ ...@@ -10,26 +10,31 @@
const COMPROMISE_TIME = 158322960000; const COMPROMISE_TIME = 158322960000;
var availableTests = [ var availableTests = [
function changeSavedPassword() { function changeSavedPasswordSucceeds() {
var numCalls = 0; chrome.passwordsPrivate.changeSavedPassword(0, 'new_pass', () => {
var callback = function(savedPasswordsList) { chrome.test.assertNoLastError();
numCalls++; chrome.test.succeed();
if (numCalls == 1) { });
chrome.passwordsPrivate.changeSavedPassword(0, 'new_user'); },
} else if (numCalls == 2) {
chrome.test.assertEq('new_user', savedPasswordsList[0].username);
chrome.passwordsPrivate.changeSavedPassword(
0, 'another_user', 'new_pass');
} else if (numCalls == 3) {
chrome.test.assertEq('another_user', savedPasswordsList[0].username);
chrome.test.succeed();
} else {
chrome.test.fail();
}
};
chrome.passwordsPrivate.onSavedPasswordsListChanged.addListener(callback); function changeSavedPasswordFails() {
chrome.passwordsPrivate.getSavedPasswordList(callback); chrome.passwordsPrivate.changeSavedPassword(-1, 'new_pass', () => {
chrome.test.assertLastError(
'Could not change the password. Either the password is empty, ' +
'the user is not authenticated or no matching password could be ' +
'found.');
chrome.test.succeed();
});
},
function changeSavedPasswordWithEmptyPasswordFails() {
chrome.passwordsPrivate.changeSavedPassword(0, '', () => {
chrome.test.assertLastError(
'Could not change the password. Either the password is empty, ' +
'the user is not authenticated or no matching password could be ' +
'found.');
chrome.test.succeed();
});
}, },
function removeAndUndoRemoveSavedPassword() { function removeAndUndoRemoveSavedPassword() {
......
...@@ -59,6 +59,7 @@ export class TestPasswordManagerProxy extends TestBrowserProxy { ...@@ -59,6 +59,7 @@ export class TestPasswordManagerProxy extends TestBrowserProxy {
'movePasswordToAccount', 'movePasswordToAccount',
'removeException', 'removeException',
'removeExceptions', 'removeExceptions',
'changeSavedPassword',
]); ]);
/** @private {!PasswordManagerExpectations} */ /** @private {!PasswordManagerExpectations} */
...@@ -297,6 +298,12 @@ export class TestPasswordManagerProxy extends TestBrowserProxy { ...@@ -297,6 +298,12 @@ export class TestPasswordManagerProxy extends TestBrowserProxy {
this.methodCalled('recordPasswordCheckReferrer', referrer); this.methodCalled('recordPasswordCheckReferrer', referrer);
} }
/** override */
changeSavedPassword(id, new_password) {
this.methodCalled('changeSavedPassword', {id, new_password});
return Promise.resolve();
}
/** override */ /** override */
addPasswordsFileExportProgressListener() {} addPasswordsFileExportProgressListener() {}
......
...@@ -131,13 +131,15 @@ chrome.passwordsPrivate.PasswordCheckStatus; ...@@ -131,13 +131,15 @@ chrome.passwordsPrivate.PasswordCheckStatus;
chrome.passwordsPrivate.recordPasswordsPageAccessInSettings = function() {}; chrome.passwordsPrivate.recordPasswordsPageAccessInSettings = function() {};
/** /**
* Changes the username and password corresponding to |id|. * Changes the password corresponding to |id|. Invokes |callback| or raises an
* error depending on whether the operation succeeded.
* @param {number} id The id for the password entry being updated. * @param {number} id The id for the password entry being updated.
* @param {string} new_username The new username. * @param {string} new_password The new password.
* @param {string=} new_password The new password. * @param {function(): void=} callback The callback that gets invoked in the
* end.
*/ */
chrome.passwordsPrivate.changeSavedPassword = function( chrome.passwordsPrivate.changeSavedPassword = function(
id, new_username, new_password) {}; id, new_password, callback) {};
/** /**
* Removes the saved password corresponding to |id|. If no saved password for * Removes the saved password corresponding to |id|. If no saved password for
...@@ -147,28 +149,32 @@ chrome.passwordsPrivate.changeSavedPassword = function( ...@@ -147,28 +149,32 @@ chrome.passwordsPrivate.changeSavedPassword = function(
chrome.passwordsPrivate.removeSavedPassword = function(id) {}; chrome.passwordsPrivate.removeSavedPassword = function(id) {};
/** /**
* Removes the saved passwords corresponding to |ids|. If no saved password * Removes the saved password corresponding to |ids|. If no saved password
* exists for a certain id, that id is ignored. * exists for a certain id, that id is ignored. Undoing this operation via
* @param {Array<number>} ids The ids for the password entries being removed. * undoRemoveSavedPasswordOrException will restore all the removed passwords in
* the batch.
* @param {!Array<number>} ids
*/ */
chrome.passwordsPrivate.removeSavedPasswords = function(ids) {}; chrome.passwordsPrivate.removeSavedPasswords = function(ids) {};
/** /**
* Removes the saved password exception corresponding to |id|. If no * Removes the saved password exception corresponding to |id|. If no exception
* exception with this id exists, this function is a no-op. * with this id exists, this function is a no-op.
* @param {number} id The id for the exception url entry being removed. * @param {number} id The id for the exception url entry being removed.
*/ */
chrome.passwordsPrivate.removePasswordException = function(id) {}; chrome.passwordsPrivate.removePasswordException = function(id) {};
/** /**
* Removes the saved password exceptions corresponding to |ids|. If no * Removes the saved password exceptions corresponding to |ids|. If no exception
* exception exists for a certain id, that id is ignored. * exists for a certain id, that id is ignored. Undoing this operation via
* @param {Array<number>} ids The ids for the exception entries being removed. * undoRemoveSavedPasswordOrException will restore all the removed exceptions in
* the batch.
* @param {!Array<number>} ids
*/ */
chrome.passwordsPrivate.removePasswordExceptions = function(ids) {}; chrome.passwordsPrivate.removePasswordExceptions = function(ids) {};
/** /**
* Undoes the last removal of a saved password or exception. * Undoes the last removal of saved password(s) or exception(s).
*/ */
chrome.passwordsPrivate.undoRemoveSavedPasswordOrException = function() {}; chrome.passwordsPrivate.undoRemoveSavedPasswordOrException = function() {};
...@@ -201,10 +207,9 @@ chrome.passwordsPrivate.getPasswordExceptionList = function(callback) {}; ...@@ -201,10 +207,9 @@ chrome.passwordsPrivate.getPasswordExceptionList = function(callback) {};
/** /**
* Moves a password currently stored on the device to being stored in the * Moves a password currently stored on the device to being stored in the
* signed-in, non-syncing Google Account. The result is a no-op if any of * signed-in, non-syncing Google Account. The result is a no-op if any of these
* these is true: |id| is invalid; |id| corresponds to a password already * is true: |id| is invalid; |id| corresponds to a password already stored in
* stored in the account; or the user is not using the account-scoped password * the account; or the user is not using the account-scoped password storage.
* storage.
* @param {number} id The id for the password entry being moved. * @param {number} id The id for the password entry being moved.
*/ */
chrome.passwordsPrivate.movePasswordToAccount = function(id) {}; chrome.passwordsPrivate.movePasswordToAccount = function(id) {};
...@@ -218,8 +223,8 @@ chrome.passwordsPrivate.importPasswords = function() {}; ...@@ -218,8 +223,8 @@ chrome.passwordsPrivate.importPasswords = function() {};
* Triggers the Password Manager password export functionality. Completion Will * Triggers the Password Manager password export functionality. Completion Will
* be signaled by the onPasswordsFileExportProgress event. |callback| will be * be signaled by the onPasswordsFileExportProgress event. |callback| will be
* called when the request is started or rejected. If rejected * called when the request is started or rejected. If rejected
* <code>chrome.runtime.lastError</code> will be set to 'in-progress' or * $(ref:runtime.lastError) will be set to <code>'in-progress'</code> or
* 'reauth-failed'. * <code>'reauth-failed'</code>.
* @param {function(): void} callback * @param {function(): void} callback
*/ */
chrome.passwordsPrivate.exportPasswords = function(callback) {}; chrome.passwordsPrivate.exportPasswords = function(callback) {};
......
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