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 "components/user_manager/user_manager_base.h"
6
7 #include <stddef.h>
8 #include <memory>
9 #include <set>
10 #include <utility>
11
12 #include "base/bind.h"
13 #include "base/callback_helpers.h"
14 #include "base/command_line.h"
15 #include "base/compiler_specific.h"
16 #include "base/format_macros.h"
17 #include "base/location.h"
18 #include "base/logging.h"
19 #include "base/macros.h"
20 #include "base/metrics/histogram_macros.h"
21 #include "base/single_thread_task_runner.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/values.h"
26 #include "components/prefs/pref_registry_simple.h"
27 #include "components/prefs/pref_service.h"
28 #include "components/prefs/scoped_user_pref_update.h"
29 #include "components/user_manager/known_user.h"
30 #include "components/user_manager/remove_user_delegate.h"
31 #include "components/user_manager/user_type.h"
32 #include "google_apis/gaia/gaia_auth_util.h"
33
34 namespace user_manager {
35 namespace {
36
37 // A dictionary that maps user IDs to the displayed name.
38 const char kUserDisplayName[] = "UserDisplayName";
39
40 // A dictionary that maps user IDs to the user's given name.
41 const char kUserGivenName[] = "UserGivenName";
42
43 // A dictionary that maps user IDs to the displayed (non-canonical) emails.
44 const char kUserDisplayEmail[] = "UserDisplayEmail";
45
46 // A dictionary that maps user IDs to OAuth token presence flag.
47 const char kUserOAuthTokenStatus[] = "OAuthTokenStatus";
48
49 // A dictionary that maps user IDs to a flag indicating whether online
50 // authentication against GAIA should be enforced during the next sign-in.
51 const char kUserForceOnlineSignin[] = "UserForceOnlineSignin";
52
53 // A dictionary that maps user ID to the user type.
54 const char kUserType[] = "UserType";
55
56 // A string pref containing the ID of the last user who logged in if it was
57 // a user with gaia account (regular) or an empty string if it was another type
58 // of user (guest, kiosk, public account, etc.).
59 const char kLastLoggedInGaiaUser[] = "LastLoggedInRegularUser";
60
61 // A string pref containing the ID of the last active user.
62 // In case of browser crash, this pref will be used to set active user after
63 // session restore.
64 const char kLastActiveUser[] = "LastActiveUser";
65
66 // Upper bound for a histogram metric reporting the amount of time between
67 // one regular user logging out and a different regular user logging in.
68 const int kLogoutToLoginDelayMaxSec = 1800;
69
70 // This reads integer value from kUserType Local State preference and
71 // interprets it as UserType. It is used in initial users load.
GetStoredUserType(const base::DictionaryValue * prefs_user_types,const AccountId & account_id)72 UserType GetStoredUserType(const base::DictionaryValue* prefs_user_types,
73 const AccountId& account_id) {
74 const base::Value* stored_user_type = prefs_user_types->FindKey(
75 account_id.HasAccountIdKey() ? account_id.GetAccountIdKey()
76 : account_id.GetUserEmail());
77 if (!stored_user_type || !stored_user_type->is_int())
78 return USER_TYPE_REGULAR;
79
80 int int_user_type = stored_user_type->GetInt();
81 if (int_user_type < 0 || int_user_type >= NUM_USER_TYPES ||
82 int_user_type == 2) {
83 LOG(ERROR) << "Bad user type " << int_user_type;
84 return USER_TYPE_REGULAR;
85 }
86 return static_cast<UserType>(int_user_type);
87 }
88
89 } // namespace
90
91 // static
RegisterPrefs(PrefRegistrySimple * registry)92 void UserManagerBase::RegisterPrefs(PrefRegistrySimple* registry) {
93 registry->RegisterListPref(kRegularUsersPref);
94 registry->RegisterStringPref(kLastLoggedInGaiaUser, std::string());
95 registry->RegisterDictionaryPref(kUserDisplayName);
96 registry->RegisterDictionaryPref(kUserGivenName);
97 registry->RegisterDictionaryPref(kUserDisplayEmail);
98 registry->RegisterDictionaryPref(kUserOAuthTokenStatus);
99 registry->RegisterDictionaryPref(kUserForceOnlineSignin);
100 registry->RegisterDictionaryPref(kUserType);
101 registry->RegisterStringPref(kLastActiveUser, std::string());
102
103 known_user::RegisterPrefs(registry);
104 }
105
UserManagerBase(scoped_refptr<base::SingleThreadTaskRunner> task_runner)106 UserManagerBase::UserManagerBase(
107 scoped_refptr<base::SingleThreadTaskRunner> task_runner)
108 : task_runner_(std::move(task_runner)) {}
109
~UserManagerBase()110 UserManagerBase::~UserManagerBase() {
111 // Can't use STLDeleteElements because of the private destructor of User.
112 for (UserList::iterator it = users_.begin(); it != users_.end();
113 it = users_.erase(it)) {
114 DeleteUser(*it);
115 }
116 // These are pointers to the same User instances that were in users_ list.
117 logged_in_users_.clear();
118 lru_logged_in_users_.clear();
119
120 DeleteUser(active_user_);
121 }
122
Shutdown()123 void UserManagerBase::Shutdown() {
124 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
125 }
126
GetUsers() const127 const UserList& UserManagerBase::GetUsers() const {
128 const_cast<UserManagerBase*>(this)->EnsureUsersLoaded();
129 return users_;
130 }
131
GetLoggedInUsers() const132 const UserList& UserManagerBase::GetLoggedInUsers() const {
133 return logged_in_users_;
134 }
135
GetLRULoggedInUsers() const136 const UserList& UserManagerBase::GetLRULoggedInUsers() const {
137 return lru_logged_in_users_;
138 }
139
GetOwnerAccountId() const140 const AccountId& UserManagerBase::GetOwnerAccountId() const {
141 return owner_account_id_;
142 }
143
UserLoggedIn(const AccountId & account_id,const std::string & username_hash,bool browser_restart,bool is_child)144 void UserManagerBase::UserLoggedIn(const AccountId& account_id,
145 const std::string& username_hash,
146 bool browser_restart,
147 bool is_child) {
148 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
149
150 if (!last_session_active_account_id_initialized_) {
151 last_session_active_account_id_ =
152 AccountId::FromUserEmail(GetLocalState()->GetString(kLastActiveUser));
153 last_session_active_account_id_initialized_ = true;
154 }
155
156 User* user = FindUserInListAndModify(account_id);
157
158 const UserType user_type =
159 CalculateUserType(account_id, user, browser_restart, is_child);
160 if (active_user_ && user) {
161 user->set_is_logged_in(true);
162 user->set_username_hash(username_hash);
163 logged_in_users_.push_back(user);
164 lru_logged_in_users_.push_back(user);
165
166 // Reset the new user flag if the user already exists.
167 SetIsCurrentUserNew(false);
168 NotifyUserAddedToSession(user, true /* user switch pending */);
169
170 return;
171 }
172
173 if (IsDemoApp(account_id)) {
174 DemoAccountLoggedIn();
175 } else {
176 switch (user_type) {
177 case USER_TYPE_REGULAR: // fallthrough
178 case USER_TYPE_CHILD: // fallthrough
179 case USER_TYPE_ACTIVE_DIRECTORY:
180 if (account_id != GetOwnerAccountId() && !user &&
181 (AreEphemeralUsersEnabled() || browser_restart)) {
182 RegularUserLoggedInAsEphemeral(account_id, user_type);
183 } else {
184 RegularUserLoggedIn(account_id, user_type);
185 }
186 break;
187
188 case USER_TYPE_GUEST:
189 GuestUserLoggedIn();
190 break;
191
192 case USER_TYPE_PUBLIC_ACCOUNT:
193 PublicAccountUserLoggedIn(
194 user ? user : User::CreatePublicAccountUser(account_id));
195 break;
196
197 case USER_TYPE_SUPERVISED:
198 NOTREACHED() << "Supervised users are not supported anymore";
199 break;
200
201 case USER_TYPE_KIOSK_APP:
202 case USER_TYPE_ARC_KIOSK_APP:
203 case USER_TYPE_WEB_KIOSK_APP:
204 KioskAppLoggedIn(user);
205 break;
206
207 default:
208 NOTREACHED() << "Unhandled usert type " << user_type;
209 }
210 }
211
212 DCHECK(active_user_);
213 active_user_->set_is_logged_in(true);
214 active_user_->set_is_active(true);
215 active_user_->set_username_hash(username_hash);
216
217 logged_in_users_.push_back(active_user_);
218 SetLRUUser(active_user_);
219
220 if (!primary_user_) {
221 primary_user_ = active_user_;
222 if (primary_user_->HasGaiaAccount())
223 SendGaiaUserLoginMetrics(account_id);
224 } else if (primary_user_ != active_user_) {
225 // This is only needed for tests where a new user session is created
226 // for non-existent user. The new user is created and automatically set
227 // to active and there will be no pending user switch in such case.
228 SetIsCurrentUserNew(true);
229 NotifyUserAddedToSession(active_user_, false /* user switch pending */);
230 }
231
232 UMA_HISTOGRAM_ENUMERATION(
233 "UserManager.LoginUserType", active_user_->GetType(), NUM_USER_TYPES);
234
235 GetLocalState()->SetString(
236 kLastLoggedInGaiaUser,
237 active_user_->HasGaiaAccount() ? account_id.GetUserEmail() : "");
238
239 NotifyOnLogin();
240 PerformPostUserLoggedInActions(browser_restart);
241 }
242
SwitchActiveUser(const AccountId & account_id)243 void UserManagerBase::SwitchActiveUser(const AccountId& account_id) {
244 User* user = FindUserAndModify(account_id);
245 if (!user) {
246 NOTREACHED() << "Switching to a non-existing user";
247 return;
248 }
249 if (user == active_user_) {
250 NOTREACHED() << "Switching to a user who is already active";
251 return;
252 }
253 if (!user->is_logged_in()) {
254 NOTREACHED() << "Switching to a user that is not logged in";
255 return;
256 }
257 if (!user->HasGaiaAccount()) {
258 NOTREACHED() <<
259 "Switching to a user without gaia account (non-regular one)";
260 return;
261 }
262 if (user->username_hash().empty()) {
263 NOTREACHED() << "Switching to a user that doesn't have username_hash set";
264 return;
265 }
266
267 DCHECK(active_user_);
268 active_user_->set_is_active(false);
269 user->set_is_active(true);
270 active_user_ = user;
271
272 // Move the user to the front.
273 SetLRUUser(active_user_);
274
275 NotifyActiveUserHashChanged(active_user_->username_hash());
276 NotifyActiveUserChanged(active_user_);
277 CallUpdateLoginState();
278 }
279
SwitchToLastActiveUser()280 void UserManagerBase::SwitchToLastActiveUser() {
281 if (!last_session_active_account_id_.is_valid())
282 return;
283
284 if (AccountId::FromUserEmail(
285 GetActiveUser()->GetAccountId().GetUserEmail()) !=
286 last_session_active_account_id_)
287 SwitchActiveUser(last_session_active_account_id_);
288
289 // Make sure that this function gets run only once.
290 last_session_active_account_id_.clear();
291 }
292
OnSessionStarted()293 void UserManagerBase::OnSessionStarted() {
294 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
295
296 CallUpdateLoginState();
297 GetLocalState()->CommitPendingWrite();
298 }
299
RemoveUser(const AccountId & account_id,RemoveUserDelegate * delegate)300 void UserManagerBase::RemoveUser(const AccountId& account_id,
301 RemoveUserDelegate* delegate) {
302 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
303
304 if (!CanUserBeRemoved(FindUser(account_id)))
305 return;
306
307 RemoveUserInternal(account_id, delegate);
308 }
309
RemoveUserInternal(const AccountId & account_id,RemoveUserDelegate * delegate)310 void UserManagerBase::RemoveUserInternal(const AccountId& account_id,
311 RemoveUserDelegate* delegate) {
312 RemoveNonOwnerUserInternal(account_id, delegate);
313 }
314
RemoveNonOwnerUserInternal(const AccountId & account_id,RemoveUserDelegate * delegate)315 void UserManagerBase::RemoveNonOwnerUserInternal(const AccountId& account_id,
316 RemoveUserDelegate* delegate) {
317 // If account_id points to AccountId in User object, it will become deleted
318 // after RemoveUserFromList(), which could lead to use-after-free in observer.
319 // TODO(https://crbug.com/928534): Update user removal flow to prevent this.
320 const AccountId account_id_copy(account_id);
321
322 if (delegate)
323 delegate->OnBeforeUserRemoved(account_id);
324 AsyncRemoveCryptohome(account_id);
325 RemoveUserFromList(account_id);
326
327 if (delegate)
328 delegate->OnUserRemoved(account_id_copy);
329 }
330
RemoveUserFromList(const AccountId & account_id)331 void UserManagerBase::RemoveUserFromList(const AccountId& account_id) {
332 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
333 RemoveNonCryptohomeData(account_id);
334 known_user::RemovePrefs(account_id);
335 if (user_loading_stage_ == STAGE_LOADED) {
336 // After the User object is deleted from memory in DeleteUser() here,
337 // the account_id reference will be invalid if the reference points
338 // to the account_id in the User object.
339 DeleteUser(
340 RemoveRegularOrSupervisedUserFromList(account_id, true /* notify */));
341 } else {
342 NOTREACHED() << "Users are not loaded yet.";
343 return;
344 }
345
346 // Make sure that new data is persisted to Local State.
347 GetLocalState()->CommitPendingWrite();
348 }
349
IsKnownUser(const AccountId & account_id) const350 bool UserManagerBase::IsKnownUser(const AccountId& account_id) const {
351 return FindUser(account_id) != nullptr;
352 }
353
FindUser(const AccountId & account_id) const354 const User* UserManagerBase::FindUser(const AccountId& account_id) const {
355 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
356 if (active_user_ && active_user_->GetAccountId() == account_id)
357 return active_user_;
358 return FindUserInList(account_id);
359 }
360
FindUserAndModify(const AccountId & account_id)361 User* UserManagerBase::FindUserAndModify(const AccountId& account_id) {
362 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
363 if (active_user_ && active_user_->GetAccountId() == account_id)
364 return active_user_;
365 return FindUserInListAndModify(account_id);
366 }
367
GetActiveUser() const368 const User* UserManagerBase::GetActiveUser() const {
369 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
370 return active_user_;
371 }
372
GetActiveUser()373 User* UserManagerBase::GetActiveUser() {
374 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
375 return active_user_;
376 }
377
GetPrimaryUser() const378 const User* UserManagerBase::GetPrimaryUser() const {
379 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
380 return primary_user_;
381 }
382
SaveUserOAuthStatus(const AccountId & account_id,User::OAuthTokenStatus oauth_token_status)383 void UserManagerBase::SaveUserOAuthStatus(
384 const AccountId& account_id,
385 User::OAuthTokenStatus oauth_token_status) {
386 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
387
388 DVLOG(1) << "Saving user OAuth token status in Local State";
389 User* user = FindUserAndModify(account_id);
390 if (user)
391 user->set_oauth_token_status(oauth_token_status);
392
393 // Do not update local state if data stored or cached outside the user's
394 // cryptohome is to be treated as ephemeral.
395 if (IsUserNonCryptohomeDataEphemeral(account_id))
396 return;
397
398 {
399 DictionaryPrefUpdate oauth_status_update(GetLocalState(),
400 kUserOAuthTokenStatus);
401 oauth_status_update->SetKey(
402 account_id.GetUserEmail(),
403 base::Value(static_cast<int>(oauth_token_status)));
404 }
405 GetLocalState()->CommitPendingWrite();
406 }
407
SaveForceOnlineSignin(const AccountId & account_id,bool force_online_signin)408 void UserManagerBase::SaveForceOnlineSignin(const AccountId& account_id,
409 bool force_online_signin) {
410 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
411
412 User* const user = FindUserAndModify(account_id);
413 if (user)
414 user->set_force_online_signin(force_online_signin);
415
416 // Do not update local state if data stored or cached outside the user's
417 // cryptohome is to be treated as ephemeral.
418 if (IsUserNonCryptohomeDataEphemeral(account_id))
419 return;
420
421 {
422 DictionaryPrefUpdate force_online_update(GetLocalState(),
423 kUserForceOnlineSignin);
424 force_online_update->SetKey(account_id.GetUserEmail(),
425 base::Value(force_online_signin));
426 }
427 GetLocalState()->CommitPendingWrite();
428 }
429
SaveUserDisplayName(const AccountId & account_id,const base::string16 & display_name)430 void UserManagerBase::SaveUserDisplayName(const AccountId& account_id,
431 const base::string16& display_name) {
432 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
433
434 if (User* user = FindUserAndModify(account_id)) {
435 user->set_display_name(display_name);
436
437 // Do not update local state if data stored or cached outside the user's
438 // cryptohome is to be treated as ephemeral.
439 if (!IsUserNonCryptohomeDataEphemeral(account_id)) {
440 DictionaryPrefUpdate display_name_update(GetLocalState(),
441 kUserDisplayName);
442 display_name_update->SetKey(account_id.GetUserEmail(),
443 base::Value(display_name));
444 }
445 }
446 }
447
GetUserDisplayName(const AccountId & account_id) const448 base::string16 UserManagerBase::GetUserDisplayName(
449 const AccountId& account_id) const {
450 const User* user = FindUser(account_id);
451 return user ? user->display_name() : base::string16();
452 }
453
SaveUserDisplayEmail(const AccountId & account_id,const std::string & display_email)454 void UserManagerBase::SaveUserDisplayEmail(const AccountId& account_id,
455 const std::string& display_email) {
456 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
457
458 User* user = FindUserAndModify(account_id);
459 if (!user) {
460 LOG(ERROR) << "User not found: " << account_id.GetUserEmail();
461 return; // Ignore if there is no such user.
462 }
463
464 user->set_display_email(display_email);
465
466 // Do not update local state if data stored or cached outside the user's
467 // cryptohome is to be treated as ephemeral.
468 if (IsUserNonCryptohomeDataEphemeral(account_id))
469 return;
470
471 DictionaryPrefUpdate display_email_update(GetLocalState(), kUserDisplayEmail);
472 display_email_update->SetKey(account_id.GetUserEmail(),
473 base::Value(display_email));
474 }
475
SaveUserType(const User * user)476 void UserManagerBase::SaveUserType(const User* user) {
477 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
478
479 CHECK(user);
480 // Do not update local state if data stored or cached outside the user's
481 // cryptohome is to be treated as ephemeral.
482 if (IsUserNonCryptohomeDataEphemeral(user->GetAccountId()))
483 return;
484
485 DictionaryPrefUpdate user_type_update(GetLocalState(), kUserType);
486 user_type_update->SetKey(user->GetAccountId().GetAccountIdKey(),
487 base::Value(static_cast<int>(user->GetType())));
488 GetLocalState()->CommitPendingWrite();
489 }
490
UpdateUserAccountData(const AccountId & account_id,const UserAccountData & account_data)491 void UserManagerBase::UpdateUserAccountData(
492 const AccountId& account_id,
493 const UserAccountData& account_data) {
494 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
495
496 SaveUserDisplayName(account_id, account_data.display_name());
497
498 if (User* user = FindUserAndModify(account_id)) {
499 base::string16 given_name = account_data.given_name();
500 user->set_given_name(given_name);
501 if (!IsUserNonCryptohomeDataEphemeral(account_id)) {
502 DictionaryPrefUpdate given_name_update(GetLocalState(), kUserGivenName);
503 given_name_update->SetKey(account_id.GetUserEmail(),
504 base::Value(given_name));
505 }
506 }
507
508 UpdateUserAccountLocale(account_id, account_data.locale());
509 }
510
ParseUserList(const base::ListValue & users_list,const std::set<AccountId> & existing_users,std::vector<AccountId> * users_vector,std::set<AccountId> * users_set)511 void UserManagerBase::ParseUserList(const base::ListValue& users_list,
512 const std::set<AccountId>& existing_users,
513 std::vector<AccountId>* users_vector,
514 std::set<AccountId>* users_set) {
515 users_vector->clear();
516 users_set->clear();
517 for (size_t i = 0; i < users_list.GetSize(); ++i) {
518 std::string email;
519 if (!users_list.GetString(i, &email) || email.empty()) {
520 LOG(ERROR) << "Corrupt entry in user list at index " << i << ".";
521 continue;
522 }
523
524 const AccountId account_id = known_user::GetAccountId(
525 email, std::string() /* id */, AccountType::UNKNOWN);
526
527 if (existing_users.find(account_id) != existing_users.end() ||
528 !users_set->insert(account_id).second) {
529 LOG(ERROR) << "Duplicate user: " << email;
530 continue;
531 }
532 users_vector->push_back(account_id);
533 }
534 }
535
IsCurrentUserOwner() const536 bool UserManagerBase::IsCurrentUserOwner() const {
537 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
538 return !owner_account_id_.empty() && active_user_ &&
539 active_user_->GetAccountId() == owner_account_id_;
540 }
541
IsCurrentUserNew() const542 bool UserManagerBase::IsCurrentUserNew() const {
543 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
544 return is_current_user_new_;
545 }
546
IsCurrentUserNonCryptohomeDataEphemeral() const547 bool UserManagerBase::IsCurrentUserNonCryptohomeDataEphemeral() const {
548 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
549 return IsUserLoggedIn() &&
550 IsUserNonCryptohomeDataEphemeral(GetActiveUser()->GetAccountId());
551 }
552
IsCurrentUserCryptohomeDataEphemeral() const553 bool UserManagerBase::IsCurrentUserCryptohomeDataEphemeral() const {
554 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
555 return IsUserLoggedIn() &&
556 IsUserCryptohomeDataEphemeral(GetActiveUser()->GetAccountId());
557 }
558
CanCurrentUserLock() const559 bool UserManagerBase::CanCurrentUserLock() const {
560 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
561 return IsUserLoggedIn() && active_user_->can_lock();
562 }
563
IsUserLoggedIn() const564 bool UserManagerBase::IsUserLoggedIn() const {
565 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
566 return active_user_;
567 }
568
IsLoggedInAsUserWithGaiaAccount() const569 bool UserManagerBase::IsLoggedInAsUserWithGaiaAccount() const {
570 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
571 return IsUserLoggedIn() && active_user_->HasGaiaAccount();
572 }
573
IsLoggedInAsChildUser() const574 bool UserManagerBase::IsLoggedInAsChildUser() const {
575 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
576 return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_CHILD;
577 }
578
IsLoggedInAsPublicAccount() const579 bool UserManagerBase::IsLoggedInAsPublicAccount() const {
580 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
581 return IsUserLoggedIn() &&
582 active_user_->GetType() == USER_TYPE_PUBLIC_ACCOUNT;
583 }
584
IsLoggedInAsGuest() const585 bool UserManagerBase::IsLoggedInAsGuest() const {
586 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
587 return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_GUEST;
588 }
589
IsLoggedInAsKioskApp() const590 bool UserManagerBase::IsLoggedInAsKioskApp() const {
591 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
592 return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_KIOSK_APP;
593 }
594
IsLoggedInAsArcKioskApp() const595 bool UserManagerBase::IsLoggedInAsArcKioskApp() const {
596 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
597 return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_ARC_KIOSK_APP;
598 }
599
IsLoggedInAsWebKioskApp() const600 bool UserManagerBase::IsLoggedInAsWebKioskApp() const {
601 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
602 return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_WEB_KIOSK_APP;
603 }
604
IsLoggedInAsAnyKioskApp() const605 bool UserManagerBase::IsLoggedInAsAnyKioskApp() const {
606 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
607 return IsUserLoggedIn() && active_user_->IsKioskType();
608 }
609
IsLoggedInAsStub() const610 bool UserManagerBase::IsLoggedInAsStub() const {
611 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
612 return IsUserLoggedIn() && IsStubAccountId(active_user_->GetAccountId());
613 }
614
IsUserNonCryptohomeDataEphemeral(const AccountId & account_id) const615 bool UserManagerBase::IsUserNonCryptohomeDataEphemeral(
616 const AccountId& account_id) const {
617 // Data belonging to the guest and stub users is always ephemeral.
618 if (IsGuestAccountId(account_id) || IsStubAccountId(account_id))
619 return true;
620
621 // Data belonging to the owner, anyone found on the user list and obsolete
622 // device local accounts whose data has not been removed yet is not ephemeral.
623 if (account_id == GetOwnerAccountId() || UserExistsInList(account_id) ||
624 IsDeviceLocalAccountMarkedForRemoval(account_id)) {
625 return false;
626 }
627
628 // Data belonging to the currently logged-in user is ephemeral when:
629 // a) The user logged into a regular gaia account while the ephemeral users
630 // policy was enabled.
631 // - or -
632 // b) The user logged into any other account type.
633 if (IsUserLoggedIn() && (account_id == GetActiveUser()->GetAccountId()) &&
634 (is_current_user_ephemeral_regular_user_ ||
635 !IsLoggedInAsUserWithGaiaAccount())) {
636 return true;
637 }
638
639 // Data belonging to any other user is ephemeral when:
640 // a) Going through the regular login flow and the ephemeral users policy is
641 // enabled.
642 // - or -
643 // b) The browser is restarting after a crash.
644 return AreEphemeralUsersEnabled() || HasBrowserRestarted();
645 }
646
IsUserCryptohomeDataEphemeral(const AccountId & account_id) const647 bool UserManagerBase::IsUserCryptohomeDataEphemeral(
648 const AccountId& account_id) const {
649 // Don't consider stub users data as ephemeral.
650 if (IsStubAccountId(account_id))
651 return false;
652
653 // Data belonging to the guest and demo users is always ephemeral.
654 if (IsGuestAccountId(account_id) || IsDemoApp(account_id))
655 return true;
656
657 // Data belonging to the public accounts is always ephemeral.
658 const User* user = FindUser(account_id);
659 if (user && user->GetType() == USER_TYPE_PUBLIC_ACCOUNT)
660 return true;
661
662 // Ephemeral users.
663 if (AreEphemeralUsersEnabled() && user &&
664 user->GetType() == USER_TYPE_REGULAR &&
665 FindUserInList(account_id) == nullptr) {
666 return true;
667 }
668
669 return false;
670 }
671
AddObserver(UserManager::Observer * obs)672 void UserManagerBase::AddObserver(UserManager::Observer* obs) {
673 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
674 observer_list_.AddObserver(obs);
675 }
676
RemoveObserver(UserManager::Observer * obs)677 void UserManagerBase::RemoveObserver(UserManager::Observer* obs) {
678 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
679 observer_list_.RemoveObserver(obs);
680 }
681
AddSessionStateObserver(UserManager::UserSessionStateObserver * obs)682 void UserManagerBase::AddSessionStateObserver(
683 UserManager::UserSessionStateObserver* obs) {
684 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
685 session_state_observer_list_.AddObserver(obs);
686 }
687
RemoveSessionStateObserver(UserManager::UserSessionStateObserver * obs)688 void UserManagerBase::RemoveSessionStateObserver(
689 UserManager::UserSessionStateObserver* obs) {
690 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
691 session_state_observer_list_.RemoveObserver(obs);
692 }
693
NotifyLocalStateChanged()694 void UserManagerBase::NotifyLocalStateChanged() {
695 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
696 for (auto& observer : observer_list_)
697 observer.LocalStateChanged(this);
698 }
699
NotifyUserImageChanged(const User & user)700 void UserManagerBase::NotifyUserImageChanged(const User& user) {
701 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
702 for (auto& observer : observer_list_)
703 observer.OnUserImageChanged(user);
704 }
705
NotifyUserProfileImageUpdateFailed(const User & user)706 void UserManagerBase::NotifyUserProfileImageUpdateFailed(const User& user) {
707 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
708 for (auto& observer : observer_list_)
709 observer.OnUserProfileImageUpdateFailed(user);
710 }
711
NotifyUserProfileImageUpdated(const User & user,const gfx::ImageSkia & profile_image)712 void UserManagerBase::NotifyUserProfileImageUpdated(
713 const User& user,
714 const gfx::ImageSkia& profile_image) {
715 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
716 for (auto& observer : observer_list_)
717 observer.OnUserProfileImageUpdated(user, profile_image);
718 }
719
NotifyUsersSignInConstraintsChanged()720 void UserManagerBase::NotifyUsersSignInConstraintsChanged() {
721 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
722 for (auto& observer : observer_list_)
723 observer.OnUsersSignInConstraintsChanged();
724 }
725
CanUserBeRemoved(const User * user) const726 bool UserManagerBase::CanUserBeRemoved(const User* user) const {
727 // Only regular users are allowed to be manually removed.
728 if (!user || !(user->HasGaiaAccount() || user->IsActiveDirectoryUser()))
729 return false;
730
731 // Sanity check: we must not remove single user unless it's an enterprise
732 // device. This check may seem redundant at a first sight because
733 // this single user must be an owner and we perform special check later
734 // in order not to remove an owner. However due to non-instant nature of
735 // ownership assignment this later check may sometimes fail.
736 // See http://crosbug.com/12723
737 if (users_.size() < 2 && !IsEnterpriseManaged())
738 return false;
739
740 // Sanity check: do not allow any of the the logged in users to be removed.
741 for (UserList::const_iterator it = logged_in_users_.begin();
742 it != logged_in_users_.end();
743 ++it) {
744 if ((*it)->GetAccountId() == user->GetAccountId())
745 return false;
746 }
747
748 return true;
749 }
750
GetEphemeralUsersEnabled() const751 bool UserManagerBase::GetEphemeralUsersEnabled() const {
752 return ephemeral_users_enabled_;
753 }
754
SetEphemeralUsersEnabled(bool enabled)755 void UserManagerBase::SetEphemeralUsersEnabled(bool enabled) {
756 ephemeral_users_enabled_ = enabled;
757 }
758
SetIsCurrentUserNew(bool is_new)759 void UserManagerBase::SetIsCurrentUserNew(bool is_new) {
760 is_current_user_new_ = is_new;
761 }
762
SetOwnerId(const AccountId & owner_account_id)763 void UserManagerBase::SetOwnerId(const AccountId& owner_account_id) {
764 owner_account_id_ = owner_account_id;
765 CallUpdateLoginState();
766 }
767
GetPendingUserSwitchID() const768 const AccountId& UserManagerBase::GetPendingUserSwitchID() const {
769 return pending_user_switch_;
770 }
771
SetPendingUserSwitchId(const AccountId & account_id)772 void UserManagerBase::SetPendingUserSwitchId(const AccountId& account_id) {
773 pending_user_switch_ = account_id;
774 }
775
EnsureUsersLoaded()776 void UserManagerBase::EnsureUsersLoaded() {
777 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
778 if (!GetLocalState())
779 return;
780
781 if (user_loading_stage_ != STAGE_NOT_LOADED)
782 return;
783 user_loading_stage_ = STAGE_LOADING;
784
785 PrefService* local_state = GetLocalState();
786 const base::ListValue* prefs_regular_users =
787 local_state->GetList(kRegularUsersPref);
788
789 const base::DictionaryValue* prefs_display_names =
790 local_state->GetDictionary(kUserDisplayName);
791 const base::DictionaryValue* prefs_given_names =
792 local_state->GetDictionary(kUserGivenName);
793 const base::DictionaryValue* prefs_display_emails =
794 local_state->GetDictionary(kUserDisplayEmail);
795 const base::DictionaryValue* prefs_user_types =
796 local_state->GetDictionary(kUserType);
797
798 // Load public sessions first.
799 std::set<AccountId> device_local_accounts_set;
800 LoadDeviceLocalAccounts(&device_local_accounts_set);
801
802 // Load regular users and supervised users.
803 std::vector<AccountId> regular_users;
804 std::set<AccountId> regular_users_set;
805 ParseUserList(*prefs_regular_users, device_local_accounts_set, ®ular_users,
806 ®ular_users_set);
807 for (std::vector<AccountId>::const_iterator it = regular_users.begin();
808 it != regular_users.end(); ++it) {
809 if (IsSupervisedAccountId(*it))
810 continue;
811 User* user =
812 User::CreateRegularUser(*it, GetStoredUserType(prefs_user_types, *it));
813 user->set_oauth_token_status(LoadUserOAuthStatus(*it));
814 user->set_force_online_signin(LoadForceOnlineSignin(*it));
815 user->set_using_saml(known_user::IsUsingSAML(*it));
816 users_.push_back(user);
817 }
818
819 for (auto* user : users_) {
820 auto& account_id = user->GetAccountId();
821 base::string16 display_name;
822 if (prefs_display_names->GetStringWithoutPathExpansion(
823 account_id.GetUserEmail(), &display_name)) {
824 user->set_display_name(display_name);
825 }
826
827 base::string16 given_name;
828 if (prefs_given_names->GetStringWithoutPathExpansion(
829 account_id.GetUserEmail(), &given_name)) {
830 user->set_given_name(given_name);
831 }
832
833 std::string display_email;
834 if (prefs_display_emails->GetStringWithoutPathExpansion(
835 account_id.GetUserEmail(), &display_email)) {
836 user->set_display_email(display_email);
837 }
838 }
839 user_loading_stage_ = STAGE_LOADED;
840
841 PerformPostUserListLoadingActions();
842 }
843
GetUsersAndModify()844 UserList& UserManagerBase::GetUsersAndModify() {
845 EnsureUsersLoaded();
846 return users_;
847 }
848
FindUserInList(const AccountId & account_id) const849 const User* UserManagerBase::FindUserInList(const AccountId& account_id) const {
850 const UserList& users = GetUsers();
851 for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) {
852 if ((*it)->GetAccountId() == account_id)
853 return *it;
854 }
855 return nullptr;
856 }
857
UserExistsInList(const AccountId & account_id) const858 bool UserManagerBase::UserExistsInList(const AccountId& account_id) const {
859 const base::ListValue* user_list =
860 GetLocalState()->GetList(kRegularUsersPref);
861 for (size_t i = 0; i < user_list->GetSize(); ++i) {
862 std::string email;
863 if (user_list->GetString(i, &email) && (account_id.GetUserEmail() == email))
864 return true;
865 }
866 return false;
867 }
868
FindUserInListAndModify(const AccountId & account_id)869 User* UserManagerBase::FindUserInListAndModify(const AccountId& account_id) {
870 UserList& users = GetUsersAndModify();
871 for (UserList::iterator it = users.begin(); it != users.end(); ++it) {
872 if ((*it)->GetAccountId() == account_id)
873 return *it;
874 }
875 return nullptr;
876 }
877
GuestUserLoggedIn()878 void UserManagerBase::GuestUserLoggedIn() {
879 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
880 active_user_ = User::CreateGuestUser(GetGuestAccountId());
881 }
882
AddUserRecord(User * user)883 void UserManagerBase::AddUserRecord(User* user) {
884 // Add the user to the front of the user list.
885 ListPrefUpdate prefs_users_update(GetLocalState(), kRegularUsersPref);
886 prefs_users_update->Insert(
887 0, std::make_unique<base::Value>(user->GetAccountId().GetUserEmail()));
888 users_.insert(users_.begin(), user);
889 }
890
RegularUserLoggedIn(const AccountId & account_id,const UserType user_type)891 void UserManagerBase::RegularUserLoggedIn(const AccountId& account_id,
892 const UserType user_type) {
893 // Remove the user from the user list.
894 active_user_ =
895 RemoveRegularOrSupervisedUserFromList(account_id, false /* notify */);
896
897 if (active_user_ && active_user_->GetType() != user_type)
898 active_user_->UpdateType(user_type);
899
900 // If the user was not found on the user list, create a new user.
901 SetIsCurrentUserNew(!active_user_);
902 if (IsCurrentUserNew()) {
903 active_user_ = User::CreateRegularUser(account_id, user_type);
904 SaveUserType(active_user_);
905
906 active_user_->set_oauth_token_status(LoadUserOAuthStatus(account_id));
907 SaveUserDisplayName(active_user_->GetAccountId(),
908 base::UTF8ToUTF16(active_user_->GetAccountName(true)));
909 } else {
910 SaveUserType(active_user_);
911 }
912
913 AddUserRecord(active_user_);
914 known_user::SetIsEphemeralUser(active_user_->GetAccountId(), false);
915
916 // Make sure that new data is persisted to Local State.
917 GetLocalState()->CommitPendingWrite();
918 }
919
RegularUserLoggedInAsEphemeral(const AccountId & account_id,const UserType user_type)920 void UserManagerBase::RegularUserLoggedInAsEphemeral(
921 const AccountId& account_id,
922 const UserType user_type) {
923 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
924 SetIsCurrentUserNew(true);
925 is_current_user_ephemeral_regular_user_ = true;
926 active_user_ = User::CreateRegularUser(account_id, user_type);
927 known_user::SetIsEphemeralUser(active_user_->GetAccountId(), true);
928 }
929
NotifyActiveUserChanged(User * active_user)930 void UserManagerBase::NotifyActiveUserChanged(User* active_user) {
931 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
932 for (auto& observer : session_state_observer_list_)
933 observer.ActiveUserChanged(active_user);
934 }
935
NotifyOnLogin()936 void UserManagerBase::NotifyOnLogin() {
937 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
938
939 NotifyActiveUserHashChanged(active_user_->username_hash());
940 NotifyActiveUserChanged(active_user_);
941 CallUpdateLoginState();
942 }
943
LoadUserOAuthStatus(const AccountId & account_id) const944 User::OAuthTokenStatus UserManagerBase::LoadUserOAuthStatus(
945 const AccountId& account_id) const {
946 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
947
948 const base::DictionaryValue* prefs_oauth_status =
949 GetLocalState()->GetDictionary(kUserOAuthTokenStatus);
950 int oauth_token_status = User::OAUTH_TOKEN_STATUS_UNKNOWN;
951 if (prefs_oauth_status &&
952 prefs_oauth_status->GetIntegerWithoutPathExpansion(
953 account_id.GetUserEmail(), &oauth_token_status)) {
954 User::OAuthTokenStatus status =
955 static_cast<User::OAuthTokenStatus>(oauth_token_status);
956 return status;
957 }
958 return User::OAUTH_TOKEN_STATUS_UNKNOWN;
959 }
960
LoadForceOnlineSignin(const AccountId & account_id) const961 bool UserManagerBase::LoadForceOnlineSignin(const AccountId& account_id) const {
962 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
963
964 const base::DictionaryValue* prefs_force_online =
965 GetLocalState()->GetDictionary(kUserForceOnlineSignin);
966 bool force_online_signin = false;
967 if (prefs_force_online) {
968 prefs_force_online->GetBooleanWithoutPathExpansion(
969 account_id.GetUserEmail(), &force_online_signin);
970 }
971 return force_online_signin;
972 }
973
RemoveNonCryptohomeData(const AccountId & account_id)974 void UserManagerBase::RemoveNonCryptohomeData(const AccountId& account_id) {
975 PrefService* prefs = GetLocalState();
976 DictionaryPrefUpdate prefs_display_name_update(prefs, kUserDisplayName);
977 prefs_display_name_update->RemoveKey(account_id.GetUserEmail());
978
979 DictionaryPrefUpdate prefs_given_name_update(prefs, kUserGivenName);
980 prefs_given_name_update->RemoveKey(account_id.GetUserEmail());
981
982 DictionaryPrefUpdate prefs_display_email_update(prefs, kUserDisplayEmail);
983 prefs_display_email_update->RemoveKey(account_id.GetUserEmail());
984
985 DictionaryPrefUpdate prefs_oauth_update(prefs, kUserOAuthTokenStatus);
986 prefs_oauth_update->RemoveKey(account_id.GetUserEmail());
987
988 DictionaryPrefUpdate prefs_force_online_update(prefs, kUserForceOnlineSignin);
989 prefs_force_online_update->RemoveKey(account_id.GetUserEmail());
990
991 known_user::RemovePrefs(account_id);
992
993 const AccountId last_active_user =
994 AccountId::FromUserEmail(GetLocalState()->GetString(kLastActiveUser));
995 if (account_id == last_active_user)
996 GetLocalState()->SetString(kLastActiveUser, std::string());
997 }
998
RemoveRegularOrSupervisedUserFromList(const AccountId & account_id,bool notify)999 User* UserManagerBase::RemoveRegularOrSupervisedUserFromList(
1000 const AccountId& account_id,
1001 bool notify) {
1002 ListPrefUpdate prefs_users_update(GetLocalState(), kRegularUsersPref);
1003 prefs_users_update->Clear();
1004 User* user = nullptr;
1005 for (UserList::iterator it = users_.begin(); it != users_.end();) {
1006 if ((*it)->GetAccountId() == account_id) {
1007 user = *it;
1008 it = users_.erase(it);
1009 } else {
1010 if ((*it)->HasGaiaAccount() || (*it)->IsActiveDirectoryUser()) {
1011 const std::string user_email = (*it)->GetAccountId().GetUserEmail();
1012 prefs_users_update->AppendString(user_email);
1013 }
1014 ++it;
1015 }
1016 }
1017 if (notify) {
1018 OnUserRemoved(account_id);
1019 NotifyLocalStateChanged();
1020 }
1021 return user;
1022 }
1023
NotifyUserAddedToSession(const User * added_user,bool user_switch_pending)1024 void UserManagerBase::NotifyUserAddedToSession(const User* added_user,
1025 bool user_switch_pending) {
1026 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
1027 for (auto& observer : session_state_observer_list_)
1028 observer.UserAddedToSession(added_user);
1029 }
1030
NotifyActiveUserHashChanged(const std::string & hash)1031 void UserManagerBase::NotifyActiveUserHashChanged(const std::string& hash) {
1032 DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
1033 for (auto& observer : session_state_observer_list_)
1034 observer.ActiveUserHashChanged(hash);
1035 }
1036
Initialize()1037 void UserManagerBase::Initialize() {
1038 UserManager::Initialize();
1039 if (!HasBrowserRestarted())
1040 known_user::CleanEphemeralUsers();
1041 CallUpdateLoginState();
1042 }
1043
CallUpdateLoginState()1044 void UserManagerBase::CallUpdateLoginState() {
1045 UpdateLoginState(active_user_, primary_user_, IsCurrentUserOwner());
1046 }
1047
SetLRUUser(User * user)1048 void UserManagerBase::SetLRUUser(User* user) {
1049 GetLocalState()->SetString(kLastActiveUser,
1050 user->GetAccountId().GetUserEmail());
1051 GetLocalState()->CommitPendingWrite();
1052
1053 UserList::iterator it =
1054 std::find(lru_logged_in_users_.begin(), lru_logged_in_users_.end(), user);
1055 if (it != lru_logged_in_users_.end())
1056 lru_logged_in_users_.erase(it);
1057 lru_logged_in_users_.insert(lru_logged_in_users_.begin(), user);
1058 }
1059
SendGaiaUserLoginMetrics(const AccountId & account_id)1060 void UserManagerBase::SendGaiaUserLoginMetrics(const AccountId& account_id) {
1061 // If this isn't the first time Chrome was run after the system booted,
1062 // assume that Chrome was restarted because a previous session ended.
1063 if (IsFirstExecAfterBoot())
1064 return;
1065
1066 const std::string last_email =
1067 GetLocalState()->GetString(kLastLoggedInGaiaUser);
1068 const base::TimeDelta time_to_login =
1069 base::TimeTicks::Now() - manager_creation_time_;
1070 if (!last_email.empty() &&
1071 account_id != AccountId::FromUserEmail(last_email) &&
1072 time_to_login.InSeconds() <= kLogoutToLoginDelayMaxSec) {
1073 UMA_HISTOGRAM_CUSTOM_COUNTS("UserManager.LogoutToLoginDelay",
1074 time_to_login.InSeconds(), 1,
1075 kLogoutToLoginDelayMaxSec, 50);
1076 }
1077 }
1078
UpdateUserAccountLocale(const AccountId & account_id,const std::string & locale)1079 void UserManagerBase::UpdateUserAccountLocale(const AccountId& account_id,
1080 const std::string& locale) {
1081 std::unique_ptr<std::string> resolved_locale(new std::string());
1082 if (!locale.empty() && locale != GetApplicationLocale()) {
1083 // std::move will nullptr out |resolved_locale|, so cache the underlying
1084 // ptr.
1085 std::string* raw_resolved_locale = resolved_locale.get();
1086 ScheduleResolveLocale(
1087 locale,
1088 base::BindOnce(&UserManagerBase::DoUpdateAccountLocale,
1089 weak_factory_.GetWeakPtr(), account_id,
1090 std::move(resolved_locale)),
1091 raw_resolved_locale);
1092 } else {
1093 resolved_locale.reset(new std::string(locale));
1094 DoUpdateAccountLocale(account_id, std::move(resolved_locale));
1095 }
1096 }
1097
DoUpdateAccountLocale(const AccountId & account_id,std::unique_ptr<std::string> resolved_locale)1098 void UserManagerBase::DoUpdateAccountLocale(
1099 const AccountId& account_id,
1100 std::unique_ptr<std::string> resolved_locale) {
1101 User* user = FindUserAndModify(account_id);
1102 if (user && resolved_locale)
1103 user->SetAccountLocale(*resolved_locale);
1104 }
1105
DeleteUser(User * user)1106 void UserManagerBase::DeleteUser(User* user) {
1107 const bool is_active_user = (user == active_user_);
1108 delete user;
1109 if (is_active_user)
1110 active_user_ = nullptr;
1111 }
1112
1113 } // namespace user_manager
1114