#include <string>
#include <vector>
+#include "base/basictypes.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/time/time.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/psl_matching_helper.h"
#include "content/public/browser/browser_thread.h"
using autofill::PasswordForm;
+using base::UTF8ToUTF16;
+using base::UTF16ToUTF8;
using content::BrowserThread;
#define GNOME_KEYRING_DEFINE_POINTER(name) \
// Convert the attributes of a given keyring entry into a new PasswordForm.
// Note: does *not* get the actual password, as that is not a key attribute!
// Returns NULL if the attributes are for the wrong application.
-PasswordForm* FormFromAttributes(GnomeKeyringAttributeList* attrs) {
+scoped_ptr<PasswordForm> FormFromAttributes(GnomeKeyringAttributeList* attrs) {
// Read the string and int attributes into the appropriate map.
std::map<std::string, std::string> string_attr_map;
std::map<std::string, uint32_t> uint_attr_map;
// Check to make sure this is a password we care about.
const std::string& app_value = string_attr_map["application"];
if (!base::StringPiece(app_value).starts_with(kGnomeKeyringAppString))
- return NULL;
+ return scoped_ptr<PasswordForm>();
- PasswordForm* form = new PasswordForm();
+ scoped_ptr<PasswordForm> form(new PasswordForm());
form->origin = GURL(string_attr_map["origin_url"]);
form->action = GURL(string_attr_map["action_url"]);
form->username_element = UTF8ToUTF16(string_attr_map["username_element"]);
DCHECK(date_ok);
form->date_created = base::Time::FromTimeT(date_created);
form->blacklisted_by_user = uint_attr_map["blacklisted_by_user"];
+ form->type = static_cast<PasswordForm::Type>(uint_attr_map["type"]);
+ form->times_used = uint_attr_map["times_used"];
form->scheme = static_cast<PasswordForm::Scheme>(uint_attr_map["scheme"]);
-
- return form;
+ int64 date_synced = 0;
+ base::StringToInt64(string_attr_map["date_synced"], &date_synced);
+ form->date_synced = base::Time::FromInternalValue(date_synced);
+ form->display_name = UTF8ToUTF16(string_attr_map["display_name"]);
+ form->avatar_url = GURL(string_attr_map["avatar_url"]);
+ form->federation_url = GURL(string_attr_map["federation_url"]);
+ form->is_zero_click = uint_attr_map["is_zero_click"];
+
+ return form.Pass();
}
// Parse all the results from the given GList into a PasswordFormList, and free
// the GList. PasswordForms are allocated on the heap, and should be deleted by
-// the consumer.
+// the consumer. If not NULL, |lookup_form| is used to filter out results --
+// only credentials with signon realms passing the PSL matching against
+// |lookup_form->signon_realm| will be kept. PSL matched results get their
+// signon_realm, origin, and action rewritten to those of |lookup_form_|, with
+// the original signon_realm saved into the result's original_signon_realm data
+// member.
void ConvertFormList(GList* found,
+ const PasswordForm* lookup_form,
NativeBackendGnome::PasswordFormList* forms) {
- GList* element = g_list_first(found);
- while (element != NULL) {
+ password_manager::PSLDomainMatchMetric psl_domain_match_metric =
+ password_manager::PSL_DOMAIN_MATCH_NONE;
+ for (GList* element = g_list_first(found); element != NULL;
+ element = g_list_next(element)) {
GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
GnomeKeyringAttributeList* attrs = data->attributes;
- PasswordForm* form = FormFromAttributes(attrs);
+ scoped_ptr<PasswordForm> form(FormFromAttributes(attrs));
if (form) {
+ if (lookup_form && form->signon_realm != lookup_form->signon_realm) {
+ // This is not an exact match, we try PSL matching.
+ if (lookup_form->scheme != PasswordForm::SCHEME_HTML ||
+ form->scheme != PasswordForm::SCHEME_HTML ||
+ !(password_manager::IsPublicSuffixDomainMatch(
+ lookup_form->signon_realm, form->signon_realm))) {
+ continue;
+ }
+ psl_domain_match_metric = password_manager::PSL_DOMAIN_MATCH_FOUND;
+ form->original_signon_realm = form->signon_realm;
+ form->signon_realm = lookup_form->signon_realm;
+ form->origin = lookup_form->origin;
+ form->action = lookup_form->action;
+ }
if (data->secret) {
form->password_value = UTF8ToUTF16(data->secret);
} else {
LOG(WARNING) << "Unable to access password from list element!";
}
- forms->push_back(form);
+ forms->push_back(form.release());
} else {
LOG(WARNING) << "Could not initialize PasswordForm from attributes!";
}
-
- element = g_list_next(element);
+ }
+ if (lookup_form) {
+ const GURL signon_realm(lookup_form->signon_realm);
+ std::string registered_domain =
+ password_manager::GetRegistryControlledDomain(signon_realm);
+ UMA_HISTOGRAM_ENUMERATION(
+ "PasswordManager.PslDomainMatchTriggering",
+ password_manager::ShouldPSLDomainMatchingApply(registered_domain)
+ ? psl_domain_match_metric
+ : password_manager::PSL_DOMAIN_MATCH_NOT_USED,
+ password_manager::PSL_DOMAIN_MATCH_COUNT);
}
}
// Schema is analagous to the fields in PasswordForm.
+// TODO(gcasto): Adding 'form_data' would be nice, but we would need to
+// serialize in a way that is guaranteed to not have any embedded NULLs. Pickle
+// doesn't make this guarantee, so we just don't serialize this field. Since
+// it's only used to crowd source data collection it doesn't matter that much
+// if it's not available on this platform.
const GnomeKeyringPasswordSchema kGnomeSchema = {
GNOME_KEYRING_ITEM_GENERIC_SECRET, {
{ "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
{ "date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
{ "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
{ "scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
+ { "type", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
+ { "times_used", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
+ { "date_synced", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+ { "display_name", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+ { "avatar_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+ { "federation_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
+ { "is_zero_click", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
// This field is always "chrome" so that we can search for it.
{ "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
{ NULL }
GnomeKeyringResult WaitResult(PasswordFormList* forms);
private:
+ struct GnomeKeyringAttributeListFreeDeleter {
+ inline void operator()(void* list) const {
+ gnome_keyring_attribute_list_free(
+ static_cast<GnomeKeyringAttributeList*>(list));
+ }
+ };
+
+ typedef scoped_ptr<GnomeKeyringAttributeList,
+ GnomeKeyringAttributeListFreeDeleter> ScopedAttributeList;
+
+ // Helper methods to abbreviate Gnome Keyring long API names.
+ static void AppendString(ScopedAttributeList* list,
+ const char* name,
+ const char* value);
+ static void AppendString(ScopedAttributeList* list,
+ const char* name,
+ const std::string& value);
+ static void AppendUint32(ScopedAttributeList* list,
+ const char* name,
+ guint32 value);
+
// All these callbacks are called on UI thread.
static void OnOperationDone(GnomeKeyringResult result, gpointer data);
base::WaitableEvent event_;
GnomeKeyringResult result_;
NativeBackendGnome::PasswordFormList forms_;
+ // If the credential search is specified by a single form and needs to use PSL
+ // matching, then the specifying form is stored in |lookup_form_|. If PSL
+ // matching is used to find a result, then the results signon realm, origin
+ // and action are stored are replaced by those of |lookup_form_|.
+ // Additionally, |lookup_form_->signon_realm| is also used to narrow down the
+ // found logins to those which indeed PSL-match the look-up. And finally,
+ // |lookup_form_| set to NULL means that PSL matching is not required.
+ scoped_ptr<PasswordForm> lookup_form_;
};
void GKRMethod::AddLogin(const PasswordForm& form, const char* app_string) {
// We don't want to actually save passwords as though on January 1, 1970.
if (!date_created)
date_created = time(NULL);
+ int64 date_synced = form.date_synced.ToInternalValue();
gnome_keyring_store_password(
&kGnomeSchema,
NULL, // Default keyring.
"preferred", form.preferred,
"date_created", base::Int64ToString(date_created).c_str(),
"blacklisted_by_user", form.blacklisted_by_user,
+ "type", form.type,
+ "times_used", form.times_used,
"scheme", form.scheme,
+ "date_synced", base::Int64ToString(date_synced).c_str(),
+ "display_name", UTF16ToUTF8(form.display_name).c_str(),
+ "avatar_url", form.avatar_url.spec().c_str(),
+ "federation_url", form.federation_url.spec().c_str(),
+ "is_zero_click", form.is_zero_click,
"application", app_string,
NULL);
}
void GKRMethod::AddLoginSearch(const PasswordForm& form,
const char* app_string) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ lookup_form_.reset(NULL);
// Search GNOME Keyring for matching passwords to update.
- gnome_keyring_find_itemsv(
- GNOME_KEYRING_ITEM_GENERIC_SECRET,
- OnOperationGetList,
- this, // data
- NULL, // destroy_data
- "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- form.origin.spec().c_str(),
- "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- UTF16ToUTF8(form.username_element).c_str(),
- "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- UTF16ToUTF8(form.username_value).c_str(),
- "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- UTF16ToUTF8(form.password_element).c_str(),
- "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- UTF16ToUTF8(form.submit_element).c_str(),
- "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- form.signon_realm.c_str(),
- "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- app_string,
- NULL);
+ ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
+ AppendString(&attrs, "origin_url", form.origin.spec());
+ AppendString(&attrs, "username_element", UTF16ToUTF8(form.username_element));
+ AppendString(&attrs, "username_value", UTF16ToUTF8(form.username_value));
+ AppendString(&attrs, "password_element", UTF16ToUTF8(form.password_element));
+ AppendString(&attrs, "submit_element", UTF16ToUTF8(form.submit_element));
+ AppendString(&attrs, "signon_realm", form.signon_realm);
+ AppendString(&attrs, "application", app_string);
+ gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
+ attrs.get(),
+ OnOperationGetList,
+ /*data=*/this,
+ /*destroy_data=*/NULL);
}
void GKRMethod::UpdateLoginSearch(const PasswordForm& form,
const char* app_string) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ lookup_form_.reset(NULL);
// Search GNOME Keyring for matching passwords to update.
- gnome_keyring_find_itemsv(
- GNOME_KEYRING_ITEM_GENERIC_SECRET,
- OnOperationGetList,
- this, // data
- NULL, // destroy_data
- "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- form.origin.spec().c_str(),
- "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- UTF16ToUTF8(form.username_element).c_str(),
- "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- UTF16ToUTF8(form.username_value).c_str(),
- "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- UTF16ToUTF8(form.password_element).c_str(),
- "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- form.signon_realm.c_str(),
- "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- app_string,
- NULL);
+ ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
+ AppendString(&attrs, "origin_url", form.origin.spec());
+ AppendString(&attrs, "username_element", UTF16ToUTF8(form.username_element));
+ AppendString(&attrs, "username_value", UTF16ToUTF8(form.username_value));
+ AppendString(&attrs, "password_element", UTF16ToUTF8(form.password_element));
+ AppendString(&attrs, "signon_realm", form.signon_realm);
+ AppendString(&attrs, "application", app_string);
+ gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
+ attrs.get(),
+ OnOperationGetList,
+ /*data=*/this,
+ /*destroy_data=*/NULL);
}
void GKRMethod::RemoveLogin(const PasswordForm& form, const char* app_string) {
this, // data
NULL, // destroy_data
"origin_url", form.origin.spec().c_str(),
- "action_url", form.action.spec().c_str(),
"username_element", UTF16ToUTF8(form.username_element).c_str(),
"username_value", UTF16ToUTF8(form.username_value).c_str(),
"password_element", UTF16ToUTF8(form.password_element).c_str(),
void GKRMethod::GetLogins(const PasswordForm& form, const char* app_string) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ lookup_form_.reset(new PasswordForm(form));
// Search GNOME Keyring for matching passwords.
- gnome_keyring_find_itemsv(
- GNOME_KEYRING_ITEM_GENERIC_SECRET,
- OnOperationGetList,
- this, // data
- NULL, // destroy_data
- "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- form.signon_realm.c_str(),
- "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- app_string,
- NULL);
+ ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
+ if (!password_manager::ShouldPSLDomainMatchingApply(
+ password_manager::GetRegistryControlledDomain(
+ GURL(form.signon_realm)))) {
+ AppendString(&attrs, "signon_realm", form.signon_realm);
+ }
+ AppendString(&attrs, "application", app_string);
+ gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
+ attrs.get(),
+ OnOperationGetList,
+ /*data=*/this,
+ /*destroy_data=*/NULL);
}
void GKRMethod::GetLoginsList(uint32_t blacklisted_by_user,
const char* app_string) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ lookup_form_.reset(NULL);
// Search GNOME Keyring for matching passwords.
- gnome_keyring_find_itemsv(
- GNOME_KEYRING_ITEM_GENERIC_SECRET,
- OnOperationGetList,
- this, // data
- NULL, // destroy_data
- "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32,
- blacklisted_by_user,
- "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- app_string,
- NULL);
+ ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
+ AppendUint32(&attrs, "blacklisted_by_user", blacklisted_by_user);
+ AppendString(&attrs, "application", app_string);
+ gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
+ attrs.get(),
+ OnOperationGetList,
+ /*data=*/this,
+ /*destroy_data=*/NULL);
}
void GKRMethod::GetAllLogins(const char* app_string) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ lookup_form_.reset(NULL);
// We need to search for something, otherwise we get no results - so
// we search for the fixed application string.
- gnome_keyring_find_itemsv(
- GNOME_KEYRING_ITEM_GENERIC_SECRET,
- OnOperationGetList,
- this, // data
- NULL, // destroy_data
- "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- app_string,
- NULL);
+ ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
+ AppendString(&attrs, "application", app_string);
+ gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
+ attrs.get(),
+ OnOperationGetList,
+ /*data=*/this,
+ /*destroy_data=*/NULL);
}
GnomeKeyringResult GKRMethod::WaitResult() {
}
// static
+void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list,
+ const char* name,
+ const char* value) {
+ gnome_keyring_attribute_list_append_string(list->get(), name, value);
+}
+
+// static
+void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list,
+ const char* name,
+ const std::string& value) {
+ AppendString(list, name, value.c_str());
+}
+
+// static
+void GKRMethod::AppendUint32(GKRMethod::ScopedAttributeList* list,
+ const char* name,
+ guint32 value) {
+ gnome_keyring_attribute_list_append_uint32(list->get(), name, value);
+}
+
+// static
void GKRMethod::OnOperationDone(GnomeKeyringResult result, gpointer data) {
GKRMethod* method = static_cast<GKRMethod*>(data);
method->result_ = result;
method->result_ = result;
method->forms_.clear();
// |list| will be freed after this callback returns, so convert it now.
- ConvertFormList(list, &method->forms_);
+ ConvertFormList(list, method->lookup_form_.get(), &method->forms_);
+ method->lookup_form_.reset(NULL);
method->event_.Signal();
}
} // namespace
-NativeBackendGnome::NativeBackendGnome(LocalProfileId id, PrefService* prefs)
- : profile_id_(id), prefs_(prefs) {
- // TODO(mdm): after a few more releases, remove the code which is now dead due
- // to the true || here, and simplify this code. We don't do it yet to make it
- // easier to revert if necessary.
- if (true || PasswordStoreX::PasswordsUseLocalProfileId(prefs)) {
- app_string_ = GetProfileSpecificAppString();
- // We already did the migration previously. Don't try again.
- migrate_tried_ = true;
- } else {
- app_string_ = kGnomeKeyringAppString;
- migrate_tried_ = false;
- }
+NativeBackendGnome::NativeBackendGnome(LocalProfileId id)
+ : profile_id_(id) {
+ app_string_ = GetProfileSpecificAppString();
}
NativeBackendGnome::~NativeBackendGnome() {
<< gnome_keyring_result_to_message(result);
return false;
}
- // Successful write. Try migration if necessary.
- if (!migrate_tried_)
- MigrateToProfileSpecificLogins();
return true;
}
-bool NativeBackendGnome::AddLogin(const PasswordForm& form) {
+password_manager::PasswordStoreChangeList NativeBackendGnome::AddLogin(
+ const PasswordForm& form) {
// Based on LoginDatabase::AddLogin(), we search for an existing match based
// on origin_url, username_element, username_value, password_element, submit
// element, and signon_realm first, remove that, and then add the new entry.
base::Bind(&GKRMethod::AddLoginSearch,
base::Unretained(&method),
form, app_string_.c_str()));
- PasswordFormList forms;
- GnomeKeyringResult result = method.WaitResult(&forms);
+ ScopedVector<autofill::PasswordForm> forms;
+ GnomeKeyringResult result = method.WaitResult(&forms.get());
if (result != GNOME_KEYRING_RESULT_OK &&
result != GNOME_KEYRING_RESULT_NO_MATCH) {
LOG(ERROR) << "Keyring find failed: "
<< gnome_keyring_result_to_message(result);
- return false;
+ return password_manager::PasswordStoreChangeList();
}
+ password_manager::PasswordStoreChangeList changes;
if (forms.size() > 0) {
if (forms.size() > 1) {
LOG(WARNING) << "Adding login when there are " << forms.size()
<< " matching logins already! Will replace only the first.";
}
- // We try migration before updating the existing logins, since otherwise
- // we'd do it after making some but not all of the changes below.
- if (forms.size() > 0 && !migrate_tried_)
- MigrateToProfileSpecificLogins();
-
- RemoveLogin(*forms[0]);
- for (size_t i = 0; i < forms.size(); ++i)
- delete forms[i];
+ if (RemoveLogin(*forms[0])) {
+ changes.push_back(password_manager::PasswordStoreChange(
+ password_manager::PasswordStoreChange::REMOVE, *forms[0]));
+ }
+ }
+ if (RawAddLogin(form)) {
+ changes.push_back(password_manager::PasswordStoreChange(
+ password_manager::PasswordStoreChange::ADD, form));
}
- return RawAddLogin(form);
+ return changes;
}
-bool NativeBackendGnome::UpdateLogin(const PasswordForm& form) {
+bool NativeBackendGnome::UpdateLogin(
+ const PasswordForm& form,
+ password_manager::PasswordStoreChangeList* changes) {
// Based on LoginDatabase::UpdateLogin(), we search for forms to update by
// origin_url, username_element, username_value, password_element, and
// signon_realm. We then compare the result to the updated form. If they
- // differ in any of the action, password_value, ssl_valid, or preferred
- // fields, then we remove the original, and then add the new entry. We'd add
- // the new one first, and then delete the original, but then the delete might
- // actually delete the newly-added entry!
+ // differ in any of the mutable fields, then we remove the original, and
+ // then add the new entry. We'd add the new one first, and then delete the
+ // original, but then the delete might actually delete the newly-added entry!
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ DCHECK(changes);
+ changes->clear();
GKRMethod method;
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&GKRMethod::UpdateLoginSearch,
base::Unretained(&method),
form, app_string_.c_str()));
- PasswordFormList forms;
- GnomeKeyringResult result = method.WaitResult(&forms);
+ ScopedVector<autofill::PasswordForm> forms;
+ GnomeKeyringResult result = method.WaitResult(&forms.get());
if (result != GNOME_KEYRING_RESULT_OK) {
LOG(ERROR) << "Keyring find failed: "
<< gnome_keyring_result_to_message(result);
return false;
}
- // We try migration before updating the existing logins, since otherwise
- // we'd do it after making some but not all of the changes below.
- if (forms.size() > 0 && !migrate_tried_)
- MigrateToProfileSpecificLogins();
-
- bool ok = true;
+ bool removed = false;
for (size_t i = 0; i < forms.size(); ++i) {
- if (forms[i]->action != form.action ||
- forms[i]->password_value != form.password_value ||
- forms[i]->ssl_valid != form.ssl_valid ||
- forms[i]->preferred != form.preferred) {
+ if (*forms[i] != form) {
RemoveLogin(*forms[i]);
+ removed = true;
}
}
- for (size_t i = 0; i < forms.size(); ++i) {
- if (forms[i]->action != form.action ||
- forms[i]->password_value != form.password_value ||
- forms[i]->ssl_valid != form.ssl_valid ||
- forms[i]->preferred != form.preferred) {
- forms[i]->action = form.action;
- forms[i]->password_value = form.password_value;
- forms[i]->ssl_valid = form.ssl_valid;
- forms[i]->preferred = form.preferred;
- if (!RawAddLogin(*forms[i]))
- ok = false;
- }
- delete forms[i];
+ if (!removed)
+ return true;
+
+ if (RawAddLogin(form)) {
+ password_manager::PasswordStoreChange change(
+ password_manager::PasswordStoreChange::UPDATE, form);
+ changes->push_back(change);
+ return true;
}
- return ok;
+ return false;
}
bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) {
<< gnome_keyring_result_to_message(result);
return false;
}
- // Successful write. Try migration if necessary. Note that presumably if we've
- // been asked to delete a login, it's because we returned it previously; thus,
- // this will probably never happen since we'd have already tried migration.
- if (!migrate_tried_)
- MigrateToProfileSpecificLogins();
return true;
}
bool NativeBackendGnome::RemoveLoginsCreatedBetween(
- const base::Time& delete_begin,
- const base::Time& delete_end) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
- bool ok = true;
- // We could walk the list and delete items as we find them, but it is much
- // easier to build the list and use RemoveLogin() to delete them.
- PasswordFormList forms;
- if (!GetAllLogins(&forms))
- return false;
- // No need to try migration here: GetAllLogins() does it.
+ base::Time delete_begin,
+ base::Time delete_end,
+ password_manager::PasswordStoreChangeList* changes) {
+ return RemoveLoginsBetween(
+ delete_begin, delete_end, CREATION_TIMESTAMP, changes);
+}
- for (size_t i = 0; i < forms.size(); ++i) {
- if (delete_begin <= forms[i]->date_created &&
- (delete_end.is_null() || forms[i]->date_created < delete_end)) {
- if (!RemoveLogin(*forms[i]))
- ok = false;
- }
- delete forms[i];
- }
- return ok;
+bool NativeBackendGnome::RemoveLoginsSyncedBetween(
+ base::Time delete_begin,
+ base::Time delete_end,
+ password_manager::PasswordStoreChangeList* changes) {
+ return RemoveLoginsBetween(delete_begin, delete_end, SYNC_TIMESTAMP, changes);
}
bool NativeBackendGnome::GetLogins(const PasswordForm& form,
<< gnome_keyring_result_to_message(result);
return false;
}
- // Successful read of actual data. Try migration if necessary.
- if (!migrate_tried_)
- MigrateToProfileSpecificLogins();
- return true;
-}
-
-bool NativeBackendGnome::GetLoginsCreatedBetween(const base::Time& get_begin,
- const base::Time& get_end,
- PasswordFormList* forms) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
- // We could walk the list and add items as we find them, but it is much
- // easier to build the list and then filter the results.
- PasswordFormList all_forms;
- if (!GetAllLogins(&all_forms))
- return false;
- // No need to try migration here: GetAllLogins() does it.
-
- forms->reserve(forms->size() + all_forms.size());
- for (size_t i = 0; i < all_forms.size(); ++i) {
- if (get_begin <= all_forms[i]->date_created &&
- (get_end.is_null() || all_forms[i]->date_created < get_end)) {
- forms->push_back(all_forms[i]);
- } else {
- delete all_forms[i];
- }
- }
-
return true;
}
<< gnome_keyring_result_to_message(result);
return false;
}
- // Successful read of actual data. Try migration if necessary.
- if (!migrate_tried_)
- MigrateToProfileSpecificLogins();
return true;
}
<< gnome_keyring_result_to_message(result);
return false;
}
- // Successful read of actual data. Try migration if necessary.
- if (!migrate_tried_)
- MigrateToProfileSpecificLogins();
return true;
}
-std::string NativeBackendGnome::GetProfileSpecificAppString() const {
- // Originally, the application string was always just "chrome" and used only
- // so that we had *something* to search for since GNOME Keyring won't search
- // for nothing. Now we use it to distinguish passwords for different profiles.
- return base::StringPrintf("%s-%d", kGnomeKeyringAppString, profile_id_);
-}
-
-void NativeBackendGnome::MigrateToProfileSpecificLogins() {
+bool NativeBackendGnome::GetLoginsBetween(base::Time get_begin,
+ base::Time get_end,
+ TimestampToCompare date_to_compare,
+ PasswordFormList* forms) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ // We could walk the list and add items as we find them, but it is much
+ // easier to build the list and then filter the results.
+ PasswordFormList all_forms;
+ if (!GetAllLogins(&all_forms))
+ return false;
- DCHECK(!migrate_tried_);
- DCHECK_EQ(app_string_, kGnomeKeyringAppString);
-
- // Record the fact that we've attempted migration already right away, so that
- // we don't get recursive calls back to MigrateToProfileSpecificLogins().
- migrate_tried_ = true;
+ base::Time autofill::PasswordForm::*date_member =
+ date_to_compare == CREATION_TIMESTAMP
+ ? &autofill::PasswordForm::date_created
+ : &autofill::PasswordForm::date_synced;
+ for (size_t i = 0; i < all_forms.size(); ++i) {
+ if (get_begin <= all_forms[i]->*date_member &&
+ (get_end.is_null() || all_forms[i]->*date_member < get_end)) {
+ forms->push_back(all_forms[i]);
+ } else {
+ delete all_forms[i];
+ }
+ }
- // First get all the logins, using the old app string.
- PasswordFormList forms;
- if (!GetAllLogins(&forms))
- return;
+ return true;
+}
- // Now switch to a profile-specific app string.
- app_string_ = GetProfileSpecificAppString();
+bool NativeBackendGnome::RemoveLoginsBetween(
+ base::Time get_begin,
+ base::Time get_end,
+ TimestampToCompare date_to_compare,
+ password_manager::PasswordStoreChangeList* changes) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ DCHECK(changes);
+ changes->clear();
+ // We could walk the list and delete items as we find them, but it is much
+ // easier to build the list and use RemoveLogin() to delete them.
+ ScopedVector<autofill::PasswordForm> forms;
+ if (!GetLoginsBetween(get_begin, get_end, date_to_compare, &forms.get()))
+ return false;
- // Try to add all the logins with the new app string.
bool ok = true;
for (size_t i = 0; i < forms.size(); ++i) {
- if (!RawAddLogin(*forms[i]))
+ if (RemoveLogin(*forms[i])) {
+ changes->push_back(password_manager::PasswordStoreChange(
+ password_manager::PasswordStoreChange::REMOVE, *forms[i]));
+ } else {
ok = false;
- delete forms[i];
+ }
}
+ return ok;
+}
- if (ok) {
- // All good! Keep the new app string and set a persistent pref.
- // NOTE: We explicitly don't delete the old passwords yet. They are
- // potentially shared with other profiles and other user data dirs!
- // Each other profile must be able to migrate the shared data as well,
- // so we must leave it alone. After a few releases, we'll add code to
- // delete them, and eventually remove this migration code.
- // TODO(mdm): follow through with the plan above.
- PasswordStoreX::SetPasswordsUseLocalProfileId(prefs_);
- } else {
- // We failed to migrate for some reason. Use the old app string.
- app_string_ = kGnomeKeyringAppString;
- }
+std::string NativeBackendGnome::GetProfileSpecificAppString() const {
+ // Originally, the application string was always just "chrome" and used only
+ // so that we had *something* to search for since GNOME Keyring won't search
+ // for nothing. Now we use it to distinguish passwords for different profiles.
+ return base::StringPrintf("%s-%d", kGnomeKeyringAppString, profile_id_);
}