chromium/ios/chrome/browser/ui/settings/supervised_user_settings_app_interface.mm

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/ui/settings/supervised_user_settings_app_interface.h"

#import "base/memory/ptr_util.h"
#import "base/memory/singleton.h"
#import "base/version_info/channel.h"
#import "components/prefs/pref_service.h"
#import "components/signin/public/identity_manager/identity_manager.h"
#import "components/supervised_user/core/browser/kids_chrome_management_url_checker_client.h"
#import "components/supervised_user/core/browser/permission_request_creator.h"
#import "components/supervised_user/core/browser/permission_request_creator_mock.h"
#import "components/supervised_user/core/browser/proto/kidsmanagement_messages.pb.h"
#import "components/supervised_user/core/browser/supervised_user_service.h"
#import "components/supervised_user/core/browser/supervised_user_settings_service.h"
#import "components/supervised_user/core/browser/supervised_user_utils.h"
#import "components/supervised_user/core/common/pref_names.h"
#import "components/supervised_user/core/common/supervised_user_constants.h"
#import "ios/chrome/app/main_controller.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/browser/browser_provider.h"
#import "ios/chrome/browser/shared/model/browser/browser_provider_interface.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/signin/model/identity_manager_factory.h"
#import "ios/chrome/browser/supervised_user/model/supervised_user_error_container.h"
#import "ios/chrome/browser/supervised_user/model/supervised_user_service_factory.h"
#import "ios/chrome/browser/supervised_user/model/supervised_user_settings_service_factory.h"
#import "ios/chrome/test/app/chrome_test_util.h"
#import "ios/chrome/test/app/tab_test_util.h"
#import "ios/components/security_interstitials/ios_blocking_page_tab_helper.h"
#import "ios/components/security_interstitials/ios_security_interstitial_page.h"
#import "ios/web/public/web_state.h"
#import "net/base/apple/url_conversions.h"
#import "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#import "services/network/test/test_url_loader_factory.h"

namespace {

// Helper class that holds a instance of TestUrlLoaderFactory.
// It allows the callers of this class to keep an alive instance
// of a TestUrlLoaderFactory for the duration of a test.
class TestUrlLoaderFactoryHelper {
 public:
  static TestUrlLoaderFactoryHelper* SharedInstance() {
    return base::Singleton<TestUrlLoaderFactoryHelper>::get();
  }

  void SetUp() {
    test_url_loader_factory_ =
        std::make_unique<network::TestURLLoaderFactory>();
    shared_url_loader_factory_ =
        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
            test_url_loader_factory_.get());
  }

  void TearDown() {
    shared_url_loader_factory_.reset();
    test_url_loader_factory_.reset();
  }

  scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory() {
    return std::ref(shared_url_loader_factory_);
  }
  network::TestURLLoaderFactory* test_url_loader_factory() {
    return test_url_loader_factory_.get();
  }

 private:
  std::unique_ptr<network::TestURLLoaderFactory> test_url_loader_factory_;
  scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
};

void setUrlFilteringForUrl(const GURL& url, bool isAllowed) {
  supervised_user::SupervisedUserSettingsService* settings_service =
      SupervisedUserSettingsServiceFactory::GetForBrowserState(
          chrome_test_util::GetOriginalBrowserState());

  const base::Value::Dict& local_settings =
      settings_service->LocalSettingsForTest();
  base::Value::Dict dict_to_insert;

  const base::Value::Dict* dict_value =
      local_settings.FindDict(supervised_user::kContentPackManualBehaviorHosts);
  if (dict_value) {
    dict_to_insert = dict_value->Clone();
  }
  dict_to_insert.Set(url.host(), isAllowed);
  settings_service->SetLocalSetting(
      supervised_user::kContentPackManualBehaviorHosts,
      std::move(dict_to_insert));
}

bool isShowingInterstitialForState(web::WebState* web_state) {
  CHECK(web_state);
  auto* blocking_tab_helper =
      security_interstitials::IOSBlockingPageTabHelper::FromWebState(web_state);

  CHECK(blocking_tab_helper);
  security_interstitials::IOSSecurityInterstitialPage* blocking_page =
      blocking_tab_helper->GetCurrentBlockingPage();
  return blocking_page && blocking_page->GetInterstitialType() ==
                              kSupervisedUserInterstitialType;
}

}  // namespace

@implementation SupervisedUserSettingsAppInterface : NSObject

+ (void)setSupervisedUserURLFilterBehavior:
    (supervised_user::FilteringBehavior)behavior {
  supervised_user::SupervisedUserSettingsService* settings_service =
      SupervisedUserSettingsServiceFactory::GetForBrowserState(
          chrome_test_util::GetOriginalBrowserState());
  settings_service->SetLocalSetting(
      supervised_user::kContentPackDefaultFilteringBehavior,
      base::Value(static_cast<int>(behavior)));
  if (behavior == supervised_user::FilteringBehavior::kAllow) {
    settings_service->SetLocalSetting(supervised_user::kSafeSitesEnabled,
                                      base::Value(true));
  }
}

+ (void)resetSupervisedUserURLFilterBehavior {
  supervised_user::SupervisedUserSettingsService* settings_service =
      SupervisedUserSettingsServiceFactory::GetForBrowserState(
          chrome_test_util::GetOriginalBrowserState());
  settings_service->RemoveLocalSetting(
      supervised_user::kContentPackDefaultFilteringBehavior);
}

