Implement DeviceFamilyLinkAccountsAllowed policy
This policy allows adding Family Link accounts additionally to
accounts listed in DeviceUserAllowlist policy and is dedicated
for school use.
(cherry picked from commit aa95dfdff6b4f1105c99ef2217a5edc5a9363fa0)
Bug: 1126155
Change-Id: Ia0f34b97b31dd143db3a0c2d839e2b4a314ac086
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2448671
Reviewed-by: Roman Sorokin [CET] <[email protected]>
Commit-Queue: Aga Wronska <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#815535}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2465748
Reviewed-by: Aga Wronska <[email protected]>
Cr-Commit-Position: refs/branch-heads/4280@{#284}
Cr-Branched-From: ea420fb963f9658c9969b6513c56b8f47efa1a2a-refs/heads/master@{#812852}
diff --git a/chrome/browser/chromeos/login/auth/chrome_login_performer.cc b/chrome/browser/chromeos/login/auth/chrome_login_performer.cc
index d25118a3..61fd3f8 100644
--- a/chrome/browser/chromeos/login/auth/chrome_login_performer.cc
+++ b/chrome/browser/chromeos/login/auth/chrome_login_performer.cc
@@ -82,10 +82,12 @@
}
}
-bool ChromeLoginPerformer::IsUserAllowlisted(const AccountId& account_id,
- bool* wildcard_match) {
+bool ChromeLoginPerformer::IsUserAllowlisted(
+ const AccountId& account_id,
+ bool* wildcard_match,
+ const base::Optional<user_manager::UserType>& user_type) {
return CrosSettings::Get()->IsUserAllowlisted(account_id.GetUserEmail(),
- wildcard_match);
+ wildcard_match, user_type);
}
void ChromeLoginPerformer::RunOnlineAllowlistCheck(
diff --git a/chrome/browser/chromeos/login/auth/chrome_login_performer.h b/chrome/browser/chromeos/login/auth/chrome_login_performer.h
index 5ae87d6..d1c31e7 100644
--- a/chrome/browser/chromeos/login/auth/chrome_login_performer.h
+++ b/chrome/browser/chromeos/login/auth/chrome_login_performer.h
@@ -10,12 +10,14 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
#include "chrome/browser/chromeos/policy/wildcard_login_checker.h"
#include "chromeos/login/auth/auth_status_consumer.h"
#include "chromeos/login/auth/authenticator.h"
#include "chromeos/login/auth/extended_authenticator.h"
#include "chromeos/login/auth/login_performer.h"
#include "chromeos/login/auth/user_context.h"
+#include "components/user_manager/user_type.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "google_apis/gaia/google_service_auth_error.h"
@@ -35,8 +37,11 @@
explicit ChromeLoginPerformer(Delegate* delegate);
~ChromeLoginPerformer() override;
- bool IsUserAllowlisted(const AccountId& account_id,
- bool* wildcard_match) override;
+ // LoginPerformer:
+ bool IsUserAllowlisted(
+ const AccountId& account_id,
+ bool* wildcard_match,
+ const base::Optional<user_manager::UserType>& user_type) override;
protected:
bool RunTrustedCheck(base::OnceClosure callback) override;
diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc
index a0f34c3d..95e10a0 100644
--- a/chrome/browser/chromeos/login/existing_user_controller.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller.cc
@@ -848,13 +848,17 @@
given_name_ = base::UTF8ToUTF16(given_name);
}
-bool ExistingUserController::IsUserAllowlisted(const AccountId& account_id) {
+bool ExistingUserController::IsUserAllowlisted(
+ const AccountId& account_id,
+ const base::Optional<user_manager::UserType>& user_type) {
bool wildcard_match = false;
- if (login_performer_.get())
- return login_performer_->IsUserAllowlisted(account_id, &wildcard_match);
+ if (login_performer_.get()) {
+ return login_performer_->IsUserAllowlisted(account_id, &wildcard_match,
+ user_type);
+ }
return cros_settings_->IsUserAllowlisted(account_id.GetUserEmail(),
- &wildcard_match);
+ &wildcard_match, user_type);
}
void ExistingUserController::LocalStateChanged(
diff --git a/chrome/browser/chromeos/login/existing_user_controller.h b/chrome/browser/chromeos/login/existing_user_controller.h
index 1f65c480..19bbfba 100644
--- a/chrome/browser/chromeos/login/existing_user_controller.h
+++ b/chrome/browser/chromeos/login/existing_user_controller.h
@@ -16,6 +16,7 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
+#include "base/optional.h"
#include "base/scoped_observer.h"
#include "base/strings/string16.h"
#include "base/time/time.h"
@@ -34,6 +35,7 @@
#include "components/prefs/pref_registry_simple.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
+#include "components/user_manager/user_type.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "third_party/cros_system_api/dbus/cryptohome/dbus-constants.h"
@@ -117,7 +119,9 @@
void SetDisplayEmail(const std::string& email);
void SetDisplayAndGivenName(const std::string& display_name,
const std::string& given_name);
- bool IsUserAllowlisted(const AccountId& account_id);
+ bool IsUserAllowlisted(
+ const AccountId& account_id,
+ const base::Optional<user_manager::UserType>& user_type);
// user_manager::UserManager::Observer:
void LocalStateChanged(user_manager::UserManager* user_manager) override;
diff --git a/chrome/browser/chromeos/login/ui/fake_login_display_host.cc b/chrome/browser/chromeos/login/ui/fake_login_display_host.cc
index 20ec078..6db9628 100644
--- a/chrome/browser/chromeos/login/ui/fake_login_display_host.cc
+++ b/chrome/browser/chromeos/login/ui/fake_login_display_host.cc
@@ -106,7 +106,9 @@
void FakeLoginDisplayHost::LoadSigninWallpaper() {}
-bool FakeLoginDisplayHost::IsUserAllowlisted(const AccountId& account_id) {
+bool FakeLoginDisplayHost::IsUserAllowlisted(
+ const AccountId& account_id,
+ const base::Optional<user_manager::UserType>& user_type) {
return false;
}
diff --git a/chrome/browser/chromeos/login/ui/fake_login_display_host.h b/chrome/browser/chromeos/login/ui/fake_login_display_host.h
index 73d552f..4f00e6d 100644
--- a/chrome/browser/chromeos/login/ui/fake_login_display_host.h
+++ b/chrome/browser/chromeos/login/ui/fake_login_display_host.h
@@ -9,7 +9,9 @@
#include <string>
#include "base/macros.h"
+#include "base/optional.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
+#include "components/user_manager/user_type.h"
namespace session_manager {
class SessionManager;
@@ -50,7 +52,9 @@
const std::string& given_name) override;
void LoadWallpaper(const AccountId& account_id) override;
void LoadSigninWallpaper() override;
- bool IsUserAllowlisted(const AccountId& account_id) override;
+ bool IsUserAllowlisted(
+ const AccountId& account_id,
+ const base::Optional<user_manager::UserType>& user_type) override;
void ShowGaiaDialog(const AccountId& prefilled_account) override;
void HideOobeDialog() override;
void UpdateOobeDialogState(ash::OobeDialogState state) override;
diff --git a/chrome/browser/chromeos/login/ui/login_display_host.h b/chrome/browser/chromeos/login/ui/login_display_host.h
index 9b42633..ae3de75 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host.h
+++ b/chrome/browser/chromeos/login/ui/login_display_host.h
@@ -16,6 +16,8 @@
#include "chrome/browser/chromeos/login/auth/auth_prewarmer.h"
#include "chrome/browser/chromeos/login/oobe_screen.h"
#include "chrome/browser/chromeos/login/ui/login_display.h"
+#include "components/user_manager/user_type.h"
+
#include "ui/gfx/native_widget_types.h"
class AccountId;
@@ -166,7 +168,9 @@
virtual void LoadSigninWallpaper() = 0;
// Returns true if user is allowed to log in by domain policy.
- virtual bool IsUserAllowlisted(const AccountId& account_id) = 0;
+ virtual bool IsUserAllowlisted(
+ const AccountId& account_id,
+ const base::Optional<user_manager::UserType>& user_type) = 0;
// ----- Password change flow methods -----
// Cancels current password changed flow.
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_common.cc b/chrome/browser/chromeos/login/ui/login_display_host_common.cc
index 26c78937..196083ca 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_common.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_common.cc
@@ -229,10 +229,12 @@
WallpaperControllerClient::Get()->ShowSigninWallpaper();
}
-bool LoginDisplayHostCommon::IsUserAllowlisted(const AccountId& account_id) {
+bool LoginDisplayHostCommon::IsUserAllowlisted(
+ const AccountId& account_id,
+ const base::Optional<user_manager::UserType>& user_type) {
if (!GetExistingUserController())
return true;
- return GetExistingUserController()->IsUserAllowlisted(account_id);
+ return GetExistingUserController()->IsUserAllowlisted(account_id, user_type);
}
void LoginDisplayHostCommon::CancelPasswordChangedFlow() {
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_common.h b/chrome/browser/chromeos/login/ui/login_display_host_common.h
index c99e0b5..93c5485a 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_common.h
+++ b/chrome/browser/chromeos/login/ui/login_display_host_common.h
@@ -10,10 +10,12 @@
#include <vector>
#include "ash/public/cpp/login_accelerators.h"
+#include "base/optional.h"
#include "chrome/browser/chromeos/login/ui/kiosk_app_menu_controller.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/ui/browser_list_observer.h"
#include "components/keep_alive_registry/scoped_keep_alive.h"
+#include "components/user_manager/user_type.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
@@ -50,7 +52,9 @@
const std::string& given_name) final;
void LoadWallpaper(const AccountId& account_id) final;
void LoadSigninWallpaper() final;
- bool IsUserAllowlisted(const AccountId& account_id) final;
+ bool IsUserAllowlisted(
+ const AccountId& account_id,
+ const base::Optional<user_manager::UserType>& user_type) final;
void CancelPasswordChangedFlow() final;
void MigrateUserData(const std::string& old_password) final;
void ResyncUserData() final;
diff --git a/chrome/browser/chromeos/login/ui/login_display_mojo.cc b/chrome/browser/chromeos/login/ui/login_display_mojo.cc
index 544d4a9..5042d3a 100644
--- a/chrome/browser/chromeos/login/ui/login_display_mojo.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_mojo.cc
@@ -173,6 +173,7 @@
// related.
if (error_msg_id != IDS_LOGIN_ERROR_ALLOWLIST &&
error_msg_id != IDS_ENTERPRISE_LOGIN_ERROR_ALLOWLIST &&
+ error_msg_id != IDS_ENTERPRISE_AND_FAMILY_LINK_LOGIN_ERROR_ALLOWLIST &&
error_msg_id != IDS_LOGIN_ERROR_OWNER_KEY_LOST &&
error_msg_id != IDS_LOGIN_ERROR_OWNER_REQUIRED &&
error_msg_id != IDS_LOGIN_ERROR_GOOGLE_ACCOUNT_NOT_ALLOWED &&
diff --git a/chrome/browser/chromeos/login/ui/login_display_webui.cc b/chrome/browser/chromeos/login/ui/login_display_webui.cc
index a7c57e84..e91b50ac 100644
--- a/chrome/browser/chromeos/login/ui/login_display_webui.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_webui.cc
@@ -144,6 +144,7 @@
// related.
if (error_msg_id != IDS_LOGIN_ERROR_ALLOWLIST &&
error_msg_id != IDS_ENTERPRISE_LOGIN_ERROR_ALLOWLIST &&
+ error_msg_id != IDS_ENTERPRISE_AND_FAMILY_LINK_LOGIN_ERROR_ALLOWLIST &&
error_msg_id != IDS_LOGIN_ERROR_OWNER_KEY_LOST &&
error_msg_id != IDS_LOGIN_ERROR_OWNER_REQUIRED &&
error_msg_id != IDS_LOGIN_ERROR_GOOGLE_ACCOUNT_NOT_ALLOWED &&
diff --git a/chrome/browser/chromeos/login/ui/mock_login_display_host.h b/chrome/browser/chromeos/login/ui/mock_login_display_host.h
index bd53615..4c59012 100644
--- a/chrome/browser/chromeos/login/ui/mock_login_display_host.h
+++ b/chrome/browser/chromeos/login/ui/mock_login_display_host.h
@@ -9,9 +9,11 @@
#include "ash/public/cpp/login_accelerators.h"
#include "base/macros.h"
+#include "base/optional.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_types.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
+#include "components/user_manager/user_type.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace chromeos {
@@ -72,7 +74,10 @@
(override));
MOCK_METHOD(void, LoadWallpaper, (const AccountId&), (override));
MOCK_METHOD(void, LoadSigninWallpaper, (), (override));
- MOCK_METHOD(bool, IsUserAllowlisted, (const AccountId&), (override));
+ MOCK_METHOD(bool,
+ IsUserAllowlisted,
+ (const AccountId&, const base::Optional<user_manager::UserType>&),
+ (override));
MOCK_METHOD(void, CancelPasswordChangedFlow, (), (override));
MOCK_METHOD(void, MigrateUserData, (const std::string&), (override));
MOCK_METHOD(void, ResyncUserData, (), (override));
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 3023d66f..ee91f6c0 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -1227,7 +1227,7 @@
const user_manager::User& user) const {
DCHECK(user.HasGaiaAccount());
return cros_settings_->IsUserAllowlisted(user.GetAccountId().GetUserEmail(),
- nullptr);
+ nullptr, user.GetType());
}
void ChromeUserManagerImpl::OnMinimumVersionStateChanged() {
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 f8b4f2100..c6f36405 100644
--- a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
+++ b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
@@ -623,7 +623,7 @@
const user_manager::User& user) const {
DCHECK(user.HasGaiaAccount());
return CrosSettings::Get()->IsUserAllowlisted(
- user.GetAccountId().GetUserEmail(), nullptr);
+ user.GetAccountId().GetUserEmail(), nullptr, user.GetType());
}
bool FakeChromeUserManager::IsUserAllowed(
diff --git a/chrome/browser/chromeos/policy/user_policy_manager_builder_chromeos.cc b/chrome/browser/chromeos/policy/user_policy_manager_builder_chromeos.cc
index 6d75afe..44ab761 100644
--- a/chrome/browser/chromeos/policy/user_policy_manager_builder_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_policy_manager_builder_chromeos.cc
@@ -308,7 +308,7 @@
bool wildcard_match = false;
if (connector->IsEnterpriseManaged() &&
chromeos::CrosSettings::Get()->IsUserAllowlisted(
- account_id.GetUserEmail(), &wildcard_match) &&
+ account_id.GetUserEmail(), &wildcard_match, user->GetType()) &&
wildcard_match &&
!connector->IsNonEnterpriseUser(account_id.GetUserEmail())) {
manager->EnableWildcardLoginCheck(account_id.GetUserEmail());
diff --git a/chrome/browser/chromeos/settings/cros_settings.cc b/chrome/browser/chromeos/settings/cros_settings.cc
index f4f2c0c0..4f6e84f9 100644
--- a/chrome/browser/chromeos/settings/cros_settings.cc
+++ b/chrome/browser/chromeos/settings/cros_settings.cc
@@ -76,20 +76,6 @@
g_using_cros_settings_for_testing = false;
}
-bool CrosSettings::IsUserAllowlisted(const std::string& username,
- bool* wildcard_match) const {
- // Skip allowlist check for tests.
- if (chromeos::switches::ShouldSkipOobePostLogin()) {
- return true;
- }
-
- bool allow_new_user = false;
- GetBoolean(kAccountsPrefAllowNewUser, &allow_new_user);
- if (allow_new_user)
- return true;
- return FindEmailInList(kAccountsPrefUsers, username, wildcard_match);
-}
-
CrosSettings::CrosSettings() = default;
CrosSettings::CrosSettings(DeviceSettingsService* device_settings_service,
@@ -194,6 +180,28 @@
return false;
}
+bool CrosSettings::IsUserAllowlisted(
+ const std::string& username,
+ bool* wildcard_match,
+ const base::Optional<user_manager::UserType>& user_type) const {
+ // Skip allowlist check for tests.
+ if (chromeos::switches::ShouldSkipOobePostLogin()) {
+ return true;
+ }
+
+ bool allow_new_user = false;
+ GetBoolean(kAccountsPrefAllowNewUser, &allow_new_user);
+ if (allow_new_user)
+ return true;
+
+ if (FindEmailInList(kAccountsPrefUsers, username, wildcard_match))
+ return true;
+
+ bool family_link_allowed = false;
+ GetBoolean(kAccountsPrefFamilyLinkAccountsAllowed, &family_link_allowed);
+ return family_link_allowed && user_type == user_manager::USER_TYPE_CHILD;
+}
+
bool CrosSettings::FindEmailInList(const std::string& path,
const std::string& email,
bool* wildcard_match) const {
diff --git a/chrome/browser/chromeos/settings/cros_settings.h b/chrome/browser/chromeos/settings/cros_settings.h
index 0219252..b6c07ad 100644
--- a/chrome/browser/chromeos/settings/cros_settings.h
+++ b/chrome/browser/chromeos/settings/cros_settings.h
@@ -13,9 +13,11 @@
#include "base/callback_forward.h"
#include "base/callback_list.h"
#include "base/macros.h"
+#include "base/optional.h"
#include "base/sequence_checker.h"
#include "chromeos/settings/cros_settings_names.h"
#include "chromeos/settings/cros_settings_provider.h"
+#include "components/user_manager/user_type.h"
class PrefService;
@@ -45,12 +47,6 @@
static void SetForTesting(CrosSettings* test_instance);
static void ShutdownForTesting();
- // Checks if the given username is on the list of users allowed to sign-in to
- // this device. |wildcard_match| may be NULL. If it's present, it'll be set to
- // true if the list check was satisfied via a wildcard.
- bool IsUserAllowlisted(const std::string& username,
- bool* wildcard_match) const;
-
// Creates an instance with no providers as yet. This is meant for unit tests,
// production code uses the singleton returned by Get() above.
CrosSettings();
@@ -95,6 +91,16 @@
bool GetDictionary(const std::string& path,
const base::DictionaryValue** out_value) const;
+ // Checks if the given username is on the list of users allowed to sign-in to
+ // this device. |wildcard_match| may be nullptr. If it's present, it'll be set
+ // to true if the list check was satisfied via a wildcard. In some
+ // configurations user can be allowed based on the |user_type|. See
+ // |DeviceFamilyLinkAccountsAllowed| policy.
+ bool IsUserAllowlisted(
+ const std::string& username,
+ bool* wildcard_match,
+ const base::Optional<user_manager::UserType>& user_type) const;
+
// Helper function for the allowlist op. Implemented here because we will need
// this in a few places. The functions searches for |email| in the pref |path|
// It respects allowlists so [email protected] will match *@bar.baz too. If the
diff --git a/chrome/browser/chromeos/settings/cros_settings_unittest.cc b/chrome/browser/chromeos/settings/cros_settings_unittest.cc
index 77326026..681409b9 100644
--- a/chrome/browser/chromeos/settings/cros_settings_unittest.cc
+++ b/chrome/browser/chromeos/settings/cros_settings_unittest.cc
@@ -10,6 +10,8 @@
#include "base/bind.h"
#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
@@ -20,12 +22,14 @@
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
+#include "chromeos/constants/chromeos_features.h"
#include "chromeos/settings/cros_settings_names.h"
#include "chromeos/tpm/stub_install_attributes.h"
#include "components/ownership/mock_owner_key_util.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/proto/chrome_device_policy.pb.h"
#include "components/policy/proto/device_management_backend.pb.h"
+#include "components/user_manager/user_type.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_utils.h"
@@ -131,6 +135,11 @@
NULL);
}
+ bool IsUserAllowed(const std::string& username,
+ const base::Optional<user_manager::UserType>& user_type) {
+ return CrosSettings::Get()->IsUserAllowlisted(username, nullptr, user_type);
+ }
+
content::BrowserTaskEnvironment task_environment_{
content::BrowserTaskEnvironment::IO_MAINLOOP};
@@ -348,4 +357,83 @@
EXPECT_TRUE(wildcard_match);
}
+// DeviceFamilyLinkAccountsAllowed should not have any effect if allowlist is
+// not set.
+TEST_F(CrosSettingsTest, AllowFamilyLinkAccountsWithEmptyAllowlist) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ chromeos::features::kFamilyLinkOnSchoolDevice);
+
+ device_policy_.payload().mutable_allow_new_users()->set_allow_new_users(
+ false);
+ device_policy_.payload().mutable_user_allowlist()->clear_user_allowlist();
+ device_policy_.payload()
+ .mutable_family_link_accounts_allowed()
+ ->set_family_link_accounts_allowed(true);
+
+ StoreDevicePolicy();
+
+ ExpectPref(kAccountsPrefAllowNewUser, base::Value(false));
+ ExpectPref(kAccountsPrefUsers, base::ListValue());
+ ExpectPref(kAccountsPrefFamilyLinkAccountsAllowed, base::Value(false));
+
+ EXPECT_FALSE(IsUserAllowed(kUser1, base::nullopt));
+ EXPECT_FALSE(IsUserAllowed(kUser1, user_manager::USER_TYPE_CHILD));
+ EXPECT_FALSE(IsUserAllowed(kUser1, user_manager::USER_TYPE_REGULAR));
+}
+
+// DeviceFamilyLinkAccountsAllowed should not have any effect if the feature is
+// disabled.
+TEST_F(CrosSettingsTest, AllowFamilyLinkAccountsWithFeatureDisabled) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(
+ chromeos::features::kFamilyLinkOnSchoolDevice);
+
+ device_policy_.payload().mutable_allow_new_users()->set_allow_new_users(
+ false);
+ device_policy_.payload().mutable_user_allowlist()->add_user_allowlist(kOwner);
+ device_policy_.payload()
+ .mutable_family_link_accounts_allowed()
+ ->set_family_link_accounts_allowed(true);
+
+ StoreDevicePolicy();
+
+ base::ListValue allowlist;
+ allowlist.AppendString(kOwner);
+ ExpectPref(kAccountsPrefAllowNewUser, base::Value(false));
+ ExpectPref(kAccountsPrefUsers, allowlist);
+ ExpectPref(kAccountsPrefFamilyLinkAccountsAllowed, base::Value(false));
+
+ EXPECT_TRUE(IsUserAllowed(kOwner, base::nullopt));
+ EXPECT_FALSE(IsUserAllowed(kUser1, base::nullopt));
+ EXPECT_FALSE(IsUserAllowed(kUser1, user_manager::USER_TYPE_CHILD));
+ EXPECT_FALSE(IsUserAllowed(kUser1, user_manager::USER_TYPE_REGULAR));
+}
+
+TEST_F(CrosSettingsTest, AllowFamilyLinkAccountsWithAllowlist) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ chromeos::features::kFamilyLinkOnSchoolDevice);
+
+ device_policy_.payload().mutable_allow_new_users()->set_allow_new_users(
+ false);
+ device_policy_.payload().mutable_user_allowlist()->add_user_allowlist(kOwner);
+ device_policy_.payload()
+ .mutable_family_link_accounts_allowed()
+ ->set_family_link_accounts_allowed(true);
+
+ StoreDevicePolicy();
+
+ base::ListValue allowlist;
+ allowlist.AppendString(kOwner);
+ ExpectPref(kAccountsPrefAllowNewUser, base::Value(false));
+ ExpectPref(kAccountsPrefUsers, allowlist);
+ ExpectPref(kAccountsPrefFamilyLinkAccountsAllowed, base::Value(true));
+
+ EXPECT_TRUE(IsUserAllowed(kOwner, base::nullopt));
+ EXPECT_FALSE(IsUserAllowed(kUser1, base::nullopt));
+ EXPECT_TRUE(IsUserAllowed(kUser1, user_manager::USER_TYPE_CHILD));
+ EXPECT_FALSE(IsUserAllowed(kUser1, user_manager::USER_TYPE_REGULAR));
+}
+
} // namespace chromeos
diff --git a/chrome/browser/chromeos/settings/device_settings_provider.cc b/chrome/browser/chromeos/settings/device_settings_provider.cc
index 278fa30..ccbe3b4 100644
--- a/chrome/browser/chromeos/settings/device_settings_provider.cc
+++ b/chrome/browser/chromeos/settings/device_settings_provider.cc
@@ -30,6 +30,7 @@
#include "chrome/browser/chromeos/settings/device_settings_cache.h"
#include "chrome/browser/chromeos/settings/stats_reporting_controller.h"
#include "chrome/browser/chromeos/tpm_firmware_update.h"
+#include "chromeos/constants/chromeos_features.h"
#include "chromeos/constants/chromeos_switches.h"
#include "chromeos/dbus/cryptohome/cryptohome_client.h"
#include "chromeos/dbus/dbus_thread_manager.h"
@@ -216,12 +217,18 @@
}
// Value of DeviceFamilyLinkAccountsAllowed policy does not affect
- // |kAccountsPrefAllowNewUser| setting. Family Link accounts will be only
- // allowed if both |kAccountsPrefAllowNewUser| and
- // |kAccountsPrefFamilyLinkAccountsAllowed| are true.
+ // |kAccountsPrefAllowNewUser| setting. Family Link accounts are only
+ // allowed if user allowlist is enforced.
+ bool user_allowlist_enforced =
+ ((policy.has_user_whitelist() &&
+ policy.user_whitelist().user_whitelist_size() > 0) ||
+ (policy.has_user_allowlist() &&
+ policy.user_allowlist().user_allowlist_size() > 0));
new_values_cache->SetBoolean(
kAccountsPrefFamilyLinkAccountsAllowed,
- policy.has_family_link_accounts_allowed() &&
+ chromeos::features::IsFamilyLinkOnSchoolDeviceEnabled() &&
+ user_allowlist_enforced &&
+ policy.has_family_link_accounts_allowed() &&
policy.family_link_accounts_allowed()
.has_family_link_accounts_allowed() &&
policy.family_link_accounts_allowed().family_link_accounts_allowed());
diff --git a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
index 8f4ea60..70cc07db 100644
--- a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
+++ b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
@@ -16,6 +16,7 @@
#include "base/macros.h"
#include "base/path_service.h"
#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_path_override.h"
#include "base/values.h"
#include "chrome/browser/chromeos/policy/device_local_account.h"
@@ -25,6 +26,7 @@
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
+#include "chromeos/constants/chromeos_features.h"
#include "chromeos/settings/cros_settings_names.h"
#include "chromeos/tpm/stub_install_attributes.h"
#include "components/policy/proto/chrome_device_policy.pb.h"
@@ -421,6 +423,13 @@
BuildAndInstallDevicePolicy();
}
+ void AddUserToAllowlist(const std::string& user_id) {
+ em::UserAllowlistProto* proto =
+ device_policy_->payload().mutable_user_allowlist();
+ proto->add_user_allowlist(user_id);
+ BuildAndInstallDevicePolicy();
+ }
+
void VerifyDeviceShowLowDiskSpaceNotification(bool expected) {
const base::Value expected_value(expected);
EXPECT_EQ(expected_value,
@@ -1177,14 +1186,43 @@
VerifyDeviceShowLowDiskSpaceNotification(false);
}
-TEST_F(DeviceSettingsProviderTest, DeviceFamilyLinkAccountsAllowed) {
+// Tests DeviceFamilyLinkAccountsAllowed policy with the feature disabled.
+// The policy should have no effect.
+TEST_F(DeviceSettingsProviderTest, DeviceFamilyLinkAccountsAllowedDisabled) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(
+ chromeos::features::kFamilyLinkOnSchoolDevice);
+
base::Value default_value(false);
VerifyPolicyValue(kAccountsPrefFamilyLinkAccountsAllowed, &default_value);
+ // Family Link allowed with allowlist set, but the feature is disabled.
SetDeviceFamilyLinkAccountsAllowed(true);
+ AddUserToAllowlist("*@managedchrome.com");
+ EXPECT_EQ(base::Value(false),
+ *provider_->Get(kAccountsPrefFamilyLinkAccountsAllowed));
+}
+
+// Tests DeviceFamilyLinkAccountsAllowed policy with the feature enabled.
+TEST_F(DeviceSettingsProviderTest, DeviceFamilyLinkAccountsAllowedEnabled) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndEnableFeature(
+ chromeos::features::kFamilyLinkOnSchoolDevice);
+
+ base::Value default_value(false);
+ VerifyPolicyValue(kAccountsPrefFamilyLinkAccountsAllowed, &default_value);
+
+ // Family Link allowed, but no allowlist set.
+ SetDeviceFamilyLinkAccountsAllowed(true);
+ EXPECT_EQ(base::Value(false),
+ *provider_->Get(kAccountsPrefFamilyLinkAccountsAllowed));
+
+ // Family Link allowed with allowlist set.
+ AddUserToAllowlist("*@managedchrome.com");
EXPECT_EQ(base::Value(true),
*provider_->Get(kAccountsPrefFamilyLinkAccountsAllowed));
+ // Family Link disallowed with allowlist set.
SetDeviceFamilyLinkAccountsAllowed(false);
EXPECT_EQ(base::Value(false),
*provider_->Get(kAccountsPrefFamilyLinkAccountsAllowed));
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
index 9c577ba..d91ca1b 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
@@ -1411,8 +1411,18 @@
showAllowlistCheckFailedError(show, opt_data) {
if (show) {
const isManaged = opt_data && opt_data.enterpriseManaged;
- this.$['gaia-allowlist-error'].textContent = loadTimeData.getValue(
- isManaged ? 'allowlistErrorEnterprise' : 'allowlistErrorConsumer');
+ const isFamilyLinkAllowed = opt_data && opt_data.familyLinkAllowed;
+ errorMessage = '';
+ if (isManaged && isFamilyLinkAllowed) {
+ errorMessage = 'allowlistErrorEnterpriseAndFamilyLink';
+ } else if (isManaged) {
+ errorMessage = 'allowlistErrorEnterprise';
+ } else {
+ errorMessage = 'allowlistErrorConsumer';
+ }
+
+ this.$['gaia-allowlist-error'].textContent =
+ loadTimeData.getValue(errorMessage);
// To make animations correct, we need to make sure Gaia is completely
// reloaded. Otherwise ChromeOS overlays hide and Gaia page is shown
// somewhere in the middle of animations.
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index 4263b3d..4a6dbf3 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -214,6 +214,18 @@
params->SetString("flow", "nosignup");
}
+bool ShouldCheckUserTypeBeforeAllowing() {
+ if (!chromeos::features::IsFamilyLinkOnSchoolDeviceEnabled())
+ return false;
+
+ CrosSettings* cros_settings = CrosSettings::Get();
+ bool family_link_allowed = false;
+ cros_settings->GetBoolean(kAccountsPrefFamilyLinkAccountsAllowed,
+ &family_link_allowed);
+
+ return family_link_allowed;
+}
+
void RecordSAMLScrapingVerificationResultInHistogram(bool success) {
UMA_HISTOGRAM_BOOLEAN("ChromeOS.SAML.Scraping.VerificationResult", success);
}
@@ -631,6 +643,8 @@
builder->Add("allowlistErrorConsumer", IDS_LOGIN_ERROR_ALLOWLIST);
builder->Add("allowlistErrorEnterprise",
IDS_ENTERPRISE_LOGIN_ERROR_ALLOWLIST);
+ builder->Add("allowlistErrorEnterpriseAndFamilyLink",
+ IDS_ENTERPRISE_AND_FAMILY_LINK_LOGIN_ERROR_ALLOWLIST);
builder->Add("tryAgainButton", IDS_ALLOWLIST_ERROR_TRY_AGAIN_BUTTON);
builder->Add("learnMoreButton", IDS_LEARN_MORE);
builder->Add("gaiaLoading", IDS_LOGIN_GAIA_LOADING_MESSAGE);
@@ -772,10 +786,16 @@
}
void GaiaScreenHandler::HandleIdentifierEntered(const std::string& user_email) {
+ // We cannot tell a user type from the identifier, so we delay checking if
+ // the account should be allowed.
+ if (ShouldCheckUserTypeBeforeAllowing())
+ return;
+
if (LoginDisplayHost::default_host() &&
!LoginDisplayHost::default_host()->IsUserAllowlisted(
user_manager::known_user::GetAccountId(
- user_email, std::string() /* id */, AccountType::UNKNOWN))) {
+ user_email, std::string() /* id */, AccountType::UNKNOWN),
+ base::nullopt)) {
ShowAllowlistCheckFailedError();
}
}
@@ -919,6 +939,17 @@
DCHECK(!email.empty());
DCHECK(!gaia_id.empty());
+
+ // Execute delayed allowlist check that is based on user type.
+ const user_manager::UserType user_type =
+ GetUsertypeFromServicesString(services);
+ if (ShouldCheckUserTypeBeforeAllowing() &&
+ !LoginDisplayHost::default_host()->IsUserAllowlisted(
+ GetAccountId(email, gaia_id, AccountType::GOOGLE), user_type)) {
+ ShowAllowlistCheckFailedError();
+ return;
+ }
+
const std::string sanitized_email = gaia::SanitizeEmail(email);
LoginDisplayHost::default_host()->SetDisplayEmail(sanitized_email);
@@ -1519,6 +1550,12 @@
g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->IsEnterpriseManaged());
+
+ bool family_link_allowed = false;
+ CrosSettings::Get()->GetBoolean(kAccountsPrefFamilyLinkAccountsAllowed,
+ &family_link_allowed);
+ params.SetBoolean("familyLinkAllowed", family_link_allowed);
+
CallJS("login.GaiaSigninScreen.showAllowlistCheckFailedError", true, params);
}
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 4ab2e2c..5a0afd2c 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -256,6 +256,11 @@
const base::Feature kExoPointerLock{"ExoPointerLock",
base::FEATURE_DISABLED_BY_DEFAULT};
+// Enables policy that controls feature to allow Family Link accounts on school
+// owned devices.
+const base::Feature kFamilyLinkOnSchoolDevice{
+ "FamilyLinkOnSchoolDevice", base::FEATURE_DISABLED_BY_DEFAULT};
+
// Enables the camera folder handling in files app.
const base::Feature kFilesCameraFolder{"FilesCameraFolder",
base::FEATURE_ENABLED_BY_DEFAULT};
@@ -653,6 +658,10 @@
return base::FeatureList::IsEnabled(kDiagnosticsApp);
}
+bool IsFamilyLinkOnSchoolDeviceEnabled() {
+ return base::FeatureList::IsEnabled(kFamilyLinkOnSchoolDevice);
+}
+
bool IsImeSandboxEnabled() {
return base::FeatureList::IsEnabled(kEnableImeSandbox);
}
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index 1ef3278..96e5e6b 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -126,6 +126,8 @@
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kExoPointerLock;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kFamilyLinkOnSchoolDevice;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kFilesCameraFolder;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kFilesNG;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
@@ -290,6 +292,7 @@
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsChildSpecificSigninEnabled();
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsDeepLinkingEnabled();
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsDiagnosticsAppEnabled();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsFamilyLinkOnSchoolDeviceEnabled();
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsImeSandboxEnabled();
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
bool IsInstantTetheringBackgroundAdvertisingSupported();
diff --git a/chromeos/login/auth/login_performer.cc b/chromeos/login/auth/login_performer.cc
index 012297a1..b267434 100644
--- a/chromeos/login/auth/login_performer.cc
+++ b/chromeos/login/auth/login_performer.cc
@@ -142,7 +142,8 @@
bool wildcard_match = false;
const AccountId& account_id = user_context.GetAccountId();
- if (!IsUserAllowlisted(account_id, &wildcard_match)) {
+ if (!IsUserAllowlisted(account_id, &wildcard_match,
+ user_context.GetUserType())) {
NotifyAllowlistCheckFailure();
return;
}
diff --git a/chromeos/login/auth/login_performer.h b/chromeos/login/auth/login_performer.h
index 2c33d9aa..7b1ff222 100644
--- a/chromeos/login/auth/login_performer.h
+++ b/chromeos/login/auth/login_performer.h
@@ -13,10 +13,12 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
#include "chromeos/login/auth/auth_status_consumer.h"
#include "chromeos/login/auth/authenticator.h"
#include "chromeos/login/auth/extended_authenticator.h"
#include "chromeos/login/auth/user_context.h"
+#include "components/user_manager/user_type.h"
#include "google_apis/gaia/google_service_auth_error.h"
class AccountId;
@@ -129,9 +131,13 @@
// Check if user is allowed to sign in on device. |wildcard_match| will
// contain additional information whether this user is explicitly listed or
- // not (may be relevant for external-based sign-in).
- virtual bool IsUserAllowlisted(const AccountId& account_id,
- bool* wildcard_match) = 0;
+ // not (may be relevant for external-based sign-in). |user_type| will be used
+ // to check if the user is allowed because of the user type, pass
+ // base::nullopt if user type is not known.
+ virtual bool IsUserAllowlisted(
+ const AccountId& account_id,
+ bool* wildcard_match,
+ const base::Optional<user_manager::UserType>& user_type) = 0;
protected:
// Platform-dependant methods to be implemented by concrete class.