Skip to content

Commit

Permalink
[iOS] Integrate URLBlocklist and URLAllowlist policies
Browse files Browse the repository at this point in the history
Enables the URLBlacklist and URLWhitelist enterprise policies on iOS by
showing an error page for blocked URLs.

Bug: 1065161
Change-Id: I7af234098d832d261b32f15d672e02fc44749073
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2159046
Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
Reviewed-by: Maksim Ivanov <emaxx@chromium.org>
Reviewed-by: Rohit Rao <rohitrao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#762596}
  • Loading branch information
michaeldo1 authored and Commit Bot committed Apr 25, 2020
1 parent de7741c commit f6df236
Show file tree
Hide file tree
Showing 13 changed files with 410 additions and 0 deletions.
24 changes: 24 additions & 0 deletions components/policy/core/browser/url_blacklist_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "base/task_runner_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/policy/core/browser/url_blacklist_policy_handler.h"
#include "components/policy/core/common/policy_pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
Expand Down Expand Up @@ -70,6 +71,14 @@ const char* kBypassBlacklistWildcardForSchemes[] = {
"chrome-search",
};

#if defined(OS_IOS)
// The two schemes used on iOS for the NTP.
constexpr char kIosNtpAboutScheme[] = "about";
constexpr char kIosNtpChromeScheme[] = "chrome";
// The host string used on iOS for the NTP.
constexpr char kIosNtpHost[] = "newtab";
#endif

// Returns a blacklist based on the given |block| and |allow| pattern lists.
std::unique_ptr<URLBlacklist> BuildBlacklist(const base::ListValue* block,
const base::ListValue* allow) {
Expand All @@ -85,6 +94,21 @@ bool BypassBlacklistWildcardForURL(const GURL& url) {
if (scheme == kBypassBlacklistWildcardForSchemes[i])
return true;
}
#if defined(OS_IOS)
// Compare the chrome scheme and host against the chrome://newtab version of
// the NTP URL.
if (scheme == kIosNtpChromeScheme && url.host() == kIosNtpHost) {
return true;
}
// Compare the URL scheme and path to the about:newtab version of the NTP URL.
// Leading and trailing slashes must be removed because the host name is
// parsed as the URL path (which may contain slashes).
base::StringPiece trimmed_path =
base::TrimString(url.path(), "/", base::TrimPositions::TRIM_ALL);
if (scheme == kIosNtpAboutScheme && trimmed_path == kIosNtpHost) {
return true;
}
#endif
return false;
}

