| // 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/user_manager/known_user.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <optional> |
| #include <string_view> |
| #include <utility> |
| |
| #include "base/json/values_util.h" |
| #include "base/logging.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/notreached.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "components/account_id/account_id.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/scoped_user_pref_update.h" |
| #include "components/user_manager/account_id_util.h" |
| #include "components/user_manager/common_types.h" |
| #include "components/user_manager/user_manager.h" |
| #include "components/user_manager/user_names.h" |
| #include "google_apis/gaia/gaia_auth_util.h" |
| |
| namespace user_manager { |
| 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). All keys should be |
| // listed in kReservedKeys or kObsoleteKeys below. |
| |
| // Key of whether this user ID refers to a SAML user. |
| const char kUsingSAMLKey[] = "using_saml"; |
| |
| // Key of whether this user authenticated via SAML using the principals API. |
| const char kIsUsingSAMLPrincipalsAPI[] = "using_saml_principals_api"; |
| |
| // 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 kGaiaIdMigrationObsolete[] = "gaia_id_migration"; |
| |
| // Key of the boolean flag telling if a minimal user home migration has been |
| // attempted. This flag is not used since M88 and is only kept here to be able |
| // to remove it from existing entries. |
| const char kMinimalMigrationAttemptedObsolete[] = "minimal_migration_attempted"; |
| |
| // Key of the boolean flag telling if user session requires policy. |
| const char kProfileRequiresPolicy[] = "profile_requires_policy"; |
| |
| // Key of the boolean flag telling if user is ephemeral and should be removed |
| // from the local state on logout. |
| const char kIsEphemeral[] = "is_ephemeral"; |
| |
| // Key of the list value that stores challenge-response authentication keys. |
| const char kChallengeResponseKeys[] = "challenge_response_keys"; |
| |
| const char kLastOnlineSignin[] = "last_online_singin"; |
| const char kOfflineSigninLimitObsolete[] = "offline_signin_limit"; |
| const char kOfflineSigninLimit[] = "offline_signin_limit2"; |
| |
| // Key of the boolean flag telling if user is enterprise managed. |
| const char kIsEnterpriseManaged[] = "is_enterprise_managed"; |
| |
| // Key of the name of the entity (either a domain or email address) that manages |
| // the policies for this account. |
| const char kAccountManager[] = "enterprise_account_manager"; |
| |
| // Key of the last input method user used which is suitable for login/lock |
| // screen. |
| const char kLastInputMethod[] = "last_input_method"; |
| |
| // Key of the PIN auto submit length. |
| const char kPinAutosubmitLength[] = "pin_autosubmit_length"; |
| |
| // Key for the PIN auto submit backfill needed indicator. |
| const char kPinAutosubmitBackfillNeeded[] = "pin_autosubmit_backfill_needed"; |
| |
| // Sync token for SAML password multi-device sync |
| const char kPasswordSyncToken[] = "password_sync_token"; |
| |
| // Major version in which the user completed the onboarding flow. |
| const char kOnboardingCompletedVersion[] = "onboarding_completed_version"; |
| |
| // Last screen shown in the onboarding flow. |
| const char kPendingOnboardingScreen[] = "onboarding_screen_pending"; |
| |
| // Key of the obsolete token handle rotation flag. |
| const char kTokenHandleRotatedObsolete[] = "TokenHandleRotated"; |
| |
| // Cache of the auth factors configured for the user. |
| const char kAuthFactorPresenceCache[] = "AuthFactorsPresenceCache"; |
| |
| // Records for each user whether Lacros is enabled. |
| const char kLacrosEnabled[] = "lacros_enabled"; |
| |
| // List containing all the known user preferences keys. |
| const char* kReservedKeys[] = {kCanonicalEmail, |
| kGAIAIdKey, |
| kObjGuidKey, |
| kAccountTypeKey, |
| kUsingSAMLKey, |
| kIsUsingSAMLPrincipalsAPI, |
| kDeviceId, |
| kGAPSCookie, |
| kReauthReasonKey, |
| kProfileRequiresPolicy, |
| kIsEphemeral, |
| kChallengeResponseKeys, |
| kLastOnlineSignin, |
| kOfflineSigninLimit, |
| kIsEnterpriseManaged, |
| kAccountManager, |
| kLastInputMethod, |
| kPinAutosubmitLength, |
| kPinAutosubmitBackfillNeeded, |
| kPasswordSyncToken, |
| kOnboardingCompletedVersion, |
| kPendingOnboardingScreen, |
| kAuthFactorPresenceCache, |
| kLacrosEnabled}; |
| |
| // List containing all known user preference keys that used to be reserved and |
| // are now obsolete. |
| const char* kObsoleteKeys[] = { |
| kMinimalMigrationAttemptedObsolete, |
| kGaiaIdMigrationObsolete, |
| kOfflineSigninLimitObsolete, |
| kTokenHandleRotatedObsolete, |
| }; |
| |
| |
| // Checks for platform-specific known users matching given |user_email|. If |
| // data matches a known account, returns it. |
| std::optional<AccountId> GetPlatformKnownUserId( |
| const std::string_view user_email) { |
| if (user_email == kStubUserEmail) { |
| return StubAccountId(); |
| } |
| if (user_email == kGuestUserName) { |
| return GuestAccountId(); |
| } |
| return std::nullopt; |
| } |
| |
| } // namespace |
| |
| KnownUser::KnownUser(PrefService* local_state) : local_state_(local_state) { |
| DCHECK(local_state); |
| } |
| |
| KnownUser::~KnownUser() = default; |
| |
| const base::Value::Dict* KnownUser::FindPrefs( |
| const AccountId& account_id) const { |
| // UserManager is usually NULL in unit tests. |
| if (account_id.GetAccountType() != AccountType::ACTIVE_DIRECTORY && |
| UserManager::IsInitialized() && |
| UserManager::Get()->IsUserNonCryptohomeDataEphemeral(account_id)) { |
| return nullptr; |
| } |
| |
| if (!account_id.is_valid()) |
| return nullptr; |
| |
| const base::Value::List& known_users = local_state_->GetList(kKnownUsers); |
| for (const base::Value& element_value : known_users) { |
| if (!element_value.is_dict()) |
| continue; |
| const base::Value::Dict& dict = element_value.GetDict(); |
| if (!AccountIdMatches(account_id, dict)) { |
| continue; |
| } |
| return &dict; |
| } |
| return nullptr; |
| } |
| |
| void KnownUser::SetPath(const AccountId& account_id, |
| const std::string& path, |
| std::optional<base::Value> opt_value) { |
| // UserManager is usually NULL in unit tests. |
| if (account_id.GetAccountType() != AccountType::ACTIVE_DIRECTORY && |
| UserManager::IsInitialized() && |
| UserManager::Get()->IsUserNonCryptohomeDataEphemeral(account_id)) { |
| return; |
| } |
| |
| if (!account_id.is_valid()) |
| return; |
| |
| ScopedListPrefUpdate update(local_state_, kKnownUsers); |
| for (base::Value& element_value : *update) { |
| if (!element_value.is_dict()) |
| continue; |
| base::Value::Dict& dict = element_value.GetDict(); |
| if (!AccountIdMatches(account_id, dict)) { |
| continue; |
| } |
| if (opt_value.has_value()) { |
| dict.SetByDottedPath(path, std::move(opt_value).value()); |
| } else { |
| dict.RemoveByDottedPath(path); |
| } |
| |
| StoreAccountId(account_id, dict); |
| return; |
| } |
| if (!opt_value.has_value()) |
| return; |
| |
| base::Value::Dict new_dict; |
| new_dict.SetByDottedPath(path, std::move(opt_value).value()); |
| StoreAccountId(account_id, new_dict); |
| update->Append(std::move(new_dict)); |
| } |
| |
| const std::string* KnownUser::FindStringPath(const AccountId& account_id, |
| std::string_view path) const { |
| const base::Value::Dict* user_pref_dict = FindPrefs(account_id); |
| if (!user_pref_dict) |
| return nullptr; |
| |
| return user_pref_dict->FindStringByDottedPath(path); |
| } |
| |
| bool KnownUser::GetStringPrefForTest(const AccountId& account_id, |
| const std::string& path, |
| std::string* out_value) { |
| const std::string* res = FindStringPath(account_id, path); |
| if (out_value && res) |
| *out_value = *res; |
| return res; |
| } |
| |
| void KnownUser::SetStringPref(const AccountId& account_id, |
| const std::string& path, |
| const std::string& in_value) { |
| SetPath(account_id, path, base::Value(in_value)); |
| } |
| |
| std::optional<bool> KnownUser::FindBoolPath(const AccountId& account_id, |
| std::string_view path) const { |
| const base::Value::Dict* user_pref_dict = FindPrefs(account_id); |
| if (!user_pref_dict) |
| return std::nullopt; |
| |
| return user_pref_dict->FindBoolByDottedPath(path); |
| } |
| |
| bool KnownUser::GetBooleanPrefForTest(const AccountId& account_id, |
| const std::string& path, |
| bool* out_value) { |
| auto opt_val = FindBoolPath(account_id, path); |
| if (out_value && opt_val.has_value()) |
| *out_value = opt_val.value(); |
| |
| return opt_val.has_value(); |
| } |
| |
| void KnownUser::SetBooleanPref(const AccountId& account_id, |
| const std::string& path, |
| const bool in_value) { |
| SetPath(account_id, path, base::Value(in_value)); |
| } |
| |
| std::optional<int> KnownUser::FindIntPath(const AccountId& account_id, |
| std::string_view path) const { |
| const base::Value::Dict* user_pref_dict = FindPrefs(account_id); |
| if (!user_pref_dict) |
| return std::nullopt; |
| |
| return user_pref_dict->FindIntByDottedPath(path); |
| } |
| |
| bool KnownUser::GetIntegerPrefForTest(const AccountId& account_id, |
| const std::string& path, |
| int* out_value) { |
| auto opt_val = FindIntPath(account_id, path); |
| if (out_value && opt_val.has_value()) |
| *out_value = opt_val.value(); |
| |
| return opt_val.has_value(); |
| } |
| |
| void KnownUser::SetIntegerPref(const AccountId& account_id, |
| const std::string& path, |
| const int in_value) { |
| SetPath(account_id, path, base::Value(in_value)); |
| } |
| |
| bool KnownUser::GetPrefForTest(const AccountId& account_id, |
| const std::string& path, |
| const base::Value** out_value) { |
| *out_value = FindPath(account_id, path); |
| return *out_value != nullptr; |
| } |
| |
| const base::Value* KnownUser::FindPath(const AccountId& account_id, |
| const std::string& path) const { |
| const base::Value::Dict* user_pref_dict = FindPrefs(account_id); |
| if (!user_pref_dict) |
| return nullptr; |
| |
| return user_pref_dict->FindByDottedPath(path); |
| } |
| |
| void KnownUser::RemovePref(const AccountId& account_id, |
| const std::string& path) { |
| // Prevent removing keys that are used internally. |
| for (const std::string& key : kReservedKeys) |
| CHECK_NE(path, key); |
| |
| SetPath(account_id, path, std::nullopt); |
| } |
| |
| AccountId KnownUser::GetAccountId(const std::string& user_email, |
| const std::string& id, |
| const AccountType& account_type) const { |
| DCHECK((id.empty() && account_type == AccountType::UNKNOWN) || |
| (!id.empty() && account_type != AccountType::UNKNOWN)); |
| // In tests empty accounts are possible. |
| if (user_email.empty() && id.empty() && |
| account_type == AccountType::UNKNOWN) { |
| return EmptyAccountId(); |
| } |
| |
| // UserManager is usually NULL in unit tests. |
| if (account_type == AccountType::UNKNOWN) { |
| if (std::optional<AccountId> result = GetPlatformKnownUserId(user_email); |
| result.has_value()) { |
| return result.value(); |
| } |
| } |
| |
| const std::string sanitized_email = |
| user_email.empty() |
| ? std::string() |
| : gaia::CanonicalizeEmail(gaia::SanitizeEmail(user_email)); |
| |
| if (!sanitized_email.empty()) { |
| const AccountId account_id(AccountId::FromUserEmail(sanitized_email)); |
| if (const std::string* stored_gaia_id = |
| FindStringPath(account_id, kGAIAIdKey)) { |
| if (!id.empty()) { |
| DCHECK(account_type == AccountType::GOOGLE); |
| if (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); |
| } |
| |
| if (const std::string* stored_obj_guid = |
| FindStringPath(account_id, kObjGuidKey)) { |
| if (!id.empty()) { |
| DCHECK(account_type == AccountType::ACTIVE_DIRECTORY); |
| if (id != *stored_obj_guid) |
| LOG(ERROR) << "User object guid has changed. Sync will not work."; |
| } |
| |
| // obj_guid is associated with cryptohome. |
| return AccountId::AdFromUserEmailObjGuid(sanitized_email, |
| *stored_obj_guid); |
| } |
| } |
| |
| switch (account_type) { |
| case AccountType::GOOGLE: |
| return AccountId::FromUserEmailGaiaId(sanitized_email, id); |
| case AccountType::ACTIVE_DIRECTORY: |
| return AccountId::AdFromUserEmailObjGuid(sanitized_email, id); |
| case AccountType::UNKNOWN: |
| return AccountId::FromUserEmail(sanitized_email); |
| } |
| NOTREACHED_IN_MIGRATION(); |
| return EmptyAccountId(); |
| } |
| |
| AccountId KnownUser::GetAccountIdByCryptohomeId( |
| const CryptohomeId& cryptohome_id) { |
| if (cryptohome_id->empty()) |
| return EmptyAccountId(); |
| |
| const std::vector<AccountId> known_account_ids = GetKnownAccountIds(); |
| |
| // A LOT of tests start with --login_user <user>, and not registering this |
| // user before. So we might have "known_user" entry without gaia_id. |
| for (const AccountId& known_id : known_account_ids) { |
| if (known_id.HasAccountIdKey() && |
| known_id.GetAccountIdKey() == cryptohome_id.value()) { |
| return known_id; |
| } |
| } |
| |
| for (const AccountId& known_id : known_account_ids) { |
| if (known_id.GetUserEmail() == cryptohome_id.value()) { |
| return known_id; |
| } |
| } |
| |
| if (std::optional<AccountId> result = |
| GetPlatformKnownUserId(cryptohome_id.value()); |
| result.has_value()) { |
| return result.value(); |
| } |
| return AccountId::FromNonCanonicalEmail(cryptohome_id.value(), std::string(), |
| AccountType::UNKNOWN); |
| } |
| |
| std::vector<AccountId> KnownUser::GetKnownAccountIds() { |
| std::vector<AccountId> result; |
| |
| const base::Value::List& known_users = local_state_->GetList(kKnownUsers); |
| for (const base::Value& element_value : known_users) { |
| if (!element_value.is_dict()) |
| continue; |
| const base::Value::Dict& dict = element_value.GetDict(); |
| if (std::optional<AccountId> account_id = LoadAccountId(dict)) { |
| result.push_back(*account_id); |
| } |
| } |
| return result; |
| } |
| |
| void KnownUser::SaveKnownUser(const AccountId& account_id) { |
| const bool is_ephemeral = |
| UserManager::IsInitialized() && |
| UserManager::Get()->IsUserNonCryptohomeDataEphemeral(account_id); |
| if (is_ephemeral && |
| account_id.GetAccountType() != AccountType::ACTIVE_DIRECTORY) { |
| return; |
| } |
| UpdateId(account_id); |
| local_state_->CommitPendingWrite(); |
| } |
| |
| void KnownUser::SetIsEphemeralUser(const AccountId& account_id, |
| bool is_ephemeral) { |
| if (account_id.GetAccountType() != AccountType::ACTIVE_DIRECTORY) |
| return; |
| SetBooleanPref(account_id, kIsEphemeral, is_ephemeral); |
| } |
| |
| void KnownUser::UpdateId(const AccountId& account_id) { |
| switch (account_id.GetAccountType()) { |
| case AccountType::GOOGLE: |
| SetStringPref(account_id, kGAIAIdKey, account_id.GetGaiaId()); |
| break; |
| case AccountType::ACTIVE_DIRECTORY: |
| SetStringPref(account_id, kObjGuidKey, account_id.GetObjGuid()); |
| break; |
| case AccountType::UNKNOWN: |
| return; |
| } |
| SetStringPref(account_id, kAccountTypeKey, |
| AccountId::AccountTypeToString(account_id.GetAccountType())); |
| } |
| |
| const std::string* KnownUser::FindGaiaID(const AccountId& account_id) { |
| return FindStringPath(account_id, kGAIAIdKey); |
| } |
| |
| void KnownUser::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_IN_MIGRATION() << "Trying to change device ID for known user."; |
| } |
| SetStringPref(account_id, kDeviceId, device_id); |
| } |
| |
| std::string KnownUser::GetDeviceId(const AccountId& account_id) const { |
| const std::string* device_id = FindStringPath(account_id, kDeviceId); |
| if (device_id) |
| return *device_id; |
| return std::string(); |
| } |
| |
| void KnownUser::SetGAPSCookie(const AccountId& account_id, |
| const std::string& gaps_cookie) { |
| SetStringPref(account_id, kGAPSCookie, gaps_cookie); |
| } |
| |
| std::string KnownUser::GetGAPSCookie(const AccountId& account_id) { |
| const std::string* gaps_cookie = FindStringPath(account_id, kGAPSCookie); |
| if (gaps_cookie) |
| return *gaps_cookie; |
| return std::string(); |
| } |
| |
| void KnownUser::UpdateUsingSAML(const AccountId& account_id, |
| const bool using_saml) { |
| SetBooleanPref(account_id, kUsingSAMLKey, using_saml); |
| } |
| |
| bool KnownUser::IsUsingSAML(const AccountId& account_id) { |
| return FindBoolPath(account_id, kUsingSAMLKey).value_or(false); |
| } |
| |
| void KnownUser::UpdateIsUsingSAMLPrincipalsAPI( |
| const AccountId& account_id, |
| bool is_using_saml_principals_api) { |
| SetBooleanPref(account_id, kIsUsingSAMLPrincipalsAPI, |
| is_using_saml_principals_api); |
| } |
| |
| bool KnownUser::GetIsUsingSAMLPrincipalsAPI(const AccountId& account_id) { |
| return FindBoolPath(account_id, kIsUsingSAMLPrincipalsAPI).value_or(false); |
| } |
| |
| void KnownUser::SetProfileRequiresPolicy(const AccountId& account_id, |
| ProfileRequiresPolicy required) { |
| DCHECK_NE(required, ProfileRequiresPolicy::kUnknown); |
| SetBooleanPref(account_id, kProfileRequiresPolicy, |
| required == ProfileRequiresPolicy::kPolicyRequired); |
| } |
| |
| ProfileRequiresPolicy KnownUser::GetProfileRequiresPolicy( |
| const AccountId& account_id) { |
| std::optional<bool> requires_policy = |
| FindBoolPath(account_id, kProfileRequiresPolicy); |
| if (requires_policy.has_value()) { |
| return requires_policy.value() ? ProfileRequiresPolicy::kPolicyRequired |
| : ProfileRequiresPolicy::kNoPolicyRequired; |
| } |
| return ProfileRequiresPolicy::kUnknown; |
| } |
| |
| void KnownUser::ClearProfileRequiresPolicy(const AccountId& account_id) { |
| SetPath(account_id, kProfileRequiresPolicy, std::nullopt); |
| } |
| |
| void KnownUser::UpdateReauthReason(const AccountId& account_id, |
| const int reauth_reason) { |
| SetIntegerPref(account_id, kReauthReasonKey, reauth_reason); |
| } |
| |
| std::optional<int> KnownUser::FindReauthReason( |
| const AccountId& account_id) const { |
| return FindIntPath(account_id, kReauthReasonKey); |
| } |
| |
| void KnownUser::SetChallengeResponseKeys(const AccountId& account_id, |
| base::Value::List value) { |
| SetPath(account_id, kChallengeResponseKeys, base::Value(std::move(value))); |
| } |
| |
| base::Value::List KnownUser::GetChallengeResponseKeys( |
| const AccountId& account_id) { |
| const base::Value* value = FindPath(account_id, kChallengeResponseKeys); |
| if (!value || !value->is_list()) |
| return base::Value::List(); |
| return value->GetList().Clone(); |
| } |
| |
| void KnownUser::SetLastOnlineSignin(const AccountId& account_id, |
| base::Time time) { |
| SetPath(account_id, kLastOnlineSignin, base::TimeToValue(time)); |
| } |
| |
| base::Time KnownUser::GetLastOnlineSignin(const AccountId& account_id) { |
| const base::Value* value = FindPath(account_id, kLastOnlineSignin); |
| if (!value) |
| return base::Time(); |
| std::optional<base::Time> time = base::ValueToTime(value); |
| if (!time) |
| return base::Time(); |
| return *time; |
| } |
| |
| void KnownUser::SetOfflineSigninLimit( |
| const AccountId& account_id, |
| std::optional<base::TimeDelta> time_delta) { |
| if (!time_delta) { |
| SetPath(account_id, kOfflineSigninLimit, std::nullopt); |
| } else { |
| SetPath(account_id, kOfflineSigninLimit, |
| base::TimeDeltaToValue(time_delta.value())); |
| } |
| } |
| |
| std::optional<base::TimeDelta> KnownUser::GetOfflineSigninLimit( |
| const AccountId& account_id) { |
| return base::ValueToTimeDelta(FindPath(account_id, kOfflineSigninLimit)); |
| } |
| |
| void KnownUser::SetIsEnterpriseManaged(const AccountId& account_id, |
| bool is_enterprise_managed) { |
| SetBooleanPref(account_id, kIsEnterpriseManaged, is_enterprise_managed); |
| } |
| |
| bool KnownUser::GetIsEnterpriseManaged(const AccountId& account_id) { |
| return FindBoolPath(account_id, kIsEnterpriseManaged).value_or(false); |
| } |
| |
| void KnownUser::SetAccountManager(const AccountId& account_id, |
| const std::string& manager) { |
| SetStringPref(account_id, kAccountManager, manager); |
| } |
| |
| const std::string* KnownUser::GetAccountManager(const AccountId& account_id) { |
| return FindStringPath(account_id, kAccountManager); |
| } |
| |
| void KnownUser::SetUserLastLoginInputMethodId( |
| const AccountId& account_id, |
| const std::string& input_method_id) { |
| SetStringPref(account_id, kLastInputMethod, input_method_id); |
| } |
| |
| const std::string* KnownUser::GetUserLastInputMethodId( |
| const AccountId& account_id) { |
| return FindStringPath(account_id, kLastInputMethod); |
| } |
| |
| void KnownUser::SetUserPinLength(const AccountId& account_id, int pin_length) { |
| SetIntegerPref(account_id, kPinAutosubmitLength, pin_length); |
| } |
| |
| int KnownUser::GetUserPinLength(const AccountId& account_id) { |
| return FindIntPath(account_id, kPinAutosubmitLength).value_or(0); |
| } |
| |
| bool KnownUser::PinAutosubmitIsBackfillNeeded(const AccountId& account_id) { |
| // If the pref is not set, the pref needs to be backfilled. |
| return FindBoolPath(account_id, kPinAutosubmitBackfillNeeded).value_or(true); |
| } |
| |
| void KnownUser::PinAutosubmitSetBackfillNotNeeded(const AccountId& account_id) { |
| SetBooleanPref(account_id, kPinAutosubmitBackfillNeeded, false); |
| } |
| |
| void KnownUser::PinAutosubmitSetBackfillNeededForTests( |
| const AccountId& account_id) { |
| SetBooleanPref(account_id, kPinAutosubmitBackfillNeeded, true); |
| } |
| |
| void KnownUser::SetAuthFactorCache(const AccountId& account_id, |
| base::Value::Dict cache) { |
| SetPath(account_id, kAuthFactorPresenceCache, base::Value(std::move(cache))); |
| } |
| |
| base::Value::Dict KnownUser::GetAuthFactorCache(const AccountId& account_id) { |
| const auto* value = FindPath(account_id, kAuthFactorPresenceCache); |
| if (!value || !value->is_dict()) { |
| return base::Value::Dict(); |
| } |
| return value->GetDict().Clone(); |
| } |
| |
| void KnownUser::SetPasswordSyncToken(const AccountId& account_id, |
| const std::string& token) { |
| SetStringPref(account_id, kPasswordSyncToken, token); |
| } |
| |
| const std::string* KnownUser::GetPasswordSyncToken( |
| const AccountId& account_id) const { |
| return FindStringPath(account_id, kPasswordSyncToken); |
| } |
| |
| void KnownUser::ClearPasswordSyncToken(const AccountId& account_id) { |
| SetPath(account_id, kPasswordSyncToken, std::nullopt); |
| } |
| |
| void KnownUser::SetOnboardingCompletedVersion( |
| const AccountId& account_id, |
| const std::optional<base::Version> version) { |
| if (!version) { |
| SetPath(account_id, kOnboardingCompletedVersion, std::nullopt); |
| } else { |
| SetStringPref(account_id, kOnboardingCompletedVersion, |
| version.value().GetString()); |
| } |
| } |
| |
| std::optional<base::Version> KnownUser::GetOnboardingCompletedVersion( |
| const AccountId& account_id) { |
| const std::string* str_version = |
| FindStringPath(account_id, kOnboardingCompletedVersion); |
| |
| if (!str_version) |
| return std::nullopt; |
| |
| base::Version version = base::Version(*str_version); |
| if (!version.IsValid()) |
| return std::nullopt; |
| return version; |
| } |
| |
| void KnownUser::RemoveOnboardingCompletedVersionForTests( |
| const AccountId& account_id) { |
| SetPath(account_id, kOnboardingCompletedVersion, std::nullopt); |
| } |
| |
| void KnownUser::SetPendingOnboardingScreen(const AccountId& account_id, |
| const std::string& screen) { |
| SetStringPref(account_id, kPendingOnboardingScreen, screen); |
| } |
| |
| void KnownUser::RemovePendingOnboardingScreen(const AccountId& account_id) { |
| SetPath(account_id, kPendingOnboardingScreen, std::nullopt); |
| } |
| |
| std::string KnownUser::GetPendingOnboardingScreen(const AccountId& account_id) { |
| if (const std::string* screen = |
| FindStringPath(account_id, kPendingOnboardingScreen)) { |
| return *screen; |
| } |
| // Return empty string if no screen is pending. |
| return std::string(); |
| } |
| |
| void KnownUser::SetLacrosEnabled(const AccountId& account_id, bool enabled) { |
| SetBooleanPref(account_id, kLacrosEnabled, enabled); |
| } |
| |
| bool KnownUser::GetLacrosEnabledForAnyUser() { |
| const std::vector<AccountId> account_ids = GetKnownAccountIds(); |
| return base::ranges::any_of(account_ids, [this](const AccountId& account_id) { |
| return FindBoolPath(account_id, kLacrosEnabled).value_or(false); |
| }); |
| } |
| |
| bool KnownUser::UserExists(const AccountId& account_id) { |
| return FindPrefs(account_id); |
| } |
| |
| void KnownUser::RemovePrefs(const AccountId& account_id) { |
| if (!account_id.is_valid()) |
| return; |
| |
| ScopedListPrefUpdate update(local_state_, kKnownUsers); |
| base::Value::List& update_list = update.Get(); |
| for (auto it = update_list.begin(); it != update_list.end(); ++it) { |
| if (AccountIdMatches(account_id, it->GetDict())) { |
| update_list.erase(it); |
| break; |
| } |
| } |
| } |
| |
| void KnownUser::CleanEphemeralUsers() { |
| ScopedListPrefUpdate update(local_state_, kKnownUsers); |
| update->EraseIf([](const auto& value) { |
| if (!value.is_dict()) |
| return false; |
| |
| std::optional<bool> is_ephemeral = value.GetDict().FindBool(kIsEphemeral); |
| return is_ephemeral && *is_ephemeral; |
| }); |
| } |
| |
| void KnownUser::CleanObsoletePrefs() { |
| ScopedListPrefUpdate update(local_state_, kKnownUsers); |
| for (base::Value& user_entry : *update) { |
| if (!user_entry.is_dict()) |
| continue; |
| for (const std::string& key : kObsoleteKeys) |
| user_entry.GetDict().Remove(key); |
| } |
| } |
| |
| // static |
| void KnownUser::RegisterPrefs(PrefRegistrySimple* registry) { |
| registry->RegisterListPref(kKnownUsers); |
| } |
| |
| } // namespace user_manager |