blob: f471d5acb6176a5625641d47522e048d61dcab46 [file] [log] [blame]
// Copyright (c) 2012 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/extensions/api/permissions/permissions_api.h"
#include <memory>
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/api/permissions/permissions_api_helpers.h"
#include "chrome/browser/extensions/chrome_extension_function_details.h"
#include "chrome/browser/extensions/extension_management.h"
#include "chrome/browser/extensions/permissions_updater.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/permissions.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest_handlers/permissions_parser.h"
#include "extensions/common/permissions/permission_message_provider.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/common/permissions/permissions_info.h"
namespace extensions {
using api::permissions::Permissions;
namespace {
const char kBlockedByEnterprisePolicy[] =
"Permissions are blocked by enterprise policy.";
const char kCantRemoveRequiredPermissionsError[] =
"You cannot remove required permissions.";
const char kNotInOptionalPermissionsError[] =
"Optional permissions must be listed in extension manifest.";
const char kNotWhitelistedError[] =
"The optional permissions API does not support '*'.";
const char kUserGestureRequiredError[] =
"This function must be called during a user gesture";
enum AutoConfirmForTest {
DO_NOT_SKIP = 0,
PROCEED,
ABORT
};
AutoConfirmForTest auto_confirm_for_tests = DO_NOT_SKIP;
bool ignore_user_gesture_for_tests = false;
} // namespace
ExtensionFunction::ResponseAction PermissionsContainsFunction::Run() {
std::unique_ptr<api::permissions::Contains::Params> params(
api::permissions::Contains::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
// NOTE: |permissions| is not used to make any security decisions. Therefore,
// it is entirely fine to set |allow_file_access| to true below. This will
// avoid throwing error when extension() doesn't have access to file://.
std::string error;
std::unique_ptr<const PermissionSet> permissions =
permissions_api_helpers::UnpackPermissionSet(
params->permissions, true /* allow_file_access */, &error);
if (!permissions.get())
return RespondNow(Error(error));
return RespondNow(ArgumentList(api::permissions::Contains::Results::Create(
extension()->permissions_data()->active_permissions().Contains(
*permissions))));
}
ExtensionFunction::ResponseAction PermissionsGetAllFunction::Run() {
std::unique_ptr<Permissions> permissions =
permissions_api_helpers::PackPermissionSet(
extension()->permissions_data()->active_permissions());
return RespondNow(
ArgumentList(api::permissions::GetAll::Results::Create(*permissions)));
}
ExtensionFunction::ResponseAction PermissionsRemoveFunction::Run() {
std::unique_ptr<api::permissions::Remove::Params> params(
api::permissions::Remove::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
std::string error;
std::unique_ptr<const PermissionSet> permissions =
permissions_api_helpers::UnpackPermissionSet(
params->permissions,
ExtensionPrefs::Get(browser_context())
->AllowFileAccess(extension_->id()),
&error);
if (!permissions.get())
return RespondNow(Error(error));
// Make sure they're only trying to remove permissions supported by this API.
APIPermissionSet apis = permissions->apis();
for (const APIPermission* permission : apis) {
if (!permission->info()->supports_optional())
return RespondNow(Error(kNotWhitelistedError, permission->name()));
}
// Make sure we only remove optional permissions, and not required
// permissions. Sadly, for some reason we support having a permission be both
// optional and required (and should assume its required), so we need both of
// these checks.
// TODO(devlin): *Why* do we support that? Should be a load error.
const PermissionSet& optional =
PermissionsParser::GetOptionalPermissions(extension());
const PermissionSet& required =
PermissionsParser::GetRequiredPermissions(extension());
if (!optional.Contains(*permissions) ||
!std::unique_ptr<const PermissionSet>(
PermissionSet::CreateIntersection(*permissions, required))
->IsEmpty()) {
return RespondNow(Error(kCantRemoveRequiredPermissionsError));
}
// Only try and remove those permissions that are active on the extension.
// For backwards compatability with behavior before this check was added, just
// silently remove any that aren't present.
permissions = PermissionSet::CreateIntersection(
*permissions, extension()->permissions_data()->active_permissions());
PermissionsUpdater(browser_context())
.RevokeOptionalPermissions(*extension(), *permissions,
PermissionsUpdater::REMOVE_SOFT);
return RespondNow(
ArgumentList(api::permissions::Remove::Results::Create(true)));
}
// static
void PermissionsRequestFunction::SetAutoConfirmForTests(bool should_proceed) {
auto_confirm_for_tests = should_proceed ? PROCEED : ABORT;
}
// static
void PermissionsRequestFunction::SetIgnoreUserGestureForTests(
bool ignore) {
ignore_user_gesture_for_tests = ignore;
}
PermissionsRequestFunction::PermissionsRequestFunction() {}
PermissionsRequestFunction::~PermissionsRequestFunction() {}
ExtensionFunction::ResponseAction PermissionsRequestFunction::Run() {
if (!user_gesture() &&
!ignore_user_gesture_for_tests &&
extension_->location() != Manifest::COMPONENT) {
return RespondNow(Error(kUserGestureRequiredError));
}
gfx::NativeWindow native_window =
ChromeExtensionFunctionDetails(this).GetNativeWindowForUI();
if (!native_window)
return RespondNow(Error("Could not find an active window."));
std::unique_ptr<api::permissions::Request::Params> params(
api::permissions::Request::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
std::string error;
requested_permissions_ = permissions_api_helpers::UnpackPermissionSet(
params->permissions,
ExtensionPrefs::Get(browser_context())->AllowFileAccess(extension_->id()),
&error);
if (!requested_permissions_.get())
return RespondNow(Error(error));
// Make sure they're only requesting permissions supported by this API.
APIPermissionSet apis = requested_permissions_->apis();
for (APIPermissionSet::const_iterator i = apis.begin();
i != apis.end(); ++i) {
if (!i->info()->supports_optional()) {
return RespondNow(Error(
ErrorUtils::FormatErrorMessage(kNotWhitelistedError, i->name())));
}
}
// The requested permissions must be defined as optional in the manifest.
if (!PermissionsParser::GetOptionalPermissions(extension())
.Contains(*requested_permissions_)) {
return RespondNow(Error(kNotInOptionalPermissionsError));
}
// Automatically declines api permissions requests, which are blocked by
// enterprise policy.
if (!ExtensionManagementFactory::GetForBrowserContext(browser_context())
->IsPermissionSetAllowed(extension(), *requested_permissions_)) {
return RespondNow(Error(kBlockedByEnterprisePolicy));
}
// We don't need to prompt the user if the requested permissions are a subset
// of the granted permissions set.
std::unique_ptr<const PermissionSet> granted =
ExtensionPrefs::Get(browser_context())
->GetGrantedPermissions(extension()->id());
if (granted.get() && granted->Contains(*requested_permissions_)) {
PermissionsUpdater perms_updater(browser_context());
perms_updater.GrantOptionalPermissions(*extension(),
*requested_permissions_);
return RespondNow(
ArgumentList(api::permissions::Request::Results::Create(true)));
}
// Filter out the granted permissions so we only prompt for new ones.
requested_permissions_ =
PermissionSet::CreateDifference(*requested_permissions_, *granted);
// Filter out the active permissions.
requested_permissions_ = PermissionSet::CreateDifference(
*requested_permissions_,
extension()->permissions_data()->active_permissions());
AddRef(); // Balanced in OnInstallPromptDone().
// We don't need to show the prompt if there are no new warnings, or if
// we're skipping the confirmation UI. All extension types but INTERNAL
// are allowed to silently increase their permission level.
const PermissionMessageProvider* message_provider =
PermissionMessageProvider::Get();
bool has_no_warnings =
message_provider->GetPermissionMessages(
message_provider->GetAllPermissionIDs(
*requested_permissions_, extension()->GetType()))
.empty();
if (auto_confirm_for_tests == PROCEED || has_no_warnings ||
extension_->location() == Manifest::COMPONENT) {
OnInstallPromptDone(ExtensionInstallPrompt::Result::ACCEPTED);
return AlreadyResponded();
}
if (auto_confirm_for_tests == ABORT) {
// Pretend the user clicked cancel.
OnInstallPromptDone(ExtensionInstallPrompt::Result::USER_CANCELED);
return AlreadyResponded();
}
CHECK_EQ(DO_NOT_SKIP, auto_confirm_for_tests);
install_ui_.reset(new ExtensionInstallPrompt(
Profile::FromBrowserContext(browser_context()), native_window));
install_ui_->ShowDialog(
base::Bind(&PermissionsRequestFunction::OnInstallPromptDone, this),
extension(), nullptr,
std::make_unique<ExtensionInstallPrompt::Prompt>(
ExtensionInstallPrompt::PERMISSIONS_PROMPT),
requested_permissions_->Clone(),
ExtensionInstallPrompt::GetDefaultShowDialogCallback());
// ExtensionInstallPrompt::ShowDialog() can call the response synchronously.
return did_respond() ? AlreadyResponded() : RespondLater();
}
void PermissionsRequestFunction::OnInstallPromptDone(
ExtensionInstallPrompt::Result result) {
bool granted = result == ExtensionInstallPrompt::Result::ACCEPTED;
if (granted) {
PermissionsUpdater perms_updater(browser_context());
perms_updater.GrantOptionalPermissions(*extension(),
*requested_permissions_);
}
Respond(ArgumentList(api::permissions::Request::Results::Create(granted)));
Release(); // Balanced in Run().
}
} // namespace extensions