blob: c55ff04d88a29eec31e115b0608cbfc5177da671 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/account_id/account_id.h"
#include <functional>
#include <memory>
#include "base/check.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "google_apis/gaia/gaia_id.h"
namespace {
// Serialization keys
constexpr char kGaiaIdKey[] = "gaia_id";
constexpr char kEmailKey[] = "email";
constexpr char kAccountTypeKey[] = "account_type";
// Serialization values for account type.
constexpr char kGoogle[] = "google";
constexpr char kUnknown[] = "unknown";
// Prefix for GetAccountIdKey().
constexpr char kKeyGaiaIdPrefix[] = "g-";
} // anonymous namespace
AccountId::AccountId() = default;
AccountId::AccountId(std::string_view user_email,
AccountType account_type,
const GaiaId& gaia_id)
: user_email_(user_email), account_type_(account_type), gaia_id_(gaia_id) {
DCHECK_EQ(user_email, gaia::CanonicalizeEmail(user_email));
switch (account_type_) {
case AccountType::UNKNOWN:
CHECK(gaia_id_.empty());
break;
case AccountType::GOOGLE:
// TODO(alemate): check gaia_id is not empty once it is required.
break;
}
// Fail if e-mail looks similar to GaiaIdKey.
LOG_ASSERT(!base::StartsWith(user_email, kKeyGaiaIdPrefix,
base::CompareCase::SENSITIVE) ||
user_email.find('@') != std::string::npos)
<< "Bad e-mail: '" << user_email << "' with gaia_id='" << gaia_id_ << "'";
}
AccountId::AccountId(const AccountId& other) = default;
AccountId& AccountId::operator=(const AccountId& other) = default;
bool AccountId::operator==(const AccountId& other) const {
if (this == &other) {
return true;
}
if (account_type_ == AccountType::UNKNOWN ||
other.account_type_ == AccountType::UNKNOWN) {
return user_email_ == other.user_email_;
}
if (account_type_ != other.account_type_) {
return false;
}
switch (account_type_) {
case AccountType::UNKNOWN:
NOTREACHED() << "Unknown account type";
case AccountType::GOOGLE:
return (gaia_id_ == other.gaia_id_ && user_email_ == other.user_email_) ||
(!gaia_id_.empty() && gaia_id_ == other.gaia_id_) ||
(!user_email_.empty() && user_email_ == other.user_email_);
}
}
bool AccountId::operator<(const AccountId& right) const {
// TODO(alemate): update this once all AccountId members are filled.
return user_email_ < right.user_email_;
}
bool AccountId::empty() const {
return gaia_id_.empty() && user_email_.empty() &&
account_type_ == AccountType::UNKNOWN;
}
bool AccountId::is_valid() const {
switch (account_type_) {
case AccountType::GOOGLE:
// TODO(http://b/279005619): Add an additional check for empty account ids
// when this bug is fixed.
return !user_email_.empty();
case AccountType::UNKNOWN:
return gaia_id_.empty() && !user_email_.empty();
}
NOTREACHED();
}
void AccountId::clear() {
gaia_id_ = GaiaId();
user_email_.clear();
account_type_ = AccountType::UNKNOWN;
}
AccountType AccountId::GetAccountType() const {
return account_type_;
}
const GaiaId& AccountId::GetGaiaId() const {
if (account_type_ != AccountType::GOOGLE) {
NOTIMPLEMENTED() << "Failed to get gaia_id for non-Google account.";
}
return gaia_id_;
}
const std::string& AccountId::GetUserEmail() const {
return user_email_;
}
bool AccountId::HasAccountIdKey() const {
switch (account_type_) {
case AccountType::UNKNOWN:
return false;
case AccountType::GOOGLE:
return !gaia_id_.empty();
}
NOTREACHED();
}
const std::string AccountId::GetAccountIdKey() const {
switch (GetAccountType()) {
case AccountType::UNKNOWN:
NOTREACHED() << "Unknown account type";
case AccountType::GOOGLE:
#ifdef NDEBUG
if (gaia_id_.empty()) {
LOG(FATAL) << "GetAccountIdKey(): no id for " << Serialize();
}
#else
CHECK(!gaia_id_.empty());
#endif
return std::string(kKeyGaiaIdPrefix) + gaia_id_.ToString();
}
}
void AccountId::SetUserEmail(std::string_view email) {
DCHECK(email == gaia::CanonicalizeEmail(email));
DCHECK(!email.empty());
user_email_ = email;
}
// static
AccountId AccountId::FromNonCanonicalEmail(std::string_view email,
const GaiaId& gaia_id,
AccountType account_type) {
DCHECK(!email.empty());
return AccountId(gaia::CanonicalizeEmail(gaia::SanitizeEmail(email)),
account_type, gaia_id);
}
// static
AccountId AccountId::FromUserEmail(std::string_view email) {
// TODO(alemate): DCHECK(!email.empty());
return AccountId(email, AccountType::UNKNOWN, GaiaId());
}
// static
AccountId AccountId::FromUserEmailGaiaId(std::string_view email,
const GaiaId& gaia_id) {
DCHECK(!(email.empty() && gaia_id.empty()));
return AccountId(email, AccountType::GOOGLE, gaia_id);
}
// static
AccountType AccountId::StringToAccountType(
std::string_view account_type_string) {
if (account_type_string == kGoogle) {
return AccountType::GOOGLE;
}
if (account_type_string == kUnknown) {
return AccountType::UNKNOWN;
}
NOTREACHED() << "Unknown account type " << account_type_string;
}
// static
const char* AccountId::AccountTypeToString(AccountType account_type) {
switch (account_type) {
case AccountType::GOOGLE:
return kGoogle;
case AccountType::UNKNOWN:
return kUnknown;
}
return "";
}
std::string AccountId::Serialize() const {
base::Value::Dict value;
switch (GetAccountType()) {
case AccountType::GOOGLE:
value.Set(kGaiaIdKey, gaia_id_.ToString());
break;
case AccountType::UNKNOWN:
break;
}
value.Set(kAccountTypeKey, AccountTypeToString(GetAccountType()));
value.Set(kEmailKey, user_email_);
std::string serialized;
base::JSONWriter::Write(value, &serialized);
return serialized;
}
// static
std::optional<AccountId> AccountId::Deserialize(std::string_view serialized) {
std::optional<base::Value::Dict> value =
base::JSONReader::ReadDict(serialized);
if (!value) {
return std::nullopt;
}
AccountType account_type = AccountType::GOOGLE;
const std::string* gaia_id = value->FindString(kGaiaIdKey);
const std::string* user_email = value->FindString(kEmailKey);
const std::string* account_type_string = value->FindString(kAccountTypeKey);
if (account_type_string) {
account_type = StringToAccountType(*account_type_string);
}
switch (account_type) {
case AccountType::GOOGLE:
if (!gaia_id) {
DLOG(ERROR) << "gaia_id is not found in '" << serialized << "'";
}
if (!user_email) {
DLOG(ERROR) << "user_email is not found in '" << serialized << "'";
}
if (!gaia_id && !user_email) {
return std::nullopt;
}
return FromUserEmailGaiaId(user_email ? *user_email : "",
gaia_id ? GaiaId(*gaia_id) : GaiaId());
case AccountType::UNKNOWN:
if (!user_email) {
return std::nullopt;
}
return FromUserEmail(*user_email);
}
return std::nullopt;
}
std::ostream& operator<<(std::ostream& stream, const AccountId& account_id) {
stream << "{gaia_id: " << account_id.gaia_id_
<< ", email: " << account_id.user_email_ << ", type: "
<< static_cast<
std::underlying_type<decltype(account_id.account_type_)>::type>(
account_id.account_type_)
<< "}";
return stream;
}
const AccountId& EmptyAccountId() {
static const base::NoDestructor<AccountId> empty_account_id;
return *empty_account_id;
}
namespace std {
std::size_t hash<AccountId>::operator()(const AccountId& user_id) const {
return hash<std::string>()(user_id.GetUserEmail());
}
} // namespace std