| Index: chrome/browser/supervised_user/supervised_user_service.cc
|
| diff --git a/chrome/browser/supervised_user/supervised_user_service.cc b/chrome/browser/supervised_user/supervised_user_service.cc
|
| index 7719facefb5c3d719ae0d779a9dd7faf3420bef4..84eed10198df99676ef7bb08dbb8e54cae7c79ad 100644
|
| --- a/chrome/browser/supervised_user/supervised_user_service.cc
|
| +++ b/chrome/browser/supervised_user/supervised_user_service.cc
|
| @@ -7,6 +7,7 @@
|
| #include <utility>
|
|
|
| #include "base/command_line.h"
|
| +#include "base/feature_list.h"
|
| #include "base/files/file_path.h"
|
| #include "base/files/file_util.h"
|
| #include "base/memory/ref_counted.h"
|
| @@ -27,6 +28,8 @@
|
| #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_features.h"
|
| +#include "chrome/browser/supervised_user/supervised_user_service_factory.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"
|
| @@ -70,6 +73,7 @@
|
| #include "chrome/browser/extensions/extension_service.h"
|
| #include "chrome/browser/extensions/extension_util.h"
|
| #include "extensions/browser/extension_prefs.h"
|
| +#include "extensions/browser/extension_registry.h"
|
| #include "extensions/browser/extension_system.h"
|
| #endif
|
|
|
| @@ -83,6 +87,13 @@ using base::UserMetricsAction;
|
| using content::BrowserThread;
|
|
|
| #if defined(ENABLE_EXTENSIONS)
|
| +using extensions::Extension;
|
| +using extensions::ExtensionPrefs;
|
| +using extensions::ExtensionRegistry;
|
| +using extensions::ExtensionSystem;
|
| +#endif
|
| +
|
| +#if defined(ENABLE_EXTENSIONS)
|
| using extensions::ExtensionPrefs;
|
| #endif
|
|
|
| @@ -112,6 +123,13 @@ void CreateURLAccessRequest(
|
| creator->CreateURLAccessRequest(url, callback);
|
| }
|
|
|
| +void CreateExtensionInstallRequest(
|
| + const std::string& id,
|
| + PermissionRequestCreator* creator,
|
| + const SupervisedUserService::SuccessCallback& callback) {
|
| + creator->CreateExtensionInstallRequest(id, callback);
|
| +}
|
| +
|
| void CreateExtensionUpdateRequest(
|
| const std::string& id,
|
| PermissionRequestCreator* creator,
|
| @@ -119,6 +137,11 @@ void CreateExtensionUpdateRequest(
|
| creator->CreateExtensionUpdateRequest(id, callback);
|
| }
|
|
|
| +// Default callback for AddExtensionInstallRequest.
|
| +void ExtensionInstallRequestSent(const std::string& id, bool success) {
|
| + VLOG_IF(1, !success) << "Failed sending install request for " << id;
|
| +}
|
| +
|
| // Default callback for AddExtensionUpdateRequest.
|
| void ExtensionUpdateRequestSent(const std::string& id, bool success) {
|
| VLOG_IF(1, !success) << "Failed sending update request for " << id;
|
| @@ -140,6 +163,7 @@ SupervisedUserService::~SupervisedUserService() {
|
| // static
|
| void SupervisedUserService::RegisterProfilePrefs(
|
| user_prefs::PrefRegistrySyncable* registry) {
|
| + registry->RegisterDictionaryPref(prefs::kSupervisedUserApprovedExtensions);
|
| registry->RegisterDictionaryPref(prefs::kSupervisedUserManualHosts);
|
| registry->RegisterDictionaryPref(prefs::kSupervisedUserManualURLs);
|
| registry->RegisterIntegerPref(prefs::kDefaultSupervisedUserFilteringBehavior,
|
| @@ -230,11 +254,28 @@ void SupervisedUserService::ReportURL(const GURL& url,
|
| callback.Run(false);
|
| }
|
|
|
| +void SupervisedUserService::AddExtensionInstallRequest(
|
| + const std::string& extension_id,
|
| + const base::Version& version,
|
| + const SuccessCallback& callback) {
|
| + std::string id = GetExtensionRequestId(extension_id, version);
|
| + AddPermissionRequestInternal(base::Bind(CreateExtensionInstallRequest, id),
|
| + callback, 0);
|
| +}
|
| +
|
| +void SupervisedUserService::AddExtensionInstallRequest(
|
| + const std::string& extension_id,
|
| + const base::Version& version) {
|
| + std::string id = GetExtensionRequestId(extension_id, version);
|
| + AddPermissionRequestInternal(base::Bind(CreateExtensionInstallRequest, id),
|
| + base::Bind(ExtensionInstallRequestSent, id), 0);
|
| +}
|
| +
|
| void SupervisedUserService::AddExtensionUpdateRequest(
|
| const std::string& extension_id,
|
| const base::Version& version,
|
| const SuccessCallback& callback) {
|
| - std::string id = GetExtensionUpdateRequestId(extension_id, version);
|
| + std::string id = GetExtensionRequestId(extension_id, version);
|
| AddPermissionRequestInternal(
|
| base::Bind(CreateExtensionUpdateRequest, id), callback, 0);
|
| }
|
| @@ -242,13 +283,13 @@ void SupervisedUserService::AddExtensionUpdateRequest(
|
| void SupervisedUserService::AddExtensionUpdateRequest(
|
| const std::string& extension_id,
|
| const base::Version& version) {
|
| - std::string id = GetExtensionUpdateRequestId(extension_id, version);
|
| + std::string id = GetExtensionRequestId(extension_id, version);
|
| AddExtensionUpdateRequest(extension_id, version,
|
| base::Bind(ExtensionUpdateRequestSent, id));
|
| }
|
|
|
| // static
|
| -std::string SupervisedUserService::GetExtensionUpdateRequestId(
|
| +std::string SupervisedUserService::GetExtensionRequestId(
|
| const std::string& extension_id,
|
| const base::Version& version) {
|
| return base::StringPrintf("%s:%s", extension_id.c_str(),
|
| @@ -488,8 +529,14 @@ SupervisedUserService::SupervisedUserService(Profile* profile)
|
| did_init_(false),
|
| did_shutdown_(false),
|
| blacklist_state_(BlacklistLoadState::NOT_LOADED),
|
| +#if defined(ENABLE_EXTENSIONS)
|
| + registry_observer_(this),
|
| +#endif
|
| weak_ptr_factory_(this) {
|
| url_filter_context_.ui_url_filter()->AddObserver(this);
|
| +#if defined(ENABLE_EXTENSIONS)
|
| + registry_observer_.Add(extensions::ExtensionRegistry::Get(profile));
|
| +#endif
|
| }
|
|
|
| void SupervisedUserService::SetActive(bool active) {
|
| @@ -554,6 +601,12 @@ void SupervisedUserService::SetActive(bool active) {
|
| prefs::kDefaultSupervisedUserFilteringBehavior,
|
| base::Bind(&SupervisedUserService::OnDefaultFilteringBehaviorChanged,
|
| base::Unretained(this)));
|
| +#if defined(ENABLE_EXTENSIONS)
|
| + pref_change_registrar_.Add(
|
| + prefs::kSupervisedUserApprovedExtensions,
|
| + base::Bind(&SupervisedUserService::UpdateApprovedExtensions,
|
| + base::Unretained(this)));
|
| +#endif
|
| pref_change_registrar_.Add(prefs::kSupervisedUserSafeSites,
|
| base::Bind(&SupervisedUserService::OnSafeSitesSettingChanged,
|
| base::Unretained(this)));
|
| @@ -576,6 +629,10 @@ void SupervisedUserService::SetActive(bool active) {
|
| UpdateManualHosts();
|
| UpdateManualURLs();
|
|
|
| +#if defined(ENABLE_EXTENSIONS)
|
| + UpdateApprovedExtensions();
|
| +#endif
|
| +
|
| #if !defined(OS_ANDROID)
|
| // TODO(bauerb): Get rid of the platform-specific #ifdef here.
|
| // http://crbug.com/313377
|
| @@ -587,6 +644,9 @@ void SupervisedUserService::SetActive(bool active) {
|
|
|
| pref_change_registrar_.Remove(
|
| prefs::kDefaultSupervisedUserFilteringBehavior);
|
| +#if defined(ENABLE_EXTENSIONS)
|
| + pref_change_registrar_.Remove(prefs::kSupervisedUserApprovedExtensions);
|
| +#endif
|
| pref_change_registrar_.Remove(prefs::kSupervisedUserManualHosts);
|
| pref_change_registrar_.Remove(prefs::kSupervisedUserManualURLs);
|
| for (const char* pref : kCustodianInfoPrefs) {
|
| @@ -924,8 +984,8 @@ void SupervisedUserService::Shutdown() {
|
|
|
| #if defined(ENABLE_EXTENSIONS)
|
| SupervisedUserService::ExtensionState SupervisedUserService::GetExtensionState(
|
| - const extensions::Extension* extension) const {
|
| - bool was_installed_by_default = extension->was_installed_by_default();
|
| + const Extension& extension) const {
|
| + 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
|
| @@ -934,25 +994,35 @@ SupervisedUserService::ExtensionState SupervisedUserService::GetExtensionState(
|
| // 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());
|
| + 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 ExtensionState::EXTENSION_ALLOWED;
|
| + 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 ExtensionState::ALLOWED;
|
| }
|
|
|
| - if (extensions::util::WasInstalledByCustodian(extension->id(), profile_))
|
| - return ExtensionState::EXTENSION_FORCED;
|
| + if (extensions::util::WasInstalledByCustodian(extension.id(), profile_))
|
| + return ExtensionState::FORCED;
|
|
|
| - return ExtensionState::EXTENSION_BLOCKED;
|
| + if (!base::FeatureList::IsEnabled(
|
| + supervised_users::kSupervisedUserInitiatedExtensionInstall)) {
|
| + return ExtensionState::BLOCKED;
|
| + }
|
| +
|
| + auto extension_it = approved_extensions_map_.find(extension.id());
|
| + // If the installed version is approved, then the extension is allowed,
|
| + // otherwise, it requires approval.
|
| + if (extension_it != approved_extensions_map_.end() &&
|
| + extension_it->second == *extension.version()) {
|
| + return ExtensionState::ALLOWED;
|
| + }
|
| + return ExtensionState::REQUIRE_APPROVAL;
|
| }
|
|
|
| std::string SupervisedUserService::GetDebugPolicyProviderName() const {
|
| @@ -965,22 +1035,26 @@ std::string SupervisedUserService::GetDebugPolicyProviderName() const {
|
| #endif
|
| }
|
|
|
| -bool SupervisedUserService::UserMayLoad(const extensions::Extension* extension,
|
| +bool SupervisedUserService::UserMayLoad(const Extension* extension,
|
| base::string16* error) const {
|
| DCHECK(ProfileIsSupervised());
|
| - ExtensionState result = GetExtensionState(extension);
|
| - bool may_load = (result != EXTENSION_BLOCKED);
|
| + ExtensionState result = GetExtensionState(*extension);
|
| + bool may_load = result != ExtensionState::BLOCKED;
|
| if (!may_load && error)
|
| *error = GetExtensionsLockedMessage();
|
| return may_load;
|
| }
|
|
|
| -bool SupervisedUserService::UserMayModifySettings(
|
| - const extensions::Extension* extension,
|
| - base::string16* error) const {
|
| +bool SupervisedUserService::UserMayModifySettings(const Extension* extension,
|
| + base::string16* error) const {
|
| DCHECK(ProfileIsSupervised());
|
| - ExtensionState result = GetExtensionState(extension);
|
| - bool may_modify = (result == EXTENSION_ALLOWED);
|
| + ExtensionState result = GetExtensionState(*extension);
|
| + // While the following check allows the supervised user to modify the settings
|
| + // and enable or disable the extension, MustRemainDisabled properly takes care
|
| + // of keeping an extension disabled when required.
|
| + // For custodian-installed extensions, the state is always FORCED, even if
|
| + // it's waiting for an update approval.
|
| + bool may_modify = result != ExtensionState::FORCED;
|
| if (!may_modify && error)
|
| *error = GetExtensionsLockedMessage();
|
| return may_modify;
|
| @@ -989,17 +1063,159 @@ bool SupervisedUserService::UserMayModifySettings(
|
| // 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 {
|
| +bool SupervisedUserService::MustRemainInstalled(const Extension* extension,
|
| + base::string16* error) const {
|
| DCHECK(ProfileIsSupervised());
|
| - ExtensionState result = GetExtensionState(extension);
|
| - bool may_not_uninstall = (result == EXTENSION_FORCED);
|
| + ExtensionState result = GetExtensionState(*extension);
|
| + bool may_not_uninstall = result == ExtensionState::FORCED;
|
| if (may_not_uninstall && error)
|
| *error = GetExtensionsLockedMessage();
|
| return may_not_uninstall;
|
| }
|
|
|
| +bool SupervisedUserService::MustRemainDisabled(const Extension* extension,
|
| + Extension::DisableReason* reason,
|
| + base::string16* error) const {
|
| + DCHECK(ProfileIsSupervised());
|
| + ExtensionState state = GetExtensionState(*extension);
|
| + // Only extensions that require approval should be disabled.
|
| + // Blocked extensions should be not loaded at all, and are taken care of
|
| + // at UserMayLoad.
|
| + bool must_remain_disabled = state == ExtensionState::REQUIRE_APPROVAL;
|
| +
|
| + if (must_remain_disabled) {
|
| + if (error)
|
| + *error = l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOCKED_SUPERVISED_USER);
|
| + // If the extension must remain disabled due to permission increase,
|
| + // then the update request has been already sent at update time.
|
| + // We do nothing and we don't add an extra disable reason.
|
| + ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
|
| + if (extension_prefs->HasDisableReason(
|
| + extension->id(), Extension::DISABLE_PERMISSIONS_INCREASE)) {
|
| + if (reason)
|
| + *reason = Extension::DISABLE_PERMISSIONS_INCREASE;
|
| + return true;
|
| + }
|
| + if (reason)
|
| + *reason = Extension::DISABLE_CUSTODIAN_APPROVAL_REQUIRED;
|
| + if (base::FeatureList::IsEnabled(
|
| + supervised_users::kSupervisedUserInitiatedExtensionInstall)) {
|
| + // If the Extension isn't pending a custodian approval already, send
|
| + // an approval request.
|
| + if (!extension_prefs->HasDisableReason(
|
| + extension->id(),
|
| + Extension::DISABLE_CUSTODIAN_APPROVAL_REQUIRED)) {
|
| + // MustRemainDisabled is a const method and hence cannot call
|
| + // AddExtensionInstallRequest directly.
|
| + SupervisedUserService* supervised_user_service =
|
| + SupervisedUserServiceFactory::GetForProfile(profile_);
|
| + supervised_user_service->AddExtensionInstallRequest(
|
| + extension->id(), *extension->version());
|
| + }
|
| + }
|
| + }
|
| + return must_remain_disabled;
|
| +}
|
| +
|
| +void SupervisedUserService::OnExtensionInstalled(
|
| + content::BrowserContext* browser_context,
|
| + const extensions::Extension* extension,
|
| + bool is_update) {
|
| + // This callback method is responsible for updating extension state and
|
| + // approved_extensions_map_ upon extension updates.
|
| + if (!is_update)
|
| + return;
|
| +
|
| + ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
|
| + const std::string& id = extension->id();
|
| + const base::Version& version = *extension->version();
|
| +
|
| + // If an already approved extension is updated without requiring
|
| + // new permissions, we update the approved_version.
|
| + if (!extension_prefs->HasDisableReason(
|
| + id, Extension::DISABLE_PERMISSIONS_INCREASE) &&
|
| + approved_extensions_map_.count(id) > 0 &&
|
| + approved_extensions_map_[id] < version) {
|
| + approved_extensions_map_[id] = version;
|
| +
|
| + std::string key = SupervisedUserSettingsService::MakeSplitSettingKey(
|
| + supervised_users::kApprovedExtensions, id);
|
| + std::unique_ptr<base::Value> version_value(
|
| + new base::StringValue(version.GetString()));
|
| + GetSettingsService()->UpdateSetting(key, std::move(version_value));
|
| + }
|
| + // Upon extension update, the approved version may (or may not) match the
|
| + // installed one. Therefore, a change in extension state might be required.
|
| + ChangeExtensionStateIfNecessary(id);
|
| +}
|
| +
|
| +void SupervisedUserService::UpdateApprovedExtensions() {
|
| + const base::DictionaryValue* dict = profile_->GetPrefs()->GetDictionary(
|
| + prefs::kSupervisedUserApprovedExtensions);
|
| + // Keep track of currently approved extensions. We may need to disable them if
|
| + // they are not in the approved map anymore.
|
| + std::set<std::string> extensions_to_be_checked;
|
| + for (const auto& extension : approved_extensions_map_)
|
| + extensions_to_be_checked.insert(extension.first);
|
| +
|
| + approved_extensions_map_.clear();
|
| +
|
| + for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
|
| + std::string version_str;
|
| + bool result = it.value().GetAsString(&version_str);
|
| + DCHECK(result);
|
| + base::Version version(version_str);
|
| + if (version.IsValid()) {
|
| + approved_extensions_map_[it.key()] = version;
|
| + extensions_to_be_checked.insert(it.key());
|
| + } else {
|
| + LOG(WARNING) << "Invalid version number " << version_str;
|
| + }
|
| + }
|
| +
|
| + for (const auto& extension_id : extensions_to_be_checked) {
|
| + ChangeExtensionStateIfNecessary(extension_id);
|
| + }
|
| +}
|
| +
|
| +void SupervisedUserService::ChangeExtensionStateIfNecessary(
|
| + const std::string& extension_id) {
|
| + ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
|
| + const Extension* extension = registry->GetInstalledExtension(extension_id);
|
| + // If the extension is not installed (yet), do nothing.
|
| + // Things will be handled after installation.
|
| + if (!extension)
|
| + return;
|
| +
|
| + ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
|
| + ExtensionService* service =
|
| + ExtensionSystem::Get(profile_)->extension_service();
|
| +
|
| + ExtensionState state = GetExtensionState(*extension);
|
| + switch (state) {
|
| + // BLOCKED/FORCED extensions should be already disabled/enabled
|
| + // and we don't need to change their state here.
|
| + case ExtensionState::BLOCKED:
|
| + case ExtensionState::FORCED:
|
| + break;
|
| + case ExtensionState::REQUIRE_APPROVAL:
|
| + service->DisableExtension(extension_id,
|
| + Extension::DISABLE_CUSTODIAN_APPROVAL_REQUIRED);
|
| + break;
|
| + case ExtensionState::ALLOWED:
|
| + extension_prefs->RemoveDisableReason(
|
| + extension_id, Extension::DISABLE_CUSTODIAN_APPROVAL_REQUIRED);
|
| + extension_prefs->RemoveDisableReason(
|
| + extension_id, Extension::DISABLE_PERMISSIONS_INCREASE);
|
| + // If not disabled for other reasons, enable it.
|
| + if (extension_prefs->GetDisableReasons(extension_id) ==
|
| + Extension::DISABLE_NONE) {
|
| + service->EnableExtension(extension_id);
|
| + }
|
| + break;
|
| + }
|
| +}
|
| +
|
| void SupervisedUserService::SetExtensionsActive() {
|
| extensions::ExtensionSystem* extension_system =
|
| extensions::ExtensionSystem::Get(profile_);
|
|
|