1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/login/users/multi_profile_user_controller.h"
6
7#include "base/bind.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/prefs/pref_change_registrar.h"
10#include "base/prefs/pref_registry_simple.h"
11#include "base/prefs/pref_service.h"
12#include "base/prefs/scoped_user_pref_update.h"
13#include "chrome/browser/chromeos/login/users/multi_profile_user_controller_delegate.h"
14#include "chrome/browser/chromeos/policy/policy_cert_service.h"
15#include "chrome/browser/chromeos/policy/policy_cert_service_factory.h"
16#include "chrome/browser/chromeos/profiles/profile_helper.h"
17#include "chrome/browser/prefs/pref_service_syncable.h"
18#include "chrome/browser/profiles/profile.h"
19#include "chrome/common/pref_names.h"
20#include "components/user_manager/user.h"
21#include "components/user_manager/user_manager.h"
22#include "google_apis/gaia/gaia_auth_util.h"
23
24namespace chromeos {
25
26namespace {
27
28std::string SanitizeBehaviorValue(const std::string& value) {
29  if (value == MultiProfileUserController::kBehaviorUnrestricted ||
30      value == MultiProfileUserController::kBehaviorPrimaryOnly ||
31      value == MultiProfileUserController::kBehaviorNotAllowed) {
32    return value;
33  }
34
35  return std::string(MultiProfileUserController::kBehaviorUnrestricted);
36}
37
38bool SetUserAllowedReason(
39    MultiProfileUserController::UserAllowedInSessionReason* reason,
40    MultiProfileUserController::UserAllowedInSessionReason value) {
41  if (reason)
42    *reason = value;
43  return value == MultiProfileUserController::ALLOWED;
44}
45
46}  // namespace
47
48// static
49const char MultiProfileUserController::kBehaviorUnrestricted[] = "unrestricted";
50const char MultiProfileUserController::kBehaviorPrimaryOnly[] = "primary-only";
51const char MultiProfileUserController::kBehaviorNotAllowed[] = "not-allowed";
52
53// Note: this policy value is not a real one an is only returned locally for
54// owner users instead of default one kBehaviorUnrestricted.
55const char MultiProfileUserController::kBehaviorOwnerPrimaryOnly[] =
56    "owner-primary-only";
57
58MultiProfileUserController::MultiProfileUserController(
59    MultiProfileUserControllerDelegate* delegate,
60    PrefService* local_state)
61    : delegate_(delegate),
62      local_state_(local_state) {
63}
64
65MultiProfileUserController::~MultiProfileUserController() {}
66
67// static
68void MultiProfileUserController::RegisterPrefs(
69    PrefRegistrySimple* registry) {
70  registry->RegisterDictionaryPref(prefs::kCachedMultiProfileUserBehavior);
71}
72
73// static
74void MultiProfileUserController::RegisterProfilePrefs(
75    user_prefs::PrefRegistrySyncable* registry) {
76  registry->RegisterStringPref(
77      prefs::kMultiProfileUserBehavior,
78      kBehaviorUnrestricted,
79      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
80  registry->RegisterBooleanPref(
81      prefs::kMultiProfileNeverShowIntro,
82      false,
83      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
84  registry->RegisterBooleanPref(
85      prefs::kMultiProfileWarningShowDismissed,
86      false,
87      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
88}
89
90// static
91MultiProfileUserController::UserAllowedInSessionReason
92MultiProfileUserController::GetPrimaryUserPolicy() {
93  user_manager::UserManager* user_manager = user_manager::UserManager::Get();
94  CHECK(user_manager);
95
96  const user_manager::User* user = user_manager->GetPrimaryUser();
97  if (!user)
98    return ALLOWED;
99
100  // Don't allow any secondary profiles if the primary profile is tainted.
101  if (policy::PolicyCertServiceFactory::UsedPolicyCertificates(user->email())) {
102    // Check directly in local_state before checking if the primary user has
103    // a PolicyCertService. His profile may have been tainted previously though
104    // he didn't get a PolicyCertService created for this session.
105    return NOT_ALLOWED_PRIMARY_POLICY_CERT_TAINTED;
106  }
107
108  Profile* profile = ProfileHelper::Get()->GetProfileByUser(user);
109  if (!profile)
110    return ALLOWED;
111
112  // If the primary profile already has policy certificates installed but
113  // hasn't used them yet then it can become tainted at any time during this
114  // session disable secondary profiles in this case too.
115  policy::PolicyCertService* service =
116      policy::PolicyCertServiceFactory::GetForProfile(profile);
117  if (service && service->has_policy_certificates())
118    return NOT_ALLOWED_PRIMARY_POLICY_CERT_TAINTED;
119
120  // No user is allowed if the primary user policy forbids it.
121  const std::string behavior = profile->GetPrefs()->GetString(
122      prefs::kMultiProfileUserBehavior);
123  if (behavior == kBehaviorNotAllowed)
124    return NOT_ALLOWED_PRIMARY_USER_POLICY_FORBIDS;
125
126  return ALLOWED;
127}
128
129bool MultiProfileUserController::IsUserAllowedInSession(
130    const std::string& user_email,
131    MultiProfileUserController::UserAllowedInSessionReason* reason) const {
132  user_manager::UserManager* user_manager = user_manager::UserManager::Get();
133  CHECK(user_manager);
134
135  const user_manager::User* primary_user = user_manager->GetPrimaryUser();
136  std::string primary_user_email;
137  if (primary_user)
138    primary_user_email = primary_user->email();
139
140  // Always allow if there is no primary user or user being checked is the
141  // primary user.
142  if (primary_user_email.empty() || primary_user_email == user_email)
143    return SetUserAllowedReason(reason, ALLOWED);
144
145  // Owner is not allowed to be secondary user.
146  if (user_manager->GetOwnerEmail() == user_email)
147    return SetUserAllowedReason(reason, NOT_ALLOWED_OWNER_AS_SECONDARY);
148
149  // Don't allow profiles potentially tainted by data fetched with policy-pushed
150  // certificates to join a multiprofile session.
151  if (policy::PolicyCertServiceFactory::UsedPolicyCertificates(user_email))
152    return SetUserAllowedReason(reason, NOT_ALLOWED_POLICY_CERT_TAINTED);
153
154  UserAllowedInSessionReason primary_user_policy = GetPrimaryUserPolicy();
155  if (primary_user_policy != ALLOWED)
156    return SetUserAllowedReason(reason, primary_user_policy);
157
158  // The user must have 'unrestricted' policy to be a secondary user.
159  const std::string behavior = GetCachedValue(user_email);
160  return SetUserAllowedReason(
161      reason,
162      behavior == kBehaviorUnrestricted ? ALLOWED : NOT_ALLOWED_POLICY_FORBIDS);
163}
164
165void MultiProfileUserController::StartObserving(Profile* user_profile) {
166  // Profile name could be empty during tests.
167  if (user_profile->GetProfileName().empty())
168    return;
169
170  scoped_ptr<PrefChangeRegistrar> registrar(new PrefChangeRegistrar);
171  registrar->Init(user_profile->GetPrefs());
172  registrar->Add(
173      prefs::kMultiProfileUserBehavior,
174      base::Bind(&MultiProfileUserController::OnUserPrefChanged,
175                 base::Unretained(this),
176                 user_profile));
177  pref_watchers_.push_back(registrar.release());
178
179  OnUserPrefChanged(user_profile);
180}
181
182void MultiProfileUserController::RemoveCachedValues(
183    const std::string& user_email) {
184  DictionaryPrefUpdate update(local_state_,
185                              prefs::kCachedMultiProfileUserBehavior);
186  update->RemoveWithoutPathExpansion(user_email, NULL);
187  policy::PolicyCertServiceFactory::ClearUsedPolicyCertificates(user_email);
188}
189
190std::string MultiProfileUserController::GetCachedValue(
191    const std::string& user_email) const {
192  const base::DictionaryValue* dict =
193      local_state_->GetDictionary(prefs::kCachedMultiProfileUserBehavior);
194  std::string value;
195  if (dict && dict->GetStringWithoutPathExpansion(user_email, &value))
196    return SanitizeBehaviorValue(value);
197
198  return std::string(kBehaviorUnrestricted);
199}
200
201void MultiProfileUserController::SetCachedValue(
202    const std::string& user_email,
203    const std::string& behavior) {
204  DictionaryPrefUpdate update(local_state_,
205                              prefs::kCachedMultiProfileUserBehavior);
206  update->SetStringWithoutPathExpansion(user_email,
207                                        SanitizeBehaviorValue(behavior));
208}
209
210void MultiProfileUserController::CheckSessionUsers() {
211  const user_manager::UserList& users =
212      user_manager::UserManager::Get()->GetLoggedInUsers();
213  for (user_manager::UserList::const_iterator it = users.begin();
214       it != users.end();
215       ++it) {
216    if (!IsUserAllowedInSession((*it)->email(), NULL)) {
217      delegate_->OnUserNotAllowed((*it)->email());
218      return;
219    }
220  }
221}
222
223void MultiProfileUserController::OnUserPrefChanged(
224    Profile* user_profile) {
225  std::string user_email = user_profile->GetProfileName();
226  CHECK(!user_email.empty());
227  user_email = gaia::CanonicalizeEmail(user_email);
228
229  PrefService* prefs = user_profile->GetPrefs();
230  if (prefs->FindPreference(prefs::kMultiProfileUserBehavior)
231          ->IsDefaultValue()) {
232    // Migration code to clear cached default behavior.
233    // TODO(xiyuan): Remove this after M35.
234    DictionaryPrefUpdate update(local_state_,
235                                prefs::kCachedMultiProfileUserBehavior);
236    update->RemoveWithoutPathExpansion(user_email, NULL);
237  } else {
238    const std::string behavior =
239        prefs->GetString(prefs::kMultiProfileUserBehavior);
240    SetCachedValue(user_email, behavior);
241  }
242
243  CheckSessionUsers();
244}
245
246}  // namespace chromeos
247