diff options
author | nkostylev@chromium.org <nkostylev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-15 09:22:58 +0000 |
---|---|---|
committer | nkostylev@chromium.org <nkostylev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-15 09:24:19 +0000 |
commit | 4d3907860d4d46a98f07c59ba69125be0faab404 (patch) | |
tree | 305014dd85fd28832ebc1b5acda4865747583241 /components/user_manager | |
parent | 502c06e7deef18b28a0795222521aeffe83c209d (diff) | |
download | chromium_src-4d3907860d4d46a98f07c59ba69125be0faab404.zip chromium_src-4d3907860d4d46a98f07c59ba69125be0faab404.tar.gz chromium_src-4d3907860d4d46a98f07c59ba69125be0faab404.tar.bz2 |
[cros] user_manager component - move UserManagerBase and UserManager
* Move UserManager test helper classes in their own files
* Move RemoveUserDelegate to user_manager component
* Move UserManager ownership to BrowserProcessPlatformPart
* UserManager::Get() still stays but instance is explicitly initialized/cleared with instance->Initialize()|Destroy()
* ScopedUserManagerEnabler / ScopedTestUserManager classes still work
* Introduce ChromeUserManager base class (impl: ChromeUserManagerImpl)
* ChromeUserManager adds getters for MultiProfileUserController, UserImageManager, SupervisedUserManager.
All clients that were using these methods now access ChromeUserManager instance through ChromeUserManager::Get().
* FakeUserManager/MockUserManager implement ChromeUserManager
* Move *UserFlow() methods out of UserManager to ChromeUserManager
BUG=387614
TBR=thestig,benwells
Review URL: https://codereview.chromium.org/444903002
Cr-Commit-Position: refs/heads/master@{#289827}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@289827 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components/user_manager')
-rw-r--r-- | components/user_manager/DEPS | 4 | ||||
-rw-r--r-- | components/user_manager/remove_user_delegate.h | 24 | ||||
-rw-r--r-- | components/user_manager/user.h | 9 | ||||
-rw-r--r-- | components/user_manager/user_manager.cc | 85 | ||||
-rw-r--r-- | components/user_manager/user_manager.h | 316 | ||||
-rw-r--r-- | components/user_manager/user_manager_base.cc | 1026 | ||||
-rw-r--r-- | components/user_manager/user_manager_base.h | 387 |
7 files changed, 1847 insertions, 4 deletions
diff --git a/components/user_manager/DEPS b/components/user_manager/DEPS index 2cbd1db..f3fe77c 100644 --- a/components/user_manager/DEPS +++ b/components/user_manager/DEPS @@ -1,5 +1,9 @@ include_rules = [ +"+chromeos/chromeos_switches.h", +"+chromeos/cryptohome/async_method_caller.h", +"+chromeos/login/login_state.h", "+chromeos/login/user_names.h", +"+components/session_manager/core/session_manager.h", "+google_apis/gaia/gaia_auth_util.h", "+grit/ui_chromeos_resources.h", "+grit/ui_chromeos_strings.h", diff --git a/components/user_manager/remove_user_delegate.h b/components/user_manager/remove_user_delegate.h new file mode 100644 index 0000000..1077bda --- /dev/null +++ b/components/user_manager/remove_user_delegate.h @@ -0,0 +1,24 @@ +// Copyright 2014 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. + +#ifndef COMPONENTS_USER_MANAGER_REMOVE_USER_DELEGATE_H_ +#define COMPONENTS_USER_MANAGER_REMOVE_USER_DELEGATE_H_ + +#include "components/user_manager/user_manager_export.h" + +namespace user_manager { + +// Delegate to be used with |UserManager::RemoveUser|. +class USER_MANAGER_EXPORT RemoveUserDelegate { + public: + // Called right before actual user removal process is initiated. + virtual void OnBeforeUserRemoved(const std::string& username) = 0; + + // Called right after user removal process has been initiated. + virtual void OnUserRemoved(const std::string& username) = 0; +}; + +} // namespace user_manager + +#endif // COMPONENTS_USER_MANAGER_REMOVE_USER_DELEGATE_H_ diff --git a/components/user_manager/user.h b/components/user_manager/user.h index 23e8230..5ff8a10 100644 --- a/components/user_manager/user.h +++ b/components/user_manager/user.h @@ -18,19 +18,20 @@ #include "ui/gfx/image/image_skia.h" namespace chromeos { -class ChromeUserManager; +class ChromeUserManagerImpl; class FakeLoginUtils; class FakeUserManager; class MockUserManager; class SupervisedUserManagerImpl; class UserAddingScreenTest; class UserImageManagerImpl; -class UserManagerBase; class UserSessionManager; } namespace user_manager { +class UserManagerBase; + // A class representing information about a previously logged in user. // Each user has a canonical email (username), returned by |email()| and // may have a different displayed email (in the raw form as entered by user), @@ -148,10 +149,10 @@ class USER_MANAGER_EXPORT User : public UserInfo { bool is_profile_created() const { return profile_is_created_; } protected: - friend class chromeos::ChromeUserManager; + friend class UserManagerBase; + friend class chromeos::ChromeUserManagerImpl; friend class chromeos::SupervisedUserManagerImpl; friend class chromeos::UserImageManagerImpl; - friend class chromeos::UserManagerBase; friend class chromeos::UserSessionManager; // For testing: diff --git a/components/user_manager/user_manager.cc b/components/user_manager/user_manager.cc new file mode 100644 index 0000000..597407f --- /dev/null +++ b/components/user_manager/user_manager.cc @@ -0,0 +1,85 @@ +// Copyright 2014 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/user_manager.h" + +#include "base/logging.h" + +namespace user_manager { + +UserManager* UserManager::instance = NULL; + +UserManager::Observer::~Observer() { +} + +void UserManager::Observer::LocalStateChanged(UserManager* user_manager) { +} + +void UserManager::UserSessionStateObserver::ActiveUserChanged( + const User* active_user) { +} + +void UserManager::UserSessionStateObserver::UserAddedToSession( + const User* active_user) { +} + +void UserManager::UserSessionStateObserver::ActiveUserHashChanged( + const std::string& hash) { +} + +UserManager::UserSessionStateObserver::~UserSessionStateObserver() { +} + +UserManager::UserAccountData::UserAccountData( + const base::string16& display_name, + const base::string16& given_name, + const std::string& locale) + : display_name_(display_name), given_name_(given_name), locale_(locale) { +} + +UserManager::UserAccountData::~UserAccountData() { +} + +void UserManager::Initialize() { + DCHECK(!UserManager::instance); + UserManager::SetInstance(this); +} + +// static +bool UserManager::IsInitialized() { + return UserManager::instance; +} + +void UserManager::Destroy() { + DCHECK(UserManager::instance == this); + UserManager::SetInstance(NULL); +} + +// static +UserManager* user_manager::UserManager::Get() { + CHECK(UserManager::instance); + return UserManager::instance; +} + +UserManager::~UserManager() { +} + +// static +void UserManager::SetInstance(UserManager* user_manager) { + UserManager::instance = user_manager; +} + +// static +UserManager* user_manager::UserManager::GetForTesting() { + return UserManager::instance; +} + +// static +UserManager* UserManager::SetForTesting(UserManager* user_manager) { + UserManager* previous_instance = UserManager::instance; + UserManager::instance = user_manager; + return previous_instance; +} + +} // namespace user_manager diff --git a/components/user_manager/user_manager.h b/components/user_manager/user_manager.h new file mode 100644 index 0000000..e775279 --- /dev/null +++ b/components/user_manager/user_manager.h @@ -0,0 +1,316 @@ +// Copyright 2014 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. + +#ifndef COMPONENTS_USER_MANAGER_USER_MANAGER_H_ +#define COMPONENTS_USER_MANAGER_USER_MANAGER_H_ + +#include <string> + +#include "components/user_manager/user.h" +#include "components/user_manager/user_manager_export.h" + +namespace chromeos { +class ScopedUserManagerEnabler; +} + +namespace user_manager { + +class RemoveUserDelegate; + +// Interface for UserManagerBase - that provides base implementation for +// Chrome OS user management. Typical features: +// * Get list of all know users (who have logged into this Chrome OS device) +// * Keep track for logged in/LRU users, active user in multi-user session. +// * Find/modify users, store user meta-data such as display name/email. +class USER_MANAGER_EXPORT UserManager { + public: + // Interface that observers of UserManager must implement in order + // to receive notification when local state preferences is changed + class Observer { + public: + // Called when the local state preferences is changed. + virtual void LocalStateChanged(UserManager* user_manager); + + protected: + virtual ~Observer(); + }; + + // TODO(nkostylev): Refactor and move this observer out of UserManager. + // Observer interface that defines methods used to notify on user session / + // active user state changes. Default implementation is empty. + class UserSessionStateObserver { + public: + // Called when active user has changed. + virtual void ActiveUserChanged(const User* active_user); + + // Called when another user got added to the existing session. + virtual void UserAddedToSession(const User* added_user); + + // Called right before notifying on user change so that those who rely + // on user_id hash would be accessing up-to-date value. + virtual void ActiveUserHashChanged(const std::string& hash); + + protected: + virtual ~UserSessionStateObserver(); + }; + + // Data retrieved from user account. + class UserAccountData { + public: + UserAccountData(const base::string16& display_name, + const base::string16& given_name, + const std::string& locale); + ~UserAccountData(); + const base::string16& display_name() const { return display_name_; } + const base::string16& given_name() const { return given_name_; } + const std::string& locale() const { return locale_; } + + private: + const base::string16 display_name_; + const base::string16 given_name_; + const std::string locale_; + + DISALLOW_COPY_AND_ASSIGN(UserAccountData); + }; + + // Initializes UserManager instance to this. Normally should be called right + // after creation so that user_manager::UserManager::Get() doesn't fail. + // Tests could call this method if they are replacing existing UserManager + // instance with their own test instance. + void Initialize(); + + // Checks whether the UserManager instance has been created already. + // This method is not thread-safe and must be called from the main UI thread. + static bool IsInitialized(); + + // Shuts down the UserManager. After this method has been called, the + // singleton has unregistered itself as an observer but remains available so + // that other classes can access it during their shutdown. This method is not + // thread-safe and must be called from the main UI thread. + virtual void Shutdown() = 0; + + // Sets UserManager instance to NULL. Always call Shutdown() first. + // This method is not thread-safe and must be called from the main UI thread. + void Destroy(); + + // Returns UserManager instance or will crash if it is |NULL| (has either not + // been created yet or is already destroyed). This method is not thread-safe + // and must be called from the main UI thread. + static UserManager* Get(); + + virtual ~UserManager(); + + // Returns a list of users who have logged into this device previously. This + // is sorted by last login date with the most recent user at the beginning. + virtual const UserList& GetUsers() const = 0; + + // Returns list of users admitted for logging in into multi-profile session. + // Users that have a policy that prevents them from being added to the + // multi-profile session will still be part of this list as long as they + // are regular users (i.e. not a public session/supervised etc.). + // Returns an empty list in case when primary user is not a regular one or + // has a policy that prohibids it to be part of multi-profile session. + virtual UserList GetUsersAdmittedForMultiProfile() const = 0; + + // Returns a list of users who are currently logged in. + virtual const UserList& GetLoggedInUsers() const = 0; + + // Returns a list of users who are currently logged in in the LRU order - + // so the active user is the first one in the list. If there is no user logged + // in, the current user will be returned. + virtual const UserList& GetLRULoggedInUsers() const = 0; + + // Returns a list of users who can unlock the device. + // This list is based on policy and whether user is able to do unlock. + // Policy: + // * If user has primary-only policy then it is the only user in unlock users. + // * Otherwise all users with unrestricted policy are added to this list. + // All users that are unable to perform unlock are excluded from this list. + virtual UserList GetUnlockUsers() const = 0; + + // Returns the email of the owner user. Returns an empty string if there is + // no owner for the device. + virtual const std::string& GetOwnerEmail() const = 0; + + // Indicates that a user with the given |user_id| has just logged in. The + // persistent list is updated accordingly if the user is not ephemeral. + // |browser_restart| is true when reloading Chrome after crash to distinguish + // from normal sign in flow. + // |username_hash| is used to identify homedir mount point. + virtual void UserLoggedIn(const std::string& user_id, + const std::string& username_hash, + bool browser_restart) = 0; + + // Switches to active user identified by |user_id|. User has to be logged in. + virtual void SwitchActiveUser(const std::string& user_id) = 0; + + // Called when browser session is started i.e. after + // browser_creator.LaunchBrowser(...) was called after user sign in. + // When user is at the image screen IsUserLoggedIn() will return true + // but IsSessionStarted() will return false. During the kiosk splash screen, + // we perform additional initialization after the user is logged in but + // before the session has been started. + // Fires NOTIFICATION_SESSION_STARTED. + virtual void SessionStarted() = 0; + + // Removes the user from the device. Note, it will verify that the given user + // isn't the owner, so calling this method for the owner will take no effect. + // Note, |delegate| can be NULL. + virtual void RemoveUser(const std::string& user_id, + RemoveUserDelegate* delegate) = 0; + + // Removes the user from the persistent list only. Also removes the user's + // picture. + virtual void RemoveUserFromList(const std::string& user_id) = 0; + + // Returns true if a user with the given user id is found in the persistent + // list or currently logged in as ephemeral. + virtual bool IsKnownUser(const std::string& user_id) const = 0; + + // Returns the user with the given user id if found in the persistent + // list or currently logged in as ephemeral. Returns |NULL| otherwise. + virtual const User* FindUser(const std::string& user_id) const = 0; + + // Returns the user with the given user id if found in the persistent + // list or currently logged in as ephemeral. Returns |NULL| otherwise. + // Same as FindUser but returns non-const pointer to User object. + virtual User* FindUserAndModify(const std::string& user_id) = 0; + + // Returns the logged-in user. + // TODO(nkostylev): Deprecate this call, move clients to GetActiveUser(). + // http://crbug.com/230852 + virtual const User* GetLoggedInUser() const = 0; + virtual User* GetLoggedInUser() = 0; + + // Returns the logged-in user that is currently active within this session. + // There could be multiple users logged in at the the same but for now + // we support only one of them being active. + virtual const User* GetActiveUser() const = 0; + virtual User* GetActiveUser() = 0; + + // Returns the primary user of the current session. It is recorded for the + // first signed-in user and does not change thereafter. + virtual const User* GetPrimaryUser() const = 0; + + // Saves user's oauth token status in local state preferences. + virtual void SaveUserOAuthStatus( + const std::string& user_id, + User::OAuthTokenStatus oauth_token_status) = 0; + + // Saves a flag indicating whether online authentication against GAIA should + // be enforced during the user's next sign-in. + virtual void SaveForceOnlineSignin(const std::string& user_id, + bool force_online_signin) = 0; + + // Saves user's displayed name in local state preferences. + // Ignored If there is no such user. + virtual void SaveUserDisplayName(const std::string& user_id, + const base::string16& display_name) = 0; + + // Updates data upon User Account download. + virtual void UpdateUserAccountData(const std::string& user_id, + const UserAccountData& account_data) = 0; + + // Returns the display name for user |user_id| if it is known (was + // previously set by a |SaveUserDisplayName| call). + // Otherwise, returns an empty string. + virtual base::string16 GetUserDisplayName( + const std::string& user_id) const = 0; + + // Saves user's displayed (non-canonical) email in local state preferences. + // Ignored If there is no such user. + virtual void SaveUserDisplayEmail(const std::string& user_id, + const std::string& display_email) = 0; + + // Returns the display email for user |user_id| if it is known (was + // previously set by a |SaveUserDisplayEmail| call). + // Otherwise, returns |user_id| itself. + virtual std::string GetUserDisplayEmail(const std::string& user_id) const = 0; + + // Returns true if current user is an owner. + virtual bool IsCurrentUserOwner() const = 0; + + // Returns true if current user is not existing one (hasn't signed in before). + virtual bool IsCurrentUserNew() const = 0; + + // Returns true if data stored or cached for the current user outside that + // user's cryptohome (wallpaper, avatar, OAuth token status, display name, + // display email) is ephemeral. + virtual bool IsCurrentUserNonCryptohomeDataEphemeral() const = 0; + + // Returns true if the current user's session can be locked (i.e. the user has + // a password with which to unlock the session). + virtual bool CanCurrentUserLock() const = 0; + + // Returns true if at least one user has signed in. + virtual bool IsUserLoggedIn() const = 0; + + // Returns true if we're logged in as a regular user. + virtual bool IsLoggedInAsRegularUser() const = 0; + + // Returns true if we're logged in as a demo user. + virtual bool IsLoggedInAsDemoUser() const = 0; + + // Returns true if we're logged in as a public account. + virtual bool IsLoggedInAsPublicAccount() const = 0; + + // Returns true if we're logged in as a Guest. + virtual bool IsLoggedInAsGuest() const = 0; + + // Returns true if we're logged in as a supervised user. + virtual bool IsLoggedInAsSupervisedUser() const = 0; + + // Returns true if we're logged in as a kiosk app. + virtual bool IsLoggedInAsKioskApp() const = 0; + + // Returns true if we're logged in as the stub user used for testing on Linux. + virtual bool IsLoggedInAsStub() const = 0; + + // Returns true if we're logged in and browser has been started i.e. + // browser_creator.LaunchBrowser(...) was called after sign in + // or restart after crash. + virtual bool IsSessionStarted() const = 0; + + // Returns true if data stored or cached for the user with the given user id + // address outside that user's cryptohome (wallpaper, avatar, OAuth token + // status, display name, display email) is to be treated as ephemeral. + virtual bool IsUserNonCryptohomeDataEphemeral( + const std::string& user_id) const = 0; + + virtual void AddObserver(Observer* obs) = 0; + virtual void RemoveObserver(Observer* obs) = 0; + + virtual void AddSessionStateObserver(UserSessionStateObserver* obs) = 0; + virtual void RemoveSessionStateObserver(UserSessionStateObserver* obs) = 0; + + virtual void NotifyLocalStateChanged() = 0; + + // Returns true if supervised users allowed. + virtual bool AreSupervisedUsersAllowed() const = 0; + + protected: + // Sets UserManager instance. + static void SetInstance(UserManager* user_manager); + + // Pointer to the existing UserManager instance (if any). + // Usually is set by calling Initialize(), reset by calling Destroy(). + // Not owned since specific implementation of UserManager should decide on its + // own appropriate owner. For src/chrome implementation such place is + // g_browser_process->platform_part(). + static UserManager* instance; + + private: + friend class chromeos::ScopedUserManagerEnabler; + + // Same as Get() but doesn't won't crash is current instance is NULL. + static UserManager* GetForTesting(); + + // Sets UserManager instance to the given |user_manager|. + // Returns the previous value of the instance. + static UserManager* SetForTesting(UserManager* user_manager); +}; + +} // namespace user_manager + +#endif // COMPONENTS_USER_MANAGER_USER_MANAGER_H_ diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc new file mode 100644 index 0000000..3914819 --- /dev/null +++ b/components/user_manager/user_manager_base.cc @@ -0,0 +1,1026 @@ +// Copyright 2014 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/user_manager_base.h" + +#include <cstddef> +#include <set> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/command_line.h" +#include "base/compiler_specific.h" +#include "base/format_macros.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/metrics/histogram.h" +#include "base/prefs/pref_registry_simple.h" +#include "base/prefs/pref_service.h" +#include "base/prefs/scoped_user_pref_update.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/task_runner.h" +#include "base/values.h" +#include "chromeos/chromeos_switches.h" +#include "chromeos/cryptohome/async_method_caller.h" +#include "chromeos/login/login_state.h" +#include "chromeos/login/user_names.h" +#include "components/session_manager/core/session_manager.h" +#include "components/user_manager/remove_user_delegate.h" +#include "components/user_manager/user_type.h" +#include "google_apis/gaia/gaia_auth_util.h" +#include "ui/base/l10n/l10n_util.h" + +namespace user_manager { +namespace { + +// A vector pref of the the regular users known on this device, arranged in LRU +// order. +const char kRegularUsers[] = "LoggedInUsers"; + +// A dictionary that maps user IDs to the displayed name. +const char kUserDisplayName[] = "UserDisplayName"; + +// A dictionary that maps user IDs to the user's given name. +const char kUserGivenName[] = "UserGivenName"; + +// A dictionary that maps user IDs to the displayed (non-canonical) emails. +const char kUserDisplayEmail[] = "UserDisplayEmail"; + +// A dictionary that maps user IDs to OAuth token presence flag. +const char kUserOAuthTokenStatus[] = "OAuthTokenStatus"; + +// A dictionary that maps user IDs to a flag indicating whether online +// authentication against GAIA should be enforced during the next sign-in. +const char kUserForceOnlineSignin[] = "UserForceOnlineSignin"; + +// A string pref containing the ID of the last user who logged in if it was +// a regular user or an empty string if it was another type of user (guest, +// kiosk, public account, etc.). +const char kLastLoggedInRegularUser[] = "LastLoggedInRegularUser"; + +// Upper bound for a histogram metric reporting the amount of time between +// one regular user logging out and a different regular user logging in. +const int kLogoutToLoginDelayMaxSec = 1800; + +// Callback that is called after user removal is complete. +void OnRemoveUserComplete(const std::string& user_email, + bool success, + cryptohome::MountError return_code) { + // Log the error, but there's not much we can do. + if (!success) { + LOG(ERROR) << "Removal of cryptohome for " << user_email + << " failed, return code: " << return_code; + } +} + +} // namespace + +// static +void UserManagerBase::RegisterPrefs(PrefRegistrySimple* registry) { + registry->RegisterListPref(kRegularUsers); + registry->RegisterStringPref(kLastLoggedInRegularUser, std::string()); + registry->RegisterDictionaryPref(kUserDisplayName); + registry->RegisterDictionaryPref(kUserGivenName); + registry->RegisterDictionaryPref(kUserDisplayEmail); + registry->RegisterDictionaryPref(kUserOAuthTokenStatus); + registry->RegisterDictionaryPref(kUserForceOnlineSignin); +} + +UserManagerBase::UserManagerBase( + scoped_refptr<base::TaskRunner> task_runner, + scoped_refptr<base::TaskRunner> blocking_task_runner) + : active_user_(NULL), + primary_user_(NULL), + user_loading_stage_(STAGE_NOT_LOADED), + session_started_(false), + is_current_user_owner_(false), + is_current_user_new_(false), + is_current_user_ephemeral_regular_user_(false), + ephemeral_users_enabled_(false), + manager_creation_time_(base::TimeTicks::Now()), + task_runner_(task_runner), + blocking_task_runner_(blocking_task_runner), + weak_factory_(this) { + UpdateLoginState(); +} + +UserManagerBase::~UserManagerBase() { + // Can't use STLDeleteElements because of the private destructor of User. + for (UserList::iterator it = users_.begin(); it != users_.end(); + it = users_.erase(it)) { + DeleteUser(*it); + } + // These are pointers to the same User instances that were in users_ list. + logged_in_users_.clear(); + lru_logged_in_users_.clear(); + + DeleteUser(active_user_); +} + +void UserManagerBase::Shutdown() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); +} + +const UserList& UserManagerBase::GetUsers() const { + const_cast<UserManagerBase*>(this)->EnsureUsersLoaded(); + return users_; +} + +const UserList& UserManagerBase::GetLoggedInUsers() const { + return logged_in_users_; +} + +const UserList& UserManagerBase::GetLRULoggedInUsers() const { + return lru_logged_in_users_; +} + +const std::string& UserManagerBase::GetOwnerEmail() const { + return owner_email_; +} + +void UserManagerBase::UserLoggedIn(const std::string& user_id, + const std::string& username_hash, + bool browser_restart) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + User* user = FindUserInListAndModify(user_id); + if (active_user_ && user) { + user->set_is_logged_in(true); + user->set_username_hash(username_hash); + logged_in_users_.push_back(user); + lru_logged_in_users_.push_back(user); + + // Reset the new user flag if the user already exists. + SetIsCurrentUserNew(false); + NotifyUserAddedToSession(user, true /* user switch pending */); + + return; + } + + if (user_id == chromeos::login::kGuestUserName) { + GuestUserLoggedIn(); + } else if (user_id == chromeos::login::kRetailModeUserName) { + RetailModeUserLoggedIn(); + } else if (IsKioskApp(user_id)) { + KioskAppLoggedIn(user_id); + } else if (IsDemoApp(user_id)) { + DemoAccountLoggedIn(); + } else { + EnsureUsersLoaded(); + + if (user && user->GetType() == USER_TYPE_PUBLIC_ACCOUNT) { + PublicAccountUserLoggedIn(user); + } else if ((user && user->GetType() == USER_TYPE_SUPERVISED) || + (!user && + gaia::ExtractDomainName(user_id) == + chromeos::login::kSupervisedUserDomain)) { + SupervisedUserLoggedIn(user_id); + } else if (browser_restart && IsPublicAccountMarkedForRemoval(user_id)) { + PublicAccountUserLoggedIn(User::CreatePublicAccountUser(user_id)); + } else if (user_id != GetOwnerEmail() && !user && + (AreEphemeralUsersEnabled() || browser_restart)) { + RegularUserLoggedInAsEphemeral(user_id); + } else { + RegularUserLoggedIn(user_id); + } + } + + DCHECK(active_user_); + active_user_->set_is_logged_in(true); + active_user_->set_is_active(true); + active_user_->set_username_hash(username_hash); + + // Place user who just signed in to the top of the logged in users. + logged_in_users_.insert(logged_in_users_.begin(), active_user_); + SetLRUUser(active_user_); + + if (!primary_user_) { + primary_user_ = active_user_; + if (primary_user_->GetType() == USER_TYPE_REGULAR) + SendRegularUserLoginMetrics(user_id); + } + + UMA_HISTOGRAM_ENUMERATION( + "UserManager.LoginUserType", active_user_->GetType(), NUM_USER_TYPES); + + GetLocalState()->SetString( + kLastLoggedInRegularUser, + (active_user_->GetType() == USER_TYPE_REGULAR) ? user_id : ""); + + NotifyOnLogin(); + PerformPostUserLoggedInActions(browser_restart); +} + +void UserManagerBase::SwitchActiveUser(const std::string& user_id) { + User* user = FindUserAndModify(user_id); + if (!user) { + NOTREACHED() << "Switching to a non-existing user"; + return; + } + if (user == active_user_) { + NOTREACHED() << "Switching to a user who is already active"; + return; + } + if (!user->is_logged_in()) { + NOTREACHED() << "Switching to a user that is not logged in"; + return; + } + if (user->GetType() != USER_TYPE_REGULAR) { + NOTREACHED() << "Switching to a non-regular user"; + return; + } + if (user->username_hash().empty()) { + NOTREACHED() << "Switching to a user that doesn't have username_hash set"; + return; + } + + DCHECK(active_user_); + active_user_->set_is_active(false); + user->set_is_active(true); + active_user_ = user; + + // Move the user to the front. + SetLRUUser(active_user_); + + NotifyActiveUserHashChanged(active_user_->username_hash()); + NotifyActiveUserChanged(active_user_); +} + +void UserManagerBase::SessionStarted() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + session_started_ = true; + + UpdateLoginState(); + session_manager::SessionManager::Get()->SetSessionState( + session_manager::SESSION_STATE_ACTIVE); + + if (IsCurrentUserNew()) { + // Make sure that the new user's data is persisted to Local State. + GetLocalState()->CommitPendingWrite(); + } +} + +void UserManagerBase::RemoveUser(const std::string& user_id, + RemoveUserDelegate* delegate) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + if (!CanUserBeRemoved(FindUser(user_id))) + return; + + RemoveUserInternal(user_id, delegate); +} + +void UserManagerBase::RemoveUserInternal(const std::string& user_email, + RemoveUserDelegate* delegate) { + RemoveNonOwnerUserInternal(user_email, delegate); +} + +void UserManagerBase::RemoveNonOwnerUserInternal(const std::string& user_email, + RemoveUserDelegate* delegate) { + if (delegate) + delegate->OnBeforeUserRemoved(user_email); + RemoveUserFromList(user_email); + cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove( + user_email, base::Bind(&OnRemoveUserComplete, user_email)); + + if (delegate) + delegate->OnUserRemoved(user_email); +} + +void UserManagerBase::RemoveUserFromList(const std::string& user_id) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + RemoveNonCryptohomeData(user_id); + if (user_loading_stage_ == STAGE_LOADED) { + DeleteUser(RemoveRegularOrSupervisedUserFromList(user_id)); + } else if (user_loading_stage_ == STAGE_LOADING) { + DCHECK(gaia::ExtractDomainName(user_id) == + chromeos::login::kSupervisedUserDomain); + // Special case, removing partially-constructed supervised user during user + // list loading. + ListPrefUpdate users_update(GetLocalState(), kRegularUsers); + users_update->Remove(base::StringValue(user_id), NULL); + } else { + NOTREACHED() << "Users are not loaded yet."; + return; + } + + // Make sure that new data is persisted to Local State. + GetLocalState()->CommitPendingWrite(); +} + +bool UserManagerBase::IsKnownUser(const std::string& user_id) const { + return FindUser(user_id) != NULL; +} + +const User* UserManagerBase::FindUser(const std::string& user_id) const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + if (active_user_ && active_user_->email() == user_id) + return active_user_; + return FindUserInList(user_id); +} + +User* UserManagerBase::FindUserAndModify(const std::string& user_id) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + if (active_user_ && active_user_->email() == user_id) + return active_user_; + return FindUserInListAndModify(user_id); +} + +const User* UserManagerBase::GetLoggedInUser() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return active_user_; +} + +User* UserManagerBase::GetLoggedInUser() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return active_user_; +} + +const User* UserManagerBase::GetActiveUser() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return active_user_; +} + +User* UserManagerBase::GetActiveUser() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return active_user_; +} + +const User* UserManagerBase::GetPrimaryUser() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return primary_user_; +} + +void UserManagerBase::SaveUserOAuthStatus( + const std::string& user_id, + User::OAuthTokenStatus oauth_token_status) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + DVLOG(1) << "Saving user OAuth token status in Local State"; + User* user = FindUserAndModify(user_id); + if (user) + user->set_oauth_token_status(oauth_token_status); + + // Do not update local state if data stored or cached outside the user's + // cryptohome is to be treated as ephemeral. + if (IsUserNonCryptohomeDataEphemeral(user_id)) + return; + + DictionaryPrefUpdate oauth_status_update(GetLocalState(), + kUserOAuthTokenStatus); + oauth_status_update->SetWithoutPathExpansion( + user_id, + new base::FundamentalValue(static_cast<int>(oauth_token_status))); +} + +void UserManagerBase::SaveForceOnlineSignin(const std::string& user_id, + bool force_online_signin) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + // Do not update local state if data stored or cached outside the user's + // cryptohome is to be treated as ephemeral. + if (IsUserNonCryptohomeDataEphemeral(user_id)) + return; + + DictionaryPrefUpdate force_online_update(GetLocalState(), + kUserForceOnlineSignin); + force_online_update->SetBooleanWithoutPathExpansion(user_id, + force_online_signin); +} + +void UserManagerBase::SaveUserDisplayName(const std::string& user_id, + const base::string16& display_name) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + if (User* user = FindUserAndModify(user_id)) { + user->set_display_name(display_name); + + // Do not update local state if data stored or cached outside the user's + // cryptohome is to be treated as ephemeral. + if (!IsUserNonCryptohomeDataEphemeral(user_id)) { + DictionaryPrefUpdate display_name_update(GetLocalState(), + kUserDisplayName); + display_name_update->SetWithoutPathExpansion( + user_id, new base::StringValue(display_name)); + } + } +} + +base::string16 UserManagerBase::GetUserDisplayName( + const std::string& user_id) const { + const User* user = FindUser(user_id); + return user ? user->display_name() : base::string16(); +} + +void UserManagerBase::SaveUserDisplayEmail(const std::string& user_id, + const std::string& display_email) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + User* user = FindUserAndModify(user_id); + if (!user) + return; // Ignore if there is no such user. + + user->set_display_email(display_email); + + // Do not update local state if data stored or cached outside the user's + // cryptohome is to be treated as ephemeral. + if (IsUserNonCryptohomeDataEphemeral(user_id)) + return; + + DictionaryPrefUpdate display_email_update(GetLocalState(), kUserDisplayEmail); + display_email_update->SetWithoutPathExpansion( + user_id, new base::StringValue(display_email)); +} + +std::string UserManagerBase::GetUserDisplayEmail( + const std::string& user_id) const { + const User* user = FindUser(user_id); + return user ? user->display_email() : user_id; +} + +void UserManagerBase::UpdateUserAccountData( + const std::string& user_id, + const UserAccountData& account_data) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + SaveUserDisplayName(user_id, account_data.display_name()); + + if (User* user = FindUserAndModify(user_id)) { + base::string16 given_name = account_data.given_name(); + user->set_given_name(given_name); + if (!IsUserNonCryptohomeDataEphemeral(user_id)) { + DictionaryPrefUpdate given_name_update(GetLocalState(), kUserGivenName); + given_name_update->SetWithoutPathExpansion( + user_id, new base::StringValue(given_name)); + } + } + + UpdateUserAccountLocale(user_id, account_data.locale()); +} + +// static +void UserManagerBase::ParseUserList(const base::ListValue& users_list, + const std::set<std::string>& existing_users, + std::vector<std::string>* users_vector, + std::set<std::string>* users_set) { + users_vector->clear(); + users_set->clear(); + for (size_t i = 0; i < users_list.GetSize(); ++i) { + std::string email; + if (!users_list.GetString(i, &email) || email.empty()) { + LOG(ERROR) << "Corrupt entry in user list at index " << i << "."; + continue; + } + if (existing_users.find(email) != existing_users.end() || + !users_set->insert(email).second) { + LOG(ERROR) << "Duplicate user: " << email; + continue; + } + users_vector->push_back(email); + } +} + +bool UserManagerBase::IsCurrentUserOwner() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + base::AutoLock lk(is_current_user_owner_lock_); + return is_current_user_owner_; +} + +void UserManagerBase::SetCurrentUserIsOwner(bool is_current_user_owner) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + { + base::AutoLock lk(is_current_user_owner_lock_); + is_current_user_owner_ = is_current_user_owner; + } + UpdateLoginState(); +} + +bool UserManagerBase::IsCurrentUserNew() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return is_current_user_new_; +} + +bool UserManagerBase::IsCurrentUserNonCryptohomeDataEphemeral() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && + IsUserNonCryptohomeDataEphemeral(GetLoggedInUser()->email()); +} + +bool UserManagerBase::CanCurrentUserLock() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && active_user_->can_lock(); +} + +bool UserManagerBase::IsUserLoggedIn() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return active_user_; +} + +bool UserManagerBase::IsLoggedInAsRegularUser() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_REGULAR; +} + +bool UserManagerBase::IsLoggedInAsDemoUser() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_RETAIL_MODE; +} + +bool UserManagerBase::IsLoggedInAsPublicAccount() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && + active_user_->GetType() == USER_TYPE_PUBLIC_ACCOUNT; +} + +bool UserManagerBase::IsLoggedInAsGuest() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_GUEST; +} + +bool UserManagerBase::IsLoggedInAsSupervisedUser() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_SUPERVISED; +} + +bool UserManagerBase::IsLoggedInAsKioskApp() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_KIOSK_APP; +} + +bool UserManagerBase::IsLoggedInAsStub() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && + active_user_->email() == chromeos::login::kStubUser; +} + +bool UserManagerBase::IsSessionStarted() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return session_started_; +} + +bool UserManagerBase::IsUserNonCryptohomeDataEphemeral( + const std::string& user_id) const { + // Data belonging to the guest, retail mode and stub users is always + // ephemeral. + if (user_id == chromeos::login::kGuestUserName || + user_id == chromeos::login::kRetailModeUserName || + user_id == chromeos::login::kStubUser) { + return true; + } + + // Data belonging to the owner, anyone found on the user list and obsolete + // public accounts whose data has not been removed yet is not ephemeral. + if (user_id == GetOwnerEmail() || UserExistsInList(user_id) || + IsPublicAccountMarkedForRemoval(user_id)) { + return false; + } + + // Data belonging to the currently logged-in user is ephemeral when: + // a) The user logged into a regular account while the ephemeral users policy + // was enabled. + // - or - + // b) The user logged into any other account type. + if (IsUserLoggedIn() && (user_id == GetLoggedInUser()->email()) && + (is_current_user_ephemeral_regular_user_ || !IsLoggedInAsRegularUser())) { + return true; + } + + // Data belonging to any other user is ephemeral when: + // a) Going through the regular login flow and the ephemeral users policy is + // enabled. + // - or - + // b) The browser is restarting after a crash. + return AreEphemeralUsersEnabled() || + session_manager::SessionManager::HasBrowserRestarted(); +} + +void UserManagerBase::AddObserver(UserManager::Observer* obs) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + observer_list_.AddObserver(obs); +} + +void UserManagerBase::RemoveObserver(UserManager::Observer* obs) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + observer_list_.RemoveObserver(obs); +} + +void UserManagerBase::AddSessionStateObserver( + UserManager::UserSessionStateObserver* obs) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + session_state_observer_list_.AddObserver(obs); +} + +void UserManagerBase::RemoveSessionStateObserver( + UserManager::UserSessionStateObserver* obs) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + session_state_observer_list_.RemoveObserver(obs); +} + +void UserManagerBase::NotifyLocalStateChanged() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + FOR_EACH_OBSERVER( + UserManager::Observer, observer_list_, LocalStateChanged(this)); +} + +bool UserManagerBase::CanUserBeRemoved(const User* user) const { + // Only regular and supervised users are allowed to be manually removed. + if (!user || (user->GetType() != USER_TYPE_REGULAR && + user->GetType() != USER_TYPE_SUPERVISED)) { + return false; + } + + // Sanity check: we must not remove single user unless it's an enterprise + // device. This check may seem redundant at a first sight because + // this single user must be an owner and we perform special check later + // in order not to remove an owner. However due to non-instant nature of + // ownership assignment this later check may sometimes fail. + // See http://crosbug.com/12723 + if (users_.size() < 2 && !IsEnterpriseManaged()) + return false; + + // Sanity check: do not allow any of the the logged in users to be removed. + for (UserList::const_iterator it = logged_in_users_.begin(); + it != logged_in_users_.end(); + ++it) { + if ((*it)->email() == user->email()) + return false; + } + + return true; +} + +bool UserManagerBase::GetEphemeralUsersEnabled() const { + return ephemeral_users_enabled_; +} + +void UserManagerBase::SetEphemeralUsersEnabled(bool enabled) { + ephemeral_users_enabled_ = enabled; +} + +void UserManagerBase::SetIsCurrentUserNew(bool is_new) { + is_current_user_new_ = is_new; +} + +void UserManagerBase::SetOwnerEmail(std::string owner_user_id) { + owner_email_ = owner_user_id; +} + +const std::string& UserManagerBase::GetPendingUserSwitchID() const { + return pending_user_switch_; +} + +void UserManagerBase::SetPendingUserSwitchID(std::string user_id) { + pending_user_switch_ = user_id; +} + +void UserManagerBase::EnsureUsersLoaded() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + if (!GetLocalState()) + return; + + if (user_loading_stage_ != STAGE_NOT_LOADED) + return; + user_loading_stage_ = STAGE_LOADING; + + PerformPreUserListLoadingActions(); + + PrefService* local_state = GetLocalState(); + const base::ListValue* prefs_regular_users = + local_state->GetList(kRegularUsers); + + const base::DictionaryValue* prefs_display_names = + local_state->GetDictionary(kUserDisplayName); + const base::DictionaryValue* prefs_given_names = + local_state->GetDictionary(kUserGivenName); + const base::DictionaryValue* prefs_display_emails = + local_state->GetDictionary(kUserDisplayEmail); + + // Load public sessions first. + std::set<std::string> public_sessions_set; + LoadPublicAccounts(&public_sessions_set); + + // Load regular users and supervised users. + std::vector<std::string> regular_users; + std::set<std::string> regular_users_set; + ParseUserList(*prefs_regular_users, + public_sessions_set, + ®ular_users, + ®ular_users_set); + for (std::vector<std::string>::const_iterator it = regular_users.begin(); + it != regular_users.end(); + ++it) { + User* user = NULL; + const std::string domain = gaia::ExtractDomainName(*it); + if (domain == chromeos::login::kSupervisedUserDomain) + user = User::CreateSupervisedUser(*it); + else + user = User::CreateRegularUser(*it); + user->set_oauth_token_status(LoadUserOAuthStatus(*it)); + user->set_force_online_signin(LoadForceOnlineSignin(*it)); + users_.push_back(user); + + base::string16 display_name; + if (prefs_display_names->GetStringWithoutPathExpansion(*it, + &display_name)) { + user->set_display_name(display_name); + } + + base::string16 given_name; + if (prefs_given_names->GetStringWithoutPathExpansion(*it, &given_name)) { + user->set_given_name(given_name); + } + + std::string display_email; + if (prefs_display_emails->GetStringWithoutPathExpansion(*it, + &display_email)) { + user->set_display_email(display_email); + } + } + + user_loading_stage_ = STAGE_LOADED; + + PerformPostUserListLoadingActions(); +} + +UserList& UserManagerBase::GetUsersAndModify() { + EnsureUsersLoaded(); + return users_; +} + +const User* UserManagerBase::FindUserInList(const std::string& user_id) const { + const UserList& users = GetUsers(); + for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) { + if ((*it)->email() == user_id) + return *it; + } + return NULL; +} + +const bool UserManagerBase::UserExistsInList(const std::string& user_id) const { + const base::ListValue* user_list = GetLocalState()->GetList(kRegularUsers); + for (size_t i = 0; i < user_list->GetSize(); ++i) { + std::string email; + if (user_list->GetString(i, &email) && (user_id == email)) + return true; + } + return false; +} + +User* UserManagerBase::FindUserInListAndModify(const std::string& user_id) { + UserList& users = GetUsersAndModify(); + for (UserList::iterator it = users.begin(); it != users.end(); ++it) { + if ((*it)->email() == user_id) + return *it; + } + return NULL; +} + +void UserManagerBase::GuestUserLoggedIn() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + active_user_ = User::CreateGuestUser(); +} + +void UserManagerBase::AddUserRecord(User* user) { + // Add the user to the front of the user list. + ListPrefUpdate prefs_users_update(GetLocalState(), kRegularUsers); + prefs_users_update->Insert(0, new base::StringValue(user->email())); + users_.insert(users_.begin(), user); +} + +void UserManagerBase::RegularUserLoggedIn(const std::string& user_id) { + // Remove the user from the user list. + active_user_ = RemoveRegularOrSupervisedUserFromList(user_id); + + // If the user was not found on the user list, create a new user. + SetIsCurrentUserNew(!active_user_); + if (IsCurrentUserNew()) { + active_user_ = User::CreateRegularUser(user_id); + active_user_->set_oauth_token_status(LoadUserOAuthStatus(user_id)); + SaveUserDisplayName(active_user_->email(), + base::UTF8ToUTF16(active_user_->GetAccountName(true))); + } + + AddUserRecord(active_user_); + + // Make sure that new data is persisted to Local State. + GetLocalState()->CommitPendingWrite(); +} + +void UserManagerBase::RegularUserLoggedInAsEphemeral( + const std::string& user_id) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + SetIsCurrentUserNew(true); + is_current_user_ephemeral_regular_user_ = true; + active_user_ = User::CreateRegularUser(user_id); +} + +void UserManagerBase::NotifyOnLogin() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + NotifyActiveUserHashChanged(active_user_->username_hash()); + NotifyActiveUserChanged(active_user_); + UpdateLoginState(); +} + +User::OAuthTokenStatus UserManagerBase::LoadUserOAuthStatus( + const std::string& user_id) const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + const base::DictionaryValue* prefs_oauth_status = + GetLocalState()->GetDictionary(kUserOAuthTokenStatus); + int oauth_token_status = User::OAUTH_TOKEN_STATUS_UNKNOWN; + if (prefs_oauth_status && + prefs_oauth_status->GetIntegerWithoutPathExpansion(user_id, + &oauth_token_status)) { + User::OAuthTokenStatus status = + static_cast<User::OAuthTokenStatus>(oauth_token_status); + HandleUserOAuthTokenStatusChange(user_id, status); + + return status; + } + return User::OAUTH_TOKEN_STATUS_UNKNOWN; +} + +bool UserManagerBase::LoadForceOnlineSignin(const std::string& user_id) const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + const base::DictionaryValue* prefs_force_online = + GetLocalState()->GetDictionary(kUserForceOnlineSignin); + bool force_online_signin = false; + if (prefs_force_online) { + prefs_force_online->GetBooleanWithoutPathExpansion(user_id, + &force_online_signin); + } + return force_online_signin; +} + +void UserManagerBase::RemoveNonCryptohomeData(const std::string& user_id) { + PrefService* prefs = GetLocalState(); + DictionaryPrefUpdate prefs_display_name_update(prefs, kUserDisplayName); + prefs_display_name_update->RemoveWithoutPathExpansion(user_id, NULL); + + DictionaryPrefUpdate prefs_given_name_update(prefs, kUserGivenName); + prefs_given_name_update->RemoveWithoutPathExpansion(user_id, NULL); + + DictionaryPrefUpdate prefs_display_email_update(prefs, kUserDisplayEmail); + prefs_display_email_update->RemoveWithoutPathExpansion(user_id, NULL); + + DictionaryPrefUpdate prefs_oauth_update(prefs, kUserOAuthTokenStatus); + prefs_oauth_update->RemoveWithoutPathExpansion(user_id, NULL); + + DictionaryPrefUpdate prefs_force_online_update(prefs, kUserForceOnlineSignin); + prefs_force_online_update->RemoveWithoutPathExpansion(user_id, NULL); +} + +User* UserManagerBase::RemoveRegularOrSupervisedUserFromList( + const std::string& user_id) { + ListPrefUpdate prefs_users_update(GetLocalState(), kRegularUsers); + prefs_users_update->Clear(); + User* user = NULL; + for (UserList::iterator it = users_.begin(); it != users_.end();) { + const std::string user_email = (*it)->email(); + if (user_email == user_id) { + user = *it; + it = users_.erase(it); + } else { + if ((*it)->GetType() == USER_TYPE_REGULAR || + (*it)->GetType() == USER_TYPE_SUPERVISED) { + prefs_users_update->Append(new base::StringValue(user_email)); + } + ++it; + } + } + return user; +} + +void UserManagerBase::NotifyActiveUserChanged(const User* active_user) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + FOR_EACH_OBSERVER(UserManager::UserSessionStateObserver, + session_state_observer_list_, + ActiveUserChanged(active_user)); +} + +void UserManagerBase::NotifyUserAddedToSession(const User* added_user, + bool user_switch_pending) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + FOR_EACH_OBSERVER(UserManager::UserSessionStateObserver, + session_state_observer_list_, + UserAddedToSession(added_user)); +} + +void UserManagerBase::NotifyActiveUserHashChanged(const std::string& hash) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + FOR_EACH_OBSERVER(UserManager::UserSessionStateObserver, + session_state_observer_list_, + ActiveUserHashChanged(hash)); +} + +void UserManagerBase::UpdateLoginState() { + if (!chromeos::LoginState::IsInitialized()) + return; // LoginState may not be intialized in tests. + + chromeos::LoginState::LoggedInState logged_in_state; + logged_in_state = active_user_ ? chromeos::LoginState::LOGGED_IN_ACTIVE + : chromeos::LoginState::LOGGED_IN_NONE; + + chromeos::LoginState::LoggedInUserType login_user_type; + if (logged_in_state == chromeos::LoginState::LOGGED_IN_NONE) + login_user_type = chromeos::LoginState::LOGGED_IN_USER_NONE; + else if (is_current_user_owner_) + login_user_type = chromeos::LoginState::LOGGED_IN_USER_OWNER; + else if (active_user_->GetType() == USER_TYPE_GUEST) + login_user_type = chromeos::LoginState::LOGGED_IN_USER_GUEST; + else if (active_user_->GetType() == USER_TYPE_RETAIL_MODE) + login_user_type = chromeos::LoginState::LOGGED_IN_USER_RETAIL_MODE; + else if (active_user_->GetType() == USER_TYPE_PUBLIC_ACCOUNT) + login_user_type = chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT; + else if (active_user_->GetType() == USER_TYPE_SUPERVISED) + login_user_type = chromeos::LoginState::LOGGED_IN_USER_SUPERVISED; + else if (active_user_->GetType() == USER_TYPE_KIOSK_APP) + login_user_type = chromeos::LoginState::LOGGED_IN_USER_KIOSK_APP; + else + login_user_type = chromeos::LoginState::LOGGED_IN_USER_REGULAR; + + if (primary_user_) { + chromeos::LoginState::Get()->SetLoggedInStateAndPrimaryUser( + logged_in_state, login_user_type, primary_user_->username_hash()); + } else { + chromeos::LoginState::Get()->SetLoggedInState(logged_in_state, + login_user_type); + } +} + +void UserManagerBase::SetLRUUser(User* user) { + UserList::iterator it = + std::find(lru_logged_in_users_.begin(), lru_logged_in_users_.end(), user); + if (it != lru_logged_in_users_.end()) + lru_logged_in_users_.erase(it); + lru_logged_in_users_.insert(lru_logged_in_users_.begin(), user); +} + +void UserManagerBase::SendRegularUserLoginMetrics(const std::string& user_id) { + // If this isn't the first time Chrome was run after the system booted, + // assume that Chrome was restarted because a previous session ended. + if (!CommandLine::ForCurrentProcess()->HasSwitch( + chromeos::switches::kFirstExecAfterBoot)) { + const std::string last_email = + GetLocalState()->GetString(kLastLoggedInRegularUser); + const base::TimeDelta time_to_login = + base::TimeTicks::Now() - manager_creation_time_; + if (!last_email.empty() && user_id != last_email && + time_to_login.InSeconds() <= kLogoutToLoginDelayMaxSec) { + UMA_HISTOGRAM_CUSTOM_COUNTS("UserManager.LogoutToLoginDelay", + time_to_login.InSeconds(), + 0, + kLogoutToLoginDelayMaxSec, + 50); + } + } +} + +void UserManagerBase::UpdateUserAccountLocale(const std::string& user_id, + const std::string& locale) { + if (!locale.empty() && locale != GetApplicationLocale()) { + base::Callback<void(const std::string&)> on_resolve_callback = + base::Bind(&UserManagerBase::DoUpdateAccountLocale, + weak_factory_.GetWeakPtr(), + user_id); + blocking_task_runner_->PostTask(FROM_HERE, + base::Bind(&UserManagerBase::ResolveLocale, + weak_factory_.GetWeakPtr(), + locale, + on_resolve_callback)); + } else { + DoUpdateAccountLocale(user_id, locale); + } +} + +void UserManagerBase::ResolveLocale( + const std::string& raw_locale, + base::Callback<void(const std::string&)> on_resolve_callback) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + std::string resolved_locale; + ignore_result(l10n_util::CheckAndResolveLocale(raw_locale, &resolved_locale)); + task_runner_->PostTask(FROM_HERE, + base::Bind(on_resolve_callback, resolved_locale)); +} + +void UserManagerBase::DoUpdateAccountLocale( + const std::string& user_id, + const std::string& resolved_locale) { + if (User* user = FindUserAndModify(user_id)) + user->SetAccountLocale(resolved_locale); +} + +void UserManagerBase::DeleteUser(User* user) { + const bool is_active_user = (user == active_user_); + delete user; + if (is_active_user) + active_user_ = NULL; +} + +} // namespace user_manager diff --git a/components/user_manager/user_manager_base.h b/components/user_manager/user_manager_base.h new file mode 100644 index 0000000..e7651cb --- /dev/null +++ b/components/user_manager/user_manager_base.h @@ -0,0 +1,387 @@ +// Copyright 2014 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. + +#ifndef COMPONENTS_USER_MANAGER_USER_MANAGER_BASE_H_ +#define COMPONENTS_USER_MANAGER_USER_MANAGER_BASE_H_ + +#include <set> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "base/synchronization/lock.h" +#include "base/time/time.h" +#include "components/user_manager/user.h" +#include "components/user_manager/user_manager.h" +#include "components/user_manager/user_manager_export.h" + +class PrefService; +class PrefRegistrySimple; + +namespace base { +class ListValue; +class TaskRunner; +} + +namespace user_manager { + +class RemoveUserDelegate; + +// Base implementation of the UserManager interface. +class USER_MANAGER_EXPORT UserManagerBase : public UserManager { + public: + UserManagerBase(scoped_refptr<base::TaskRunner> task_runner, + scoped_refptr<base::TaskRunner> blocking_task_runner); + virtual ~UserManagerBase(); + + // Registers UserManagerBase preferences. + static void RegisterPrefs(PrefRegistrySimple* registry); + + // UserManager implementation: + virtual void Shutdown() OVERRIDE; + virtual const UserList& GetUsers() const OVERRIDE; + virtual const UserList& GetLoggedInUsers() const OVERRIDE; + virtual const UserList& GetLRULoggedInUsers() const OVERRIDE; + virtual const std::string& GetOwnerEmail() const OVERRIDE; + virtual void UserLoggedIn(const std::string& user_id, + const std::string& user_id_hash, + bool browser_restart) OVERRIDE; + virtual void SwitchActiveUser(const std::string& user_id) OVERRIDE; + virtual void SessionStarted() OVERRIDE; + virtual void RemoveUser(const std::string& user_id, + RemoveUserDelegate* delegate) OVERRIDE; + virtual void RemoveUserFromList(const std::string& user_id) OVERRIDE; + virtual bool IsKnownUser(const std::string& user_id) const OVERRIDE; + virtual const User* FindUser(const std::string& user_id) const OVERRIDE; + virtual User* FindUserAndModify(const std::string& user_id) OVERRIDE; + virtual const User* GetLoggedInUser() const OVERRIDE; + virtual User* GetLoggedInUser() OVERRIDE; + virtual const User* GetActiveUser() const OVERRIDE; + virtual User* GetActiveUser() OVERRIDE; + virtual const User* GetPrimaryUser() const OVERRIDE; + virtual void SaveUserOAuthStatus( + const std::string& user_id, + User::OAuthTokenStatus oauth_token_status) OVERRIDE; + virtual void SaveForceOnlineSignin(const std::string& user_id, + bool force_online_signin) OVERRIDE; + virtual void SaveUserDisplayName(const std::string& user_id, + const base::string16& display_name) OVERRIDE; + virtual base::string16 GetUserDisplayName( + const std::string& user_id) const OVERRIDE; + virtual void SaveUserDisplayEmail(const std::string& user_id, + const std::string& display_email) OVERRIDE; + virtual std::string GetUserDisplayEmail( + const std::string& user_id) const OVERRIDE; + virtual void UpdateUserAccountData( + const std::string& user_id, + const UserAccountData& account_data) OVERRIDE; + virtual bool IsCurrentUserOwner() const OVERRIDE; + virtual bool IsCurrentUserNew() const OVERRIDE; + virtual bool IsCurrentUserNonCryptohomeDataEphemeral() const OVERRIDE; + virtual bool CanCurrentUserLock() const OVERRIDE; + virtual bool IsUserLoggedIn() const OVERRIDE; + virtual bool IsLoggedInAsRegularUser() const OVERRIDE; + virtual bool IsLoggedInAsDemoUser() const OVERRIDE; + virtual bool IsLoggedInAsPublicAccount() const OVERRIDE; + virtual bool IsLoggedInAsGuest() const OVERRIDE; + virtual bool IsLoggedInAsSupervisedUser() const OVERRIDE; + virtual bool IsLoggedInAsKioskApp() const OVERRIDE; + virtual bool IsLoggedInAsStub() const OVERRIDE; + virtual bool IsSessionStarted() const OVERRIDE; + virtual bool IsUserNonCryptohomeDataEphemeral( + const std::string& user_id) const OVERRIDE; + virtual void AddObserver(UserManager::Observer* obs) OVERRIDE; + virtual void RemoveObserver(UserManager::Observer* obs) OVERRIDE; + virtual void AddSessionStateObserver( + UserManager::UserSessionStateObserver* obs) OVERRIDE; + virtual void RemoveSessionStateObserver( + UserManager::UserSessionStateObserver* obs) OVERRIDE; + virtual void NotifyLocalStateChanged() OVERRIDE; + + // Helper function that copies users from |users_list| to |users_vector| and + // |users_set|. Duplicates and users already present in |existing_users| are + // skipped. + static void ParseUserList(const base::ListValue& users_list, + const std::set<std::string>& existing_users, + std::vector<std::string>* users_vector, + std::set<std::string>* users_set); + + protected: + UserManagerBase(); + + // Adds |user| to users list, and adds it to front of LRU list. It is assumed + // that there is no user with same id. + virtual void AddUserRecord(User* user); + + // Returns true if trusted device policies have successfully been retrieved + // and ephemeral users are enabled. + virtual bool AreEphemeralUsersEnabled() const = 0; + + // Returns true if user may be removed. + virtual bool CanUserBeRemoved(const User* user) const; + + // A wrapper around C++ delete operator. Deletes |user|, and when |user| + // equals to active_user_, active_user_ is reset to NULL. + virtual void DeleteUser(User* user); + + // Returns the locale used by the application. + virtual const std::string& GetApplicationLocale() const = 0; + + // Returns "Local State" PrefService instance. + virtual PrefService* GetLocalState() const = 0; + + // Loads |users_| from Local State if the list has not been loaded yet. + // Subsequent calls have no effect. Must be called on the UI thread. + void EnsureUsersLoaded(); + + // Handle OAuth token |status| change for |user_id|. + virtual void HandleUserOAuthTokenStatusChange( + const std::string& user_id, + User::OAuthTokenStatus status) const = 0; + + // Returns true if device is enterprise managed. + virtual bool IsEnterpriseManaged() const = 0; + + // Helper function that copies users from |users_list| to |users_vector| and + // |users_set|. Duplicates and users already present in |existing_users| are + // skipped. + // Loads public accounts from the Local state and fills in + // |public_sessions_set|. + virtual void LoadPublicAccounts( + std::set<std::string>* public_sessions_set) = 0; + + // Notifies that user has logged in. + virtual void NotifyOnLogin(); + + // Notifies observers that another user was added to the session. + // If |user_switch_pending| is true this means that user has not been fully + // initialized yet like waiting for profile to be loaded. + virtual void NotifyUserAddedToSession(const User* added_user, + bool user_switch_pending); + + // Performs any additional actions before user list is loaded. + virtual void PerformPreUserListLoadingActions() = 0; + + // Performs any additional actions after user list is loaded. + virtual void PerformPostUserListLoadingActions() = 0; + + // Performs any additional actions after UserLoggedIn() execution has been + // completed. + // |browser_restart| is true when reloading Chrome after crash to distinguish + // from normal sign in flow. + virtual void PerformPostUserLoggedInActions(bool browser_restart) = 0; + + // Implementation for RemoveUser method. It is synchronous. It is called from + // RemoveUserInternal after owner check. + virtual void RemoveNonOwnerUserInternal(const std::string& user_email, + RemoveUserDelegate* delegate); + + // Removes a regular or supervised user from the user list. + // Returns the user if found or NULL otherwise. + // Also removes the user from the persistent user list. + User* RemoveRegularOrSupervisedUserFromList(const std::string& user_id); + + // Implementation for RemoveUser method. This is an asynchronous part of the + // method, that verifies that owner will not get deleted, and calls + // |RemoveNonOwnerUserInternal|. + virtual void RemoveUserInternal(const std::string& user_email, + RemoveUserDelegate* delegate); + + // Removes data stored or cached outside the user's cryptohome (wallpaper, + // avatar, OAuth token status, display name, display email). + virtual void RemoveNonCryptohomeData(const std::string& user_id); + + // Check for a particular user type. + + // Returns true if |user_id| represents demo app. + virtual bool IsDemoApp(const std::string& user_id) const = 0; + + // Returns true if |user_id| represents kiosk app. + virtual bool IsKioskApp(const std::string& user_id) const = 0; + + // Returns true if |user_id| represents public account that has been marked + // for deletion. + virtual bool IsPublicAccountMarkedForRemoval( + const std::string& user_id) const = 0; + + // These methods are called when corresponding user type has signed in. + + // Indicates that the demo account has just logged in. + virtual void DemoAccountLoggedIn() = 0; + + // Indicates that a user just logged in as guest. + virtual void GuestUserLoggedIn(); + + // Indicates that a kiosk app robot just logged in. + virtual void KioskAppLoggedIn(const std::string& app_id) = 0; + + // Indicates that a user just logged into a public session. + virtual void PublicAccountUserLoggedIn(User* user) = 0; + + // Indicates that a regular user just logged in. + virtual void RegularUserLoggedIn(const std::string& user_id); + + // Indicates that a regular user just logged in as ephemeral. + virtual void RegularUserLoggedInAsEphemeral(const std::string& user_id); + + // Indicates that a user just logged into a retail mode session. + virtual void RetailModeUserLoggedIn() = 0; + + // Indicates that a supervised user just logged in. + virtual void SupervisedUserLoggedIn(const std::string& user_id) = 0; + + // Getters/setters for private members. + + virtual void SetCurrentUserIsOwner(bool is_current_user_owner); + + virtual bool GetEphemeralUsersEnabled() const; + virtual void SetEphemeralUsersEnabled(bool enabled); + + virtual void SetIsCurrentUserNew(bool is_new); + + virtual void SetOwnerEmail(std::string owner_user_id); + + virtual const std::string& GetPendingUserSwitchID() const; + virtual void SetPendingUserSwitchID(std::string user_id); + + // The logged-in user that is currently active in current session. + // NULL until a user has logged in, then points to one + // of the User instances in |users_|, the |guest_user_| instance or an + // ephemeral user instance. + User* active_user_; + + // The primary user of the current session. It is recorded for the first + // signed-in user and does not change thereafter. + User* primary_user_; + + // List of all known users. User instances are owned by |this|. Regular users + // are removed by |RemoveUserFromList|, public accounts by + // |UpdateAndCleanUpPublicAccounts|. + UserList users_; + + private: + // Stages of loading user list from preferences. Some methods can have + // different behavior depending on stage. + enum UserLoadStage { STAGE_NOT_LOADED = 0, STAGE_LOADING, STAGE_LOADED }; + + // Returns a list of users who have logged into this device previously. + // Same as GetUsers but used if you need to modify User from that list. + UserList& GetUsersAndModify(); + + // Returns the user with the given email address if found in the persistent + // list. Returns |NULL| otherwise. + const User* FindUserInList(const std::string& user_id) const; + + // Returns |true| if user with the given id is found in the persistent list. + // Returns |false| otherwise. Does not trigger user loading. + const bool UserExistsInList(const std::string& user_id) const; + + // Same as FindUserInList but returns non-const pointer to User object. + User* FindUserInListAndModify(const std::string& user_id); + + // Reads user's oauth token status from local state preferences. + User::OAuthTokenStatus LoadUserOAuthStatus(const std::string& user_id) const; + + // Read a flag indicating whether online authentication against GAIA should + // be enforced during the user's next sign-in from local state preferences. + bool LoadForceOnlineSignin(const std::string& user_id) const; + + // Notifies observers that merge session state had changed. + void NotifyMergeSessionStateChanged(); + + // Notifies observers that active user has changed. + void NotifyActiveUserChanged(const User* active_user); + + // Notifies observers that active user_id hash has changed. + void NotifyActiveUserHashChanged(const std::string& hash); + + // Update the global LoginState. + void UpdateLoginState(); + + // Insert |user| at the front of the LRU user list. + void SetLRUUser(User* user); + + // Sends metrics in response to a regular user logging in. + void SendRegularUserLoginMetrics(const std::string& user_id); + + // Sets account locale for user with id |user_id|. + virtual void UpdateUserAccountLocale(const std::string& user_id, + const std::string& locale); + + // Runs on SequencedWorkerPool thread. Passes resolved locale to + // |on_resolve_callback| on UI thread. + void ResolveLocale( + const std::string& raw_locale, + base::Callback<void(const std::string&)> on_resolve_callback); + + // Updates user account after locale was resolved. + void DoUpdateAccountLocale(const std::string& user_id, + const std::string& resolved_locale); + + // Indicates stage of loading user from prefs. + UserLoadStage user_loading_stage_; + + // List of all users that are logged in current session. These point to User + // instances in |users_|. Only one of them could be marked as active. + UserList logged_in_users_; + + // A list of all users that are logged in the current session. In contrast to + // |logged_in_users|, the order of this list is least recently used so that + // the active user should always be the first one in the list. + UserList lru_logged_in_users_; + + // True if SessionStarted() has been called. + bool session_started_; + + // Cached flag of whether currently logged-in user is owner or not. + // May be accessed on different threads, requires locking. + bool is_current_user_owner_; + mutable base::Lock is_current_user_owner_lock_; + + // Cached flag of whether the currently logged-in user existed before this + // login. + bool is_current_user_new_; + + // Cached flag of whether the currently logged-in user is a regular user who + // logged in as ephemeral. Storage of persistent information is avoided for + // such users by not adding them to the persistent user list, not downloading + // their custom avatars and mounting their cryptohomes using tmpfs. Defaults + // to |false|. + bool is_current_user_ephemeral_regular_user_; + + // Cached flag indicating whether the ephemeral user policy is enabled. + // Defaults to |false| if the value has not been read from trusted device + // policy yet. + bool ephemeral_users_enabled_; + + // Cached name of device owner. Defaults to empty string if the value has not + // been read from trusted device policy yet. + std::string owner_email_; + + ObserverList<UserManager::Observer> observer_list_; + + // TODO(nkostylev): Merge with session state refactoring CL. + ObserverList<UserManager::UserSessionStateObserver> + session_state_observer_list_; + + // Time at which this object was created. + base::TimeTicks manager_creation_time_; + + // ID of the user just added to the session that needs to be activated + // as soon as user's profile is loaded. + std::string pending_user_switch_; + + scoped_refptr<base::TaskRunner> task_runner_; + scoped_refptr<base::TaskRunner> blocking_task_runner_; + + base::WeakPtrFactory<UserManagerBase> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(UserManagerBase); +}; + +} // namespace user_manager + +#endif // COMPONENTS_USER_MANAGER_USER_MANAGER_BASE_H_ |