Expand Down
11 changes: 11 additions & 0 deletions components/policy/core/browser/url_blacklist_manager_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "base/test/task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/policy/core/common/policy_pref_names.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
Expand Down Expand Up @@ -474,6 +475,16 @@ TEST_F(URLBlacklistManagerTest, DefaultBlacklistExceptions) {
EXPECT_FALSE((blacklist.IsURLBlocked(GURL("chrome-extension://xyz"))));
EXPECT_FALSE((blacklist.IsURLBlocked(GURL("chrome-search://local-ntp"))));
EXPECT_FALSE((blacklist.IsURLBlocked(GURL("chrome-native://ntp"))));
#if defined(OS_IOS)
// Ensure that the NTP is not blocked on iOS by "*".
// TODO(crbug.com/1073291): On iOS, the NTP can not be blocked even by
// explicitly listing it as a blocked URL. This is due to the usage of
// "about:newtab" as its URL which is not recognized and filtered by the
// URLBlacklist code.
EXPECT_FALSE((blacklist.IsURLBlocked(GURL("about:newtab"))));
EXPECT_FALSE((blacklist.IsURLBlocked(GURL("about://newtab/"))));
EXPECT_FALSE((blacklist.IsURLBlocked(GURL("chrome://newtab"))));
#endif

// Unless they are explicitly blacklisted:
blocked->AppendString("chrome-extension://*");
Expand Down
1 change: 1 addition & 0 deletions ios/chrome/browser/browser_state/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ source_set("browser_state_impl") {
"//ios/chrome/browser/ntp_snippets",
"//ios/chrome/browser/passwords",
"//ios/chrome/browser/policy",
"//ios/chrome/browser/policy_url_blocking",
"//ios/chrome/browser/prefs",
"//ios/chrome/browser/prefs:browser_prefs",
"//ios/chrome/browser/reading_list",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
#import "ios/chrome/browser/metrics/ios_profile_session_durations_service_factory.h"
#include "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h"
#include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
#import "ios/chrome/browser/policy/policy_features.h"
#include "ios/chrome/browser/policy_url_blocking/policy_url_blocking_service.h"
#include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
#include "ios/chrome/browser/search_engines/template_url_service_factory.h"
#include "ios/chrome/browser/signin/about_signin_internals_factory.h"
Expand Down Expand Up @@ -128,4 +130,8 @@ void EnsureBrowserStateKeyedServiceFactoriesBuilt() {
TranslateAcceptLanguagesFactory::GetInstance();
UnifiedConsentServiceFactory::GetInstance();
UrlLanguageHistogramFactory::GetInstance();

if (IsURLBlocklistEnabled()) {
PolicyBlocklistServiceFactory::GetInstance();
}
}
47 changes: 47 additions & 0 deletions ios/chrome/browser/policy_url_blocking/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 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.

source_set("policy_url_blocking") {
configs += [ "//build/config/compiler:enable_arc" ]
sources = [
"policy_url_blocking_service.cc",
"policy_url_blocking_service.h",
"policy_url_blocking_tab_helper.h",
"policy_url_blocking_tab_helper.mm",
]
deps = [
"//base",
"//components/keyed_service/core",
"//components/keyed_service/ios",
"//components/policy/core/browser",
"//ios/chrome/browser",
"//ios/chrome/browser/browser_state",
"//ios/net",
"//ios/web",
"//net",
]
}

source_set("eg2_tests") {
defines = [ "CHROME_EARL_GREY_2" ]
configs += [
"//build/config/compiler:enable_arc",
"//build/config/ios:xctest_config",
]
testonly = true
sources = [ "policy_url_blocking_egtest.mm" ]
deps = [
"//base",
"//components/policy:generated",
"//ios/chrome/browser:utils",
"//ios/chrome/browser/policy:eg_test_support+eg2",
"//ios/chrome/browser/policy:feature_flags",
"//ios/chrome/test/earl_grey:eg_test_support+eg2",
"//ios/testing/earl_grey:eg_test_support+eg2",
"//ios/third_party/earl_grey2:test_lib",
"//ios/web/public/test/http_server",
"//net:test_support",
]
libs = [ "UIKit.framework" ]
}
113 changes: 113 additions & 0 deletions ios/chrome/browser/policy_url_blocking/policy_url_blocking_egtest.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// 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 <string>

#include "base/strings/sys_string_conversions.h"
#include "components/policy/policy_constants.h"
#import "ios/chrome/browser/chrome_switches.h"
#import "ios/chrome/browser/policy/policy_app_interface.h"
#import "ios/chrome/browser/policy/policy_features.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
#import "ios/chrome/test/earl_grey/chrome_matchers.h"
#import "ios/chrome/test/earl_grey/chrome_test_case.h"
#include "ios/testing/earl_grey/app_launch_configuration.h"
#import "ios/testing/earl_grey/earl_grey_test.h"
#include "net/base/net_errors.h"
#include "net/test/embedded_test_server/embedded_test_server.h"

#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif

// Tests the URLBlocklist and URLWhitelist enterprise policies.
@interface PolicyURLBlockingTestCase : ChromeTestCase
@end

@implementation PolicyURLBlockingTestCase

- (AppLaunchConfiguration)appConfigurationForTestCase {
// Use commandline args to insert fake policy data into NSUserDefaults. To the
// app, this policy data will appear under the
// "com.apple.configuration.managed" key.
AppLaunchConfiguration config;
config.additional_args.push_back(std::string("--") +
switches::kEnableEnterprisePolicy);
config.additional_args.push_back(std::string("--") +
switches::kInstallURLBlocklistHandlers);
config.additional_args.push_back(
std::string("--enable-features=URLBlocklistIOS,UseJSForErrorPage"));
config.relaunch_policy = NoForceRelaunchAndResetState;
return config;
}

- (void)setUp {
[super setUp];
GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
}

// Tests that pages are not blocked when the blocklist exists, but is empty.
- (void)testEmptyBlocklist {
[PolicyAppInterface
setPolicyValue:@"[]"
forKey:base::SysUTF8ToNSString(policy::key::kURLBlacklist)];

[ChromeEarlGrey loadURL:self.testServer->GetURL("/echo")];

[ChromeEarlGrey waitForWebStateContainingText:"Echo"];
}

// Tests that a page load is blocked when the URLBlocklist policy is set to
// block all URLs.
- (void)testWildcardBlocklist {
[PolicyAppInterface
setPolicyValue:@"[\"*\"]"
forKey:base::SysUTF8ToNSString(policy::key::kURLBlacklist)];

[ChromeEarlGrey loadURL:self.testServer->GetURL("/echo")];

[ChromeEarlGrey
waitForWebStateContainingText:net::ErrorToShortString(
net::ERR_BLOCKED_BY_ADMINISTRATOR)];
}

// Tests that the NTP is not blocked by the wildcard blocklist.
- (void)testNTPIsNotBlocked {
[PolicyAppInterface
setPolicyValue:@"[\"*\"]"
forKey:base::SysUTF8ToNSString(policy::key::kURLBlacklist)];

[[EarlGrey selectElementWithMatcher:chrome_test_util::FakeOmnibox()]
assertWithMatcher:grey_sufficientlyVisible()];
}

// Tests that a page is blocked when the URLBlocklist policy is set to block a
// specific URL.
- (void)testExplicitBlocklist {
[PolicyAppInterface
setPolicyValue:@"[\"*/echo\"]"
forKey:base::SysUTF8ToNSString(policy::key::kURLBlacklist)];

[ChromeEarlGrey loadURL:self.testServer->GetURL("/echo")];

[ChromeEarlGrey
waitForWebStateContainingText:net::ErrorToShortString(
net::ERR_BLOCKED_BY_ADMINISTRATOR)];
}

// Tests that pages are loaded when explicitly listed in the URLAllowlist.
- (void)testAllowlist {
[PolicyAppInterface
setPolicyValue:@"[\"*\"]"
forKey:base::SysUTF8ToNSString(policy::key::kURLBlacklist)];
[PolicyAppInterface
setPolicyValue:@"[\"*/echo\"]"
forKey:base::SysUTF8ToNSString(policy::key::kURLWhitelist)];

[ChromeEarlGrey loadURL:self.testServer->GetURL("/echo")];

[ChromeEarlGrey waitForWebStateContainingText:"Echo"];
}

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// 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 "ios/chrome/browser/policy_url_blocking/policy_url_blocking_service.h"

#include "components/keyed_service/ios/browser_state_dependency_manager.h"
#include "ios/chrome/browser/application_context.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/web/public/browser_state.h"

PolicyBlocklistService::PolicyBlocklistService(
web::BrowserState* browser_state,
std::unique_ptr<policy::URLBlacklistManager> url_blacklist_manager)
: url_blacklist_manager_(std::move(url_blacklist_manager)) {}

PolicyBlocklistService::~PolicyBlocklistService() = default;

policy::URLBlacklist::URLBlacklistState
PolicyBlocklistService::GetURLBlocklistState(const GURL& url) const {
return url_blacklist_manager_->GetURLBlacklistState(url);
}

// static
PolicyBlocklistServiceFactory* PolicyBlocklistServiceFactory::GetInstance() {
static base::NoDestructor<PolicyBlocklistServiceFactory> instance;
return instance.get();
}

// static
PolicyBlocklistService* PolicyBlocklistServiceFactory::GetForBrowserState(
web::BrowserState* browser_state) {
return static_cast<PolicyBlocklistService*>(
GetInstance()->GetServiceForBrowserState(browser_state, /*create=*/true));
}

PolicyBlocklistServiceFactory::PolicyBlocklistServiceFactory()
: BrowserStateKeyedServiceFactory(
"PolicyBlocklist",
BrowserStateDependencyManager::GetInstance()) {}

PolicyBlocklistServiceFactory::~PolicyBlocklistServiceFactory() = default;

std::unique_ptr<KeyedService>
PolicyBlocklistServiceFactory::BuildServiceInstanceFor(
web::BrowserState* browser_state) const {
PrefService* prefs =
ChromeBrowserState::FromBrowserState(browser_state)->GetPrefs();
auto url_blacklist_manager =
std::make_unique<policy::URLBlacklistManager>(prefs);
return std::make_unique<PolicyBlocklistService>(
browser_state, std::move(url_blacklist_manager));
}

web::BrowserState* PolicyBlocklistServiceFactory::GetBrowserStateToUse(
web::BrowserState* browser_state) const {
// Create the service for both normal and incognito browser states.
return browser_state;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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 IOS_CHROME_BROWSER_POLICY_URL_BLOCKING_POLICY_URL_BLOCKING_SERVICE_H_
#define IOS_CHROME_BROWSER_POLICY_URL_BLOCKING_POLICY_URL_BLOCKING_SERVICE_H_

#include "base/no_destructor.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
#include "components/policy/core/browser/url_blacklist_manager.h"

namespace web {
class BrowserState;
}

// Associates a policy::URLBlacklistManager instance with a BrowserState.
class PolicyBlocklistService : public KeyedService {
public:
explicit PolicyBlocklistService(
web::BrowserState* browser_state,
std::unique_ptr<policy::URLBlacklistManager> url_blacklist_manager);
~PolicyBlocklistService() override;

// Returns the blocking state for |url|.
policy::URLBlacklist::URLBlacklistState GetURLBlocklistState(
const GURL& url) const;

private:
// The URLBlacklistManager associated with |browser_state|.
std::unique_ptr<policy::URLBlacklistManager> url_blacklist_manager_;

PolicyBlocklistService(const PolicyBlocklistService&) = delete;
PolicyBlocklistService& operator=(const PolicyBlocklistService&) = delete;
};

class PolicyBlocklistServiceFactory : public BrowserStateKeyedServiceFactory {
public:
static PolicyBlocklistServiceFactory* GetInstance();
static PolicyBlocklistService* GetForBrowserState(
web::BrowserState* browser_state);

private:
friend class base::NoDestructor<PolicyBlocklistServiceFactory>;

PolicyBlocklistServiceFactory();
~PolicyBlocklistServiceFactory() override;

// BrowserStateKeyedServiceFactory implementation.
std::unique_ptr<KeyedService> BuildServiceInstanceFor(
web::BrowserState* browser_state) const override;
web::BrowserState* GetBrowserStateToUse(
web::BrowserState* browser_state) const override;

PolicyBlocklistServiceFactory(const PolicyBlocklistServiceFactory&) = delete;
PolicyBlocklistServiceFactory& operator=(
const PolicyBlocklistServiceFactory&) = delete;
};

#endif // IOS_CHROME_BROWSER_POLICY_URL_BLOCKING_POLICY_URL_BLOCKING_SERVICE_H_
Loading

0 comments on commit f6df236

Please sign in to comment.