blob: 19d3ed013e9c8fb0f0ba6f439a29ae66b958a97b [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/enterprise/connectors/connectors_manager.h"
#include <memory>
#include "base/check.h"
#include "base/command_line.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/policy/chrome_browser_policy_connector.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "url/gurl.h"
#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
#include "chrome/browser/enterprise/connectors/analysis/content_analysis_sdk_manager.h" // nogncheck
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
namespace enterprise_connectors {
namespace {
#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
static constexpr enterprise_connectors::AnalysisConnector
kLocalAnalysisConnectors[] = {
AnalysisConnector::BULK_DATA_ENTRY,
AnalysisConnector::FILE_ATTACHED,
AnalysisConnector::PRINT,
};
#endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
} // namespace
ConnectorsManager::ConnectorsManager(PrefService* pref_service,
const ServiceProviderConfig* config,
bool observe_prefs)
: ConnectorsManagerBase(pref_service, config, observe_prefs) {
#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
// Start observing tab strip models for all browsers.
ForEachCurrentBrowserWindowInterfaceOrderedByActivation(
[this](BrowserWindowInterface* browser_window_interface) {
OnBrowserAdded(browser_window_interface->GetBrowserForMigrationOnly());
return true;
});
BrowserList::GetInstance()->AddObserver(this);
#endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
if (observe_prefs) {
StartObservingPrefs(pref_service);
#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
MaybeCloseLocalContentAnalysisAgentConnection();
#endif
}
}
#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
ConnectorsManager::~ConnectorsManager() {
BrowserList::GetInstance()->RemoveObserver(this);
ForEachCurrentBrowserWindowInterfaceOrderedByActivation(
[this](BrowserWindowInterface* browser_window_interface) {
OnBrowserRemoved(
browser_window_interface->GetBrowserForMigrationOnly());
return true;
});
}
#else
ConnectorsManager::~ConnectorsManager() = default;
#endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
bool ConnectorsManager::IsAnalysisConnectorEnabled(
AnalysisConnector connector) const {
if (analysis_connector_settings_.count(connector) == 0 &&
prefs()->HasPrefPath(AnalysisConnectorPref(connector))) {
CacheAnalysisConnectorPolicy(connector);
}
return analysis_connector_settings_.count(connector);
}
#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
bool ConnectorsManager::IsConnectorEnabledForLocalAgent(
AnalysisConnector connector) const {
if (!IsAnalysisConnectorEnabled(connector)) {
return false;
}
return analysis_connector_settings_.at(connector)[0].is_local_analysis();
}
#endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
std::optional<AnalysisSettings> ConnectorsManager::GetAnalysisSettings(
const GURL& url,
AnalysisConnector connector) {
if (!IsAnalysisConnectorEnabled(connector)) {
return std::nullopt;
}
if (analysis_connector_settings_.count(connector) == 0)
CacheAnalysisConnectorPolicy(connector);
// If the connector is still not in memory, it means the pref is set to an
// empty list or that it is not a list.
if (analysis_connector_settings_.count(connector) == 0)
return std::nullopt;
// While multiple services can be set by the connector policies, only the
// first one is considered for now.
return analysis_connector_settings_[connector][0].GetAnalysisSettings(
url, GetDataRegion(connector));
}
#if BUILDFLAG(IS_CHROMEOS)
std::optional<AnalysisSettings> ConnectorsManager::GetAnalysisSettings(
content::BrowserContext* context,
const storage::FileSystemURL& source_url,
const storage::FileSystemURL& destination_url,
AnalysisConnector connector) {
if (!IsAnalysisConnectorEnabled(connector)) {
return std::nullopt;
}
if (analysis_connector_settings_.count(connector) == 0)
CacheAnalysisConnectorPolicy(connector);
// If the connector is still not in memory, it means the pref is set to an
// empty list or that it is not a list.
if (analysis_connector_settings_.count(connector) == 0)
return std::nullopt;
// While multiple services can be set by the connector policies, only the
// first one is considered for now.
return analysis_connector_settings_[connector][0].GetAnalysisSettings(
context, source_url, destination_url, GetDataRegion(connector));
}
#endif // BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
void ConnectorsManager::OnBrowserAdded(Browser* browser) {
browser->tab_strip_model()->AddObserver(this);
}
void ConnectorsManager::OnBrowserRemoved(Browser* browser) {
browser->tab_strip_model()->RemoveObserver(this);
}
void ConnectorsManager::OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) {
// Checking only when new tab is open.
if (change.type() != TabStripModelChange::kInserted) {
return;
}
for (auto connector : kLocalAnalysisConnectors) {
if (!IsConnectorEnabledForLocalAgent(connector)) {
continue;
}
// Send a connection event to the local agent. If all the enabled connectors
// are configured to use the same agent, the same connection is reused here.
auto configs = GetAnalysisServiceConfigs(connector);
enterprise_connectors::ContentAnalysisSdkManager::Get()->GetClient(
{configs[0]->local_path, configs[0]->user_specific});
}
}
#endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
void ConnectorsManager::CacheAnalysisConnectorPolicy(
AnalysisConnector connector) const {
analysis_connector_settings_.erase(connector);
// Connectors with non-existing policies should not reach this code.
const char* pref = AnalysisConnectorPref(connector);
DCHECK(pref);
const base::Value::List& policy_value = prefs()->GetList(pref);
for (const base::Value& service_settings : policy_value)
analysis_connector_settings_[connector].emplace_back(
service_settings, *service_provider_config_);
}
#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
void ConnectorsManager::MaybeCloseLocalContentAnalysisAgentConnection() {
for (auto connector : kLocalAnalysisConnectors) {
if (IsConnectorEnabledForLocalAgent(connector)) {
// Return early because at lease one access point is enabled for local
// agent.
return;
}
}
// Delete connection with local agents when no access point is enabled.
ContentAnalysisSdkManager::Get()->ResetAllClients();
}
#endif // BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
void ConnectorsManager::SetTelemetryObserverCallback(
base::RepeatingCallback<void()> callback) {
telemetry_observer_callback_ = callback;
}
void ConnectorsManager::OnPrefChanged(AnalysisConnector connector) {
CacheAnalysisConnectorPolicy(connector);
#if BUILDFLAG(ENTERPRISE_LOCAL_CONTENT_ANALYSIS)
MaybeCloseLocalContentAnalysisAgentConnection();
#endif
}
bool ConnectorsManager::DelayUntilVerdict(AnalysisConnector connector) {
if (IsAnalysisConnectorEnabled(connector)) {
if (analysis_connector_settings_.count(connector) == 0)
CacheAnalysisConnectorPolicy(connector);
if (analysis_connector_settings_.count(connector) &&
!analysis_connector_settings_.at(connector).empty()) {
return analysis_connector_settings_.at(connector)
.at(0)
.ShouldBlockUntilVerdict();
}
}
return false;
}
std::optional<std::u16string> ConnectorsManager::GetCustomMessage(
AnalysisConnector connector,
const std::string& tag) {
if (IsAnalysisConnectorEnabled(connector)) {
if (analysis_connector_settings_.count(connector) == 0)
CacheAnalysisConnectorPolicy(connector);
if (analysis_connector_settings_.count(connector) &&
!analysis_connector_settings_.at(connector).empty()) {
return analysis_connector_settings_.at(connector).at(0).GetCustomMessage(
tag);
}
}
return std::nullopt;
}
std::optional<GURL> ConnectorsManager::GetLearnMoreUrl(
AnalysisConnector connector,
const std::string& tag) {
if (IsAnalysisConnectorEnabled(connector)) {
if (analysis_connector_settings_.count(connector) == 0)
CacheAnalysisConnectorPolicy(connector);
if (analysis_connector_settings_.count(connector) &&
!analysis_connector_settings_.at(connector).empty()) {
return analysis_connector_settings_.at(connector).at(0).GetLearnMoreUrl(
tag);
}
}
return std::nullopt;
}
bool ConnectorsManager::GetBypassJustificationRequired(
AnalysisConnector connector,
const std::string& tag) {
if (IsAnalysisConnectorEnabled(connector)) {
if (analysis_connector_settings_.count(connector) == 0)
CacheAnalysisConnectorPolicy(connector);
if (analysis_connector_settings_.count(connector) &&
!analysis_connector_settings_.at(connector).empty()) {
return analysis_connector_settings_.at(connector)
.at(0)
.GetBypassJustificationRequired(tag);
}
}
return false;
}
std::vector<std::string> ConnectorsManager::GetAnalysisServiceProviderNames(
AnalysisConnector connector) {
if (IsAnalysisConnectorEnabled(connector)) {
if (analysis_connector_settings_.count(connector) == 0) {
CacheAnalysisConnectorPolicy(connector);
}
if (analysis_connector_settings_.count(connector) &&
!analysis_connector_settings_.at(connector).empty()) {
// There can only be one provider right now, but the system is designed to
// support multiples, so return a vector.
return {analysis_connector_settings_.at(connector)
.at(0)
.service_provider_name()};
}
}
return {};
}
std::vector<const AnalysisConfig*> ConnectorsManager::GetAnalysisServiceConfigs(
AnalysisConnector connector) {
if (IsAnalysisConnectorEnabled(connector)) {
if (analysis_connector_settings_.count(connector) == 0) {
CacheAnalysisConnectorPolicy(connector);
}
if (analysis_connector_settings_.count(connector) &&
!analysis_connector_settings_.at(connector).empty()) {
// There can only be one provider right now, but the system is designed to
// support multiples, so return a vector.
return {
analysis_connector_settings_.at(connector).at(0).GetAnalysisConfig()};
}
}
return {};
}
DataRegion ConnectorsManager::GetDataRegion(AnalysisConnector connector) const {
#if BUILDFLAG(IS_ANDROID)
return DataRegion::NO_PREFERENCE;
#else
// Connector's policy scope determines the DRZ policy scope to use.
policy::PolicyScope scope = static_cast<policy::PolicyScope>(
prefs()->GetInteger(AnalysisConnectorScopePref(connector)));
const PrefService* pref_service =
(scope == policy::PolicyScope::POLICY_SCOPE_MACHINE)
? g_browser_process->local_state()
: prefs();
if (!pref_service ||
!pref_service->HasPrefPath(prefs::kChromeDataRegionSetting)) {
return DataRegion::NO_PREFERENCE;
}
return ChromeDataRegionSettingToEnum(
pref_service->GetInteger(prefs::kChromeDataRegionSetting));
#endif
}
void ConnectorsManager::StartObservingPrefs(PrefService* pref_service) {
pref_change_registrar_.Init(pref_service);
StartObservingPref(AnalysisConnector::FILE_ATTACHED);
StartObservingPref(AnalysisConnector::FILE_DOWNLOADED);
StartObservingPref(AnalysisConnector::BULK_DATA_ENTRY);
StartObservingPref(AnalysisConnector::PRINT);
#if BUILDFLAG(IS_CHROMEOS)
StartObservingPref(AnalysisConnector::FILE_TRANSFER);
#endif
ConnectorsManagerBase::StartObservingPref();
}
void ConnectorsManager::StartObservingPref(AnalysisConnector connector) {
const char* pref = AnalysisConnectorPref(connector);
DCHECK(pref);
if (!pref_change_registrar_.IsObserved(pref)) {
pref_change_registrar_.Add(
pref, base::BindRepeating(
static_cast<void (ConnectorsManager::*)(AnalysisConnector)>(
&ConnectorsManager::OnPrefChanged),
base::Unretained(this), connector));
}
}
const ConnectorsManager::AnalysisConnectorsSettings&
ConnectorsManager::GetAnalysisConnectorsSettingsForTesting() const {
return analysis_connector_settings_;
}
const base::RepeatingCallback<void()>
ConnectorsManager::GetTelemetryObserverCallbackForTesting() const {
return telemetry_observer_callback_;
}
} // namespace enterprise_connectors