chromium/components/user_manager/multi_user/multi_user_sign_in_policy_controller.cc

// Copyright 2014 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/multi_user/multi_user_sign_in_policy_controller.h"

#include <utility>

#include "base/functional/bind.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "components/user_manager/user_manager_pref_names.h"

namespace user_manager {

MultiUserSignInPolicyController::MultiUserSignInPolicyController(
    PrefService* local_state,
    UserManager* user_manager)
    : local_state_(local_state), user_manager_(user_manager) {}

MultiUserSignInPolicyController::~MultiUserSignInPolicyController() = default;

// static
void MultiUserSignInPolicyController::RegisterPrefs(
    PrefRegistrySimple* registry) {
  registry->RegisterDictionaryPref(prefs::kCachedMultiProfileUserBehavior);
}

bool MultiUserSignInPolicyController::IsUserAllowedInSession(
    const std::string& user_email) const {
  const User* primary_user = user_manager_->GetPrimaryUser();
  std::string primary_user_email;
  if (primary_user) {
    primary_user_email = primary_user->GetAccountId().GetUserEmail();
  }

  // Always allow if there is no primary user or user being checked is the
  // primary user.
  if (primary_user_email.empty() || primary_user_email == user_email) {
    return true;
  }

  auto primary_user_policy = GetMultiUserSignInPolicy(primary_user);
  if (primary_user_policy == MultiUserSignInPolicy::kNotAllowed) {
    return false;
  }

  // The user must have 'unrestricted' policy to be a secondary user.
  const auto policy = GetCachedValue(user_email);
  return policy == MultiUserSignInPolicy::kUnrestricted;
}

void MultiUserSignInPolicyController::StartObserving(User* user) {
  // Profile name could be empty during tests.
  if (user->GetAccountId().GetUserEmail().empty() || !user->GetProfilePrefs()) {
    return;
  }

  auto registrar = std::make_unique<PrefChangeRegistrar>();
  registrar->Init(user->GetProfilePrefs());
  registrar->Add(
      prefs::kMultiProfileUserBehaviorPref,
      base::BindRepeating(&MultiUserSignInPolicyController::OnUserPrefChanged,
                          base::Unretained(this), user));
  pref_watchers_.push_back(std::move(registrar));

  OnUserPrefChanged(user);
}

void MultiUserSignInPolicyController::StopObserving(User* user) {
  auto* prefs = user->GetProfilePrefs();
  std::erase_if(pref_watchers_, [prefs](auto& registrar) {
    return registrar->prefs() == prefs;
  });
}

void MultiUserSignInPolicyController::RemoveCachedValues(
    std::string_view user_email) {
  ScopedDictPrefUpdate update(local_state_,
                              prefs::kCachedMultiProfileUserBehavior);
  update->Remove(user_email);
}

MultiUserSignInPolicy MultiUserSignInPolicyController::GetCachedValue(
    std::string_view user_email) const {
  const base::Value::Dict& dict =
      local_state_->GetDict(prefs::kCachedMultiProfileUserBehavior);

  const std::string* value = dict.FindString(user_email);
  if (!value) {
    return MultiUserSignInPolicy::kUnrestricted;
  }

  return ParseMultiUserSignInPolicyPref(*value).value_or(
      MultiUserSignInPolicy::kUnrestricted);
}

void MultiUserSignInPolicyController::SetCachedValue(
    std::string_view user_email,
    MultiUserSignInPolicy policy) {
  ScopedDictPrefUpdate update(local_state_,
                              prefs::kCachedMultiProfileUserBehavior);
  update->Set(user_email, MultiUserSignInPolicyToPrefValue(policy));
}

void MultiUserSignInPolicyController::CheckSessionUsers() {
  for (const User* user : user_manager_->GetLoggedInUsers()) {
    const std::string& user_email = user->GetAccountId().GetUserEmail();
    if (!IsUserAllowedInSession(user_email)) {
      user_manager_->NotifyUserNotAllowed(user_email);
      return;
    }
  }
}

void MultiUserSignInPolicyController::OnUserPrefChanged(User* user) {
  std::string user_email = user->GetAccountId().GetUserEmail();
  CHECK(!user_email.empty());

  PrefService* prefs = user->GetProfilePrefs();
  if (prefs->FindPreference(prefs::kMultiProfileUserBehaviorPref)
          ->IsDefaultValue()) {
    // Migration code to clear cached default behavior.
    // TODO(xiyuan): Remove this after M35.
    ScopedDictPrefUpdate update(local_state_,
                                prefs::kCachedMultiProfileUserBehavior);
    update->Remove(user_email);
  } else {
    auto policy = ParseMultiUserSignInPolicyPref(
        prefs->GetString(prefs::kMultiProfileUserBehaviorPref));
    SetCachedValue(user_email,
                   policy.value_or(MultiUserSignInPolicy::kUnrestricted));
  }

  CheckSessionUsers();
}

}  // namespace user_manager