+ (void)resetManualUrlFiltering {
  supervised_user::SupervisedUserSettingsService* settings_service =
      SupervisedUserSettingsServiceFactory::GetForBrowserState(
          chrome_test_util::GetOriginalBrowserState());
  settings_service->RemoveLocalSetting(
      supervised_user::kContentPackManualBehaviorHosts);
}

+ (void)setFakePermissionCreator {
  supervised_user::SupervisedUserSettingsService* settings_service =
      SupervisedUserSettingsServiceFactory::GetForBrowserState(
          chrome_test_util::GetOriginalBrowserState());
  CHECK(settings_service);
  std::unique_ptr<supervised_user::PermissionRequestCreator> creator =
      std::make_unique<supervised_user::PermissionRequestCreatorMock>(
          *settings_service);
  supervised_user::PermissionRequestCreatorMock* mocked_creator =
      static_cast<supervised_user::PermissionRequestCreatorMock*>(
          creator.get());
  mocked_creator->SetEnabled();

  supervised_user::SupervisedUserService* service =
      SupervisedUserServiceFactory::GetForBrowserState(
          chrome_test_util::GetOriginalBrowserState());
  CHECK(service);
  service->remote_web_approvals_manager().ClearApprovalRequestsCreators();
  service->remote_web_approvals_manager().AddApprovalRequestCreator(
      std::move(creator));
}

+ (void)approveWebsiteDomain:(NSURL*)url {
  supervised_user::SupervisedUserSettingsService* settings_service =
      SupervisedUserSettingsServiceFactory::GetForBrowserState(
          chrome_test_util::GetOriginalBrowserState());
  settings_service->RecordLocalWebsiteApproval(net::GURLWithNSURL(url).host());
}

+ (void)setFilteringToAllowAllSites {
  [self setSupervisedUserURLFilterBehavior:supervised_user::FilteringBehavior::
                                               kAllow];
}

+ (void)setFilteringToAllowApprovedSites {
  [self setSupervisedUserURLFilterBehavior:supervised_user::FilteringBehavior::
                                               kBlock];
}

+ (void)addWebsiteToAllowList:(NSURL*)host {
  setUrlFilteringForUrl(net::GURLWithNSURL(host), true);
}

+ (void)addWebsiteToBlockList:(NSURL*)host {
  setUrlFilteringForUrl(net::GURLWithNSURL(host), false);
}

+ (void)resetFirstTimeBanner {
  ChromeBrowserState* browser_state = ChromeBrowserState::FromBrowserState(
      chrome_test_util::GetOriginalBrowserState());
  PrefService* user_prefs = browser_state->GetPrefs();
  CHECK(user_prefs);
  user_prefs->SetInteger(
      prefs::kFirstTimeInterstitialBannerState,
      static_cast<int>(
          supervised_user::FirstTimeInterstitialBannerState::kNeedToShow));
}

+ (void)setDefaultClassifyURLNavigationIsAllowed:(BOOL)is_allowed {
  // Fake the ClassifyUrl responses.
  kidsmanagement::ClassifyUrlResponse response;
  auto url_classification =
      is_allowed ? kidsmanagement::ClassifyUrlResponse::ALLOWED
                 : kidsmanagement::ClassifyUrlResponse::RESTRICTED;
  response.set_display_classification(url_classification);
  std::string classify_url_service_url =
      "https://kidsmanagement-pa.googleapis.com/kidsmanagement/v1/people/"
      "me:classifyUrl?alt=proto";

  auto* test_url_loader_factory =
      TestUrlLoaderFactoryHelper::SharedInstance()->test_url_loader_factory();
  CHECK(test_url_loader_factory);
  auto shared_url_loader_factory =
      TestUrlLoaderFactoryHelper::SharedInstance()->shared_url_loader_factory();
  CHECK(shared_url_loader_factory);

  test_url_loader_factory->AddResponse(classify_url_service_url,
                                       response.SerializeAsString());

  // Set up the KidsChromeManagementClient that provides fake safe search
  // responses.
  ChromeBrowserState* browser_state = ChromeBrowserState::FromBrowserState(
      chrome_test_util::GetOriginalBrowserState());
  signin::IdentityManager* identity_manager =
      IdentityManagerFactory::GetForBrowserState(browser_state);
  CHECK(identity_manager);

  supervised_user::SupervisedUserService* supervised_user_service =
      SupervisedUserServiceFactory::GetForBrowserState(browser_state);

  std::unique_ptr<safe_search_api::URLCheckerClient> url_checker_client =
      std::make_unique<supervised_user::KidsChromeManagementURLCheckerClient>(
          identity_manager, shared_url_loader_factory, /*country=*/"",
          version_info::Channel::UNKNOWN);
  supervised_user_service->GetURLFilter()->SetURLCheckerClient(
      std::move(url_checker_client));
}

+ (void)setUpTestUrlLoaderFactoryHelper {
  TestUrlLoaderFactoryHelper::SharedInstance()->SetUp();
}

+ (void)tearDownTestUrlLoaderFactoryHelper {
  TestUrlLoaderFactoryHelper::SharedInstance()->TearDown();
}

+ (NSInteger)countSupervisedUserIntersitialsForExistingWebStates {
  int count = 0;
  int tab_count = chrome_test_util::GetMainTabCount();
  for (int i = 0; i < tab_count; i++) {
    if (isShowingInterstitialForState(
            chrome_test_util::GetWebStateAtIndexInCurrentMode(i))) {
      count++;
    }
  }
  return count;
}

@end