1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/api/permissions/permissions_api.h"
6
7#include "base/memory/scoped_ptr.h"
8#include "chrome/browser/chrome_notification_types.h"
9#include "chrome/browser/extensions/api/permissions/permissions_api_helpers.h"
10#include "chrome/browser/extensions/permissions_updater.h"
11#include "chrome/browser/profiles/profile.h"
12#include "chrome/common/extensions/api/permissions.h"
13#include "extensions/browser/extension_prefs.h"
14#include "extensions/common/error_utils.h"
15#include "extensions/common/extension.h"
16#include "extensions/common/manifest_handlers/permissions_parser.h"
17#include "extensions/common/permissions/permission_message_provider.h"
18#include "extensions/common/permissions/permissions_data.h"
19#include "extensions/common/permissions/permissions_info.h"
20
21namespace extensions {
22
23using api::permissions::Permissions;
24
25namespace Contains = api::permissions::Contains;
26namespace GetAll = api::permissions::GetAll;
27namespace Remove = api::permissions::Remove;
28namespace Request  = api::permissions::Request;
29namespace helpers = permissions_api_helpers;
30
31namespace {
32
33const char kCantRemoveRequiredPermissionsError[] =
34    "You cannot remove required permissions.";
35const char kNotInOptionalPermissionsError[] =
36    "Optional permissions must be listed in extension manifest.";
37const char kNotWhitelistedError[] =
38    "The optional permissions API does not support '*'.";
39const char kUserGestureRequiredError[] =
40    "This function must be called during a user gesture";
41
42enum AutoConfirmForTest {
43  DO_NOT_SKIP = 0,
44  PROCEED,
45  ABORT
46};
47AutoConfirmForTest auto_confirm_for_tests = DO_NOT_SKIP;
48bool ignore_user_gesture_for_tests = false;
49
50}  // namespace
51
52bool PermissionsContainsFunction::RunSync() {
53  scoped_ptr<Contains::Params> params(Contains::Params::Create(*args_));
54  EXTENSION_FUNCTION_VALIDATE(params);
55
56  scoped_refptr<PermissionSet> permissions = helpers::UnpackPermissionSet(
57      params->permissions,
58      ExtensionPrefs::Get(GetProfile())->AllowFileAccess(extension_->id()),
59      &error_);
60  if (!permissions.get())
61    return false;
62
63  results_ = Contains::Results::Create(
64      extension()->permissions_data()->active_permissions()->Contains(
65          *permissions.get()));
66  return true;
67}
68
69bool PermissionsGetAllFunction::RunSync() {
70  scoped_ptr<Permissions> permissions = helpers::PackPermissionSet(
71      extension()->permissions_data()->active_permissions().get());
72  results_ = GetAll::Results::Create(*permissions);
73  return true;
74}
75
76bool PermissionsRemoveFunction::RunSync() {
77  scoped_ptr<Remove::Params> params(Remove::Params::Create(*args_));
78  EXTENSION_FUNCTION_VALIDATE(params);
79
80  scoped_refptr<PermissionSet> permissions = helpers::UnpackPermissionSet(
81      params->permissions,
82      ExtensionPrefs::Get(GetProfile())->AllowFileAccess(extension_->id()),
83      &error_);
84  if (!permissions.get())
85    return false;
86
87  // Make sure they're only trying to remove permissions supported by this API.
88  APIPermissionSet apis = permissions->apis();
89  for (APIPermissionSet::const_iterator i = apis.begin();
90       i != apis.end(); ++i) {
91    if (!i->info()->supports_optional()) {
92      error_ = ErrorUtils::FormatErrorMessage(
93          kNotWhitelistedError, i->name());
94      return false;
95    }
96  }
97
98  // Make sure we don't remove any required pemissions.
99  scoped_refptr<const PermissionSet> required =
100      PermissionsParser::GetRequiredPermissions(extension());
101  scoped_refptr<PermissionSet> intersection(
102      PermissionSet::CreateIntersection(permissions.get(), required.get()));
103  if (!intersection->IsEmpty()) {
104    error_ = kCantRemoveRequiredPermissionsError;
105    return false;
106  }
107
108  PermissionsUpdater(GetProfile())
109      .RemovePermissions(extension(), permissions.get());
110  results_ = Remove::Results::Create(true);
111  return true;
112}
113
114// static
115void PermissionsRequestFunction::SetAutoConfirmForTests(bool should_proceed) {
116  auto_confirm_for_tests = should_proceed ? PROCEED : ABORT;
117}
118
119// static
120void PermissionsRequestFunction::SetIgnoreUserGestureForTests(
121    bool ignore) {
122  ignore_user_gesture_for_tests = ignore;
123}
124
125PermissionsRequestFunction::PermissionsRequestFunction() {}
126
127void PermissionsRequestFunction::InstallUIProceed() {
128  PermissionsUpdater perms_updater(GetProfile());
129  perms_updater.AddPermissions(extension(), requested_permissions_.get());
130
131  results_ = Request::Results::Create(true);
132  SendResponse(true);
133
134  Release();  // Balanced in RunAsync().
135}
136
137void PermissionsRequestFunction::InstallUIAbort(bool user_initiated) {
138  SendResponse(true);
139
140  Release();  // Balanced in RunAsync().
141}
142
143PermissionsRequestFunction::~PermissionsRequestFunction() {}
144
145bool PermissionsRequestFunction::RunAsync() {
146  results_ = Request::Results::Create(false);
147
148  if (!user_gesture() &&
149      !ignore_user_gesture_for_tests &&
150      extension_->location() != Manifest::COMPONENT) {
151    error_ = kUserGestureRequiredError;
152    return false;
153  }
154
155  scoped_ptr<Request::Params> params(Request::Params::Create(*args_));
156  EXTENSION_FUNCTION_VALIDATE(params);
157
158  requested_permissions_ = helpers::UnpackPermissionSet(
159      params->permissions,
160      ExtensionPrefs::Get(GetProfile())->AllowFileAccess(extension_->id()),
161      &error_);
162  if (!requested_permissions_.get())
163    return false;
164
165  // Make sure they're only requesting permissions supported by this API.
166  APIPermissionSet apis = requested_permissions_->apis();
167  for (APIPermissionSet::const_iterator i = apis.begin();
168       i != apis.end(); ++i) {
169    if (!i->info()->supports_optional()) {
170      error_ = ErrorUtils::FormatErrorMessage(
171          kNotWhitelistedError, i->name());
172      return false;
173    }
174  }
175
176  // The requested permissions must be defined as optional in the manifest.
177  if (!PermissionsParser::GetOptionalPermissions(extension())
178           ->Contains(*requested_permissions_.get())) {
179    error_ = kNotInOptionalPermissionsError;
180    return false;
181  }
182
183  // We don't need to prompt the user if the requested permissions are a subset
184  // of the granted permissions set.
185  scoped_refptr<const PermissionSet> granted =
186      ExtensionPrefs::Get(GetProfile())
187          ->GetGrantedPermissions(extension()->id());
188  if (granted.get() && granted->Contains(*requested_permissions_.get())) {
189    PermissionsUpdater perms_updater(GetProfile());
190    perms_updater.AddPermissions(extension(), requested_permissions_.get());
191    results_ = Request::Results::Create(true);
192    SendResponse(true);
193    return true;
194  }
195
196  // Filter out the granted permissions so we only prompt for new ones.
197  requested_permissions_ = PermissionSet::CreateDifference(
198      requested_permissions_.get(), granted.get());
199
200  AddRef();  // Balanced in InstallUIProceed() / InstallUIAbort().
201
202  // We don't need to show the prompt if there are no new warnings, or if
203  // we're skipping the confirmation UI. All extension types but INTERNAL
204  // are allowed to silently increase their permission level.
205  bool has_no_warnings = PermissionMessageProvider::Get()
206                             ->GetWarningMessages(requested_permissions_.get(),
207                                                  extension()->GetType())
208                             .empty();
209  if (auto_confirm_for_tests == PROCEED || has_no_warnings ||
210      extension_->location() == Manifest::COMPONENT) {
211    InstallUIProceed();
212  } else if (auto_confirm_for_tests == ABORT) {
213    // Pretend the user clicked cancel.
214    InstallUIAbort(true);
215  } else {
216    CHECK_EQ(DO_NOT_SKIP, auto_confirm_for_tests);
217    install_ui_.reset(new ExtensionInstallPrompt(GetAssociatedWebContents()));
218    install_ui_->ConfirmPermissions(
219        this, extension(), requested_permissions_.get());
220  }
221
222  return true;
223}
224
225}  // namespace extensions
226