Commit 9085494b authored by Guillaume Jenkins's avatar Guillaume Jenkins Committed by Chromium LUCI CQ

[iOS Enterprise] Support dynamic refresh of BrowserSignin policy

Adds support for changing the value of the BrowserSignin policy to 0
while the browser is running.

Adds a BrowserAgent that monitors the
kSigninAllowed pref, which is controlled by the BrowserSignin policy.
When kSigninAllowed changes to false, the BrowserAgent dispatches a
command to SceneController to dismiss any in-progress sign-in UI and
sign the user out.

Bug: 1155745
Change-Id: Ifac4c785f73e098e94be8c3df35040ec60b489c9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2616278Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Reviewed-by: default avatarDavid Roger <droger@chromium.org>
Reviewed-by: default avatarNohemi Fernandez <fernandex@chromium.org>
Commit-Queue: Guillaume Jenkins <gujen@google.com>
Cr-Commit-Position: refs/heads/master@{#844583}
parent 059c12ed
......@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/logging.h"
#include "build/build_config.h"
#include "components/signin/internal/identity_manager/primary_account_manager.h"
#include "components/signin/public/base/signin_client.h"
#include "components/signin/public/base/signin_metrics.h"
......@@ -90,6 +91,10 @@ bool PrimaryAccountPolicyManagerImpl::IsSigninAllowed() const {
void PrimaryAccountPolicyManagerImpl::OnSigninAllowedPrefChanged(
PrimaryAccountManager* primary_account_manager) {
// On iOS, monitoring the pref and signing the user out is the responsibility
// of the embedder, which allows doing additional sign-out tasks such as
// dismissing sign-in UI and clearing sync'd account data.
#if !defined(OS_IOS)
if (!IsSigninAllowed() &&
primary_account_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)) {
VLOG(0) << "IsSigninAllowed() set to false, signing out the user";
......@@ -97,6 +102,7 @@ void PrimaryAccountPolicyManagerImpl::OnSigninAllowedPrefChanged(
signin_metrics::SIGNOUT_PREF_CHANGED,
signin_metrics::SignoutDelete::IGNORE_METRIC);
}
#endif // defined(OS_IOS)
}
bool PrimaryAccountPolicyManagerImpl::IsAllowedUsername(
......
......@@ -52,6 +52,7 @@ source_set("main") {
"//ios/chrome/browser/device_sharing",
"//ios/chrome/browser/infobars/overlays/browser_agent:browser_agent_util",
"//ios/chrome/browser/metrics:metrics_browser_agent",
"//ios/chrome/browser/policy",
"//ios/chrome/browser/send_tab_to_self",
"//ios/chrome/browser/sessions",
"//ios/chrome/browser/sessions:restoration_agent",
......
......@@ -12,6 +12,7 @@
#import "ios/chrome/browser/device_sharing/device_sharing_browser_agent.h"
#include "ios/chrome/browser/infobars/overlays/browser_agent/infobar_overlay_browser_agent_util.h"
#import "ios/chrome/browser/metrics/tab_usage_recorder_browser_agent.h"
#import "ios/chrome/browser/policy/policy_watcher_browser_agent.h"
#include "ios/chrome/browser/send_tab_to_self/send_tab_to_self_browser_agent.h"
#import "ios/chrome/browser/sessions/live_tab_context_browser_agent.h"
#import "ios/chrome/browser/sessions/session_restoration_browser_agent.h"
......@@ -48,6 +49,7 @@ void AttachBrowserAgents(Browser* browser) {
ClosingWebStateObserverBrowserAgent::CreateForBrowser(browser);
SnapshotBrowserAgent::CreateForBrowser(browser);
PolicyWatcherBrowserAgent::CreateForBrowser(browser);
// Send Tab To Self is non-OTR only.
if (!browser->GetBrowserState()->IsOffTheRecord())
......
......@@ -22,6 +22,8 @@ source_set("policy") {
"device_management_service_configuration_ios.mm",
"policy_conversions_client_ios.h",
"policy_conversions_client_ios.mm",
"policy_watcher_browser_agent.h",
"policy_watcher_browser_agent.mm",
"reporting/browser_report_generator_ios.h",
"reporting/browser_report_generator_ios.mm",
"reporting/profile_report_generator_ios.h",
......@@ -59,7 +61,9 @@ source_set("policy") {
"//ios/chrome/browser",
"//ios/chrome/browser:pref_names",
"//ios/chrome/browser/browser_state",
"//ios/chrome/browser/main:public",
"//ios/chrome/browser/signin",
"//ios/chrome/browser/ui/commands",
"//ios/chrome/common",
"//ios/public/provider/chrome/browser/signin",
"//services/network/public/cpp",
......@@ -139,6 +143,7 @@ source_set("unit_tests") {
sources = [
"browser_dm_token_storage_ios_unittest.mm",
"policy_unittest.mm",
"policy_watcher_browser_agent_unittest.mm",
"reporting/browser_report_generator_ios_unittest.mm",
"reporting/profile_report_generator_ios_unittest.mm",
"reporting/report_generator_ios_unittest.mm",
......@@ -153,17 +158,26 @@ source_set("unit_tests") {
"//components/enterprise",
"//components/enterprise:test_support",
"//components/policy/core/browser:test_support",
"//components/pref_registry",
"//components/prefs",
"//components/signin/public/base",
"//components/sync_preferences",
"//components/sync_preferences:test_support",
"//ios/chrome/browser:chrome_paths",
"//ios/chrome/browser:pref_names",
"//ios/chrome/browser:utils",
"//ios/chrome/browser/browser_state:test_support",
"//ios/chrome/browser/main:test_support",
"//ios/chrome/browser/prefs",
"//ios/chrome/browser/prefs:browser_prefs",
"//ios/chrome/browser/signin:signin",
"//ios/chrome/browser/signin:test_support",
"//ios/chrome/browser/ui/commands",
"//ios/chrome/test:test_support",
"//ios/public/provider/chrome/browser/signin:test_support",
"//ios/web/public/test:test",
"//testing/gtest",
"//third_party/ocmock",
]
}
......
// Copyright 2021 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 IOS_CHROME_BROWSER_POLICY_POLICY_WATCHER_BROWSER_AGENT_H_
#define IOS_CHROME_BROWSER_POLICY_POLICY_WATCHER_BROWSER_AGENT_H_
#include <memory>
#include "base/macros.h"
#import "ios/chrome/browser/main/browser_user_data.h"
#import "ios/chrome/browser/ui/commands/application_commands.h"
class Browser;
class PrefChangeRegistrar;
// Service that listens for policy-controlled prefs changes and sends commands
// to update the UI accordingly.
class PolicyWatcherBrowserAgent
: public BrowserUserData<PolicyWatcherBrowserAgent> {
public:
~PolicyWatcherBrowserAgent() override;
// Sets the command dispatcher to use for sneding UI commands when prefs
// change. Also starts observing the kSigninAllowed pref.
void SetApplicationCommandsHandler(id<ApplicationCommands> handler);
private:
explicit PolicyWatcherBrowserAgent(Browser* browser);
friend class BrowserUserData<PolicyWatcherBrowserAgent>;
BROWSER_USER_DATA_KEY_DECL();
// Handler for changes to kSigninAllowed. When the pref changes to |false|,
// sends a command to the SceneController to dismiss any in-progress sign-in
// UI.
void ForceSignOutIfSigninDisabled();
// The owning Browser.
Browser* browser_;
// The command handler to use for sending ApplicationCommands. Must be set by
//
id<ApplicationCommands> application_commands_handler_;
// Registrar for pref change notifications.
std::unique_ptr<PrefChangeRegistrar> prefs_change_observer_;
};
#endif // IOS_CHROME_BROWSER_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_BROWSER_AGENT_H_
// Copyright 2021 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 "ios/chrome/browser/policy/policy_watcher_browser_agent.h"
#import <Foundation/Foundation.h>
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/base/signin_pref_names.h"
#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
#import "ios/chrome/browser/policy/policy_features.h"
#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
BROWSER_USER_DATA_KEY_IMPL(PolicyWatcherBrowserAgent)
PolicyWatcherBrowserAgent::PolicyWatcherBrowserAgent(Browser* browser)
: browser_(browser),
prefs_change_observer_(std::make_unique<PrefChangeRegistrar>()) {
prefs_change_observer_->Init(browser_->GetBrowserState()->GetPrefs());
}
PolicyWatcherBrowserAgent::~PolicyWatcherBrowserAgent() {}
void PolicyWatcherBrowserAgent::SetApplicationCommandsHandler(
id<ApplicationCommands> handler) {
application_commands_handler_ = handler;
// BrowserSignin policy: start observing the kSigninAllowed pref for non-OTR
// browsers. When the pref becomes false, send a UI command to sign the user
// out. This requires the given command dispatcher to be fully configured.
if (!ShouldInstallBrowserSigninPolicyHandler() ||
browser_->GetBrowserState()->IsOffTheRecord()) {
return;
}
prefs_change_observer_->Add(
prefs::kSigninAllowed,
base::BindRepeating(
&PolicyWatcherBrowserAgent::ForceSignOutIfSigninDisabled,
base::Unretained(this)));
ForceSignOutIfSigninDisabled();
}
void PolicyWatcherBrowserAgent::ForceSignOutIfSigninDisabled() {
if (!browser_->GetBrowserState()->GetPrefs()->GetBoolean(
prefs::kSigninAllowed)) {
// Trigger the command to interrupt any in-progress sign-in and to force
// sign out existing users.
[application_commands_handler_ forceSignOut];
}
}
// Copyright 2021 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 "ios/chrome/browser/policy/policy_watcher_browser_agent.h"
#import "base/test/scoped_command_line.h"
#import "components/pref_registry/pref_registry_syncable.h"
#import "components/prefs/pref_service.h"
#import "components/signin/public/base/signin_pref_names.h"
#import "components/sync_preferences/pref_service_mock_factory.h"
#import "components/sync_preferences/pref_service_syncable.h"
#import "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#import "ios/chrome/browser/chrome_switches.h"
#import "ios/chrome/browser/main/test_browser.h"
#import "ios/chrome/browser/prefs/browser_prefs.h"
#import "ios/chrome/browser/ui/commands/application_commands.h"
#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
#import "ios/web/public/test/web_task_environment.h"
#include "testing/platform_test.h"
#include "third_party/ocmock/OCMock/OCMock.h"
#include "third_party/ocmock/gtest_support.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using sync_preferences::PrefServiceMockFactory;
using sync_preferences::PrefServiceSyncable;
using user_prefs::PrefRegistrySyncable;
using web::WebTaskEnvironment;
class PolicyWatcherBrowserAgentTest : public PlatformTest {
protected:
void SetUp() override {
PlatformTest::SetUp();
TestChromeBrowserState::Builder builder;
builder.SetPrefService(CreatePrefService());
chrome_browser_state_ = builder.Build();
}
std::unique_ptr<PrefServiceSyncable> CreatePrefService() {
PrefServiceMockFactory factory;
scoped_refptr<PrefRegistrySyncable> registry(new PrefRegistrySyncable);
std::unique_ptr<PrefServiceSyncable> prefs =
factory.CreateSyncable(registry.get());
RegisterBrowserStatePrefs(registry.get());
return prefs;
}
web::WebTaskEnvironment task_environment_;
std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
};
#pragma mark - Tests.
// Tests that the browser agent monitors the kSigninAllowed pref and dispatches
// the appropriate command when the pref becomes false.
TEST_F(PolicyWatcherBrowserAgentTest, observesSigninAllowed) {
// Set up the policy handler and the initial pref value.
base::test::ScopedCommandLine scoped_command_line;
scoped_command_line.GetProcessCommandLine()->AppendSwitch(
switches::kInstallBrowserSigninHandler);
chrome_browser_state_->GetPrefs()->SetBoolean(prefs::kSigninAllowed, true);
// Set up the test browser and attach the browser agent under test.
std::unique_ptr<Browser> browser =
std::make_unique<TestBrowser>(chrome_browser_state_.get());
PolicyWatcherBrowserAgent::CreateForBrowser(browser.get());
// Set up the mock ApplicationCommands handler and inject it in the browser
// agent.
id applicationCommandHandler =
[OCMockObject mockForProtocol:@protocol(ApplicationCommands)];
[((id<ApplicationCommands>)[applicationCommandHandler expect]) forceSignOut];
PolicyWatcherBrowserAgent::FromBrowser(browser.get())
->SetApplicationCommandsHandler(applicationCommandHandler);
// Action: disable browser sign-in.
chrome_browser_state_->GetPrefs()->SetBoolean(prefs::kSigninAllowed, false);
// Verify the forceSignOut command was dispatched by the browser agent.
EXPECT_OCMOCK_VERIFY(applicationCommandHandler);
}
......@@ -144,6 +144,9 @@ enum class KeyRetrievalTriggerForUMA;
- (void)showSignin:(ShowSigninCommand*)command
baseViewController:(UIViewController*)baseViewController;
// Signs the user out and dismisses UI for any in-progress sign-in.
- (void)forceSignOut;
// TODO(crbug.com/779791) : Do not pass baseViewController through dispatcher.
// Shows the Add Account UI, presenting from |baseViewController|.
- (void)showAddAccountFromViewController:(UIViewController*)baseViewController;
......
......@@ -108,6 +108,7 @@ source_set("scene") {
"//ios/chrome/browser/infobars",
"//ios/chrome/browser/main",
"//ios/chrome/browser/ntp:features",
"//ios/chrome/browser/policy",
"//ios/chrome/browser/screenshot",
"//ios/chrome/browser/signin",
"//ios/chrome/browser/snapshots",
......
......@@ -14,6 +14,7 @@
#include "components/infobars/core/infobar_manager.h"
#include "components/prefs/pref_service.h"
#import "components/previous_session_info/previous_session_info.h"
#include "components/signin/public/base/signin_metrics.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/url_formatter/url_formatter.h"
#include "components/web_resource/web_resource_pref_names.h"
......@@ -50,7 +51,10 @@
#import "ios/chrome/browser/main/browser_list_factory.h"
#import "ios/chrome/browser/main/browser_util.h"
#include "ios/chrome/browser/ntp/features.h"
#import "ios/chrome/browser/policy/policy_watcher_browser_agent.h"
#include "ios/chrome/browser/screenshot/screenshot_delegate.h"
#import "ios/chrome/browser/signin/authentication_service.h"
#import "ios/chrome/browser/signin/authentication_service_factory.h"
#include "ios/chrome/browser/signin/constants.h"
#include "ios/chrome/browser/signin/identity_manager_factory.h"
#import "ios/chrome/browser/snapshots/snapshot_tab_helper.h"
......@@ -646,6 +650,14 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
// Ensure the main browser is created. This also creates the BVC.
[self.browserViewWrangler createMainBrowser];
// Now that the main browser's command dispatcher is fully configured, inject
// it into the PolicyWatcherBrowserAgent so it can start monitoring
// UI-impacting policy changes.
id<ApplicationCommands> handler = HandlerForProtocol(
self.mainInterface.browser->GetCommandDispatcher(), ApplicationCommands);
PolicyWatcherBrowserAgent::FromBrowser(self.mainInterface.browser)
->SetApplicationCommandsHandler(handler);
// Add Scene Agent that requires CommandDispatcher.
[self.sceneState
addAgent:[[DefaultBrowserSceneAgent alloc]
......@@ -1211,6 +1223,23 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
[self startSigninCoordinatorWithCompletion:command.callback];
}
- (void)forceSignOut {
AuthenticationService* service =
AuthenticationServiceFactory::GetForBrowserState(
self.mainInterface.browser->GetBrowserState());
auto signOut = ^{
service->SignOut(signin_metrics::ProfileSignout::SIGNOUT_PREF_CHANGED,
/*force_clear_browsing_data=*/true,
/*completion=*/nil);
};
if (self.signinCoordinator) {
[self interruptSigninCoordinatorAnimated:YES completion:signOut];
} else {
signOut();
}
}
- (void)showAdvancedSigninSettingsFromViewController:
(UIViewController*)baseViewController {
DCHECK(!self.signinCoordinator);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment