summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-24 18:31:26 +0000
committerxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-24 18:31:26 +0000
commitd600187ba6d240bc0c9f8d3b8e4d702ae3e71bae (patch)
tree267f567f7cfd48ed91c7429230833880c59f29cd /chrome
parentd6c6748f84b8311e085e19cc57ebbe0646cd122f (diff)
downloadchromium_src-d600187ba6d240bc0c9f8d3b8e4d702ae3e71bae.zip
chromium_src-d600187ba6d240bc0c9f8d3b8e4d702ae3e71bae.tar.gz
chromium_src-d600187ba6d240bc0c9f8d3b8e4d702ae3e71bae.tar.bz2
cros: Add a policy for multiprofile user behavior.
- Add a user policy "ChromeOsMultiProfileUserBehavior" that binds to user pref "settings.multiprofile_user_behavior"; - Add a MultiProfileUserController that caches the pref value in local state to make decisions before user actually signs in and also observe the pref change to update the cache and check if the policy is broken; BUG=288764 R=dpolukhin@chromium.org, joaodasilva@chromium.org, mnissler@chromium.org, sky@chromium.org Review URL: https://codereview.chromium.org/23856014 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@225039 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/app/policy/policy_templates.json52
-rw-r--r--chrome/browser/chromeos/login/fake_login_utils.cc5
-rw-r--r--chrome/browser/chromeos/login/fake_user_manager.cc12
-rw-r--r--chrome/browser/chromeos/login/fake_user_manager.h8
-rw-r--r--chrome/browser/chromeos/login/multi_profile_user_controller.cc161
-rw-r--r--chrome/browser/chromeos/login/multi_profile_user_controller.h77
-rw-r--r--chrome/browser/chromeos/login/multi_profile_user_controller_delegate.h21
-rw-r--r--chrome/browser/chromeos/login/multi_profile_user_controller_unittest.cc266
-rw-r--r--chrome/browser/chromeos/login/user_manager_impl.cc20
-rw-r--r--chrome/browser/chromeos/login/user_manager_impl.h10
-rw-r--r--chrome/browser/chromeos/login/user_manager_unittest.cc15
-rw-r--r--chrome/browser/policy/configuration_policy_handler_list.cc3
-rw-r--r--chrome/browser/prefs/browser_prefs.cc3
-rw-r--r--chrome/chrome_browser_chromeos.gypi5
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--chrome/common/pref_names.cc9
-rw-r--r--chrome/common/pref_names.h2
-rw-r--r--chrome/test/base/testing_profile.cc4
-rw-r--r--chrome/test/base/testing_profile.h6
-rw-r--r--chrome/test/data/policy/policy_test_cases.json10
20 files changed, 671 insertions, 19 deletions
diff --git a/chrome/app/policy/policy_templates.json b/chrome/app/policy/policy_templates.json
index 59aa304..c65023a 100644
--- a/chrome/app/policy/policy_templates.json
+++ b/chrome/app/policy/policy_templates.json
@@ -118,7 +118,7 @@
# persistent IDs for all fields (but not for groups!) are needed. These are
# specified by the 'id' keys of each policy. NEVER CHANGE EXISTING IDs,
# because doing so would break the deployed wire format!
-# For your editing convenience: highest ID currently used: 243
+# For your editing convenience: highest ID currently used: 244
#
# Placeholders:
# The following placeholder strings are automatically substituted:
@@ -2747,6 +2747,56 @@
If the policy is left not set the user can choose whether he wants to be asked for password to unlock the device or not.''',
},
{
+ 'name': 'ChromeOsMultiProfileUserBehavior',
+ 'type': 'string-enum',
+ 'schema': {
+ 'type': 'string',
+ 'enum': [
+ 'unrestricted',
+ 'primary-only',
+ 'not-allowed',
+ ],
+ },
+ 'items': [
+ {
+ 'name': 'MultiProfileUserBehaviorUnrestricted',
+ 'value': 'unrestricted',
+ 'caption': '''Allow enterprise user to be both primary and secondary (Default behavior)''',
+ },
+ {
+ 'name': 'MultiProfileUserBehaviorMustBePrimary',
+ 'value': 'primary-only',
+ 'caption': '''Allow enterprise user to be primary multiprofile user only''',
+ },
+ {
+ 'name': 'MultiProfileUserBehaviorNotAllowed',
+ 'value': 'not-allowed',
+ 'caption': '''Do not allow enterprise user to be part of multiprofile (primary or secondary)''',
+ },
+ ],
+ 'supported_on': ['chrome_os:31-'],
+ 'features': {
+ 'dynamic_refresh': True,
+ 'per_profile': True,
+ },
+ 'example_value': 'unrestricted',
+ 'id': 244,
+ 'caption': '''Control the user behavior in a multiprofile session''',
+ 'desc': '''Control the user behavior in a multiprofile session on <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> devices.
+
+ If this policy is set to 'MultiProfileUserBehaviorUnrestricted', the user can be either primary or secondary user in a multiprofile session.
+
+ If this policy is set to 'MultiProfileUserBehaviorMustBePrimary', the user can only be the primary user in a multiprofile session.
+
+ If this policy is set to 'MultiProfileUserBehaviorNotAllowed', the user cannot be part of a multiprofile session.
+
+ If you set this setting, users cannot change or override it.
+
+ If the setting is changed while the user is signed into a multiprofile session, all users in the session will be checked against their cooresponding settings. The session will be closed if any one of the users is no longer allowed to be in the session.
+
+ If the policy is left not set, the default value 'MultiProfileUserBehaviorUnrestricted' will be used.''',
+ },
+ {
'name': 'InstantEnabled',
'type': 'main',
'schema': { 'type': 'boolean' },
diff --git a/chrome/browser/chromeos/login/fake_login_utils.cc b/chrome/browser/chromeos/login/fake_login_utils.cc
index 91100d3..555b581 100644
--- a/chrome/browser/chromeos/login/fake_login_utils.cc
+++ b/chrome/browser/chromeos/login/fake_login_utils.cc
@@ -63,7 +63,10 @@ void FakeLoginUtils::PrepareProfile(const UserContext& user_context,
if (should_launch_browser_) {
profile = CreateProfile(user_context.username);
} else {
- profile = new TestingProfile();
+ TestingProfile* testing_profile = new TestingProfile();
+ testing_profile->set_profile_name(user_context.username);
+
+ profile = testing_profile;
g_browser_process->profile_manager()->
RegisterTestingProfile(profile, false, false);
}
diff --git a/chrome/browser/chromeos/login/fake_user_manager.cc b/chrome/browser/chromeos/login/fake_user_manager.cc
index cdfdba1..b0a139b 100644
--- a/chrome/browser/chromeos/login/fake_user_manager.cc
+++ b/chrome/browser/chromeos/login/fake_user_manager.cc
@@ -13,7 +13,8 @@ static const char kUserIdHashSuffix[] = "-hash";
namespace chromeos {
-FakeUserManager::FakeUserManager() {}
+FakeUserManager::FakeUserManager() : primary_user_(NULL) {}
+
FakeUserManager::~FakeUserManager() {
// Can't use STLDeleteElements because of the private destructor of User.
for (UserList::iterator it = user_list_.begin(); it != user_list_.end();
@@ -29,6 +30,10 @@ void FakeUserManager::AddUser(const std::string& email) {
user_list_.push_back(user);
}
+void FakeUserManager::LoginUser(const std::string& email) {
+ UserLoggedIn(email, email + kUserIdHashSuffix, false);
+}
+
const UserList& FakeUserManager::GetUsers() const {
return user_list_;
}
@@ -57,6 +62,9 @@ void FakeUserManager::UserLoggedIn(const std::string& email,
if ((*it)->username_hash() == username_hash) {
(*it)->set_is_logged_in(true);
logged_in_users_.push_back(*it);
+
+ if (!primary_user_)
+ primary_user_ = *it;
break;
}
}
@@ -140,7 +148,7 @@ User* FakeUserManager::GetLoggedInUser() {
}
const User* FakeUserManager::GetPrimaryUser() const {
- return NULL;
+ return primary_user_;
}
string16 FakeUserManager::GetUserDisplayName(
diff --git a/chrome/browser/chromeos/login/fake_user_manager.h b/chrome/browser/chromeos/login/fake_user_manager.h
index 45edd63..a74f820 100644
--- a/chrome/browser/chromeos/login/fake_user_manager.h
+++ b/chrome/browser/chromeos/login/fake_user_manager.h
@@ -24,6 +24,9 @@ class FakeUserManager : public UserManager {
// Create and add a new user.
void AddUser(const std::string& email);
+ // Calculates the user name hash and calls UserLoggedIn to login a user.
+ void LoginUser(const std::string& email);
+
// UserManager overrides.
virtual const UserList& GetUsers() const OVERRIDE;
virtual UserList GetUsersAdmittedForMultiProfile() const OVERRIDE;
@@ -126,10 +129,15 @@ class FakeUserManager : public UserManager {
virtual base::FilePath GetUserProfileDir(const std::string& email) const
OVERRIDE;
+ void set_owner_email(const std::string& owner_email) {
+ owner_email_ = owner_email;
+ }
+
private:
UserList user_list_;
UserList logged_in_users_;
std::string owner_email_;
+ User* primary_user_;
DISALLOW_COPY_AND_ASSIGN(FakeUserManager);
};
diff --git a/chrome/browser/chromeos/login/multi_profile_user_controller.cc b/chrome/browser/chromeos/login/multi_profile_user_controller.cc
new file mode 100644
index 0000000..8ba170d
--- /dev/null
+++ b/chrome/browser/chromeos/login/multi_profile_user_controller.cc
@@ -0,0 +1,161 @@
+// Copyright 2013 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 "chrome/browser/chromeos/login/multi_profile_user_controller.h"
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/prefs/pref_change_registrar.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/pref_service.h"
+#include "chrome/browser/chromeos/login/multi_profile_user_controller_delegate.h"
+#include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/prefs/pref_service_syncable.h"
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+
+namespace chromeos {
+
+namespace {
+
+std::string SanitizeBehaviorValue(const std::string& value) {
+ if (value == MultiProfileUserController::kBehaviorUnrestricted ||
+ value == MultiProfileUserController::kBehaviorPrimaryOnly ||
+ value == MultiProfileUserController::kBehaviorNotAllowed) {
+ return value;
+ }
+
+ return std::string(MultiProfileUserController::kBehaviorUnrestricted);
+}
+
+} // namespace
+
+// static
+const char MultiProfileUserController::kBehaviorUnrestricted[] = "unrestricted";
+const char MultiProfileUserController::kBehaviorPrimaryOnly[] = "primary-only";
+const char MultiProfileUserController::kBehaviorNotAllowed[] = "not-allowed";
+
+MultiProfileUserController::MultiProfileUserController(
+ MultiProfileUserControllerDelegate* delegate,
+ PrefService* local_state)
+ : delegate_(delegate),
+ local_state_(local_state) {
+}
+
+MultiProfileUserController::~MultiProfileUserController() {}
+
+// static
+void MultiProfileUserController::RegisterPrefs(
+ PrefRegistrySimple* registry) {
+ registry->RegisterDictionaryPref(prefs::kCachedMultiProfileUserBehavior);
+}
+
+// static
+void MultiProfileUserController::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterStringPref(
+ prefs::kMultiProfileUserBehavior,
+ kBehaviorUnrestricted,
+ user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+}
+
+bool MultiProfileUserController::IsUserAllowedInSession(
+ const std::string& user_email) const {
+ UserManager* user_manager = UserManager::Get();
+ CHECK(user_manager);
+
+ std::string primary_user_email;
+ if (user_manager->GetPrimaryUser())
+ primary_user_email = user_manager->GetPrimaryUser()->email();
+
+ // Always allow if there is no primary user or user being checked is the
+ // primary user.
+ if (primary_user_email.empty() || primary_user_email == user_email)
+ return true;
+
+ // Owner is not allowed to be secondary user.
+ if (user_manager->GetOwnerEmail() == user_email)
+ return false;
+
+ // No user is allowed if the primary user policy forbids it.
+ const std::string primary_user_behavior = GetCachedValue(primary_user_email);
+ if (primary_user_behavior == kBehaviorNotAllowed)
+ return false;
+
+ // The user must have 'unrestricted' policy to be a secondary user.
+ const std::string behavior = GetCachedValue(user_email);
+ return behavior == kBehaviorUnrestricted;
+}
+
+void MultiProfileUserController::StartObserving(Profile* user_profile) {
+ // Profile name could be empty during tests.
+ if (user_profile->GetProfileName().empty())
+ return;
+
+ scoped_ptr<PrefChangeRegistrar> registrar(new PrefChangeRegistrar);
+ registrar->Init(user_profile->GetPrefs());
+ registrar->Add(
+ prefs::kMultiProfileUserBehavior,
+ base::Bind(&MultiProfileUserController::OnUserPrefChanged,
+ base::Unretained(this),
+ user_profile));
+ pref_watchers_.push_back(registrar.release());
+
+ OnUserPrefChanged(user_profile);
+}
+
+void MultiProfileUserController::RemoveCachedValue(
+ const std::string& user_email) {
+ DictionaryPrefUpdate update(local_state_,
+ prefs::kCachedMultiProfileUserBehavior);
+ update->RemoveWithoutPathExpansion(user_email, NULL);
+}
+
+std::string MultiProfileUserController::GetCachedValue(
+ const std::string& user_email) const {
+ const DictionaryValue* dict =
+ local_state_->GetDictionary(prefs::kCachedMultiProfileUserBehavior);
+ std::string value;
+ if (dict && dict->GetStringWithoutPathExpansion(user_email, &value))
+ return SanitizeBehaviorValue(value);
+
+ return std::string(kBehaviorUnrestricted);
+}
+
+void MultiProfileUserController::SetCachedValue(
+ const std::string& user_email,
+ const std::string& behavior) {
+ DictionaryPrefUpdate update(local_state_,
+ prefs::kCachedMultiProfileUserBehavior);
+ update->SetStringWithoutPathExpansion(user_email,
+ SanitizeBehaviorValue(behavior));
+}
+
+void MultiProfileUserController::CheckSessionUsers() {
+ const UserList& users = UserManager::Get()->GetLoggedInUsers();
+ for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) {
+ if (!IsUserAllowedInSession((*it)->email())) {
+ delegate_->OnUserNotAllowed();
+ return;
+ }
+ }
+}
+
+void MultiProfileUserController::OnUserPrefChanged(
+ Profile* user_profile) {
+ std::string user_email = user_profile->GetProfileName();
+ CHECK(!user_email.empty());
+ user_email = gaia::CanonicalizeEmail(user_email);
+
+ PrefService* prefs = user_profile->GetPrefs();
+ const std::string behavior =
+ prefs->GetString(prefs::kMultiProfileUserBehavior);
+ SetCachedValue(user_email, behavior);
+
+ CheckSessionUsers();
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/multi_profile_user_controller.h b/chrome/browser/chromeos/login/multi_profile_user_controller.h
new file mode 100644
index 0000000..0e86e7e
--- /dev/null
+++ b/chrome/browser/chromeos/login/multi_profile_user_controller.h
@@ -0,0 +1,77 @@
+// Copyright 2013 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 CHROME_BROWSER_CHROMEOS_LOGIN_MULTI_PROFILE_USER_CONTROLLER_H_
+#define CHROME_BROWSER_CHROMEOS_LOGIN_MULTI_PROFILE_USER_CONTROLLER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_vector.h"
+
+class PrefChangeRegistrar;
+class PrefRegistrySimple;
+class PrefService;
+class Profile;
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+namespace chromeos {
+
+class MultiProfileUserControllerDelegate;
+class UserManager;
+
+// MultiProfileUserController decides whether a user is allowed to be in a
+// multi-profiles session. It caches the multiprofile user behavior pref backed
+// by user policy into local state so that the value is available before the
+// user login and checks if the meaning of the value is respected.
+class MultiProfileUserController {
+ public:
+ MultiProfileUserController(MultiProfileUserControllerDelegate* delegate,
+ PrefService* local_state);
+ ~MultiProfileUserController();
+
+ static void RegisterPrefs(PrefRegistrySimple* registry);
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ // Returns true if the user is allowed to be in the current session.
+ bool IsUserAllowedInSession(const std::string& user_email) const;
+
+ // Starts to observe the multiprofile user behavior pref of the given profile.
+ void StartObserving(Profile* user_profile);
+
+ // Removes the cached value for the given user.
+ void RemoveCachedValue(const std::string& user_email);
+
+ // Possible behavior values.
+ static const char kBehaviorUnrestricted[];
+ static const char kBehaviorPrimaryOnly[];
+ static const char kBehaviorNotAllowed[];
+
+ private:
+ friend class MultiProfileUserControllerTest;
+
+ // Gets/sets the cached policy value.
+ std::string GetCachedValue(const std::string& user_email) const;
+ void SetCachedValue(const std::string& user_email,
+ const std::string& behavior);
+
+ // Checks if all users are allowed in the current session.
+ void CheckSessionUsers();
+
+ // Invoked when user behavior pref value changes.
+ void OnUserPrefChanged(Profile* profile);
+
+ MultiProfileUserControllerDelegate* delegate_; // Not owned.
+ PrefService* local_state_; // Not owned.
+ ScopedVector<PrefChangeRegistrar> pref_watchers_;
+
+ DISALLOW_COPY_AND_ASSIGN(MultiProfileUserController);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_LOGIN_MULTI_PROFILE_USER_CONTROLLER_H_
diff --git a/chrome/browser/chromeos/login/multi_profile_user_controller_delegate.h b/chrome/browser/chromeos/login/multi_profile_user_controller_delegate.h
new file mode 100644
index 0000000..4707716
--- /dev/null
+++ b/chrome/browser/chromeos/login/multi_profile_user_controller_delegate.h
@@ -0,0 +1,21 @@
+// Copyright 2013 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 CHROME_BROWSER_CHROMEOS_LOGIN_MULTI_PROFILE_USER_CONTROLLER_DELEGATE_H_
+#define CHROME_BROWSER_CHROMEOS_LOGIN_MULTI_PROFILE_USER_CONTROLLER_DELEGATE_H_
+
+namespace chromeos {
+
+class MultiProfileUserControllerDelegate {
+ public:
+ // Invoked when the first user that is not allowed in the session is detected.
+ virtual void OnUserNotAllowed() = 0;
+
+ protected:
+ virtual ~MultiProfileUserControllerDelegate() {}
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_LOGIN_MULTI_PROFILE_USER_CONTROLLER_DELEGATE_H_
diff --git a/chrome/browser/chromeos/login/multi_profile_user_controller_unittest.cc b/chrome/browser/chromeos/login/multi_profile_user_controller_unittest.cc
new file mode 100644
index 0000000..a5e1a2a
--- /dev/null
+++ b/chrome/browser/chromeos/login/multi_profile_user_controller_unittest.cc
@@ -0,0 +1,266 @@
+// Copyright 2013 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 "chrome/browser/chromeos/login/multi_profile_user_controller.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/chromeos/login/fake_user_manager.h"
+#include "chrome/browser/chromeos/login/multi_profile_user_controller_delegate.h"
+#include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/prefs/browser_prefs.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_pref_service_syncable.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace {
+
+const char* kUsers[] = {"a@gmail.com", "b@gmail.com" };
+
+struct BehaviorTestCase {
+ const char* primary;
+ const char* secondary;
+ bool expected_allowed;
+};
+
+const BehaviorTestCase kBehaviorTestCases[] = {
+ {
+ MultiProfileUserController::kBehaviorUnrestricted,
+ MultiProfileUserController::kBehaviorUnrestricted,
+ true,
+ },
+ {
+ MultiProfileUserController::kBehaviorUnrestricted,
+ MultiProfileUserController::kBehaviorPrimaryOnly,
+ false,
+ },
+ {
+ MultiProfileUserController::kBehaviorUnrestricted,
+ MultiProfileUserController::kBehaviorNotAllowed,
+ false,
+ },
+ {
+ MultiProfileUserController::kBehaviorPrimaryOnly,
+ MultiProfileUserController::kBehaviorUnrestricted,
+ true,
+ },
+ {
+ MultiProfileUserController::kBehaviorPrimaryOnly,
+ MultiProfileUserController::kBehaviorPrimaryOnly,
+ false,
+ },
+ {
+ MultiProfileUserController::kBehaviorPrimaryOnly,
+ MultiProfileUserController::kBehaviorNotAllowed,
+ false,
+ },
+ {
+ MultiProfileUserController::kBehaviorNotAllowed,
+ MultiProfileUserController::kBehaviorUnrestricted,
+ false,
+ },
+ {
+ MultiProfileUserController::kBehaviorNotAllowed,
+ MultiProfileUserController::kBehaviorPrimaryOnly,
+ false,
+ },
+ {
+ MultiProfileUserController::kBehaviorNotAllowed,
+ MultiProfileUserController::kBehaviorNotAllowed,
+ false,
+ },
+};
+
+} // namespace
+
+class MultiProfileUserControllerTest
+ : public testing::Test,
+ public MultiProfileUserControllerDelegate {
+ public:
+ MultiProfileUserControllerTest()
+ : profile_manager_(TestingBrowserProcess::GetGlobal()),
+ fake_user_manager_(new FakeUserManager),
+ user_manager_enabler_(fake_user_manager_),
+ user_not_allowed_count_(0) {}
+ virtual ~MultiProfileUserControllerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(profile_manager_.SetUp());
+ controller_.reset(new MultiProfileUserController(
+ this, TestingBrowserProcess::GetGlobal()->local_state()));
+
+ for (size_t i = 0; i < arraysize(kUsers); ++i) {
+ const std::string user_email(kUsers[i]);
+ fake_user_manager_->AddUser(user_email);
+
+ // Note that user profiles are created after user login in reality.
+ TestingProfile* user_profile =
+ profile_manager_.CreateTestingProfile(user_email);
+ user_profile->set_profile_name(user_email);
+ user_profiles_.push_back(user_profile);
+ }
+ }
+
+ void LoginUser(size_t user_index) {
+ ASSERT_LT(user_index, arraysize(kUsers));
+ fake_user_manager_->LoginUser(kUsers[user_index]);
+ controller_->StartObserving(user_profiles_[user_index]);
+ }
+
+ void SetOwner(size_t user_index) {
+ fake_user_manager_->set_owner_email(kUsers[user_index]);
+ }
+
+ PrefService* GetUserPrefs(size_t user_index) {
+ return user_profiles_[user_index]->GetPrefs();
+ }
+
+ void SetPrefBehavior(size_t user_index, const std::string& behavior) {
+ GetUserPrefs(user_index)->SetString(prefs::kMultiProfileUserBehavior,
+ behavior);
+ }
+
+ std::string GetCachedBehavior(size_t user_index) {
+ return controller_->GetCachedValue(kUsers[user_index]);
+ }
+
+ void SetCachedBehavior(size_t user_index,
+ const std::string& behavior) {
+ controller_->SetCachedValue(kUsers[user_index], behavior);
+ }
+
+ void ResetCounts() {
+ user_not_allowed_count_ = 0;
+ }
+
+ // MultiProfileUserControllerDeleagte overrides:
+ virtual void OnUserNotAllowed() OVERRIDE {
+ ++user_not_allowed_count_;
+ }
+
+ MultiProfileUserController* controller() { return controller_.get(); }
+ int user_not_allowed_count() const { return user_not_allowed_count_; }
+
+ private:
+ TestingProfileManager profile_manager_;
+ FakeUserManager* fake_user_manager_; // Not owned
+ ScopedUserManagerEnabler user_manager_enabler_;
+
+ scoped_ptr<MultiProfileUserController> controller_;
+
+ std::vector<TestingProfile*> user_profiles_;
+
+ int user_not_allowed_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(MultiProfileUserControllerTest);
+};
+
+// Tests that everyone is allowed before a session starts.
+TEST_F(MultiProfileUserControllerTest, AllAllowedBeforeLogin) {
+ const char* kTestCases[] = {
+ MultiProfileUserController::kBehaviorUnrestricted,
+ MultiProfileUserController::kBehaviorPrimaryOnly,
+ MultiProfileUserController::kBehaviorNotAllowed,
+ };
+ for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+ SetCachedBehavior(0, kTestCases[i]);
+ EXPECT_TRUE(controller()->IsUserAllowedInSession(kUsers[0]))
+ << "Case " << i;
+ }
+}
+
+// Tests that invalid cache value would become the default "unrestricted".
+TEST_F(MultiProfileUserControllerTest, InvalidCacheBecomesDefault) {
+ const char kBad[] = "some invalid value";
+ SetCachedBehavior(0, kBad);
+ EXPECT_EQ(MultiProfileUserController::kBehaviorUnrestricted,
+ GetCachedBehavior(0));
+}
+
+// Tests that cached behavior value changes with user pref after login.
+TEST_F(MultiProfileUserControllerTest, CachedBehaviorUpdate) {
+ LoginUser(0);
+
+ const char* kTestCases[] = {
+ MultiProfileUserController::kBehaviorUnrestricted,
+ MultiProfileUserController::kBehaviorPrimaryOnly,
+ MultiProfileUserController::kBehaviorNotAllowed,
+ MultiProfileUserController::kBehaviorUnrestricted,
+ };
+ for (size_t i = 0; i < arraysize(kTestCases); ++i) {
+ SetPrefBehavior(0, kTestCases[i]);
+ EXPECT_EQ(kTestCases[i], GetCachedBehavior(0));
+ }
+}
+
+// Tests that compromised cache value would be fixed and pref value is checked
+// upon login.
+TEST_F(MultiProfileUserControllerTest, CompromisedCacheFixedOnLogin) {
+ SetPrefBehavior(0, MultiProfileUserController::kBehaviorPrimaryOnly);
+ SetCachedBehavior(0, MultiProfileUserController::kBehaviorUnrestricted);
+ EXPECT_EQ(MultiProfileUserController::kBehaviorUnrestricted,
+ GetCachedBehavior(0));
+ LoginUser(0);
+ EXPECT_EQ(MultiProfileUserController::kBehaviorPrimaryOnly,
+ GetCachedBehavior(0));
+
+ EXPECT_EQ(0, user_not_allowed_count());
+ SetPrefBehavior(1, MultiProfileUserController::kBehaviorPrimaryOnly);
+ SetCachedBehavior(1, MultiProfileUserController::kBehaviorUnrestricted);
+ EXPECT_EQ(MultiProfileUserController::kBehaviorUnrestricted,
+ GetCachedBehavior(1));
+ LoginUser(1);
+ EXPECT_EQ(MultiProfileUserController::kBehaviorPrimaryOnly,
+ GetCachedBehavior(1));
+ EXPECT_EQ(1, user_not_allowed_count());
+}
+
+// Tests cases before the second user login.
+TEST_F(MultiProfileUserControllerTest, IsSecondaryAllowed) {
+ LoginUser(0);
+
+ for (size_t i = 0; i < arraysize(kBehaviorTestCases); ++i) {
+ SetPrefBehavior(0, kBehaviorTestCases[i].primary);
+ SetCachedBehavior(1, kBehaviorTestCases[i].secondary);
+ EXPECT_EQ(kBehaviorTestCases[i].expected_allowed,
+ controller()->IsUserAllowedInSession(kUsers[1])) << "Case " << i;
+ }
+}
+
+// Tests user behavior changes within a two-user session.
+TEST_F(MultiProfileUserControllerTest, PrimaryBehaviorChange) {
+ LoginUser(0);
+ LoginUser(1);
+
+ for (size_t i = 0; i < arraysize(kBehaviorTestCases); ++i) {
+ SetPrefBehavior(0, MultiProfileUserController::kBehaviorUnrestricted);
+ SetPrefBehavior(1, MultiProfileUserController::kBehaviorUnrestricted);
+ ResetCounts();
+
+ SetPrefBehavior(0, kBehaviorTestCases[i].primary);
+ SetPrefBehavior(1, kBehaviorTestCases[i].secondary);
+ EXPECT_EQ(kBehaviorTestCases[i].expected_allowed,
+ user_not_allowed_count() == 0) << "Case " << i;
+ }
+}
+
+// Tests that owner could not be a secondary user.
+TEST_F(MultiProfileUserControllerTest, NoSecondaryOwner) {
+ LoginUser(0);
+ SetOwner(1);
+
+ EXPECT_FALSE(controller()->IsUserAllowedInSession(kUsers[1]));
+
+ EXPECT_EQ(0, user_not_allowed_count());
+ LoginUser(1);
+ EXPECT_EQ(1, user_not_allowed_count());
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/user_manager_impl.cc b/chrome/browser/chromeos/login/user_manager_impl.cc
index 0c7d69b..b6fa102 100644
--- a/chrome/browser/chromeos/login/user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/user_manager_impl.cc
@@ -31,6 +31,7 @@
#include "chrome/browser/chromeos/login/language_switch_menu.h"
#include "chrome/browser/chromeos/login/login_display.h"
#include "chrome/browser/chromeos/login/login_utils.h"
+#include "chrome/browser/chromeos/login/multi_profile_user_controller.h"
#include "chrome/browser/chromeos/login/remove_user_delegate.h"
#include "chrome/browser/chromeos/login/user_image_manager_impl.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
@@ -38,6 +39,7 @@
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/session_length_limiter.h"
#include "chrome/browser/chromeos/settings/cros_settings_names.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/managed_mode/managed_user_service.h"
#include "chrome/browser/policy/browser_policy_connector.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h"
@@ -250,6 +252,8 @@ UserManagerImpl::UserManagerImpl()
kAccountsPrefSupervisedUsersEnabled,
base::Bind(&UserManagerImpl::RetrieveTrustedDevicePolicies,
base::Unretained(this)));
+ multi_profile_user_controller_.reset(new MultiProfileUserController(
+ this, g_browser_process->local_state()));
UpdateLoginState();
}
@@ -279,6 +283,7 @@ void UserManagerImpl::Shutdown() {
device_local_account_policy_service_->RemoveObserver(this);
user_image_manager_->Shutdown();
+ multi_profile_user_controller_.reset();
}
UserImageManager* UserManagerImpl::GetUserImageManager() {
@@ -297,8 +302,12 @@ UserList UserManagerImpl::GetUsersAdmittedForMultiProfile() const {
UserList result;
const UserList& users = GetUsers();
for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) {
- if ((*it)->GetType() == User::USER_TYPE_REGULAR && !(*it)->is_logged_in())
+ if ((*it)->GetType() == User::USER_TYPE_REGULAR &&
+ !(*it)->is_logged_in() &&
+ multi_profile_user_controller_->IsUserAllowedInSession(
+ (*it)->email())) {
result.push_back(*it);
+ }
}
return result;
}
@@ -911,6 +920,7 @@ void UserManagerImpl::Observe(int type,
AuthSyncObserver* sync_observer =
AuthSyncObserverFactory::GetInstance()->GetForProfile(profile);
sync_observer->StartObserving();
+ multi_profile_user_controller_->StartObserving(profile);
}
}
break;
@@ -1473,6 +1483,8 @@ void UserManagerImpl::RemoveNonCryptohomeData(const std::string& email) {
DictionaryPrefUpdate manager_emails_update(prefs,
kManagedUserManagerDisplayEmails);
manager_emails_update->RemoveWithoutPathExpansion(email, NULL);
+
+ multi_profile_user_controller_->RemoveCachedValue(email);
}
User* UserManagerImpl::RemoveRegularOrLocallyManagedUserFromList(
@@ -1956,4 +1968,10 @@ void UserManagerImpl::SendRegularUserLoginMetrics(const std::string& email) {
}
}
+void UserManagerImpl::OnUserNotAllowed() {
+ LOG(ERROR) << "Shutdown session because a user is not allowed to be in the "
+ "current session";
+ chrome::AttemptUserExit();
+}
+
} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/user_manager_impl.h b/chrome/browser/chromeos/login/user_manager_impl.h
index e3e7eda..93c40bc 100644
--- a/chrome/browser/chromeos/login/user_manager_impl.h
+++ b/chrome/browser/chromeos/login/user_manager_impl.h
@@ -15,6 +15,7 @@
#include "base/synchronization/lock.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/login/login_utils.h"
+#include "chrome/browser/chromeos/login/multi_profile_user_controller_delegate.h"
#include "chrome/browser/chromeos/login/user.h"
#include "chrome/browser/chromeos/login/user_image_manager_impl.h"
#include "chrome/browser/chromeos/login/user_manager.h"
@@ -35,6 +36,7 @@ struct DeviceLocalAccount;
namespace chromeos {
+class MultiProfileUserController;
class RemoveUserDelegate;
class SessionLengthLimiter;
@@ -43,7 +45,8 @@ class UserManagerImpl
: public UserManager,
public LoginUtils::Delegate,
public content::NotificationObserver,
- public policy::DeviceLocalAccountPolicyService::Observer {
+ public policy::DeviceLocalAccountPolicyService::Observer,
+ public MultiProfileUserControllerDelegate {
public:
virtual ~UserManagerImpl();
@@ -318,6 +321,9 @@ class UserManagerImpl
Profile* GetProfileByUser(const User* user) const;
+ // MultiProfileUserControllerDelegate implementation:
+ virtual void OnUserNotAllowed() OVERRIDE;
+
// Interface to the signed settings store.
CrosSettings* cros_settings_;
@@ -425,6 +431,8 @@ class UserManagerImpl
scoped_ptr<CrosSettings::ObserverSubscription>
supervised_users_subscription_;
+ scoped_ptr<MultiProfileUserController> multi_profile_user_controller_;
+
DISALLOW_COPY_AND_ASSIGN(UserManagerImpl);
};
diff --git a/chrome/browser/chromeos/login/user_manager_unittest.cc b/chrome/browser/chromeos/login/user_manager_unittest.cc
index 19a17e7..7c909ad 100644
--- a/chrome/browser/chromeos/login/user_manager_unittest.cc
+++ b/chrome/browser/chromeos/login/user_manager_unittest.cc
@@ -7,7 +7,6 @@
#include "base/memory/scoped_ptr.h"
#include "base/prefs/pref_service.h"
-#include "base/prefs/testing_pref_service.h"
#include "base/run_loop.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
@@ -19,6 +18,7 @@
#include "chrome/browser/chromeos/settings/cros_settings_provider.h"
#include "chrome/browser/chromeos/settings/device_settings_service.h"
#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -42,20 +42,15 @@ class UserManagerTest : public testing::Test {
SetDeviceSettings(false, "", false);
// Register an in-memory local settings instance.
- local_state_.reset(new TestingPrefServiceSimple);
- TestingBrowserProcess::GetGlobal()->SetLocalState(local_state_.get());
- UserManager::RegisterPrefs(local_state_->registry());
- // Wallpaper manager and user image managers prefs will be accessed by the
- // unit-test as well.
- UserImageManager::RegisterPrefs(local_state_->registry());
- WallpaperManager::RegisterPrefs(local_state_->registry());
+ local_state_.reset(
+ new ScopedTestingLocalState(TestingBrowserProcess::GetGlobal()));
ResetUserManager();
}
virtual void TearDown() OVERRIDE {
// Unregister the in-memory local settings instance.
- TestingBrowserProcess::GetGlobal()->SetLocalState(0);
+ local_state_.reset();
// Restore the real DeviceSettingsProvider.
EXPECT_TRUE(
@@ -119,7 +114,7 @@ class UserManagerTest : public testing::Test {
CrosSettings* cros_settings_;
CrosSettingsProvider* device_settings_provider_;
StubCrosSettingsProvider stub_settings_provider_;
- scoped_ptr<TestingPrefServiceSimple> local_state_;
+ scoped_ptr<ScopedTestingLocalState> local_state_;
ScopedTestDeviceSettingsService test_device_settings_service_;
ScopedTestCrosSettings test_cros_settings_;
diff --git a/chrome/browser/policy/configuration_policy_handler_list.cc b/chrome/browser/policy/configuration_policy_handler_list.cc
index 5567c11..e19d451 100644
--- a/chrome/browser/policy/configuration_policy_handler_list.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list.cc
@@ -466,6 +466,9 @@ const PolicyToPreferenceMapEntry kSimplePolicyMap[] = {
{ key::kAttestationEnabledForUser,
prefs::kAttestationEnabled,
Value::TYPE_BOOLEAN },
+ { key::kChromeOsMultiProfileUserBehavior,
+ prefs::kMultiProfileUserBehavior,
+ Value::TYPE_STRING },
#endif // defined(OS_CHROMEOS)
#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 0bb1b21..5cad350 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -140,6 +140,7 @@
#include "chrome/browser/chromeos/extensions/echo_private_api.h"
#include "chrome/browser/chromeos/login/default_pinned_apps_field_trial.h"
#include "chrome/browser/chromeos/login/login_utils.h"
+#include "chrome/browser/chromeos/login/multi_profile_user_controller.h"
#include "chrome/browser/chromeos/login/startup_utils.h"
#include "chrome/browser/chromeos/login/user_image_manager.h"
#include "chrome/browser/chromeos/login/user_image_sync_observer.h"
@@ -280,6 +281,7 @@ void RegisterLocalState(PrefRegistrySimple* registry) {
chromeos::language_prefs::RegisterPrefs(registry);
chromeos::KioskAppManager::RegisterPrefs(registry);
chromeos::LoginUtils::RegisterPrefs(registry);
+ chromeos::MultiProfileUserController::RegisterPrefs(registry);
chromeos::Preferences::RegisterPrefs(registry);
chromeos::proxy_config::RegisterPrefs(registry);
chromeos::RegisterDisplayLocalStatePrefs(registry);
@@ -407,6 +409,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
#if defined(OS_CHROMEOS)
chromeos::attestation::PlatformVerificationFlow::RegisterProfilePrefs(
registry);
+ chromeos::MultiProfileUserController::RegisterProfilePrefs(registry);
chromeos::Preferences::RegisterProfilePrefs(registry);
chromeos::proxy_config::RegisterProfilePrefs(registry);
chromeos::UserImageSyncObserver::RegisterProfilePrefs(registry);
diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi
index 148c80e..7e92fff2 100644
--- a/chrome/chrome_browser_chromeos.gypi
+++ b/chrome/chrome_browser_chromeos.gypi
@@ -493,6 +493,9 @@
'browser/chromeos/login/merge_session_throttle.h',
'browser/chromeos/login/mount_manager.cc',
'browser/chromeos/login/mount_manager.h',
+ 'browser/chromeos/login/multi_profile_user_controller.cc',
+ 'browser/chromeos/login/multi_profile_user_controller.h',
+ 'browser/chromeos/login/multi_profile_user_controller_delegate.h',
'browser/chromeos/login/oauth2_login_manager.cc',
'browser/chromeos/login/oauth2_login_manager.h',
'browser/chromeos/login/oauth2_login_manager_factory.cc',
@@ -902,7 +905,7 @@
['use_cras==1', {
'defines': [
'USE_CRAS',
- ],
+ ],
}],
['use_ash==1', {
'dependencies': [
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 94da83c..1ef8680 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -675,6 +675,7 @@
'browser/chromeos/login/merge_session_load_page_unittest.cc',
'browser/chromeos/login/mock_auth_attempt_state_resolver.cc',
'browser/chromeos/login/mock_auth_attempt_state_resolver.h',
+ 'browser/chromeos/login/multi_profile_user_controller_unittest.cc',
'browser/chromeos/login/online_attempt_unittest.cc',
'browser/chromeos/login/parallel_authenticator_unittest.cc',
'browser/chromeos/login/screens/screen_context_unittest.cc',
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index da59d37..463319b 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -844,6 +844,11 @@ const char kRAConsentDomains[] = "settings.privacy.ra_consent_domains";
// A boolean pref that tracks whether the user indicated they wish to be asked
// for consent for every site that uses remote attestation.
const char kRAConsentAlways[] = "settings.privacy.ra_consent_always";
+
+// A string pref that holds string enum values of how the user should behave
+// in a multiprofile session. See ChromeOsMultiProfileUserBehavior policy
+// for more details of the valid values.
+const char kMultiProfileUserBehavior[] = "settings.multiprofile_user_behavior";
#endif // defined(OS_CHROMEOS)
// The disabled messages in IPC logging.
@@ -2155,6 +2160,10 @@ extern const char kUsersLRUInputMethod[] = "UsersLRUInputMethod";
// A dictionary pref of the echo offer check flag. It sets offer info when
// an offer is checked.
extern const char kEchoCheckedOffers[] = "EchoCheckedOffers";
+
+// Key name of a dictionary in local state to store cached multiprofle user
+// behavior policy value.
+const char kCachedMultiProfileUserBehavior[] = "CachedMultiProfileUserBehavior";
#endif
// Whether there is a Flash version installed that supports clearing LSO data.
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 82facc1..e22b295 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -288,6 +288,7 @@ extern const char kOpenNetworkConfiguration[];
extern const char kRAConsentFirstTime[];
extern const char kRAConsentDomains[];
extern const char kRAConsentAlways[];
+extern const char kMultiProfileUserBehavior[];
#endif // defined(OS_CHROMEOS)
extern const char kIpcDisabledMessages[];
extern const char kShowHomeButton[];
@@ -762,6 +763,7 @@ extern const char kDeviceEnrollmentAutoStart[];
extern const char kDeviceEnrollmentCanExit[];
extern const char kUsersLRUInputMethod[];
extern const char kEchoCheckedOffers[];
+extern const char kCachedMultiProfileUserBehavior[];
#endif
extern const char kClearPluginLSODataEnabled[];
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index f928f39..1cb4d99 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -344,6 +344,8 @@ void TestingProfile::Init() {
settings_service->Init(store);
store->SetInitializationCompleted();
#endif
+
+ profile_name_ = "testing_profile";
}
void TestingProfile::FinishInit() {
@@ -550,7 +552,7 @@ TestingProfile* TestingProfile::AsTestingProfile() {
}
std::string TestingProfile::GetProfileName() {
- return std::string("testing_profile");
+ return profile_name_;
}
bool TestingProfile::IsOffTheRecord() const {
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index 71de4a6..d57d5de 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -311,6 +311,10 @@ class TestingProfile : public Profile {
virtual PrefService* GetOffTheRecordPrefs() OVERRIDE;
+ void set_profile_name(const std::string& profile_name) {
+ profile_name_ = profile_name;
+ }
+
protected:
base::Time start_time_;
scoped_ptr<PrefServiceSyncable> prefs_;
@@ -380,6 +384,8 @@ class TestingProfile : public Profile {
// Weak pointer to a delegate for indicating that a profile was created.
Delegate* delegate_;
+
+ std::string profile_name_;
};
#endif // CHROME_TEST_BASE_TESTING_PROFILE_H_
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index a19fcb9..63faf7d 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -1925,6 +1925,14 @@
]
},
+ "ChromeOsMultiProfileUserBehavior": {
+ "os": ["chromeos"],
+ "test_policy": { "ChromeOsMultiProfileUserBehavior": "unrestricted" },
+ "pref_mappings": [
+ { "pref": "settings.multiprofile_user_behavior" }
+ ]
+ },
+
"----- Chrome OS device policies ---------------------------------------": {},
"DevicePolicyRefreshRate": {
@@ -2112,7 +2120,7 @@
"SuppressChromeFrameTurndownPrompt": {
},
-
+
"SkipMetadataCheck": {
}
}