Commit a05c9ecd authored by Sergey Poromov's avatar Sergey Poromov Committed by Commit Bot

DLP: Add ePrivacyScreen enforcement.

Add Data Leak Protection feature that enforces ePrivacyScreen when
confidential data is being shown on the screen.

Bug: 1112447
Test: New unit tests.
Change-Id: Ife24b02486b6a5cd5bd69b65ecc2cbfc7f0544f2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2332635Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Reviewed-by: default avatarGil Dekel <gildekel@chromium.org>
Reviewed-by: default avatarAya Elsayed <ayaelattar@chromium.org>
Reviewed-by: default avatarTim Song <tengs@chromium.org>
Commit-Queue: Sergey Poromov <poromov@chromium.org>
Cr-Commit-Position: refs/heads/master@{#795635}
parent 17506884
...@@ -39,13 +39,24 @@ bool PrivacyScreenController::IsSupported() const { ...@@ -39,13 +39,24 @@ bool PrivacyScreenController::IsSupported() const {
} }
bool PrivacyScreenController::IsManaged() const { bool PrivacyScreenController::IsManaged() const {
return active_user_pref_service_->IsManagedPreference( return dlp_enforced_ || (active_user_pref_service_ &&
prefs::kDisplayPrivacyScreenEnabled); active_user_pref_service_->IsManagedPreference(
prefs::kDisplayPrivacyScreenEnabled));
} }
bool PrivacyScreenController::GetEnabled() const { bool PrivacyScreenController::GetEnabled() const {
return active_user_pref_service_ && active_user_pref_service_->GetBoolean( if (!active_user_pref_service_)
prefs::kDisplayPrivacyScreenEnabled); return dlp_enforced_;
const bool actual_user_pref = active_user_pref_service_->GetBoolean(
prefs::kDisplayPrivacyScreenEnabled);
// If managed by policy, return the pref value.
if (active_user_pref_service_->IsManagedPreference(
prefs::kDisplayPrivacyScreenEnabled)) {
return actual_user_pref;
}
// Otherwise return true if enforced by DLP or return the last state set by
// the user.
return dlp_enforced_ || actual_user_pref;
} }
void PrivacyScreenController::SetEnabled(bool enabled, void PrivacyScreenController::SetEnabled(bool enabled,
...@@ -58,8 +69,9 @@ void PrivacyScreenController::SetEnabled(bool enabled, ...@@ -58,8 +69,9 @@ void PrivacyScreenController::SetEnabled(bool enabled,
// Do not set the policy if it is managed by policy. However, we still want to // Do not set the policy if it is managed by policy. However, we still want to
// notify observers that a change was attempted in order to show a toast. // notify observers that a change was attempted in order to show a toast.
if (IsManaged()) { if (IsManaged()) {
const bool currently_enabled = GetEnabled();
for (Observer& observer : observers_) for (Observer& observer : observers_)
observer.OnPrivacyScreenSettingChanged(GetEnabled()); observer.OnPrivacyScreenSettingChanged(currently_enabled);
return; return;
} }
...@@ -88,6 +100,11 @@ void PrivacyScreenController::RemoveObserver(Observer* observer) { ...@@ -88,6 +100,11 @@ void PrivacyScreenController::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer); observers_.RemoveObserver(observer);
} }
void PrivacyScreenController::SetEnforced(bool enforced) {
dlp_enforced_ = enforced;
OnStateChanged(true);
}
void PrivacyScreenController::OnActiveUserPrefServiceChanged( void PrivacyScreenController::OnActiveUserPrefServiceChanged(
PrefService* pref_service) { PrefService* pref_service) {
active_user_pref_service_ = pref_service; active_user_pref_service_ = pref_service;
...@@ -118,7 +135,7 @@ void PrivacyScreenController::OnDisplayModeChanged( ...@@ -118,7 +135,7 @@ void PrivacyScreenController::OnDisplayModeChanged(
} }
} }
void PrivacyScreenController::OnEnabledPrefChanged(bool notify_observers) { void PrivacyScreenController::OnStateChanged(bool notify_observers) {
if (IsSupported()) { if (IsSupported()) {
const bool is_enabled = GetEnabled(); const bool is_enabled = GetEnabled();
Shell::Get()->display_configurator()->SetPrivacyScreen(display_id_, Shell::Get()->display_configurator()->SetPrivacyScreen(display_id_,
...@@ -139,13 +156,13 @@ void PrivacyScreenController::InitFromUserPrefs() { ...@@ -139,13 +156,13 @@ void PrivacyScreenController::InitFromUserPrefs() {
pref_change_registrar_->Init(active_user_pref_service_); pref_change_registrar_->Init(active_user_pref_service_);
pref_change_registrar_->Add( pref_change_registrar_->Add(
prefs::kDisplayPrivacyScreenEnabled, prefs::kDisplayPrivacyScreenEnabled,
base::BindRepeating(&PrivacyScreenController::OnEnabledPrefChanged, base::BindRepeating(&PrivacyScreenController::OnStateChanged,
base::Unretained(this), base::Unretained(this),
/*notify_observers=*/true)); /*notify_observers=*/true));
// We don't want to notify observers upon initialization or on account change // We don't want to notify observers upon initialization or on account change
// because changes will trigger a toast to show up. // because changes will trigger a toast to show up.
OnEnabledPrefChanged(/*notify_observers=*/false); OnStateChanged(/*notify_observers=*/false);
} }
void PrivacyScreenController::UpdateSupport() { void PrivacyScreenController::UpdateSupport() {
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <memory> #include <memory>
#include "ash/ash_export.h" #include "ash/ash_export.h"
#include "ash/public/cpp/privacy_screen_dlp_helper.h"
#include "ash/public/cpp/session/session_observer.h" #include "ash/public/cpp/session/session_observer.h"
#include "base/observer_list.h" #include "base/observer_list.h"
#include "ui/display/manager/display_configurator.h" #include "ui/display/manager/display_configurator.h"
...@@ -20,7 +21,8 @@ namespace ash { ...@@ -20,7 +21,8 @@ namespace ash {
// Controls the privacy screen feature. // Controls the privacy screen feature.
class ASH_EXPORT PrivacyScreenController class ASH_EXPORT PrivacyScreenController
: public SessionObserver, : public PrivacyScreenDlpHelper,
public SessionObserver,
display::DisplayConfigurator::Observer { display::DisplayConfigurator::Observer {
public: public:
class Observer : public base::CheckedObserver { class Observer : public base::CheckedObserver {
...@@ -64,6 +66,9 @@ class ASH_EXPORT PrivacyScreenController ...@@ -64,6 +66,9 @@ class ASH_EXPORT PrivacyScreenController
void AddObserver(Observer* observer); void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer); void RemoveObserver(Observer* observer);
// PrivacyScreenDlpHelper:
void SetEnforced(bool enforced) override;
// SessionObserver: // SessionObserver:
void OnActiveUserPrefServiceChanged(PrefService* pref_service) override; void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
void OnSigninScreenPrefServiceInitialized(PrefService* pref_service) override; void OnSigninScreenPrefServiceInitialized(PrefService* pref_service) override;
...@@ -73,8 +78,9 @@ class ASH_EXPORT PrivacyScreenController ...@@ -73,8 +78,9 @@ class ASH_EXPORT PrivacyScreenController
const std::vector<display::DisplaySnapshot*>& displays) override; const std::vector<display::DisplaySnapshot*>& displays) override;
private: private:
// Called when the user pref for the status of PrivacyScreen is changed. // Called when the user pref or DLP enforcement for the state of PrivacyScreen
void OnEnabledPrefChanged(bool notify_observers); // is changed.
void OnStateChanged(bool notify_observers);
// Called when a change to |active_user_pref_service_| is detected (i.e. when // Called when a change to |active_user_pref_service_| is detected (i.e. when
// OnActiveUserPrefServiceChanged() is called. // OnActiveUserPrefServiceChanged() is called.
...@@ -98,6 +104,10 @@ class ASH_EXPORT PrivacyScreenController ...@@ -98,6 +104,10 @@ class ASH_EXPORT PrivacyScreenController
// of displays. // of displays.
int64_t display_id_; int64_t display_id_;
// Indicates whether PrivacyScreen is enforced by Data Leak Protection
// feature.
bool dlp_enforced_ = false;
base::ObserverList<Observer> observers_; base::ObserverList<Observer> observers_;
// The registrar used to watch privacy screen prefs changes in the above // The registrar used to watch privacy screen prefs changes in the above
......
...@@ -161,6 +161,53 @@ TEST_F(PrivacyScreenControllerTest, TestEnableAndDisable) { ...@@ -161,6 +161,53 @@ TEST_F(PrivacyScreenControllerTest, TestEnableAndDisable) {
EXPECT_TRUE(controller()->GetEnabled()); EXPECT_TRUE(controller()->GetEnabled());
} }
// Checks that when the privacy screen is enforced by Data Leak Prevention
// feature, it's turned on regardless of the user pref state.
TEST_F(PrivacyScreenControllerTest, TestDlpEnforced) {
// Create a single internal display that supports privacy screen.
BuildAndUpdateDisplaySnapshots({{
/*id=*/123u,
/*is_internal_display=*/true,
/*supports_privacy_screen=*/true,
}});
EXPECT_EQ(1u, display_manager()->GetNumDisplays());
ASSERT_TRUE(controller()->IsSupported());
EXPECT_FALSE(controller()->GetEnabled());
// Enforce privacy screen and check notification.
EXPECT_CALL(*observer(), OnPrivacyScreenSettingChanged(true));
controller()->SetEnforced(true);
EXPECT_TRUE(controller()->GetEnabled());
// Additionally enable it via pref, no change.
::testing::Mock::VerifyAndClear(observer());
controller()->SetEnabled(true,
PrivacyScreenController::kToggleUISurfaceCount);
EXPECT_TRUE(controller()->GetEnabled());
// Shouldn't be turned off when pref is disabled, because already enforced.
controller()->SetEnabled(false,
PrivacyScreenController::kToggleUISurfaceCount);
EXPECT_TRUE(controller()->GetEnabled());
// Remove enforcement, turned off as pref was not changed.
EXPECT_CALL(*observer(), OnPrivacyScreenSettingChanged(false));
controller()->SetEnforced(false);
EXPECT_FALSE(controller()->GetEnabled());
// Add pref back.
EXPECT_CALL(*observer(), OnPrivacyScreenSettingChanged(true));
controller()->SetEnabled(true,
PrivacyScreenController::kToggleUISurfaceCount);
EXPECT_TRUE(controller()->GetEnabled());
// Disable via pref, privacy screen is turned off with a notification.
EXPECT_CALL(*observer(), OnPrivacyScreenSettingChanged(false));
controller()->SetEnabled(false,
PrivacyScreenController::kToggleUISurfaceCount);
EXPECT_FALSE(controller()->GetEnabled());
}
// Tests that updates of the Privacy Screen user prefs from outside the // Tests that updates of the Privacy Screen user prefs from outside the
// PrivacyScreenController (such as Settings UI) are observed and applied. // PrivacyScreenController (such as Settings UI) are observed and applied.
TEST_F(PrivacyScreenControllerTest, TestOutsidePrefsUpdates) { TEST_F(PrivacyScreenControllerTest, TestOutsidePrefsUpdates) {
......
...@@ -215,6 +215,8 @@ component("cpp") { ...@@ -215,6 +215,8 @@ component("cpp") {
"power_utils.h", "power_utils.h",
"presentation_time_recorder.cc", "presentation_time_recorder.cc",
"presentation_time_recorder.h", "presentation_time_recorder.h",
"privacy_screen_dlp_helper.cc",
"privacy_screen_dlp_helper.h",
"quick_answers/controller/quick_answers_controller.cc", "quick_answers/controller/quick_answers_controller.cc",
"quick_answers/controller/quick_answers_controller.h", "quick_answers/controller/quick_answers_controller.h",
"rounded_corner_decorator.cc", "rounded_corner_decorator.cc",
......
// Copyright 2020 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 "ash/public/cpp/privacy_screen_dlp_helper.h"
#include "base/check_op.h"
namespace ash {
namespace {
PrivacyScreenDlpHelper* g_instance = nullptr;
} // namespace
// static
PrivacyScreenDlpHelper* PrivacyScreenDlpHelper::Get() {
DCHECK(g_instance);
return g_instance;
}
PrivacyScreenDlpHelper::PrivacyScreenDlpHelper() {
DCHECK(!g_instance);
g_instance = this;
}
PrivacyScreenDlpHelper::~PrivacyScreenDlpHelper() {
DCHECK_EQ(this, g_instance);
g_instance = nullptr;
}
} // namespace ash
// Copyright 2020 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 ASH_PUBLIC_CPP_PRIVACY_SCREEN_DLP_HELPER_H_
#define ASH_PUBLIC_CPP_PRIVACY_SCREEN_DLP_HELPER_H_
#include "ash/public/cpp/ash_public_export.h"
namespace ash {
// Interface for DLP (Data Leak Prevention) ash client in Chrome to interact
// with the PrivacyScreen feature.
class ASH_PUBLIC_EXPORT PrivacyScreenDlpHelper {
public:
static PrivacyScreenDlpHelper* Get();
// Set PrivacyScreen enforcement because of Data Leak Protection.
virtual void SetEnforced(bool enforced) = 0;
protected:
PrivacyScreenDlpHelper();
virtual ~PrivacyScreenDlpHelper();
PrivacyScreenDlpHelper(const PrivacyScreenDlpHelper&) = delete;
PrivacyScreenDlpHelper& operator=(const PrivacyScreenDlpHelper&) = delete;
};
} // namespace ash
#endif // ASH_PUBLIC_CPP_PRIVACY_SCREEN_DLP_HELPER_H_
...@@ -4,13 +4,24 @@ ...@@ -4,13 +4,24 @@
#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h" #include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
#include "ash/public/cpp/privacy_screen_dlp_helper.h"
#include "base/bind.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/public/browser/visibility.h" #include "content/public/browser/visibility.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace policy { namespace policy {
namespace {
// Delay to wait to turn off privacy screen enforcement after confidential data
// becomes not visible. This is done to not blink the privacy screen in case of
// a quick switch from one confidential data to another.
const base::TimeDelta kPrivacyScreenOffDelay =
base::TimeDelta::FromMilliseconds(500);
} // namespace
static DlpContentManager* g_dlp_content_manager = nullptr; static DlpContentManager* g_dlp_content_manager = nullptr;
// static // static
...@@ -94,14 +105,47 @@ void DlpContentManager::MaybeChangeOnScreenRestrictions() { ...@@ -94,14 +105,47 @@ void DlpContentManager::MaybeChangeOnScreenRestrictions() {
} }
} }
if (on_screen_restrictions_ != new_restriction_set) { if (on_screen_restrictions_ != new_restriction_set) {
DlpContentRestrictionSet added_restrictions =
new_restriction_set.DifferenceWith(on_screen_restrictions_);
DlpContentRestrictionSet removed_restrictions =
on_screen_restrictions_.DifferenceWith(new_restriction_set);
on_screen_restrictions_ = new_restriction_set; on_screen_restrictions_ = new_restriction_set;
OnScreenRestrictionsChanged(on_screen_restrictions_); OnScreenRestrictionsChanged(added_restrictions, removed_restrictions);
} }
} }
void DlpContentManager::OnScreenRestrictionsChanged( void DlpContentManager::OnScreenRestrictionsChanged(
const DlpContentRestrictionSet& restrictions) const { const DlpContentRestrictionSet& added_restrictions,
// TODO(crbug/1105991): Implement enforcing/releasing of restrictions. const DlpContentRestrictionSet& removed_restrictions) const {
DCHECK(!(added_restrictions.HasRestriction(
DlpContentRestriction::kPrivacyScreen) &&
removed_restrictions.HasRestriction(
DlpContentRestriction::kPrivacyScreen)));
if (added_restrictions.HasRestriction(
DlpContentRestriction::kPrivacyScreen)) {
ash::PrivacyScreenDlpHelper::Get()->SetEnforced(true);
}
if (removed_restrictions.HasRestriction(
DlpContentRestriction::kPrivacyScreen)) {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&DlpContentManager::MaybeRemovePrivacyScreenEnforcement,
base::Unretained(this)),
kPrivacyScreenOffDelay);
}
}
void DlpContentManager::MaybeRemovePrivacyScreenEnforcement() const {
if (!GetOnScreenPresentRestrictions().HasRestriction(
DlpContentRestriction::kPrivacyScreen)) {
ash::PrivacyScreenDlpHelper::Get()->SetEnforced(false);
}
}
// static
base::TimeDelta DlpContentManager::GetPrivacyScreenOffDelayForTesting() {
return kPrivacyScreenOffDelay;
} }
} // namespace policy } // namespace policy
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define CHROME_BROWSER_CHROMEOS_POLICY_DLP_DLP_CONTENT_MANAGER_H_ #define CHROME_BROWSER_CHROMEOS_POLICY_DLP_DLP_CONTENT_MANAGER_H_
#include "base/containers/flat_map.h" #include "base/containers/flat_map.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_content_restriction_set.h" #include "chrome/browser/chromeos/policy/dlp/dlp_content_restriction_set.h"
class GURL; class GURL;
...@@ -75,7 +76,14 @@ class DlpContentManager { ...@@ -75,7 +76,14 @@ class DlpContentManager {
// Called when the restrictions for currently visible content changes. // Called when the restrictions for currently visible content changes.
void OnScreenRestrictionsChanged( void OnScreenRestrictionsChanged(
const DlpContentRestrictionSet& restrictions) const; const DlpContentRestrictionSet& added_restrictions,
const DlpContentRestrictionSet& removed_restrictions) const;
// Removes PrivacyScreen enforcement after delay if it's still not enforced.
void MaybeRemovePrivacyScreenEnforcement() const;
// Get the delay before switching privacy screen off.
static base::TimeDelta GetPrivacyScreenOffDelayForTesting();
// Map from currently known confidential WebContents to the restrictions. // Map from currently known confidential WebContents to the restrictions.
base::flat_map<content::WebContents*, DlpContentRestrictionSet> base::flat_map<content::WebContents*, DlpContentRestrictionSet>
......
...@@ -4,10 +4,13 @@ ...@@ -4,10 +4,13 @@
#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h" #include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
#include "ash/public/cpp/privacy_screen_dlp_helper.h"
#include "base/test/task_environment.h"
#include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile.h"
#include "content/public/test/browser_task_environment.h" #include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_renderer_host.h" #include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h" #include "content/public/test/web_contents_tester.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace policy { namespace policy {
...@@ -16,6 +19,14 @@ namespace { ...@@ -16,6 +19,14 @@ namespace {
const DlpContentRestrictionSet kEmptyRestrictionSet; const DlpContentRestrictionSet kEmptyRestrictionSet;
const DlpContentRestrictionSet kNonEmptyRestrictionSet( const DlpContentRestrictionSet kNonEmptyRestrictionSet(
DlpContentRestriction::kScreenshot); DlpContentRestriction::kScreenshot);
const DlpContentRestrictionSet kPrivacyScreenEnforced(
DlpContentRestriction::kPrivacyScreen);
class MockPrivacyScreenHelper : public ash::PrivacyScreenDlpHelper {
public:
MOCK_METHOD1(SetEnforced, void(bool));
};
} // namespace } // namespace
class DlpContentManagerTest : public testing::Test { class DlpContentManagerTest : public testing::Test {
...@@ -49,10 +60,15 @@ class DlpContentManagerTest : public testing::Test { ...@@ -49,10 +60,15 @@ class DlpContentManagerTest : public testing::Test {
manager_.OnWebContentsDestroyed(web_contents); manager_.OnWebContentsDestroyed(web_contents);
} }
base::TimeDelta GetPrivacyScreenOffDelay() {
return manager_.GetPrivacyScreenOffDelayForTesting();
}
DlpContentManager manager_; DlpContentManager manager_;
content::BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
private: private:
content::BrowserTaskEnvironment task_environment_;
content::RenderViewHostTestEnabler rvh_test_enabler_; content::RenderViewHostTestEnabler rvh_test_enabler_;
std::unique_ptr<TestingProfile> profile_; std::unique_ptr<TestingProfile> profile_;
}; };
...@@ -169,4 +185,30 @@ TEST_F(DlpContentManagerTest, ...@@ -169,4 +185,30 @@ TEST_F(DlpContentManagerTest,
EXPECT_EQ(manager_.GetOnScreenPresentRestrictions(), kEmptyRestrictionSet); EXPECT_EQ(manager_.GetOnScreenPresentRestrictions(), kEmptyRestrictionSet);
} }
TEST_F(DlpContentManagerTest, PrivacyScreenEnforcement) {
MockPrivacyScreenHelper mock_privacy_screen_helper;
EXPECT_CALL(mock_privacy_screen_helper, SetEnforced(testing::_)).Times(0);
std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
testing::Mock::VerifyAndClearExpectations(&mock_privacy_screen_helper);
EXPECT_CALL(mock_privacy_screen_helper, SetEnforced(true)).Times(1);
ChangeConfidentiality(web_contents.get(), kPrivacyScreenEnforced);
testing::Mock::VerifyAndClearExpectations(&mock_privacy_screen_helper);
EXPECT_CALL(mock_privacy_screen_helper, SetEnforced(false)).Times(1);
web_contents->WasHidden();
ChangeVisibility(web_contents.get(), /*visible=*/false);
task_environment_.FastForwardBy(GetPrivacyScreenOffDelay());
testing::Mock::VerifyAndClearExpectations(&mock_privacy_screen_helper);
EXPECT_CALL(mock_privacy_screen_helper, SetEnforced(true)).Times(1);
web_contents->WasShown();
ChangeVisibility(web_contents.get(), /*visible=*/true);
testing::Mock::VerifyAndClearExpectations(&mock_privacy_screen_helper);
EXPECT_CALL(mock_privacy_screen_helper, SetEnforced(false)).Times(1);
DestroyWebContents(web_contents.get());
task_environment_.FastForwardBy(GetPrivacyScreenOffDelay());
}
} // namespace policy } // namespace policy
...@@ -15,6 +15,8 @@ namespace policy { ...@@ -15,6 +15,8 @@ namespace policy {
enum DlpContentRestriction { enum DlpContentRestriction {
// Do not allow any screenshots of the corresponding content. // Do not allow any screenshots of the corresponding content.
kScreenshot = 1 << 0, kScreenshot = 1 << 0,
// Enforce ePrivacy screen when content is visible.
kPrivacyScreen = 1 << 1,
}; };
// Represents set of restrictions applied to on-screen content. // Represents set of restrictions applied to on-screen content.
......
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