| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // 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/json/json_reader.h" |
| #include "base/json/json_writer.h" |
| #include "base/no_destructor.h" |
| #include "base/strings/string_util.h" |
| #include "base/values.h" |
| #include "google_apis/gaia/gaia_auth_util.h" |
| |
| namespace { |
| |
| // Serialization keys |
| const char kGaiaIdKey[] = "gaia_id"; |
| const char kEmailKey[] = "email"; |
| const char kObjGuid[] = "obj_guid"; |
| const char kAccountTypeKey[] = "account_type"; |
| |
| // Serialization values for account type. |
| const char kGoogle[] = "google"; |
| const char kAd[] = "ad"; |
| const char kUnknown[] = "unknown"; |
| |
| // Prefix for GetAccountIdKey(). |
| const char kKeyGaiaIdPrefix[] = "g-"; |
| const char kKeyAdIdPrefix[] = "a-"; |
| |
| } // anonymous namespace |
| |
| AccountId::AccountId() {} |
| |
| AccountId::AccountId(const std::string& id, |
| const std::string& user_email, |
| const AccountType& account_type) |
| : id_(id), user_email_(user_email), account_type_(account_type) { |
| DCHECK(user_email == gaia::CanonicalizeEmail(user_email)); |
| DCHECK(account_type != AccountType::UNKNOWN || id.empty()); |
| DCHECK(account_type != AccountType::ACTIVE_DIRECTORY || !id.empty()); |
| // 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='" << id << "'"; |
| |
| // TODO(alemate): DCHECK(!email.empty()); |
| // TODO(alemate): check gaia_id is not empty once it is required. |
| } |
| |
| AccountId::AccountId(const AccountId& other) |
| : id_(other.id_), |
| user_email_(other.user_email_), |
| account_type_(other.account_type_) {} |
| |
| 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::GOOGLE: |
| return (id_ == other.id_ && user_email_ == other.user_email_) || |
| (!id_.empty() && id_ == other.id_) || |
| (!user_email_.empty() && user_email_ == other.user_email_); |
| case AccountType::ACTIVE_DIRECTORY: |
| return id_ == other.id_ && user_email_ == other.user_email_; |
| default: |
| NOTREACHED() << "Unknown account type"; |
| } |
| return false; |
| } |
| |
| bool AccountId::operator!=(const AccountId& other) const { |
| return !operator==(other); |
| } |
| |
| 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 id_.empty() && user_email_.empty() && |
| account_type_ == AccountType::UNKNOWN; |
| } |
| |
| bool AccountId::is_valid() const { |
| switch (account_type_) { |
| case AccountType::GOOGLE: |
| return /* !id_.empty() && */ !user_email_.empty(); |
| case AccountType::ACTIVE_DIRECTORY: |
| return !id_.empty() && !user_email_.empty(); |
| case AccountType::UNKNOWN: |
| return id_.empty() && !user_email_.empty(); |
| } |
| NOTREACHED(); |
| return false; |
| } |
| |
| void AccountId::clear() { |
| id_.clear(); |
| user_email_.clear(); |
| account_type_ = AccountType::UNKNOWN; |
| } |
| |
| AccountType AccountId::GetAccountType() const { |
| return account_type_; |
| } |
| |
| const std::string& AccountId::GetGaiaId() const { |
| if (account_type_ != AccountType::GOOGLE) |
| NOTIMPLEMENTED() << "Failed to get gaia_id for non-Google account."; |
| return id_; |
| } |
| |
| const std::string& AccountId::GetObjGuid() const { |
| if (account_type_ != AccountType::ACTIVE_DIRECTORY) |
| NOTIMPLEMENTED() |
| << "Failed to get obj_guid for non-Active Directory account."; |
| return id_; |
| } |
| |
| const std::string& AccountId::GetUserEmail() const { |
| return user_email_; |
| } |
| |
| bool AccountId::HasAccountIdKey() const { |
| return account_type_ != AccountType::UNKNOWN && !id_.empty(); |
| } |
| |
| const std::string AccountId::GetAccountIdKey() const { |
| #ifdef NDEBUG |
| if (id_.empty()) |
| LOG(FATAL) << "GetAccountIdKey(): no id for " << Serialize(); |
| #else |
| CHECK(!id_.empty()); |
| #endif |
| switch (GetAccountType()) { |
| case AccountType::GOOGLE: |
| return std::string(kKeyGaiaIdPrefix) + id_; |
| case AccountType::ACTIVE_DIRECTORY: |
| return std::string(kKeyAdIdPrefix) + id_; |
| default: |
| NOTREACHED() << "Unknown account type"; |
| } |
| return std::string(); |
| } |
| |
| void AccountId::SetUserEmail(const std::string& email) { |
| DCHECK(email == gaia::CanonicalizeEmail(email)); |
| DCHECK(!email.empty()); |
| user_email_ = email; |
| } |
| |
| // static |
| AccountId AccountId::FromUserEmail(const std::string& email) { |
| // TODO(alemate): DCHECK(!email.empty()); |
| return AccountId(std::string() /* id */, email, AccountType::UNKNOWN); |
| } |
| |
| // static |
| AccountId AccountId::FromGaiaId(const std::string& gaia_id) { |
| DCHECK(!gaia_id.empty()); |
| return AccountId(gaia_id, std::string() /* email */, AccountType::GOOGLE); |
| } |
| |
| // static |
| AccountId AccountId::FromUserEmailGaiaId(const std::string& email, |
| const std::string& gaia_id) { |
| DCHECK(!(email.empty() && gaia_id.empty())); |
| return AccountId(gaia_id, email, AccountType::GOOGLE); |
| } |
| |
| // static |
| AccountId AccountId::AdFromUserEmailObjGuid(const std::string& email, |
| const std::string& obj_guid) { |
| DCHECK(!email.empty() && !obj_guid.empty()); |
| return AccountId(obj_guid, email, AccountType::ACTIVE_DIRECTORY); |
| } |
| |
| // static |
| AccountId AccountId::AdFromObjGuid(const std::string& obj_guid) { |
| DCHECK(!obj_guid.empty()); |
| return AccountId(obj_guid, std::string() /* email */, |
| AccountType::ACTIVE_DIRECTORY); |
| } |
| |
| // static |
| AccountType AccountId::StringToAccountType( |
| const std::string& account_type_string) { |
| if (account_type_string == kGoogle) |
| return AccountType::GOOGLE; |
| if (account_type_string == kAd) |
| return AccountType::ACTIVE_DIRECTORY; |
| if (account_type_string == kUnknown) |
| return AccountType::UNKNOWN; |
| NOTREACHED() << "Unknown account type " << account_type_string; |
| return AccountType::UNKNOWN; |
| } |
| |
| // static |
| std::string AccountId::AccountTypeToString(const AccountType& account_type) { |
| switch (account_type) { |
| case AccountType::GOOGLE: |
| return kGoogle; |
| case AccountType::ACTIVE_DIRECTORY: |
| return kAd; |
| case AccountType::UNKNOWN: |
| return kUnknown; |
| } |
| return std::string(); |
| } |
| |
| std::string AccountId::Serialize() const { |
| base::DictionaryValue value; |
| switch (GetAccountType()) { |
| case AccountType::GOOGLE: |
| value.SetString(kGaiaIdKey, id_); |
| break; |
| case AccountType::ACTIVE_DIRECTORY: |
| value.SetString(kObjGuid, id_); |
| break; |
| case AccountType::UNKNOWN: |
| break; |
| } |
| value.SetString(kAccountTypeKey, AccountTypeToString(GetAccountType())); |
| value.SetString(kEmailKey, user_email_); |
| |
| std::string serialized; |
| base::JSONWriter::Write(value, &serialized); |
| return serialized; |
| } |
| |
| // static |
| bool AccountId::Deserialize(const std::string& serialized, |
| AccountId* account_id) { |
| base::Optional<base::Value> value(base::JSONReader::Read(serialized)); |
| if (!value || !value->is_dict()) |
| return false; |
| |
| AccountType account_type = AccountType::GOOGLE; |
| const std::string* gaia_id = value->FindStringKey(kGaiaIdKey); |
| const std::string* user_email = value->FindStringKey(kEmailKey); |
| const std::string* obj_guid = value->FindStringKey(kObjGuid); |
| const std::string* account_type_string = |
| value->FindStringKey(kAccountTypeKey); |
| if (account_type_string) |
| account_type = StringToAccountType(*account_type_string); |
| |
| switch (account_type) { |
| case AccountType::GOOGLE: |
| if (obj_guid) { |
| DLOG(ERROR) << "AccountType is 'google' but obj_guid is found in '" |
| << serialized << "'"; |
| } |
| |
| 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 false; |
| |
| *account_id = |
| FromUserEmailGaiaId(user_email ? *user_email : std::string(), |
| gaia_id ? *gaia_id : std::string()); |
| return true; |
| |
| case AccountType::ACTIVE_DIRECTORY: |
| if (gaia_id) { |
| DLOG(ERROR) |
| << "AccountType is 'active directory' but gaia_id is found in '" |
| << serialized << "'"; |
| } |
| |
| if (!obj_guid) { |
| DLOG(ERROR) << "obj_guid is not found in '" << serialized << "'"; |
| return false; |
| } |
| |
| if (!user_email) { |
| DLOG(ERROR) << "user_email is not found in '" << serialized << "'"; |
| } |
| |
| if (!obj_guid || !user_email) |
| return false; |
| |
| *account_id = AdFromUserEmailObjGuid(*user_email, *obj_guid); |
| return true; |
| |
| case AccountType::UNKNOWN: |
| if (!user_email) |
| return false; |
| *account_id = FromUserEmail(*user_email); |
| return true; |
| } |
| return false; |
| } |
| |
| std::ostream& operator<<(std::ostream& stream, const AccountId& account_id) { |
| stream << "{id: " << account_id.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 |