Log out if user is not allowed after settings are changed

1. This is crucial for the OffHours policy implementation,
   as we need to sign out disallowed users when the OffHours mode
   switches off.
2. This change has a side effect of forcing sign-out when the session
   availability (for instance, the Guest mode being allowed via policy)
   changes to false during that same session.

Logic is similar to function here:
https://cs.chromium.org/chromium/src/chrome/browser/chromeos/login/existing_user_controller.cc?l=350&rcl=6fcdeac565292994e832db25b600331dfb030672

Bug: 739713
Change-Id: Ieac2e7f24a152f323b7abb4bf36242a9fef6edc2
Reviewed-on: https://chromium-review.googlesource.com/704662
Commit-Queue: Daria Iakovleva <[email protected]>
Reviewed-by: Sergey Poromov <[email protected]>
Reviewed-by: Maksim Ivanov <[email protected]>
Reviewed-by: Xiyuan Xia <[email protected]>
Cr-Commit-Position: refs/heads/master@{#508075}
diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc
index 6a7435b..8870bb4 100644
--- a/chrome/browser/chromeos/login/existing_user_controller.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller.cc
@@ -354,6 +354,8 @@
 
   cros_settings_->GetBoolean(kAccountsPrefShowUserNamesOnSignIn,
                              &show_users_on_signin);
+  user_manager::UserManager* const user_manager =
+      user_manager::UserManager::Get();
   for (auto* user : users) {
     // Skip kiosk apps for login screen user list. Kiosk apps as pods (aka new
     // kiosk UI) is currently disabled and it gets the apps directly from
@@ -362,15 +364,12 @@
         user->GetType() == user_manager::USER_TYPE_ARC_KIOSK_APP) {
       continue;
     }
-
     // TODO(xiyuan): Clean user profile whose email is not in whitelist.
     const bool meets_supervised_requirements =
         user->GetType() != user_manager::USER_TYPE_SUPERVISED ||
-        user_manager::UserManager::Get()->AreSupervisedUsersAllowed();
+        user_manager->AreSupervisedUsersAllowed();
     const bool meets_whitelist_requirements =
-        CrosSettings::IsWhitelisted(user->GetAccountId().GetUserEmail(),
-                                    nullptr) ||
-        !user->HasGaiaAccount();
+        !user->HasGaiaAccount() || user_manager->IsGaiaUserAllowed(*user);
 
     // Public session accounts are always shown on login screen.
     const bool meets_show_users_requirements =
@@ -384,8 +383,7 @@
 
   // If no user pods are visible, fallback to single new user pod which will
   // have guest session link.
-  bool show_guest;
-  cros_settings_->GetBoolean(kAccountsPrefAllowGuest, &show_guest);
+  bool show_guest = user_manager->IsGuestSessionAllowed();
   show_users_on_signin |= !filtered_users.empty();
   show_guest &= !filtered_users.empty();
   bool allow_new_user = true;
@@ -1202,8 +1200,7 @@
   PerformPreLoginActions(UserContext(user_manager::USER_TYPE_GUEST,
                                      user_manager::GuestAccountId()));
 
-  bool allow_guest;
-  cros_settings_->GetBoolean(kAccountsPrefAllowGuest, &allow_guest);
+  bool allow_guest = user_manager::UserManager::Get()->IsGuestSessionAllowed();
   if (!allow_guest) {
     // Disallowed. The UI should normally not show the guest session button.
     LOG(ERROR) << "Guest login attempt when guest mode is disallowed.";
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index dd9d32f..a463a7d 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -405,6 +405,7 @@
       weak_factory_(this) {
   net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
   user_manager::UserManager::Get()->AddSessionStateObserver(this);
+  user_manager::UserManager::Get()->AddObserver(this);
 }
 
 UserSessionManager::~UserSessionManager() {
@@ -412,8 +413,10 @@
   // still exists.
   // TODO(nkostylev): fix order of destruction of UserManager
   // / UserSessionManager objects.
-  if (user_manager::UserManager::IsInitialized())
+  if (user_manager::UserManager::IsInitialized()) {
     user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
+    user_manager::UserManager::Get()->RemoveObserver(this);
+  }
   net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
 }
 
@@ -941,6 +944,25 @@
   RestorePendingUserSessions();
 }
 
+void UserSessionManager::OnUsersSignInConstraintsChanged() {
+  const user_manager::UserManager* user_manager =
+      user_manager::UserManager::Get();
+  const user_manager::UserList& logged_in_users =
+      user_manager->GetLoggedInUsers();
+  for (auto* user : logged_in_users) {
+    if (user->GetType() != user_manager::USER_TYPE_REGULAR &&
+        user->GetType() != user_manager::USER_TYPE_GUEST &&
+        user->GetType() != user_manager::USER_TYPE_SUPERVISED &&
+        user->GetType() != user_manager::USER_TYPE_CHILD) {
+      continue;
+    }
+    if (!user_manager->IsUserAllowed(*user)) {
+      LOG(ERROR) << "The current user is not allowed, terminating the session.";
+      chrome::AttemptUserExit();
+    }
+  }
+}
+
 void UserSessionManager::ChildAccountStatusReceivedCallback(Profile* profile) {
   StopChildStatusObserving(profile);
 }
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.h b/chrome/browser/chromeos/login/session/user_session_manager.h
index 5f0127c..f8fd7a5 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.h
+++ b/chrome/browser/chromeos/login/session/user_session_manager.h
@@ -21,6 +21,7 @@
 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_notification_controller.h"
 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager.h"
 #include "chrome/browser/chromeos/login/signin/token_handle_util.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chromeos/dbus/session_manager_client.h"
 #include "chromeos/login/auth/authenticator.h"
 #include "chromeos/login/auth/user_context.h"
@@ -86,7 +87,8 @@
       public net::NetworkChangeNotifier::NetworkChangeObserver,
       public base::SupportsWeakPtr<UserSessionManager>,
       public UserSessionManagerDelegate,
-      public user_manager::UserManager::UserSessionStateObserver {
+      public user_manager::UserManager::UserSessionStateObserver,
+      public user_manager::UserManager::Observer {
  public:
   // Context of StartSession calls.
   typedef enum {
@@ -276,6 +278,9 @@
   // Used when restoring user sessions after crash.
   void OnProfilePrepared(Profile* profile, bool browser_launched) override;
 
+  // user_manager::UserManager::Observer overrides:
+  void OnUsersSignInConstraintsChanged() override;
+
   void ChildAccountStatusReceivedCallback(Profile* profile);
 
   void StopChildStatusObserving(Profile* profile);
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
index 50bb5a8..379d559 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -224,6 +224,20 @@
                        weak_factory_.GetWeakPtr()));
   }
 
+  allow_guest_subscription_ = cros_settings_->AddSettingsObserver(
+      kAccountsPrefAllowGuest,
+      base::Bind(&UserManager::NotifyUsersSignInConstraintsChanged,
+                 weak_factory_.GetWeakPtr()));
+  allow_supervised_user_subscription_ = cros_settings_->AddSettingsObserver(
+      kAccountsPrefSupervisedUsersEnabled,
+      base::Bind(&UserManager::NotifyUsersSignInConstraintsChanged,
+                 weak_factory_.GetWeakPtr()));
+  // user whitelist
+  users_subscription_ = cros_settings_->AddSettingsObserver(
+      kAccountsPrefUsers,
+      base::Bind(&UserManager::NotifyUsersSignInConstraintsChanged,
+                 weak_factory_.GetWeakPtr()));
+
   local_accounts_subscription_ = cros_settings_->AddSettingsObserver(
       kAccountsPrefDeviceLocalAccounts,
       base::Bind(&ChromeUserManagerImpl::RetrieveTrustedDevicePolicies,
@@ -1159,6 +1173,37 @@
   return supervised_users_allowed;
 }
 
+bool ChromeUserManagerImpl::IsGuestSessionAllowed() const {
+  bool is_guest_allowed = false;
+  cros_settings_->GetBoolean(kAccountsPrefAllowGuest, &is_guest_allowed);
+  return is_guest_allowed;
+}
+
+bool ChromeUserManagerImpl::IsGaiaUserAllowed(
+    const user_manager::User& user) const {
+  DCHECK(user.HasGaiaAccount());
+  return CrosSettings::IsWhitelisted(user.GetAccountId().GetUserEmail(),
+                                     nullptr);
+}
+
+bool ChromeUserManagerImpl::IsUserAllowed(
+    const user_manager::User& user) const {
+  DCHECK(user.GetType() == user_manager::USER_TYPE_REGULAR ||
+         user.GetType() == user_manager::USER_TYPE_GUEST ||
+         user.GetType() == user_manager::USER_TYPE_SUPERVISED ||
+         user.GetType() == user_manager::USER_TYPE_CHILD);
+
+  if (user.GetType() == user_manager::USER_TYPE_GUEST &&
+      !IsGuestSessionAllowed())
+    return false;
+  if (user.GetType() == user_manager::USER_TYPE_SUPERVISED &&
+      !AreSupervisedUsersAllowed())
+    return false;
+  if (user.HasGaiaAccount() && !IsGaiaUserAllowed(user))
+    return false;
+  return true;
+}
+
 UserFlow* ChromeUserManagerImpl::GetDefaultUserFlow() const {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!default_flow_.get())
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.h b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.h
index 12aefa83..1177c60 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.h
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.h
@@ -88,6 +88,9 @@
   bool IsUserNonCryptohomeDataEphemeral(
       const AccountId& account_id) const override;
   bool AreSupervisedUsersAllowed() const override;
+  bool IsGuestSessionAllowed() const override;
+  bool IsGaiaUserAllowed(const user_manager::User& user) const override;
+  bool IsUserAllowed(const user_manager::User& user) const override;
   void UpdateLoginState(const user_manager::User* active_user,
                         const user_manager::User* primary_user,
                         bool is_current_user_owner) const override;
@@ -258,6 +261,12 @@
   // access.
   FlowMap specific_flows_;
 
+  // Cros settings change subscriptions.
+  std::unique_ptr<CrosSettings::ObserverSubscription> allow_guest_subscription_;
+  std::unique_ptr<CrosSettings::ObserverSubscription>
+      allow_supervised_user_subscription_;
+  std::unique_ptr<CrosSettings::ObserverSubscription> users_subscription_;
+
   std::unique_ptr<CrosSettings::ObserverSubscription>
       local_accounts_subscription_;
 
diff --git a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
index 00c64a2..9f8cccd 100644
--- a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
+++ b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
@@ -569,6 +569,37 @@
   return true;
 }
 
+bool FakeChromeUserManager::IsGuestSessionAllowed() const {
+  bool is_guest_allowed = false;
+  CrosSettings::Get()->GetBoolean(kAccountsPrefAllowGuest, &is_guest_allowed);
+  return is_guest_allowed;
+}
+
+bool FakeChromeUserManager::IsGaiaUserAllowed(
+    const user_manager::User& user) const {
+  DCHECK(user.HasGaiaAccount());
+  return CrosSettings::IsWhitelisted(user.GetAccountId().GetUserEmail(),
+                                     nullptr);
+}
+
+bool FakeChromeUserManager::IsUserAllowed(
+    const user_manager::User& user) const {
+  DCHECK(user.GetType() == user_manager::USER_TYPE_REGULAR ||
+         user.GetType() == user_manager::USER_TYPE_GUEST ||
+         user.GetType() == user_manager::USER_TYPE_SUPERVISED ||
+         user.GetType() == user_manager::USER_TYPE_CHILD);
+
+  if (user.GetType() == user_manager::USER_TYPE_GUEST &&
+      !IsGuestSessionAllowed())
+    return false;
+  if (user.GetType() == user_manager::USER_TYPE_SUPERVISED &&
+      !AreSupervisedUsersAllowed())
+    return false;
+  if (user.HasGaiaAccount() && !IsGaiaUserAllowed(user))
+    return false;
+  return true;
+}
+
 void FakeChromeUserManager::CreateLocalState() {
   local_state_ = base::MakeUnique<TestingPrefServiceSimple>();
   user_manager::known_user::RegisterPrefs(local_state_->registry());
diff --git a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
index e224a35b..bd026b09 100644
--- a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
+++ b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
@@ -106,6 +106,9 @@
       const AccountId& account_id) const override;
   void ChangeUserChildStatus(user_manager::User* user, bool is_child) override;
   bool AreSupervisedUsersAllowed() const override;
+  bool IsGuestSessionAllowed() const override;
+  bool IsGaiaUserAllowed(const user_manager::User& user) const override;
+  bool IsUserAllowed(const user_manager::User& user) const override;
   PrefService* GetLocalState() const override;
   bool GetPlatformKnownUserId(const std::string& user_email,
                               const std::string& gaia_id,
diff --git a/chrome/browser/chromeos/login/users/mock_user_manager.h b/chrome/browser/chromeos/login/users/mock_user_manager.h
index 7b56c15..baa23656 100644
--- a/chrome/browser/chromeos/login/users/mock_user_manager.h
+++ b/chrome/browser/chromeos/login/users/mock_user_manager.h
@@ -76,6 +76,9 @@
                void(UserManager::UserSessionStateObserver*));
   MOCK_METHOD0(NotifyLocalStateChanged, void(void));
   MOCK_CONST_METHOD0(AreSupervisedUsersAllowed, bool(void));
+  MOCK_CONST_METHOD0(IsGuestSessionAllowed, bool(void));
+  MOCK_CONST_METHOD1(IsGaiaUserAllowed, bool(const user_manager::User& user));
+  MOCK_CONST_METHOD1(IsUserAllowed, bool(const user_manager::User& user));
   MOCK_CONST_METHOD3(UpdateLoginState,
                      void(const user_manager::User*,
                           const user_manager::User*,
diff --git a/components/user_manager/fake_user_manager.cc b/components/user_manager/fake_user_manager.cc
index 33e77cb1..33af609 100644
--- a/components/user_manager/fake_user_manager.cc
+++ b/components/user_manager/fake_user_manager.cc
@@ -261,6 +261,18 @@
   return true;
 }
 
+bool FakeUserManager::IsGuestSessionAllowed() const {
+  return true;
+}
+
+bool FakeUserManager::IsGaiaUserAllowed(const user_manager::User& user) const {
+  return true;
+}
+
+bool FakeUserManager::IsUserAllowed(const user_manager::User& user) const {
+  return true;
+}
+
 bool FakeUserManager::AreEphemeralUsersEnabled() const {
   return GetEphemeralUsersEnabled();
 }
diff --git a/components/user_manager/fake_user_manager.h b/components/user_manager/fake_user_manager.h
index c2064f4..4956a71 100644
--- a/components/user_manager/fake_user_manager.h
+++ b/components/user_manager/fake_user_manager.h
@@ -98,6 +98,9 @@
   void RemoveSessionStateObserver(UserSessionStateObserver* obs) override {}
   void NotifyLocalStateChanged() override {}
   bool AreSupervisedUsersAllowed() const override;
+  bool IsGuestSessionAllowed() const override;
+  bool IsGaiaUserAllowed(const user_manager::User& user) const override;
+  bool IsUserAllowed(const user_manager::User& user) const override;
   void UpdateLoginState(const user_manager::User* active_user,
                         const user_manager::User* primary_user,
                         bool is_current_user_owner) const override;
diff --git a/components/user_manager/user_manager.cc b/components/user_manager/user_manager.cc
index 27cea02..032ffa49 100644
--- a/components/user_manager/user_manager.cc
+++ b/components/user_manager/user_manager.cc
@@ -25,6 +25,8 @@
 
 void UserManager::Observer::OnChildStatusChanged(const User& user) {}
 
+void UserManager::Observer::OnUsersSignInConstraintsChanged() {}
+
 void UserManager::UserSessionStateObserver::ActiveUserChanged(
     const User* active_user) {
 }
diff --git a/components/user_manager/user_manager.h b/components/user_manager/user_manager.h
index e58a3f86..be6bcfd 100644
--- a/components/user_manager/user_manager.h
+++ b/components/user_manager/user_manager.h
@@ -54,6 +54,10 @@
     // Called when the child status of the given user has changed.
     virtual void OnChildStatusChanged(const User& user);
 
+    // Called when any of the device cros settings which are responsible for
+    // user sign in are changed.
+    virtual void OnUsersSignInConstraintsChanged();
+
    protected:
     virtual ~Observer();
   };
@@ -325,6 +329,7 @@
   virtual void NotifyUserProfileImageUpdated(
       const User& user,
       const gfx::ImageSkia& profile_image) = 0;
+  virtual void NotifyUsersSignInConstraintsChanged() = 0;
 
   // Changes the child status and notifies observers.
   virtual void ChangeUserChildStatus(User* user, bool is_child) = 0;
@@ -336,6 +341,19 @@
   // Returns true if supervised users allowed.
   virtual bool AreSupervisedUsersAllowed() const = 0;
 
+  // Returns true if guest user is allowed.
+  virtual bool IsGuestSessionAllowed() const = 0;
+
+  // Returns true if the |user|, which has a GAIA account is allowed according
+  // to device settings and policies.
+  // Accept only users who has gaia account.
+  virtual bool IsGaiaUserAllowed(const User& user) const = 0;
+
+  // Returns true if |user| is allowed depending on device policies.
+  // Accepted user types: USER_TYPE_REGULAR, USER_TYPE_GUEST,
+  // USER_TYPE_SUPERVISED, USER_TYPE_CHILD.
+  virtual bool IsUserAllowed(const User& user) const = 0;
+
   // Returns "Local State" PrefService instance.
   virtual PrefService* GetLocalState() const = 0;
 
diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc
index 40cbd3e..6d1bda1 100644
--- a/components/user_manager/user_manager_base.cc
+++ b/components/user_manager/user_manager_base.cc
@@ -699,6 +699,12 @@
     observer.OnUserProfileImageUpdated(user, profile_image);
 }
 
+void UserManagerBase::NotifyUsersSignInConstraintsChanged() {
+  DCHECK(!task_runner_ || task_runner_->RunsTasksInCurrentSequence());
+  for (auto& observer : observer_list_)
+    observer.OnUsersSignInConstraintsChanged();
+}
+
 bool UserManagerBase::CanUserBeRemoved(const User* user) const {
   // Only regular and supervised users are allowed to be manually removed.
   if (!user ||
diff --git a/components/user_manager/user_manager_base.h b/components/user_manager/user_manager_base.h
index 686f990..5b8eb90 100644
--- a/components/user_manager/user_manager_base.h
+++ b/components/user_manager/user_manager_base.h
@@ -110,6 +110,7 @@
   void NotifyUserProfileImageUpdated(
       const User& user,
       const gfx::ImageSkia& profile_image) override;
+  void NotifyUsersSignInConstraintsChanged() override;
   void ChangeUserChildStatus(User* user, bool is_child) override;
   void ResetProfileEverInitialized(const AccountId& account_id) override;
   void Initialize() override;