chromium/chrome/browser/password_manager/android/unified_password_manager_proto_utils.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/password_manager/android/unified_password_manager_proto_utils.h"

#include <string>

#include "base/json/json_string_value_serializer.h"
#include "base/strings/utf_string_conversions.h"
#include "base/types/cxx23_to_underlying.h"
#include "base/values.h"
#include "chrome/browser/password_manager/android/protos/list_affiliated_passwords_result.pb.h"
#include "chrome/browser/password_manager/android/protos/list_passwords_result.pb.h"
#include "chrome/browser/password_manager/android/protos/password_with_local_data.pb.h"
#include "components/password_manager/core/browser/sync/password_proto_utils.h"

using autofill::FormData;
using autofill::FormFieldData;

namespace password_manager {

namespace {

// Keys used to serialize and deserialize password form data.
constexpr char kActionKey[] = "action";
constexpr char kFieldsKey[] = "fields";
constexpr char kFormControlTypeKey[] = "form_control_type";
constexpr char kFormDataKey[] = "form_data";
constexpr char kNameKey[] = "name";
constexpr char kSkipZeroClickKey[] = "skip_zero_click";
constexpr char kUrlKey[] = "url";

base::Value::Dict SerializeSignatureRelevantMembersInFormData(
    const FormData& form_data) {
  base::Value::Dict serialized_data;
  // Stored FormData is used only for signature calculations, therefore only
  // members that are used for signature calculation are stored.
  serialized_data.Set(kNameKey, form_data.name());
  serialized_data.Set(kUrlKey, form_data.url().spec());
  serialized_data.Set(kActionKey, form_data.action().spec());

  base::Value::List serialized_fields;
  for (const auto& field : form_data.fields()) {
    base::Value::Dict serialized_field;
    // Stored FormFieldData is used only for signature calculations, therefore
    // only members that are used for signature calculation are stored.
    serialized_field.Set(kNameKey, field.name());
    serialized_field.Set(kFormControlTypeKey, autofill::FormControlTypeToString(
                                                  field.form_control_type()));
    serialized_fields.Append(std::move(serialized_field));
  }
  serialized_data.Set(kFieldsKey, std::move(serialized_fields));
  return serialized_data;
}

std::string SerializeOpaqueLocalData(const PasswordForm& password_form) {
  base::Value::Dict local_data_json;
  local_data_json.Set(kSkipZeroClickKey, password_form.skip_zero_click);

  base::Value::Dict serialized_form_data =
      SerializeSignatureRelevantMembersInFormData(password_form.form_data);
  local_data_json.Set(kFormDataKey, std::move(serialized_form_data));

  std::string serialized_local_data;
  JSONStringValueSerializer serializer(&serialized_local_data);
  serializer.Serialize(local_data_json);
  return serialized_local_data;
}

std::optional<FormData> DeserializeFormData(
    base::Value::Dict& serialized_data) {
  std::string* form_name = serialized_data.FindString(kNameKey);
  std::string* form_url = serialized_data.FindString(kUrlKey);
  std::string* form_action = serialized_data.FindString(kActionKey);
  base::Value::List* fields = serialized_data.FindList(kFieldsKey);
  if (!form_name || !form_url || !form_action || !fields) {
    return std::nullopt;
  }

  std::vector<FormFieldData> form_fields;
  form_fields.reserve(fields->size());
  for (auto& serialized_field : *fields) {
    base::Value::Dict* serialized_field_dictionary =
        serialized_field.GetIfDict();
    if (!serialized_field_dictionary) {
      return std::nullopt;
    }
    FormFieldData field;
    std::string* field_name = serialized_field_dictionary->FindString(kNameKey);
    std::string* field_type =
        serialized_field_dictionary->FindString(kFormControlTypeKey);
    if (!field_name || !field_type) {
      return std::nullopt;
    }
    field.set_name(base::UTF8ToUTF16(*field_name));
    // TODO(crbug.com/1353392,crbug.com/1482526): Why does the Password Manager
    // (de)serialize form control types? Remove it or migrate it to the enum
    // values.
    field.set_form_control_type(autofill::StringToFormControlTypeDiscouraged(
        *field_type, /*fallback=*/autofill::FormControlType::kInputText));
    form_fields.push_back(field);
  }

  FormData form_data;
  form_data.set_name(base::UTF8ToUTF16(*form_name));
  form_data.set_url(GURL(*form_url));
  form_data.set_action(GURL(*form_action));
  form_data.set_fields(std::move(form_fields));
  return form_data;
}

void DeserializeOpaqueLocalData(const std::string& opaque_metadata,
                                PasswordForm& password_form) {
  JSONStringValueDeserializer json_deserializer(opaque_metadata);
  std::unique_ptr<base::Value> root(
      json_deserializer.Deserialize(nullptr, nullptr));
  if (!root.get() || !root->is_dict()) {
    return;
  }

  base::Value::Dict serialized_data(std::move(*root).TakeDict());
  auto skip_zero_click = serialized_data.FindBool(kSkipZeroClickKey);
  auto* serialized_form_data = serialized_data.FindDict(kFormDataKey);
  if (!skip_zero_click.has_value() || !serialized_form_data) {
    return;
  }
  std::optional<FormData> form_data =
      DeserializeFormData(*serialized_form_data);
  if (!form_data.has_value()) {
    return;
  }
  password_form.skip_zero_click = *skip_zero_click;
  password_form.form_data = std::move(form_data.value());
}

void SetStoreForForm(PasswordForm& form, IsAccountStore is_account_store) {
  form.in_store = is_account_store ? PasswordForm::Store::kAccountStore
                                   : PasswordForm::Store::kProfileStore;
}

}  // namespace

PasswordWithLocalData PasswordWithLocalDataFromPassword(
    const PasswordForm& password_form) {
  PasswordWithLocalData password_with_local_data;

  *password_with_local_data.mutable_password_specifics_data() =
      SpecificsDataFromPassword(password_form, /*base_password_data=*/{});

  auto* local_data = password_with_local_data.mutable_local_data();
  local_data->set_opaque_metadata(SerializeOpaqueLocalData(password_form));
  if (!password_form.previously_associated_sync_account_email.empty()) {
    local_data->set_previously_associated_sync_account_email(
        password_form.previously_associated_sync_account_email);
  }

  return password_with_local_data;
}

PasswordForm PasswordFromProtoWithLocalData(
    const PasswordWithLocalData& password) {
  PasswordForm form = PasswordFromSpecifics(password.password_specifics_data());
  form.previously_associated_sync_account_email =
      password.local_data().previously_associated_sync_account_email();
  DeserializeOpaqueLocalData(password.local_data().opaque_metadata(), form);
  return form;
}

std::vector<PasswordForm> PasswordVectorFromListResult(
    const ListPasswordsResult& list_result,
    IsAccountStore is_account_store) {
  std::vector<PasswordForm> forms;
  for (const PasswordWithLocalData& password : list_result.password_data()) {
    forms.push_back(PasswordFromProtoWithLocalData(password));
    SetStoreForForm(forms.back(), is_account_store);
  }
  return forms;
}

std::vector<PasswordForm> PasswordVectorFromListResult(
    const ListAffiliatedPasswordsResult& list_result,
    IsAccountStore is_account_store) {
  std::vector<PasswordForm> forms;
  for (const auto& password : list_result.affiliated_passwords()) {
    PasswordForm form =
        PasswordFromProtoWithLocalData(password.password_data());
    form.app_display_name = password.password_branding_info().display_name();
    form.app_icon_url = GURL(password.password_branding_info().icon_url());
    if (password.is_credential_sharing_affiliation_match()) {
      form.match_type |= PasswordForm::MatchType::kAffiliated;
    }
    if (password.is_grouping_affiliation_match()) {
      form.match_type |= PasswordForm::MatchType::kGrouped;
    }
    SetStoreForForm(form, is_account_store);
    forms.push_back(std::move(form));
  }
  return forms;
}

std::vector<PasswordForm> PasswordVectorFromListResult(
    const ListPasswordsWithUiInfoResult& list_result,
    IsAccountStore is_account_store) {
  std::vector<PasswordForm> forms;
  for (const auto& password : list_result.passwords_with_ui_info()) {
    PasswordForm form =
        PasswordFromProtoWithLocalData(password.password_data());
    form.app_display_name = password.ui_info().display_name();
    form.app_icon_url = GURL(password.ui_info().icon_url());
    SetStoreForForm(form, is_account_store);
    forms.push_back(std::move(form));
  }
  return forms;
}

}  // namespace password_manager