// 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/user_manager/known_user.h" #include #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/values.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/scoped_user_pref_update.h" #include "components/user_manager/user_manager.h" #include "google_apis/gaia/gaia_auth_util.h" namespace user_manager { namespace known_user { namespace { // A vector pref of preferences of known users. All new preferences should be // placed in this list. const char kKnownUsers[] = "KnownUsers"; // Known user preferences keys (stored in Local State). // Key of canonical e-mail value. const char kCanonicalEmail[] = "email"; // Key of obfuscated GAIA id value. const char kGAIAIdKey[] = "gaia_id"; // Key of whether this user ID refers to a SAML user. const char kUsingSAMLKey[] = "using_saml"; // Key of Device Id. const char kDeviceId[] = "device_id"; // Key of GAPS cookie. const char kGAPSCookie[] = "gaps_cookie"; // Key of the reason for re-auth. const char kReauthReasonKey[] = "reauth_reason"; // Key for the GaiaId migration status. const char kGaiaIdMigration[] = "gaia_id_migration"; PrefService* GetLocalState() { UserManager* user_manager = UserManager::Get(); if (user_manager) return user_manager->GetLocalState(); return nullptr; } // Checks if values in |dict| correspond with |account_id| identity. bool UserMatches(const AccountId& account_id, const base::DictionaryValue& dict) { std::string value; // TODO(alemate): update code once user id is really a struct. bool has_gaia_id = dict.GetString(kGAIAIdKey, &value); if (has_gaia_id && account_id.GetGaiaId() == value) return true; bool has_email = dict.GetString(kCanonicalEmail, &value); if (has_email && account_id.GetUserEmail() == value) return true; return false; } // Fills relevant |dict| values based on |account_id|. void UpdateIdentity(const AccountId& account_id, base::DictionaryValue& dict) { dict.SetString(kCanonicalEmail, account_id.GetUserEmail()); } } // namespace bool FindPrefs(const AccountId& account_id, const base::DictionaryValue** out_value) { PrefService* local_state = GetLocalState(); // Local State may not be initialized in tests. if (!local_state) return false; // UserManager is usually NULL in unit tests. UserManager* user_manager = UserManager::Get(); if (user_manager && user_manager->IsUserNonCryptohomeDataEphemeral(account_id)) return false; const base::ListValue* known_users = local_state->GetList(kKnownUsers); for (size_t i = 0; i < known_users->GetSize(); ++i) { const base::DictionaryValue* element = nullptr; if (known_users->GetDictionary(i, &element)) { if (UserMatches(account_id, *element)) { known_users->GetDictionary(i, out_value); return true; } } } return false; } void UpdatePrefs(const AccountId& account_id, const base::DictionaryValue& values, bool clear) { PrefService* local_state = GetLocalState(); // Local State may not be initialized in tests. if (!local_state) return; // UserManager is usually NULL in unit tests. UserManager* user_manager = UserManager::Get(); if (user_manager && user_manager->IsUserNonCryptohomeDataEphemeral(account_id)) return; ListPrefUpdate update(local_state, kKnownUsers); for (size_t i = 0; i < update->GetSize(); ++i) { base::DictionaryValue* element = nullptr; if (update->GetDictionary(i, &element)) { if (UserMatches(account_id, *element)) { if (clear) element->Clear(); element->MergeDictionary(&values); UpdateIdentity(account_id, *element); return; } } } scoped_ptr new_value(new base::DictionaryValue()); new_value->MergeDictionary(&values); UpdateIdentity(account_id, *new_value); update->Append(new_value.release()); } bool GetStringPref(const AccountId& account_id, const std::string& path, std::string* out_value) { const base::DictionaryValue* user_pref_dict = nullptr; if (!FindPrefs(account_id, &user_pref_dict)) return false; return user_pref_dict->GetString(path, out_value); } void SetStringPref(const AccountId& account_id, const std::string& path, const std::string& in_value) { PrefService* local_state = GetLocalState(); // Local State may not be initialized in tests. if (!local_state) return; ListPrefUpdate update(local_state, kKnownUsers); base::DictionaryValue dict; dict.SetString(path, in_value); UpdatePrefs(account_id, dict, false); } bool GetBooleanPref(const AccountId& account_id, const std::string& path, bool* out_value) { const base::DictionaryValue* user_pref_dict = nullptr; if (!FindPrefs(account_id, &user_pref_dict)) return false; return user_pref_dict->GetBoolean(path, out_value); } void SetBooleanPref(const AccountId& account_id, const std::string& path, const bool in_value) { PrefService* local_state = GetLocalState(); // Local State may not be initialized in tests. if (!local_state) return; ListPrefUpdate update(local_state, kKnownUsers); base::DictionaryValue dict; dict.SetBoolean(path, in_value); UpdatePrefs(account_id, dict, false); } bool GetIntegerPref(const AccountId& account_id, const std::string& path, int* out_value) { const base::DictionaryValue* user_pref_dict = nullptr; if (!FindPrefs(account_id, &user_pref_dict)) return false; return user_pref_dict->GetInteger(path, out_value); } void SetIntegerPref(const AccountId& account_id, const std::string& path, const int in_value) { PrefService* local_state = GetLocalState(); // Local State may not be initialized in tests. if (!local_state) return; ListPrefUpdate update(local_state, kKnownUsers); base::DictionaryValue dict; dict.SetInteger(path, in_value); UpdatePrefs(account_id, dict, false); } AccountId GetAccountId(const std::string& user_email, const std::string& gaia_id) { // In tests empty accounts are possible. if (user_email.empty() && gaia_id.empty()) return EmptyAccountId(); AccountId result(EmptyAccountId()); UserManager* user_manager = UserManager::Get(); if (user_manager && user_manager->GetPlatformKnownUserId(user_email, gaia_id, &result)) { return result; } // We can have several users with the same gaia_id but different e-mails. // The opposite case is not possible. std::string stored_gaia_id; const std::string sanitized_email = user_email.empty() ? std::string() : gaia::CanonicalizeEmail(gaia::SanitizeEmail(user_email)); if (!sanitized_email.empty() && GetStringPref(AccountId::FromUserEmail(sanitized_email), kGAIAIdKey, &stored_gaia_id)) { if (!gaia_id.empty() && gaia_id != stored_gaia_id) LOG(ERROR) << "User gaia id has changed. Sync will not work."; // gaia_id is associated with cryptohome. return AccountId::FromUserEmailGaiaId(sanitized_email, stored_gaia_id); } std::string stored_email; // GetStringPref() returns the first user record that matches // given ID. So we will get the first one if there are multiples. if (!gaia_id.empty() && GetStringPref(AccountId::FromGaiaId(gaia_id), kCanonicalEmail, &stored_email)) { return AccountId::FromUserEmailGaiaId(stored_email, gaia_id); } return (gaia_id.empty() ? AccountId::FromUserEmail(user_email) : AccountId::FromUserEmailGaiaId(user_email, gaia_id)); } bool GetGaiaIdMigrationStatus(const AccountId& account_id, const std::string& subsystem) { bool migrated = false; if (GetBooleanPref(account_id, std::string(kGaiaIdMigration) + "." + subsystem, &migrated)) { return migrated; } return false; } void SetGaiaIdMigrationStatusDone(const AccountId& account_id, const std::string& subsystem) { SetBooleanPref(account_id, std::string(kGaiaIdMigration) + "." + subsystem, true); } void UpdateGaiaID(const AccountId& account_id, const std::string& gaia_id) { SetStringPref(account_id, kGAIAIdKey, gaia_id); } bool FindGaiaID(const AccountId& account_id, std::string* out_value) { return GetStringPref(account_id, kGAIAIdKey, out_value); } void SetDeviceId(const AccountId& account_id, const std::string& device_id) { const std::string known_device_id = GetDeviceId(account_id); if (!known_device_id.empty() && device_id != known_device_id) { NOTREACHED() << "Trying to change device ID for known user."; } SetStringPref(account_id, kDeviceId, device_id); } std::string GetDeviceId(const AccountId& account_id) { std::string device_id; if (GetStringPref(account_id, kDeviceId, &device_id)) { return device_id; } return std::string(); } void SetGAPSCookie(const AccountId& account_id, const std::string& gaps_cookie) { SetStringPref(account_id, kGAPSCookie, gaps_cookie); } std::string GetGAPSCookie(const AccountId& account_id) { std::string gaps_cookie; if (GetStringPref(account_id, kGAPSCookie, &gaps_cookie)) { return gaps_cookie; } return std::string(); } void UpdateUsingSAML(const AccountId& account_id, const bool using_saml) { SetBooleanPref(account_id, kUsingSAMLKey, using_saml); } bool IsUsingSAML(const AccountId& account_id) { bool using_saml; if (GetBooleanPref(account_id, kUsingSAMLKey, &using_saml)) return using_saml; return false; } void UpdateReauthReason(const AccountId& account_id, const int reauth_reason) { SetIntegerPref(account_id, kReauthReasonKey, reauth_reason); } bool FindReauthReason(const AccountId& account_id, int* out_value) { return GetIntegerPref(account_id, kReauthReasonKey, out_value); } void RemovePrefs(const AccountId& account_id) { PrefService* local_state = GetLocalState(); // Local State may not be initialized in tests. if (!local_state) return; ListPrefUpdate update(local_state, kKnownUsers); for (size_t i = 0; i < update->GetSize(); ++i) { base::DictionaryValue* element = nullptr; if (update->GetDictionary(i, &element)) { if (UserMatches(account_id, *element)) { update->Remove(i, nullptr); break; } } } } void RegisterPrefs(PrefRegistrySimple* registry) { registry->RegisterListPref(kKnownUsers); } } // namespace known_user } // namespace user_manager