Commit 0985fac1 authored by dvadym's avatar dvadym Committed by Commit bot

Serialize form_data in Gnome keyring password store service.

Serializing form_data is required for server-side password generation heuristics. It was implemented on all others platforms before. Here it's implemented in gnome-keyring and libsecret backends (both of them works with Gnome keyring service).

BUG=463126

Review URL: https://codereview.chromium.org/980583002

Cr-Commit-Position: refs/heads/master@{#319628}
parent c9721529
...@@ -158,7 +158,8 @@ scoped_ptr<PasswordForm> FormFromAttributes(GnomeKeyringAttributeList* attrs) { ...@@ -158,7 +158,8 @@ scoped_ptr<PasswordForm> FormFromAttributes(GnomeKeyringAttributeList* attrs) {
form->generation_upload_status = form->generation_upload_status =
static_cast<PasswordForm::GenerationUploadStatus>( static_cast<PasswordForm::GenerationUploadStatus>(
uint_attr_map["generation_upload_status"]); uint_attr_map["generation_upload_status"]);
DeserializeFormDataFromBase64String(string_attr_map["form_data"],
&form->form_data);
return form.Pass(); return form.Pass();
} }
...@@ -220,11 +221,6 @@ void ConvertFormList(GList* found, ...@@ -220,11 +221,6 @@ void ConvertFormList(GList* found,
} }
// Schema is analagous to the fields in PasswordForm. // 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 = { const GnomeKeyringPasswordSchema kGnomeSchema = {
GNOME_KEYRING_ITEM_GENERIC_SECRET, { GNOME_KEYRING_ITEM_GENERIC_SECRET, {
{ "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
...@@ -247,6 +243,7 @@ const GnomeKeyringPasswordSchema kGnomeSchema = { ...@@ -247,6 +243,7 @@ const GnomeKeyringPasswordSchema kGnomeSchema = {
{ "federation_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, { "federation_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
{ "skip_zero_click", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 }, { "skip_zero_click", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
{ "generation_upload_status", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 }, { "generation_upload_status", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
{ "form_data", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
// This field is always "chrome" so that we can search for it. // This field is always "chrome" so that we can search for it.
{ "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, { "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
{ nullptr } { nullptr }
...@@ -339,6 +336,8 @@ void GKRMethod::AddLogin(const PasswordForm& form, const char* app_string) { ...@@ -339,6 +336,8 @@ void GKRMethod::AddLogin(const PasswordForm& form, const char* app_string) {
if (!date_created) if (!date_created)
date_created = base::Time::Now().ToInternalValue(); date_created = base::Time::Now().ToInternalValue();
int64 date_synced = form.date_synced.ToInternalValue(); int64 date_synced = form.date_synced.ToInternalValue();
std::string form_data;
SerializeFormDataToBase64String(form.form_data, &form_data);
gnome_keyring_store_password( gnome_keyring_store_password(
&kGnomeSchema, &kGnomeSchema,
nullptr, // Default keyring. nullptr, // Default keyring.
...@@ -367,6 +366,7 @@ void GKRMethod::AddLogin(const PasswordForm& form, const char* app_string) { ...@@ -367,6 +366,7 @@ void GKRMethod::AddLogin(const PasswordForm& form, const char* app_string) {
"federation_url", form.federation_url.spec().c_str(), "federation_url", form.federation_url.spec().c_str(),
"skip_zero_click", form.skip_zero_click, "skip_zero_click", form.skip_zero_click,
"generation_upload_status", form.generation_upload_status, "generation_upload_status", form.generation_upload_status,
"form_data", form_data.c_str(),
"application", app_string, "application", app_string,
nullptr); nullptr);
} }
......
...@@ -359,6 +359,8 @@ class NativeBackendGnomeTest : public testing::Test { ...@@ -359,6 +359,8 @@ class NativeBackendGnomeTest : public testing::Test {
form_google_.federation_url = GURL("http://www.google.com/federation_url"); form_google_.federation_url = GURL("http://www.google.com/federation_url");
form_google_.skip_zero_click = true; form_google_.skip_zero_click = true;
form_google_.generation_upload_status = PasswordForm::POSITIVE_SIGNAL_SENT; form_google_.generation_upload_status = PasswordForm::POSITIVE_SIGNAL_SENT;
form_google_.form_data.name = UTF8ToUTF16("form_name");
form_google_.form_data.user_submitted = true;
form_facebook_.origin = GURL("http://www.facebook.com/"); form_facebook_.origin = GURL("http://www.facebook.com/");
form_facebook_.action = GURL("http://www.facebook.com/login"); form_facebook_.action = GURL("http://www.facebook.com/login");
...@@ -448,7 +450,7 @@ class NativeBackendGnomeTest : public testing::Test { ...@@ -448,7 +450,7 @@ class NativeBackendGnomeTest : public testing::Test {
EXPECT_EQ("login", item->keyring); EXPECT_EQ("login", item->keyring);
EXPECT_EQ(form.origin.spec(), item->display_name); EXPECT_EQ(form.origin.spec(), item->display_name);
EXPECT_EQ(UTF16ToUTF8(form.password_value), item->password); EXPECT_EQ(UTF16ToUTF8(form.password_value), item->password);
EXPECT_EQ(21u, item->attributes.size()); EXPECT_EQ(22u, item->attributes.size());
CheckStringAttribute(item, "origin_url", form.origin.spec()); CheckStringAttribute(item, "origin_url", form.origin.spec());
CheckStringAttribute(item, "action_url", form.action.spec()); CheckStringAttribute(item, "action_url", form.action.spec());
CheckStringAttribute(item, "username_element", CheckStringAttribute(item, "username_element",
...@@ -476,6 +478,10 @@ class NativeBackendGnomeTest : public testing::Test { ...@@ -476,6 +478,10 @@ class NativeBackendGnomeTest : public testing::Test {
CheckUint32Attribute(item, "generation_upload_status", CheckUint32Attribute(item, "generation_upload_status",
form.generation_upload_status); form.generation_upload_status);
CheckStringAttribute(item, "application", app_string); CheckStringAttribute(item, "application", app_string);
autofill::FormData actual;
DeserializeFormDataFromBase64String(
item->attributes.at("form_data").value_string, &actual);
EXPECT_TRUE(form.form_data.SameFormAs(actual));
} }
// Saves |credentials| and then gets logins matching |url| and |scheme|. // Saves |credentials| and then gets logins matching |url| and |scheme|.
...@@ -772,7 +778,7 @@ TEST_F(NativeBackendGnomeTest, BasicListLogins) { ...@@ -772,7 +778,7 @@ TEST_F(NativeBackendGnomeTest, BasicListLogins) {
BrowserThread::PostTask( BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE, BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult( &NativeBackendGnome::AddLogin), base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
base::Unretained(&backend), form_google_)); base::Unretained(&backend), form_google_));
ScopedVector<autofill::PasswordForm> form_list; ScopedVector<autofill::PasswordForm> form_list;
......
...@@ -91,11 +91,6 @@ namespace { ...@@ -91,11 +91,6 @@ namespace {
const char kLibsecretAppString[] = "chrome"; const char kLibsecretAppString[] = "chrome";
// Schema is analagous to the fields in PasswordForm. // 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 SecretSchema kLibsecretSchema = { const SecretSchema kLibsecretSchema = {
"chrome_libsecret_password_schema", "chrome_libsecret_password_schema",
// We have to use SECRET_SCHEMA_DONT_MATCH_NAME in order to get old // We have to use SECRET_SCHEMA_DONT_MATCH_NAME in order to get old
...@@ -121,6 +116,7 @@ const SecretSchema kLibsecretSchema = { ...@@ -121,6 +116,7 @@ const SecretSchema kLibsecretSchema = {
{"federation_url", SECRET_SCHEMA_ATTRIBUTE_STRING}, {"federation_url", SECRET_SCHEMA_ATTRIBUTE_STRING},
{"skip_zero_click", SECRET_SCHEMA_ATTRIBUTE_INTEGER}, {"skip_zero_click", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
{"generation_upload_status", SECRET_SCHEMA_ATTRIBUTE_INTEGER}, {"generation_upload_status", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
{"form_data", SECRET_SCHEMA_ATTRIBUTE_STRING},
// This field is always "chrome-profile_id" so that we can search for it. // This field is always "chrome-profile_id" so that we can search for it.
{"application", SECRET_SCHEMA_ATTRIBUTE_STRING}, {"application", SECRET_SCHEMA_ATTRIBUTE_STRING},
{nullptr, SECRET_SCHEMA_ATTRIBUTE_STRING}}}; {nullptr, SECRET_SCHEMA_ATTRIBUTE_STRING}}};
...@@ -194,7 +190,8 @@ scoped_ptr<PasswordForm> FormOutOfAttributes(GHashTable* attrs) { ...@@ -194,7 +190,8 @@ scoped_ptr<PasswordForm> FormOutOfAttributes(GHashTable* attrs) {
form->generation_upload_status = form->generation_upload_status =
static_cast<PasswordForm::GenerationUploadStatus>( static_cast<PasswordForm::GenerationUploadStatus>(
GetUintFromAttributes(attrs, "generation_upload_status")); GetUintFromAttributes(attrs, "generation_upload_status"));
DeserializeFormDataFromBase64String(
GetStringFromAttributes(attrs, "form_data"), &form->form_data);
return form.Pass(); return form.Pass();
} }
...@@ -416,6 +413,8 @@ bool NativeBackendLibsecret::RawAddLogin(const PasswordForm& form) { ...@@ -416,6 +413,8 @@ bool NativeBackendLibsecret::RawAddLogin(const PasswordForm& form) {
if (!date_created) if (!date_created)
date_created = base::Time::Now().ToInternalValue(); date_created = base::Time::Now().ToInternalValue();
int64 date_synced = form.date_synced.ToInternalValue(); int64 date_synced = form.date_synced.ToInternalValue();
std::string form_data;
SerializeFormDataToBase64String(form.form_data, &form_data);
GError* error = nullptr; GError* error = nullptr;
secret_password_store_sync( secret_password_store_sync(
&kLibsecretSchema, &kLibsecretSchema,
...@@ -444,6 +443,7 @@ bool NativeBackendLibsecret::RawAddLogin(const PasswordForm& form) { ...@@ -444,6 +443,7 @@ bool NativeBackendLibsecret::RawAddLogin(const PasswordForm& form) {
"federation_url", form.federation_url.spec().c_str(), "federation_url", form.federation_url.spec().c_str(),
"skip_zero_click", form.skip_zero_click, "skip_zero_click", form.skip_zero_click,
"generation_upload_status", form.generation_upload_status, "generation_upload_status", form.generation_upload_status,
"form_data", form_data.c_str(),
"application", app_string_.c_str(), nullptr); "application", app_string_.c_str(), nullptr);
if (error) { if (error) {
......
...@@ -281,6 +281,8 @@ class NativeBackendLibsecretTest : public testing::Test { ...@@ -281,6 +281,8 @@ class NativeBackendLibsecretTest : public testing::Test {
form_google_.federation_url = GURL("http://www.google.com/federation_url"); form_google_.federation_url = GURL("http://www.google.com/federation_url");
form_google_.skip_zero_click = true; form_google_.skip_zero_click = true;
form_google_.generation_upload_status = PasswordForm::POSITIVE_SIGNAL_SENT; form_google_.generation_upload_status = PasswordForm::POSITIVE_SIGNAL_SENT;
form_google_.form_data.name = UTF8ToUTF16("form_name");
form_google_.form_data.user_submitted = true;
form_facebook_.origin = GURL("http://www.facebook.com/"); form_facebook_.origin = GURL("http://www.facebook.com/");
form_facebook_.action = GURL("http://www.facebook.com/login"); form_facebook_.action = GURL("http://www.facebook.com/login");
...@@ -356,7 +358,7 @@ class NativeBackendLibsecretTest : public testing::Test { ...@@ -356,7 +358,7 @@ class NativeBackendLibsecretTest : public testing::Test {
const PasswordForm& form, const PasswordForm& form,
const std::string& app_string) { const std::string& app_string) {
EXPECT_EQ(UTF16ToUTF8(form.password_value), item->value->password); EXPECT_EQ(UTF16ToUTF8(form.password_value), item->value->password);
EXPECT_EQ(21u, g_hash_table_size(item->attributes)); EXPECT_EQ(22u, g_hash_table_size(item->attributes));
CheckStringAttribute(item, "origin_url", form.origin.spec()); CheckStringAttribute(item, "origin_url", form.origin.spec());
CheckStringAttribute(item, "action_url", form.action.spec()); CheckStringAttribute(item, "action_url", form.action.spec());
CheckStringAttribute(item, "username_element", CheckStringAttribute(item, "username_element",
...@@ -385,6 +387,11 @@ class NativeBackendLibsecretTest : public testing::Test { ...@@ -385,6 +387,11 @@ class NativeBackendLibsecretTest : public testing::Test {
CheckUint32Attribute(item, "generation_upload_status", CheckUint32Attribute(item, "generation_upload_status",
form.generation_upload_status); form.generation_upload_status);
CheckStringAttribute(item, "application", app_string); CheckStringAttribute(item, "application", app_string);
autofill::FormData actual;
DeserializeFormDataFromBase64String(
static_cast<char*>(g_hash_table_lookup(item->attributes, "form_data")),
&actual);
EXPECT_TRUE(form.form_data.SameFormAs(actual));
} }
// Saves |credentials| and then gets logins matching |url| and |scheme|. // Saves |credentials| and then gets logins matching |url| and |scheme|.
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_data.h"
#include "base/base64.h"
#include "base/pickle.h" #include "base/pickle.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
...@@ -124,6 +125,15 @@ void SerializeFormData(const FormData& form_data, Pickle* pickle) { ...@@ -124,6 +125,15 @@ void SerializeFormData(const FormData& form_data, Pickle* pickle) {
pickle->WriteBool(form_data.is_form_tag); pickle->WriteBool(form_data.is_form_tag);
} }
void SerializeFormDataToBase64String(const FormData& form_data,
std::string* output) {
Pickle pickle;
SerializeFormData(form_data, &pickle);
Base64Encode(
base::StringPiece(static_cast<const char*>(pickle.data()), pickle.size()),
output);
}
bool DeserializeFormData(PickleIterator* iter, FormData* form_data) { bool DeserializeFormData(PickleIterator* iter, FormData* form_data) {
int version; int version;
if (!iter->ReadInt(&version)) { if (!iter->ReadInt(&version)) {
...@@ -169,4 +179,15 @@ bool DeserializeFormData(PickleIterator* iter, FormData* form_data) { ...@@ -169,4 +179,15 @@ bool DeserializeFormData(PickleIterator* iter, FormData* form_data) {
return true; return true;
} }
bool DeserializeFormDataFromBase64String(const base::StringPiece& input,
FormData* form_data) {
if (input.empty())
return false;
std::string pickle_data;
Base64Decode(input, &pickle_data);
Pickle pickle(pickle_data.data(), static_cast<int>(pickle_data.size()));
PickleIterator iter(pickle);
return DeserializeFormData(&iter, form_data);
}
} // namespace autofill } // namespace autofill
...@@ -44,12 +44,21 @@ struct FormData { ...@@ -44,12 +44,21 @@ struct FormData {
std::ostream& operator<<(std::ostream& os, const FormData& form); std::ostream& operator<<(std::ostream& os, const FormData& form);
// Serialize FormData. Used by the PasswordManager to persist FormData // Serialize FormData. Used by the PasswordManager to persist FormData
// pertaining to password forms. Serialized data is appended to |pickle| // pertaining to password forms. Serialized data is appended to |pickle|.
void SerializeFormData(const FormData& form_data, Pickle* pickle); void SerializeFormData(const FormData& form_data, Pickle* pickle);
// Deserialize FormData. This assumes that |iter| is currently pointing to // Deserialize FormData. This assumes that |iter| is currently pointing to
// the part of a pickle created by SerializeFormData. Returns true on success. // the part of a pickle created by SerializeFormData. Returns true on success.
bool DeserializeFormData(PickleIterator* iter, FormData* form_data); bool DeserializeFormData(PickleIterator* iter, FormData* form_data);
// Serialize FormData. Used by the PasswordManager to persist FormData
// pertaining to password forms in base64 string. It is useful since in some
// cases we need to store C strings without embedded '\0' symbols.
void SerializeFormDataToBase64String(const FormData& form_data,
std::string* output);
// Deserialize FormData. Returns true on success.
bool DeserializeFormDataFromBase64String(const base::StringPiece& input,
FormData* form_data);
} // namespace autofill } // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_COMMON_FORM_DATA_H_ #endif // COMPONENTS_AUTOFILL_CORE_COMMON_FORM_DATA_H_
...@@ -147,4 +147,18 @@ TEST(FormDataTest, SerializeIncorrectFormatAndDeserialize) { ...@@ -147,4 +147,18 @@ TEST(FormDataTest, SerializeIncorrectFormatAndDeserialize) {
EXPECT_FALSE(DeserializeFormData(&iter, &actual)); EXPECT_FALSE(DeserializeFormData(&iter, &actual));
} }
TEST(FormDataTest, SerializeAndDeserializeInStrings) {
FormData data;
FillInDummyFormData(&data);
data.is_form_tag = false;
std::string serialized_data;
SerializeFormDataToBase64String(data, &serialized_data);
FormData actual;
EXPECT_TRUE(DeserializeFormDataFromBase64String(serialized_data, &actual));
EXPECT_TRUE(actual.SameFormAs(data));
}
} // namespace autofill } // namespace autofill
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