| // Copyright 2014 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 "chrome/browser/supervised_user/supervised_user_service.h" |
| |
| #include <utility> |
| |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/path_service.h" |
| #include "base/prefs/pref_service.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task_runner_util.h" |
| #include "base/version.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/component_updater/supervised_user_whitelist_installer.h" |
| #include "chrome/browser/net/file_downloader.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_info_cache.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" |
| #include "chrome/browser/signin/signin_manager_factory.h" |
| #include "chrome/browser/supervised_user/experimental/supervised_user_filtering_switches.h" |
| #include "chrome/browser/supervised_user/permission_request_creator.h" |
| #include "chrome/browser/supervised_user/supervised_user_constants.h" |
| #include "chrome/browser/supervised_user/supervised_user_service_observer.h" |
| #include "chrome/browser/supervised_user/supervised_user_settings_service.h" |
| #include "chrome/browser/supervised_user/supervised_user_settings_service_factory.h" |
| #include "chrome/browser/supervised_user/supervised_user_site_list.h" |
| #include "chrome/browser/supervised_user/supervised_user_whitelist_service.h" |
| #include "chrome/browser/sync/profile_sync_service_factory.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/browser_sync/browser/profile_sync_service.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/signin/core/browser/profile_oauth2_token_service.h" |
| #include "components/signin/core/browser/signin_manager.h" |
| #include "components/signin/core/browser/signin_manager_base.h" |
| #include "components/signin/core/common/signin_switches.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/user_metrics.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| #if !defined(OS_ANDROID) && !defined(OS_IOS) |
| #include "chrome/browser/supervised_user/legacy/custodian_profile_downloader_service.h" |
| #include "chrome/browser/supervised_user/legacy/custodian_profile_downloader_service_factory.h" |
| #include "chrome/browser/supervised_user/legacy/permission_request_creator_sync.h" |
| #include "chrome/browser/supervised_user/legacy/supervised_user_pref_mapping_service.h" |
| #include "chrome/browser/supervised_user/legacy/supervised_user_pref_mapping_service_factory.h" |
| #include "chrome/browser/supervised_user/legacy/supervised_user_registration_utility.h" |
| #include "chrome/browser/supervised_user/legacy/supervised_user_shared_settings_service_factory.h" |
| #endif |
| |
| #if defined(OS_CHROMEOS) |
| #include "chrome/browser/chromeos/login/users/chrome_user_manager.h" |
| #include "chrome/browser/chromeos/login/users/supervised_user_manager.h" |
| #include "components/user_manager/user_manager.h" |
| #endif |
| |
| #if defined(ENABLE_EXTENSIONS) |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "extensions/browser/extension_system.h" |
| #endif |
| |
| #if defined(ENABLE_THEMES) |
| #include "chrome/browser/themes/theme_service.h" |
| #include "chrome/browser/themes/theme_service_factory.h" |
| #endif |
| |
| using base::DictionaryValue; |
| using base::UserMetricsAction; |
| using content::BrowserThread; |
| |
| namespace { |
| |
| // The URL from which to download a host blacklist if no local one exists yet. |
| const char kBlacklistURL[] = |
| "https://www.gstatic.com/chrome/supervised_user/blacklist-20141001-1k.bin"; |
| // The filename under which we'll store the blacklist (in the user data dir). |
| const char kBlacklistFilename[] = "su-blacklist.bin"; |
| |
| const char* const kCustodianInfoPrefs[] = { |
| prefs::kSupervisedUserCustodianName, |
| prefs::kSupervisedUserCustodianEmail, |
| prefs::kSupervisedUserCustodianProfileImageURL, |
| prefs::kSupervisedUserCustodianProfileURL, |
| prefs::kSupervisedUserSecondCustodianName, |
| prefs::kSupervisedUserSecondCustodianEmail, |
| prefs::kSupervisedUserSecondCustodianProfileImageURL, |
| prefs::kSupervisedUserSecondCustodianProfileURL, |
| }; |
| |
| void CreateURLAccessRequest( |
| const GURL& url, |
| PermissionRequestCreator* creator, |
| const SupervisedUserService::SuccessCallback& callback) { |
| creator->CreateURLAccessRequest(url, callback); |
| } |
| |
| void CreateExtensionUpdateRequest( |
| const std::string& id, |
| PermissionRequestCreator* creator, |
| const SupervisedUserService::SuccessCallback& callback) { |
| creator->CreateExtensionUpdateRequest(id, callback); |
| } |
| |
| // Default callback for AddExtensionUpdateRequest. |
| void ExtensionUpdateRequestSent(const std::string& id, bool success) { |
| VLOG_IF(1, !success) << "Failed sending update request for " << id; |
| } |
| |
| base::FilePath GetBlacklistPath() { |
| base::FilePath blacklist_dir; |
| PathService::Get(chrome::DIR_USER_DATA, &blacklist_dir); |
| return blacklist_dir.AppendASCII(kBlacklistFilename); |
| } |
| |
| #if defined(ENABLE_EXTENSIONS) |
| enum ExtensionState { |
| EXTENSION_FORCED, |
| EXTENSION_BLOCKED, |
| EXTENSION_ALLOWED |
| }; |
| |
| ExtensionState GetExtensionState(const extensions::Extension* extension) { |
| bool was_installed_by_default = extension->was_installed_by_default(); |
| #if defined(OS_CHROMEOS) |
| // On Chrome OS all external sources are controlled by us so it means that |
| // they are "default". Method was_installed_by_default returns false because |
| // extensions creation flags are ignored in case of default extensions with |
| // update URL(the flags aren't passed to OnExternalExtensionUpdateUrlFound). |
| // TODO(dpolukhin): remove this Chrome OS specific code as soon as creation |
| // flags are not ignored. |
| was_installed_by_default = |
| extensions::Manifest::IsExternalLocation(extension->location()); |
| #endif |
| // Note: Component extensions are protected from modification/uninstallation |
| // anyway, so there's no need to enforce them again for supervised users. |
| // Also, leave policy-installed extensions alone - they have their own |
| // management; in particular we don't want to override the force-install list. |
| if (extensions::Manifest::IsComponentLocation(extension->location()) || |
| extensions::Manifest::IsPolicyLocation(extension->location()) || |
| extension->is_theme() || |
| extension->from_bookmark() || |
| extension->is_shared_module() || |
| was_installed_by_default) { |
| return EXTENSION_ALLOWED; |
| } |
| |
| if (extension->was_installed_by_custodian()) |
| return EXTENSION_FORCED; |
| |
| return EXTENSION_BLOCKED; |
| } |
| #endif |
| |
| } // namespace |
| |
| SupervisedUserService::~SupervisedUserService() { |
| DCHECK(!did_init_ || did_shutdown_); |
| url_filter_context_.ui_url_filter()->RemoveObserver(this); |
| } |
| |
| // static |
| void SupervisedUserService::RegisterProfilePrefs( |
| user_prefs::PrefRegistrySyncable* registry) { |
| registry->RegisterDictionaryPref(prefs::kSupervisedUserManualHosts); |
| registry->RegisterDictionaryPref(prefs::kSupervisedUserManualURLs); |
| registry->RegisterIntegerPref(prefs::kDefaultSupervisedUserFilteringBehavior, |
| SupervisedUserURLFilter::ALLOW); |
| registry->RegisterBooleanPref(prefs::kSupervisedUserCreationAllowed, true); |
| registry->RegisterBooleanPref(prefs::kSupervisedUserSafeSites, true); |
| for (const char* pref : kCustodianInfoPrefs) { |
| registry->RegisterStringPref(pref, std::string()); |
| } |
| } |
| |
| void SupervisedUserService::Init() { |
| DCHECK(!did_init_); |
| did_init_ = true; |
| DCHECK(GetSettingsService()->IsReady()); |
| |
| pref_change_registrar_.Init(profile_->GetPrefs()); |
| pref_change_registrar_.Add( |
| prefs::kSupervisedUserId, |
| base::Bind(&SupervisedUserService::OnSupervisedUserIdChanged, |
| base::Unretained(this))); |
| pref_change_registrar_.Add( |
| prefs::kRecordHistory, |
| base::Bind(&SupervisedUserService::OnHistoryRecordingStateChanged, |
| base::Unretained(this))); |
| |
| ProfileSyncService* sync_service = |
| ProfileSyncServiceFactory::GetForProfile(profile_); |
| // Can be null in tests. |
| if (sync_service) |
| sync_service->AddPreferenceProvider(this); |
| |
| std::string client_id = component_updater::SupervisedUserWhitelistInstaller:: |
| ClientIdForProfilePath(profile_->GetPath()); |
| whitelist_service_.reset(new SupervisedUserWhitelistService( |
| profile_->GetPrefs(), |
| g_browser_process->supervised_user_whitelist_installer(), client_id)); |
| whitelist_service_->AddSiteListsChangedCallback( |
| base::Bind(&SupervisedUserService::OnSiteListsChanged, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| SetActive(ProfileIsSupervised()); |
| } |
| |
| void SupervisedUserService::SetDelegate(Delegate* delegate) { |
| if (delegate) { |
| // Changing delegates isn't allowed. |
| DCHECK(!delegate_); |
| } else { |
| // If the delegate is removed, deactivate first to give the old delegate a |
| // chance to clean up. |
| SetActive(false); |
| } |
| delegate_ = delegate; |
| } |
| |
| scoped_refptr<const SupervisedUserURLFilter> |
| SupervisedUserService::GetURLFilterForIOThread() { |
| return url_filter_context_.io_url_filter(); |
| } |
| |
| SupervisedUserURLFilter* SupervisedUserService::GetURLFilterForUIThread() { |
| return url_filter_context_.ui_url_filter(); |
| } |
| |
| SupervisedUserWhitelistService* SupervisedUserService::GetWhitelistService() { |
| return whitelist_service_.get(); |
| } |
| |
| bool SupervisedUserService::AccessRequestsEnabled() { |
| return FindEnabledPermissionRequestCreator(0) < permissions_creators_.size(); |
| } |
| |
| void SupervisedUserService::AddURLAccessRequest( |
| const GURL& url, |
| const SuccessCallback& callback) { |
| AddPermissionRequestInternal( |
| base::Bind(CreateURLAccessRequest, |
| SupervisedUserURLFilter::Normalize(url)), |
| callback, 0); |
| } |
| |
| void SupervisedUserService::AddExtensionUpdateRequest( |
| const std::string& extension_id, |
| const base::Version& version, |
| const SuccessCallback& callback) { |
| std::string id = GetExtensionUpdateRequestId(extension_id, version); |
| AddPermissionRequestInternal( |
| base::Bind(CreateExtensionUpdateRequest, id), callback, 0); |
| } |
| |
| void SupervisedUserService::AddExtensionUpdateRequest( |
| const std::string& extension_id, |
| const base::Version& version) { |
| std::string id = GetExtensionUpdateRequestId(extension_id, version); |
| AddExtensionUpdateRequest(extension_id, version, |
| base::Bind(ExtensionUpdateRequestSent, id)); |
| } |
| |
| // static |
| std::string SupervisedUserService::GetExtensionUpdateRequestId( |
| const std::string& extension_id, |
| const base::Version& version) { |
| return base::StringPrintf("%s:%s", extension_id.c_str(), |
| version.GetString().c_str()); |
| } |
| |
| std::string SupervisedUserService::GetCustodianEmailAddress() const { |
| std::string email = profile_->GetPrefs()->GetString( |
| prefs::kSupervisedUserCustodianEmail); |
| #if defined(OS_CHROMEOS) |
| // |GetActiveUser()| can return null in unit tests. |
| if (email.empty() && !!user_manager::UserManager::Get()->GetActiveUser()) { |
| email = chromeos::ChromeUserManager::Get() |
| ->GetSupervisedUserManager() |
| ->GetManagerDisplayEmail( |
| user_manager::UserManager::Get()->GetActiveUser()->email()); |
| } |
| #endif |
| return email; |
| } |
| |
| std::string SupervisedUserService::GetCustodianName() const { |
| std::string name = profile_->GetPrefs()->GetString( |
| prefs::kSupervisedUserCustodianName); |
| #if defined(OS_CHROMEOS) |
| // |GetActiveUser()| can return null in unit tests. |
| if (name.empty() && !!user_manager::UserManager::Get()->GetActiveUser()) { |
| name = base::UTF16ToUTF8(chromeos::ChromeUserManager::Get() |
| ->GetSupervisedUserManager() |
| ->GetManagerDisplayName( |
| user_manager::UserManager::Get()->GetActiveUser()->email())); |
| } |
| #endif |
| return name.empty() ? GetCustodianEmailAddress() : name; |
| } |
| |
| std::string SupervisedUserService::GetSecondCustodianEmailAddress() const { |
| return profile_->GetPrefs()->GetString( |
| prefs::kSupervisedUserSecondCustodianEmail); |
| } |
| |
| std::string SupervisedUserService::GetSecondCustodianName() const { |
| std::string name = profile_->GetPrefs()->GetString( |
| prefs::kSupervisedUserSecondCustodianName); |
| return name.empty() ? GetSecondCustodianEmailAddress() : name; |
| } |
| |
| base::string16 SupervisedUserService::GetExtensionsLockedMessage() const { |
| return l10n_util::GetStringFUTF16(IDS_EXTENSIONS_LOCKED_SUPERVISED_USER, |
| base::UTF8ToUTF16(GetCustodianName())); |
| } |
| |
| #if !defined(OS_ANDROID) && !defined(OS_IOS) |
| void SupervisedUserService::InitSync(const std::string& refresh_token) { |
| StartSetupSync(); |
| |
| ProfileOAuth2TokenService* token_service = |
| ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); |
| token_service->UpdateCredentials(supervised_users::kSupervisedUserPseudoEmail, |
| refresh_token); |
| |
| FinishSetupSyncWhenReady(); |
| } |
| |
| void SupervisedUserService::RegisterAndInitSync( |
| SupervisedUserRegistrationUtility* registration_utility, |
| Profile* custodian_profile, |
| const std::string& supervised_user_id, |
| const AuthErrorCallback& callback) { |
| DCHECK(ProfileIsSupervised()); |
| DCHECK(!custodian_profile->IsSupervised()); |
| |
| base::string16 name = base::UTF8ToUTF16( |
| profile_->GetPrefs()->GetString(prefs::kProfileName)); |
| int avatar_index = profile_->GetPrefs()->GetInteger( |
| prefs::kProfileAvatarIndex); |
| SupervisedUserRegistrationInfo info(name, avatar_index); |
| registration_utility->Register( |
| supervised_user_id, |
| info, |
| base::Bind(&SupervisedUserService::OnSupervisedUserRegistered, |
| weak_ptr_factory_.GetWeakPtr(), callback, custodian_profile)); |
| |
| // Fetch the custodian's profile information, to store the name. |
| // TODO(pamg): If --google-profile-info (flag: switches::kGoogleProfileInfo) |
| // is ever enabled, take the name from the ProfileInfoCache instead. |
| CustodianProfileDownloaderService* profile_downloader_service = |
| CustodianProfileDownloaderServiceFactory::GetForProfile( |
| custodian_profile); |
| profile_downloader_service->DownloadProfile( |
| base::Bind(&SupervisedUserService::OnCustodianProfileDownloaded, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| #endif // !defined(OS_ANDROID) && !defined(OS_IOS) |
| |
| void SupervisedUserService::AddNavigationBlockedCallback( |
| const NavigationBlockedCallback& callback) { |
| navigation_blocked_callbacks_.push_back(callback); |
| } |
| |
| void SupervisedUserService::DidBlockNavigation( |
| content::WebContents* web_contents) { |
| for (const auto& callback : navigation_blocked_callbacks_) |
| callback.Run(web_contents); |
| } |
| |
| void SupervisedUserService::AddObserver( |
| SupervisedUserServiceObserver* observer) { |
| observer_list_.AddObserver(observer); |
| } |
| |
| void SupervisedUserService::RemoveObserver( |
| SupervisedUserServiceObserver* observer) { |
| observer_list_.RemoveObserver(observer); |
| } |
| |
| void SupervisedUserService::AddPermissionRequestCreator( |
| scoped_ptr<PermissionRequestCreator> creator) { |
| permissions_creators_.push_back(creator.release()); |
| } |
| |
| SupervisedUserService::URLFilterContext::URLFilterContext() |
| : ui_url_filter_(new SupervisedUserURLFilter), |
| io_url_filter_(new SupervisedUserURLFilter) {} |
| SupervisedUserService::URLFilterContext::~URLFilterContext() {} |
| |
| SupervisedUserURLFilter* |
| SupervisedUserService::URLFilterContext::ui_url_filter() const { |
| return ui_url_filter_.get(); |
| } |
| |
| SupervisedUserURLFilter* |
| SupervisedUserService::URLFilterContext::io_url_filter() const { |
| return io_url_filter_.get(); |
| } |
| |
| void SupervisedUserService::URLFilterContext::SetDefaultFilteringBehavior( |
| SupervisedUserURLFilter::FilteringBehavior behavior) { |
| ui_url_filter_->SetDefaultFilteringBehavior(behavior); |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&SupervisedUserURLFilter::SetDefaultFilteringBehavior, |
| io_url_filter_.get(), behavior)); |
| } |
| |
| void SupervisedUserService::URLFilterContext::LoadWhitelists( |
| const std::vector<scoped_refptr<SupervisedUserSiteList> >& site_lists) { |
| ui_url_filter_->LoadWhitelists(site_lists); |
| BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| base::Bind(&SupervisedUserURLFilter::LoadWhitelists, |
| io_url_filter_, site_lists)); |
| } |
| |
| void SupervisedUserService::URLFilterContext::SetBlacklist( |
| const SupervisedUserBlacklist* blacklist) { |
| ui_url_filter_->SetBlacklist(blacklist); |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&SupervisedUserURLFilter::SetBlacklist, |
| io_url_filter_, |
| blacklist)); |
| } |
| |
| bool SupervisedUserService::URLFilterContext::HasBlacklist() const { |
| return ui_url_filter_->HasBlacklist(); |
| } |
| |
| void SupervisedUserService::URLFilterContext::SetManualHosts( |
| scoped_ptr<std::map<std::string, bool> > host_map) { |
| ui_url_filter_->SetManualHosts(host_map.get()); |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&SupervisedUserURLFilter::SetManualHosts, |
| io_url_filter_, base::Owned(host_map.release()))); |
| } |
| |
| void SupervisedUserService::URLFilterContext::SetManualURLs( |
| scoped_ptr<std::map<GURL, bool> > url_map) { |
| ui_url_filter_->SetManualURLs(url_map.get()); |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&SupervisedUserURLFilter::SetManualURLs, |
| io_url_filter_, base::Owned(url_map.release()))); |
| } |
| |
| void SupervisedUserService::URLFilterContext::Clear() { |
| ui_url_filter_->Clear(); |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&SupervisedUserURLFilter::Clear, |
| io_url_filter_)); |
| } |
| |
| void SupervisedUserService::URLFilterContext::InitAsyncURLChecker( |
| const scoped_refptr<net::URLRequestContextGetter>& context) { |
| ui_url_filter_->InitAsyncURLChecker(context.get()); |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&SupervisedUserURLFilter::InitAsyncURLChecker, |
| io_url_filter_, context)); |
| } |
| |
| bool SupervisedUserService::URLFilterContext::HasAsyncURLChecker() const { |
| return ui_url_filter_->HasAsyncURLChecker(); |
| } |
| |
| void SupervisedUserService::URLFilterContext::ClearAsyncURLChecker() { |
| ui_url_filter_->ClearAsyncURLChecker(); |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&SupervisedUserURLFilter::ClearAsyncURLChecker, |
| io_url_filter_)); |
| } |
| |
| SupervisedUserService::SupervisedUserService(Profile* profile) |
| : includes_sync_sessions_type_(true), |
| profile_(profile), |
| active_(false), |
| delegate_(NULL), |
| waiting_for_sync_initialization_(false), |
| is_profile_active_(false), |
| did_init_(false), |
| did_shutdown_(false), |
| blacklist_state_(BlacklistLoadState::NOT_LOADED), |
| weak_ptr_factory_(this) { |
| url_filter_context_.ui_url_filter()->AddObserver(this); |
| } |
| |
| void SupervisedUserService::SetActive(bool active) { |
| if (active_ == active) |
| return; |
| active_ = active; |
| |
| if (!delegate_ || !delegate_->SetActive(active_)) { |
| if (active_) { |
| #if !defined(OS_ANDROID) && !defined(OS_IOS) |
| SupervisedUserPrefMappingServiceFactory::GetForBrowserContext(profile_) |
| ->Init(); |
| |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch(switches::kSupervisedUserSyncToken)) { |
| InitSync( |
| command_line->GetSwitchValueASCII( |
| switches::kSupervisedUserSyncToken)); |
| } |
| |
| ProfileOAuth2TokenService* token_service = |
| ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); |
| token_service->LoadCredentials( |
| supervised_users::kSupervisedUserPseudoEmail); |
| |
| permissions_creators_.push_back(new PermissionRequestCreatorSync( |
| GetSettingsService(), |
| SupervisedUserSharedSettingsServiceFactory::GetForBrowserContext( |
| profile_), |
| ProfileSyncServiceFactory::GetForProfile(profile_), |
| GetSupervisedUserName(), |
| profile_->GetPrefs()->GetString(prefs::kSupervisedUserId))); |
| |
| SetupSync(); |
| #else |
| NOTREACHED(); |
| #endif |
| } |
| } |
| |
| // Now activate/deactivate anything not handled by the delegate yet. |
| |
| #if defined(ENABLE_THEMES) |
| // Re-set the default theme to turn the SU theme on/off. |
| ThemeService* theme_service = ThemeServiceFactory::GetForProfile(profile_); |
| if (theme_service->UsingDefaultTheme() || theme_service->UsingSystemTheme()) |
| theme_service->UseDefaultTheme(); |
| #endif |
| |
| ProfileSyncService* sync_service = |
| ProfileSyncServiceFactory::GetForProfile(profile_); |
| sync_service->SetEncryptEverythingAllowed(!active_); |
| |
| GetSettingsService()->SetActive(active_); |
| |
| #if defined(ENABLE_EXTENSIONS) |
| SetExtensionsActive(); |
| #endif |
| |
| if (active_) { |
| pref_change_registrar_.Add( |
| prefs::kDefaultSupervisedUserFilteringBehavior, |
| base::Bind(&SupervisedUserService::OnDefaultFilteringBehaviorChanged, |
| base::Unretained(this))); |
| pref_change_registrar_.Add(prefs::kSupervisedUserSafeSites, |
| base::Bind(&SupervisedUserService::OnSafeSitesSettingChanged, |
| base::Unretained(this))); |
| pref_change_registrar_.Add(prefs::kSupervisedUserManualHosts, |
| base::Bind(&SupervisedUserService::UpdateManualHosts, |
| base::Unretained(this))); |
| pref_change_registrar_.Add(prefs::kSupervisedUserManualURLs, |
| base::Bind(&SupervisedUserService::UpdateManualURLs, |
| base::Unretained(this))); |
| for (const char* pref : kCustodianInfoPrefs) { |
| pref_change_registrar_.Add(pref, |
| base::Bind(&SupervisedUserService::OnCustodianInfoChanged, |
| base::Unretained(this))); |
| } |
| |
| // Initialize the filter. |
| OnDefaultFilteringBehaviorChanged(); |
| OnSafeSitesSettingChanged(); |
| whitelist_service_->Init(); |
| UpdateManualHosts(); |
| UpdateManualURLs(); |
| |
| #if !defined(OS_ANDROID) && !defined(OS_IOS) |
| // TODO(bauerb): Get rid of the platform-specific #ifdef here. |
| // http://crbug.com/313377 |
| BrowserList::AddObserver(this); |
| #endif |
| } else { |
| permissions_creators_.clear(); |
| |
| pref_change_registrar_.Remove( |
| prefs::kDefaultSupervisedUserFilteringBehavior); |
| pref_change_registrar_.Remove(prefs::kSupervisedUserManualHosts); |
| pref_change_registrar_.Remove(prefs::kSupervisedUserManualURLs); |
| for (const char* pref : kCustodianInfoPrefs) { |
| pref_change_registrar_.Remove(pref); |
| } |
| |
| url_filter_context_.Clear(); |
| FOR_EACH_OBSERVER( |
| SupervisedUserServiceObserver, observer_list_, OnURLFilterChanged()); |
| |
| #if !defined(OS_ANDROID) && !defined(OS_IOS) |
| if (waiting_for_sync_initialization_) |
| ProfileSyncServiceFactory::GetForProfile(profile_)->RemoveObserver(this); |
| |
| // TODO(bauerb): Get rid of the platform-specific #ifdef here. |
| // http://crbug.com/313377 |
| BrowserList::RemoveObserver(this); |
| #endif |
| } |
| } |
| |
| #if !defined(OS_ANDROID) && !defined(OS_IOS) |
| void SupervisedUserService::OnCustodianProfileDownloaded( |
| const base::string16& full_name) { |
| profile_->GetPrefs()->SetString(prefs::kSupervisedUserCustodianName, |
| base::UTF16ToUTF8(full_name)); |
| } |
| |
| void SupervisedUserService::OnSupervisedUserRegistered( |
| const AuthErrorCallback& callback, |
| Profile* custodian_profile, |
| const GoogleServiceAuthError& auth_error, |
| const std::string& token) { |
| if (auth_error.state() == GoogleServiceAuthError::NONE) { |
| InitSync(token); |
| SigninManagerBase* signin = |
| SigninManagerFactory::GetForProfile(custodian_profile); |
| profile_->GetPrefs()->SetString( |
| prefs::kSupervisedUserCustodianEmail, |
| signin->GetAuthenticatedAccountInfo().email); |
| |
| // The supervised user profile is now ready for use. |
| ProfileManager* profile_manager = g_browser_process->profile_manager(); |
| ProfileInfoCache& cache = profile_manager->GetProfileInfoCache(); |
| size_t index = cache.GetIndexOfProfileWithPath(profile_->GetPath()); |
| cache.SetIsOmittedProfileAtIndex(index, false); |
| } else { |
| DCHECK_EQ(std::string(), token); |
| } |
| |
| callback.Run(auth_error); |
| } |
| void SupervisedUserService::SetupSync() { |
| StartSetupSync(); |
| FinishSetupSyncWhenReady(); |
| } |
| |
| void SupervisedUserService::StartSetupSync() { |
| // Tell the sync service that setup is in progress so we don't start syncing |
| // until we've finished configuration. |
| ProfileSyncServiceFactory::GetForProfile(profile_)->SetSetupInProgress(true); |
| } |
| |
| void SupervisedUserService::FinishSetupSyncWhenReady() { |
| // If we're already waiting for the Sync backend, there's nothing to do here. |
| if (waiting_for_sync_initialization_) |
| return; |
| |
| // Continue in FinishSetupSync() once the Sync backend has been initialized. |
| ProfileSyncService* service = |
| ProfileSyncServiceFactory::GetForProfile(profile_); |
| if (service->IsBackendInitialized() && |
| service->backend_mode() == ProfileSyncService::SYNC) { |
| FinishSetupSync(); |
| } else { |
| service->AddObserver(this); |
| waiting_for_sync_initialization_ = true; |
| } |
| } |
| |
| void SupervisedUserService::FinishSetupSync() { |
| ProfileSyncService* service = |
| ProfileSyncServiceFactory::GetForProfile(profile_); |
| DCHECK(service->IsBackendInitialized()); |
| DCHECK(service->backend_mode() == ProfileSyncService::SYNC); |
| |
| // Sync nothing (except types which are set via GetPreferredDataTypes). |
| bool sync_everything = false; |
| syncer::ModelTypeSet synced_datatypes; |
| service->OnUserChoseDatatypes(sync_everything, synced_datatypes); |
| |
| // Notify ProfileSyncService that we are done with configuration. |
| service->SetSetupInProgress(false); |
| service->SetFirstSetupComplete(); |
| } |
| #endif |
| |
| bool SupervisedUserService::ProfileIsSupervised() const { |
| return profile_->IsSupervised(); |
| } |
| |
| void SupervisedUserService::OnCustodianInfoChanged() { |
| FOR_EACH_OBSERVER( |
| SupervisedUserServiceObserver, observer_list_, OnCustodianInfoChanged()); |
| } |
| |
| SupervisedUserSettingsService* SupervisedUserService::GetSettingsService() { |
| return SupervisedUserSettingsServiceFactory::GetForProfile(profile_); |
| } |
| |
| size_t SupervisedUserService::FindEnabledPermissionRequestCreator( |
| size_t start) { |
| for (size_t i = start; i < permissions_creators_.size(); ++i) { |
| if (permissions_creators_[i]->IsEnabled()) |
| return i; |
| } |
| return permissions_creators_.size(); |
| } |
| |
| void SupervisedUserService::AddPermissionRequestInternal( |
| const CreatePermissionRequestCallback& create_request, |
| const SuccessCallback& callback, |
| size_t index) { |
| // Find a permission request creator that is enabled. |
| size_t next_index = FindEnabledPermissionRequestCreator(index); |
| if (next_index >= permissions_creators_.size()) { |
| callback.Run(false); |
| return; |
| } |
| |
| create_request.Run( |
| permissions_creators_[next_index], |
| base::Bind(&SupervisedUserService::OnPermissionRequestIssued, |
| weak_ptr_factory_.GetWeakPtr(), create_request, |
| callback, next_index)); |
| } |
| |
| void SupervisedUserService::OnPermissionRequestIssued( |
| const CreatePermissionRequestCallback& create_request, |
| const SuccessCallback& callback, |
| size_t index, |
| bool success) { |
| if (success) { |
| callback.Run(true); |
| return; |
| } |
| |
| AddPermissionRequestInternal(create_request, callback, index + 1); |
| } |
| |
| void SupervisedUserService::OnSupervisedUserIdChanged() { |
| SetActive(ProfileIsSupervised()); |
| } |
| |
| void SupervisedUserService::OnDefaultFilteringBehaviorChanged() { |
| int behavior_value = profile_->GetPrefs()->GetInteger( |
| prefs::kDefaultSupervisedUserFilteringBehavior); |
| SupervisedUserURLFilter::FilteringBehavior behavior = |
| SupervisedUserURLFilter::BehaviorFromInt(behavior_value); |
| url_filter_context_.SetDefaultFilteringBehavior(behavior); |
| |
| FOR_EACH_OBSERVER( |
| SupervisedUserServiceObserver, observer_list_, OnURLFilterChanged()); |
| } |
| |
| void SupervisedUserService::OnSafeSitesSettingChanged() { |
| bool use_blacklist = supervised_users::IsSafeSitesBlacklistEnabled(profile_); |
| if (use_blacklist != url_filter_context_.HasBlacklist()) { |
| if (use_blacklist && blacklist_state_ == BlacklistLoadState::NOT_LOADED) { |
| LoadBlacklist(GetBlacklistPath(), GURL(kBlacklistURL)); |
| } else if (!use_blacklist || |
| blacklist_state_ == BlacklistLoadState::LOADED) { |
| // Either the blacklist was turned off, or it was turned on but has |
| // already been loaded previously. Just update the setting. |
| UpdateBlacklist(); |
| } |
| // Else: The blacklist was enabled, but the load is already in progress. |
| // Do nothing - we'll check the setting again when the load finishes. |
| } |
| |
| bool use_online_check = |
| supervised_users::IsSafeSitesOnlineCheckEnabled(profile_); |
| if (use_online_check != url_filter_context_.HasAsyncURLChecker()) { |
| if (use_online_check) |
| url_filter_context_.InitAsyncURLChecker(profile_->GetRequestContext()); |
| else |
| url_filter_context_.ClearAsyncURLChecker(); |
| } |
| } |
| |
| void SupervisedUserService::OnSiteListsChanged( |
| const std::vector<scoped_refptr<SupervisedUserSiteList> >& site_lists) { |
| url_filter_context_.LoadWhitelists(site_lists); |
| } |
| |
| void SupervisedUserService::LoadBlacklist(const base::FilePath& path, |
| const GURL& url) { |
| DCHECK(blacklist_state_ == BlacklistLoadState::NOT_LOADED); |
| blacklist_state_ = BlacklistLoadState::LOAD_STARTED; |
| base::PostTaskAndReplyWithResult( |
| BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior( |
| base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN).get(), |
| FROM_HERE, |
| base::Bind(&base::PathExists, path), |
| base::Bind(&SupervisedUserService::OnBlacklistFileChecked, |
| weak_ptr_factory_.GetWeakPtr(), path, url)); |
| } |
| |
| void SupervisedUserService::OnBlacklistFileChecked(const base::FilePath& path, |
| const GURL& url, |
| bool file_exists) { |
| DCHECK(blacklist_state_ == BlacklistLoadState::LOAD_STARTED); |
| if (file_exists) { |
| LoadBlacklistFromFile(path); |
| return; |
| } |
| |
| DCHECK(!blacklist_downloader_); |
| blacklist_downloader_.reset(new FileDownloader( |
| url, |
| path, |
| false, |
| profile_->GetRequestContext(), |
| base::Bind(&SupervisedUserService::OnBlacklistDownloadDone, |
| base::Unretained(this), path))); |
| } |
| |
| void SupervisedUserService::LoadBlacklistFromFile(const base::FilePath& path) { |
| DCHECK(blacklist_state_ == BlacklistLoadState::LOAD_STARTED); |
| blacklist_.ReadFromFile( |
| path, |
| base::Bind(&SupervisedUserService::OnBlacklistLoaded, |
| base::Unretained(this))); |
| } |
| |
| void SupervisedUserService::OnBlacklistDownloadDone(const base::FilePath& path, |
| bool success) { |
| DCHECK(blacklist_state_ == BlacklistLoadState::LOAD_STARTED); |
| if (success) { |
| LoadBlacklistFromFile(path); |
| } else { |
| LOG(WARNING) << "Blacklist download failed"; |
| // TODO(treib): Retry downloading after some time? |
| } |
| blacklist_downloader_.reset(); |
| } |
| |
| void SupervisedUserService::OnBlacklistLoaded() { |
| DCHECK(blacklist_state_ == BlacklistLoadState::LOAD_STARTED); |
| blacklist_state_ = BlacklistLoadState::LOADED; |
| UpdateBlacklist(); |
| } |
| |
| void SupervisedUserService::UpdateBlacklist() { |
| bool use_blacklist = supervised_users::IsSafeSitesBlacklistEnabled(profile_); |
| url_filter_context_.SetBlacklist(use_blacklist ? &blacklist_ : nullptr); |
| FOR_EACH_OBSERVER( |
| SupervisedUserServiceObserver, observer_list_, OnURLFilterChanged()); |
| } |
| |
| void SupervisedUserService::UpdateManualHosts() { |
| const base::DictionaryValue* dict = |
| profile_->GetPrefs()->GetDictionary(prefs::kSupervisedUserManualHosts); |
| scoped_ptr<std::map<std::string, bool> > host_map( |
| new std::map<std::string, bool>()); |
| for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) { |
| bool allow = false; |
| bool result = it.value().GetAsBoolean(&allow); |
| DCHECK(result); |
| (*host_map)[it.key()] = allow; |
| } |
| url_filter_context_.SetManualHosts(std::move(host_map)); |
| |
| FOR_EACH_OBSERVER( |
| SupervisedUserServiceObserver, observer_list_, OnURLFilterChanged()); |
| } |
| |
| void SupervisedUserService::UpdateManualURLs() { |
| const base::DictionaryValue* dict = |
| profile_->GetPrefs()->GetDictionary(prefs::kSupervisedUserManualURLs); |
| scoped_ptr<std::map<GURL, bool> > url_map(new std::map<GURL, bool>()); |
| for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) { |
| bool allow = false; |
| bool result = it.value().GetAsBoolean(&allow); |
| DCHECK(result); |
| (*url_map)[GURL(it.key())] = allow; |
| } |
| url_filter_context_.SetManualURLs(std::move(url_map)); |
| |
| FOR_EACH_OBSERVER( |
| SupervisedUserServiceObserver, observer_list_, OnURLFilterChanged()); |
| } |
| |
| std::string SupervisedUserService::GetSupervisedUserName() const { |
| #if defined(OS_CHROMEOS) |
| // The active user can be NULL in unit tests. |
| if (user_manager::UserManager::Get()->GetActiveUser()) { |
| return UTF16ToUTF8(user_manager::UserManager::Get()->GetUserDisplayName( |
| user_manager::UserManager::Get()->GetActiveUser()->GetAccountId())); |
| } |
| return std::string(); |
| #else |
| return profile_->GetPrefs()->GetString(prefs::kProfileName); |
| #endif |
| } |
| |
| void SupervisedUserService::OnHistoryRecordingStateChanged() { |
| bool record_history = |
| profile_->GetPrefs()->GetBoolean(prefs::kRecordHistory); |
| includes_sync_sessions_type_ = record_history; |
| ProfileSyncServiceFactory::GetForProfile(profile_) |
| ->ReconfigureDatatypeManager(); |
| } |
| |
| bool SupervisedUserService::IncludesSyncSessionsType() const { |
| return includes_sync_sessions_type_; |
| } |
| |
| void SupervisedUserService::Shutdown() { |
| if (!did_init_) |
| return; |
| DCHECK(!did_shutdown_); |
| did_shutdown_ = true; |
| if (ProfileIsSupervised()) { |
| content::RecordAction(UserMetricsAction("ManagedUsers_QuitBrowser")); |
| } |
| SetActive(false); |
| |
| ProfileSyncService* sync_service = |
| ProfileSyncServiceFactory::GetForProfile(profile_); |
| |
| // Can be null in tests. |
| if (sync_service) |
| sync_service->RemovePreferenceProvider(this); |
| } |
| |
| #if defined(ENABLE_EXTENSIONS) |
| std::string SupervisedUserService::GetDebugPolicyProviderName() const { |
| // Save the string space in official builds. |
| #ifdef NDEBUG |
| NOTREACHED(); |
| return std::string(); |
| #else |
| return "Supervised User Service"; |
| #endif |
| } |
| |
| bool SupervisedUserService::UserMayLoad(const extensions::Extension* extension, |
| base::string16* error) const { |
| DCHECK(ProfileIsSupervised()); |
| ExtensionState result = GetExtensionState(extension); |
| bool may_load = (result != EXTENSION_BLOCKED); |
| if (!may_load && error) |
| *error = GetExtensionsLockedMessage(); |
| return may_load; |
| } |
| |
| bool SupervisedUserService::UserMayModifySettings( |
| const extensions::Extension* extension, |
| base::string16* error) const { |
| DCHECK(ProfileIsSupervised()); |
| ExtensionState result = GetExtensionState(extension); |
| bool may_modify = (result == EXTENSION_ALLOWED); |
| if (!may_modify && error) |
| *error = GetExtensionsLockedMessage(); |
| return may_modify; |
| } |
| |
| // Note: Having MustRemainInstalled always say "true" for custodian-installed |
| // extensions does NOT prevent remote uninstalls (which is a bit unexpected, but |
| // exactly what we want). |
| bool SupervisedUserService::MustRemainInstalled( |
| const extensions::Extension* extension, |
| base::string16* error) const { |
| DCHECK(ProfileIsSupervised()); |
| ExtensionState result = GetExtensionState(extension); |
| bool may_not_uninstall = (result == EXTENSION_FORCED); |
| if (may_not_uninstall && error) |
| *error = GetExtensionsLockedMessage(); |
| return may_not_uninstall; |
| } |
| |
| void SupervisedUserService::SetExtensionsActive() { |
| extensions::ExtensionSystem* extension_system = |
| extensions::ExtensionSystem::Get(profile_); |
| extensions::ManagementPolicy* management_policy = |
| extension_system->management_policy(); |
| |
| if (management_policy) { |
| if (active_) |
| management_policy->RegisterProvider(this); |
| else |
| management_policy->UnregisterProvider(this); |
| |
| // Re-check the policy to make sure any new settings get applied. |
| extension_system->extension_service()->CheckManagementPolicy(); |
| } |
| } |
| #endif // defined(ENABLE_EXTENSIONS) |
| |
| syncer::ModelTypeSet SupervisedUserService::GetPreferredDataTypes() const { |
| if (!ProfileIsSupervised()) |
| return syncer::ModelTypeSet(); |
| |
| syncer::ModelTypeSet result; |
| if (IncludesSyncSessionsType()) |
| result.Put(syncer::SESSIONS); |
| result.Put(syncer::EXTENSIONS); |
| result.Put(syncer::EXTENSION_SETTINGS); |
| result.Put(syncer::APPS); |
| result.Put(syncer::APP_SETTINGS); |
| result.Put(syncer::APP_NOTIFICATIONS); |
| result.Put(syncer::APP_LIST); |
| return result; |
| } |
| |
| #if !defined(OS_ANDROID) && !defined(OS_IOS) |
| void SupervisedUserService::OnStateChanged() { |
| ProfileSyncService* service = |
| ProfileSyncServiceFactory::GetForProfile(profile_); |
| if (waiting_for_sync_initialization_ && service->IsBackendInitialized() && |
| service->backend_mode() == ProfileSyncService::SYNC) { |
| waiting_for_sync_initialization_ = false; |
| service->RemoveObserver(this); |
| FinishSetupSync(); |
| return; |
| } |
| |
| DLOG_IF(ERROR, service->GetAuthError().state() == |
| GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS) |
| << "Credentials rejected"; |
| } |
| |
| void SupervisedUserService::OnBrowserSetLastActive(Browser* browser) { |
| bool profile_became_active = profile_->IsSameProfile(browser->profile()); |
| if (!is_profile_active_ && profile_became_active) |
| content::RecordAction(UserMetricsAction("ManagedUsers_OpenProfile")); |
| else if (is_profile_active_ && !profile_became_active) |
| content::RecordAction(UserMetricsAction("ManagedUsers_SwitchProfile")); |
| |
| is_profile_active_ = profile_became_active; |
| } |
| #endif // !defined(OS_ANDROID) && !defined(OS_IOS) |
| |
| void SupervisedUserService::OnSiteListUpdated() { |
| FOR_EACH_OBSERVER( |
| SupervisedUserServiceObserver, observer_list_, OnURLFilterChanged()); |
| } |
| |