permission_decision_auto_blocker.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. // Copyright 2016 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. #include "components/permissions/permission_decision_auto_blocker.h"
  5. #include <algorithm>
  6. #include <memory>
  7. #include <string>
  8. #include <utility>
  9. #include "base/cxx17_backports.h"
  10. #include "base/feature_list.h"
  11. #include "base/metrics/field_trial_params.h"
  12. #include "base/strings/string_number_conversions.h"
  13. #include "base/values.h"
  14. #include "components/content_settings/core/browser/host_content_settings_map.h"
  15. #include "components/keyed_service/content/browser_context_dependency_manager.h"
  16. #include "components/permissions/features.h"
  17. #include "components/permissions/permission_util.h"
  18. #include "url/gurl.h"
  19. namespace permissions {
  20. namespace {
  21. constexpr int kDefaultDismissalsBeforeBlock = 3;
  22. constexpr int kDefaultIgnoresBeforeBlock = 4;
  23. constexpr int kDefaultDismissalsBeforeBlockWithQuietUi = 1;
  24. constexpr int kDefaultIgnoresBeforeBlockWithQuietUi = 2;
  25. constexpr int kDefaultEmbargoDays = 7;
  26. // The number of times that users may explicitly dismiss a
  27. // FEDERATED_IDENTITY_API permission prompt from an origin before it is
  28. // automatically blocked.
  29. constexpr int kFederatedIdentityApiDismissalsBeforeBlock = 1;
  30. // The durations that an origin will stay under embargo for the
  31. // FEDERATED_IDENTITY_API permission due to the user explicitly dismissing the
  32. // permission prompt.
  33. constexpr base::TimeDelta kFederatedIdentityApiEmbargoDurationDismiss[] = {
  34. base::Hours(2) /* 1st dismissal */, base::Days(1) /* 2nd dismissal */,
  35. base::Days(7), base::Days(28)};
  36. // The number of times that users may explicitly dismiss a permission prompt
  37. // from an origin before it is automatically blocked.
  38. int g_dismissals_before_block = kDefaultDismissalsBeforeBlock;
  39. // The number of times that users may ignore a permission prompt from an origin
  40. // before it is automatically blocked.
  41. int g_ignores_before_block = kDefaultIgnoresBeforeBlock;
  42. // The number of times that users may dismiss a permission prompt that uses the
  43. // quiet UI from an origin before it is automatically blocked.
  44. int g_dismissals_before_block_with_quiet_ui =
  45. kDefaultDismissalsBeforeBlockWithQuietUi;
  46. // The number of times that users may ignore a permission prompt that uses the
  47. // quiet UI from an origin before it is automatically blocked.
  48. int g_ignores_before_block_with_quiet_ui =
  49. kDefaultIgnoresBeforeBlockWithQuietUi;
  50. // The number of days that an origin will stay under embargo for a requested
  51. // permission due to repeated dismissals.
  52. int g_dismissal_embargo_days = kDefaultEmbargoDays;
  53. // The number of days that an origin will stay under embargo for a requested
  54. // permission due to repeated ignores.
  55. int g_ignore_embargo_days = kDefaultEmbargoDays;
  56. std::string GetStringForContentType(ContentSettingsType content_type) {
  57. if (content_type == ContentSettingsType::FEDERATED_IDENTITY_API)
  58. return "FederatedIdentityApi";
  59. return PermissionUtil::GetPermissionString(content_type);
  60. }
  61. std::unique_ptr<base::Value> GetOriginAutoBlockerData(
  62. HostContentSettingsMap* settings,
  63. const GURL& origin_url) {
  64. base::Value website_setting = settings->GetWebsiteSetting(
  65. origin_url, GURL(), ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA,
  66. nullptr);
  67. if (!website_setting.is_dict())
  68. return std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
  69. return base::Value::ToUniquePtrValue(std::move(website_setting));
  70. }
  71. base::Value* GetOrCreatePermissionDict(base::Value* origin_dict,
  72. const std::string& permission) {
  73. base::Value* permission_dict =
  74. origin_dict->FindKeyOfType(permission, base::Value::Type::DICTIONARY);
  75. if (permission_dict)
  76. return permission_dict;
  77. return origin_dict->SetKey(permission,
  78. base::Value(base::Value::Type::DICTIONARY));
  79. }
  80. int RecordActionInWebsiteSettings(const GURL& url,
  81. ContentSettingsType permission,
  82. const char* key,
  83. HostContentSettingsMap* settings_map) {
  84. std::unique_ptr<base::Value> dict =
  85. GetOriginAutoBlockerData(settings_map, url);
  86. base::Value* permission_dict = GetOrCreatePermissionDict(
  87. dict.get(), GetStringForContentType(permission));
  88. base::Value* value =
  89. permission_dict->FindKeyOfType(key, base::Value::Type::INTEGER);
  90. int current_count = value ? value->GetInt() : 0;
  91. permission_dict->SetKey(key, base::Value(++current_count));
  92. settings_map->SetWebsiteSettingDefaultScope(
  93. url, GURL(), ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA,
  94. base::Value::FromUniquePtrValue(std::move(dict)));
  95. return current_count;
  96. }
  97. int GetActionCount(const GURL& url,
  98. ContentSettingsType permission,
  99. const char* key,
  100. HostContentSettingsMap* settings_map) {
  101. std::unique_ptr<base::Value> dict =
  102. GetOriginAutoBlockerData(settings_map, url);
  103. base::Value* permission_dict = GetOrCreatePermissionDict(
  104. dict.get(), GetStringForContentType(permission));
  105. base::Value* value =
  106. permission_dict->FindKeyOfType(key, base::Value::Type::INTEGER);
  107. return value ? value->GetInt() : 0;
  108. }
  109. // Returns the number of times that users may explicitly dismiss a permission
  110. // prompt for an origin for the passed-in |permission| before it is
  111. // automatically blocked.
  112. int GetDismissalsBeforeBlockForContentSettingsType(
  113. ContentSettingsType permission) {
  114. return (permission == ContentSettingsType::FEDERATED_IDENTITY_API)
  115. ? kFederatedIdentityApiDismissalsBeforeBlock
  116. : g_dismissals_before_block;
  117. }
  118. // The duration that an origin will stay under embargo for the passed-in
  119. // |permission| due to the user explicitly dismissing the permission prompt.
  120. base::TimeDelta GetEmbargoDurationForContentSettingsType(
  121. ContentSettingsType permission,
  122. int dismiss_count) {
  123. if (permission == ContentSettingsType::FEDERATED_IDENTITY_API) {
  124. int duration_index = base::clamp(
  125. dismiss_count - 1, 0,
  126. static_cast<int>(
  127. std::size(kFederatedIdentityApiEmbargoDurationDismiss) - 1));
  128. return kFederatedIdentityApiEmbargoDurationDismiss[duration_index];
  129. }
  130. return base::Days(g_dismissal_embargo_days);
  131. }
  132. base::Time GetEmbargoStartTime(base::Value* permission_dict,
  133. const base::Feature& feature,
  134. const char* key) {
  135. base::Value* found =
  136. permission_dict->FindKeyOfType(key, base::Value::Type::DOUBLE);
  137. if (found && base::FeatureList::IsEnabled(feature)) {
  138. return base::Time::FromDeltaSinceWindowsEpoch(
  139. base::Microseconds(found->GetDouble()));
  140. }
  141. return base::Time();
  142. }
  143. bool IsUnderEmbargo(base::Value* permission_dict,
  144. const base::Feature& feature,
  145. const char* key,
  146. base::Time current_time,
  147. base::TimeDelta offset) {
  148. base::Value* found =
  149. permission_dict->FindKeyOfType(key, base::Value::Type::DOUBLE);
  150. if (found && base::FeatureList::IsEnabled(feature) &&
  151. current_time <
  152. base::Time::FromInternalValue(found->GetDouble()) + offset) {
  153. return true;
  154. }
  155. return false;
  156. }
  157. void UpdateValueFromVariation(const std::string& variation_value,
  158. int* value_store,
  159. const int default_value) {
  160. int tmp_value = -1;
  161. if (base::StringToInt(variation_value, &tmp_value) && tmp_value > 0)
  162. *value_store = tmp_value;
  163. else
  164. *value_store = default_value;
  165. }
  166. } // namespace
  167. // static
  168. const char PermissionDecisionAutoBlocker::kPromptDismissCountKey[] =
  169. "dismiss_count";
  170. // static
  171. const char PermissionDecisionAutoBlocker::kPromptIgnoreCountKey[] =
  172. "ignore_count";
  173. // static
  174. const char PermissionDecisionAutoBlocker::kPromptDismissCountWithQuietUiKey[] =
  175. "dismiss_count_quiet_ui";
  176. // static
  177. const char PermissionDecisionAutoBlocker::kPromptIgnoreCountWithQuietUiKey[] =
  178. "ignore_count_quiet_ui";
  179. // static
  180. const char PermissionDecisionAutoBlocker::kPermissionDismissalEmbargoKey[] =
  181. "dismissal_embargo_days";
  182. // static
  183. const char PermissionDecisionAutoBlocker::kPermissionIgnoreEmbargoKey[] =
  184. "ignore_embargo_days";
  185. // static
  186. bool PermissionDecisionAutoBlocker::IsEnabledForContentSetting(
  187. ContentSettingsType content_setting) {
  188. return PermissionUtil::IsPermission(content_setting) ||
  189. content_setting == ContentSettingsType::FEDERATED_IDENTITY_API;
  190. }
  191. // static
  192. absl::optional<PermissionResult>
  193. PermissionDecisionAutoBlocker::GetEmbargoResult(
  194. HostContentSettingsMap* settings_map,
  195. const GURL& request_origin,
  196. ContentSettingsType permission,
  197. base::Time current_time) {
  198. DCHECK(settings_map);
  199. DCHECK(IsEnabledForContentSetting(permission));
  200. std::unique_ptr<base::Value> dict =
  201. GetOriginAutoBlockerData(settings_map, request_origin);
  202. base::Value* permission_dict = GetOrCreatePermissionDict(
  203. dict.get(), GetStringForContentType(permission));
  204. int dismiss_count = GetActionCount(request_origin, permission,
  205. kPromptDismissCountKey, settings_map);
  206. if (IsUnderEmbargo(permission_dict, features::kBlockPromptsIfDismissedOften,
  207. kPermissionDismissalEmbargoKey, current_time,
  208. GetEmbargoDurationForContentSettingsType(permission,
  209. dismiss_count))) {
  210. return PermissionResult(CONTENT_SETTING_BLOCK,
  211. PermissionStatusSource::MULTIPLE_DISMISSALS);
  212. }
  213. if (IsUnderEmbargo(permission_dict, features::kBlockPromptsIfIgnoredOften,
  214. kPermissionIgnoreEmbargoKey, current_time,
  215. base::Days(g_ignore_embargo_days))) {
  216. return PermissionResult(CONTENT_SETTING_BLOCK,
  217. PermissionStatusSource::MULTIPLE_IGNORES);
  218. }
  219. return absl::nullopt;
  220. }
  221. // static
  222. void PermissionDecisionAutoBlocker::UpdateFromVariations() {
  223. std::string dismissals_before_block_value =
  224. base::GetFieldTrialParamValueByFeature(
  225. features::kBlockPromptsIfDismissedOften, kPromptDismissCountKey);
  226. std::string ignores_before_block_value =
  227. base::GetFieldTrialParamValueByFeature(
  228. features::kBlockPromptsIfIgnoredOften, kPromptIgnoreCountKey);
  229. std::string dismissals_before_block_value_with_quiet_ui =
  230. base::GetFieldTrialParamValueByFeature(
  231. features::kBlockPromptsIfDismissedOften,
  232. kPromptDismissCountWithQuietUiKey);
  233. std::string ignores_before_block_value_with_quiet_ui =
  234. base::GetFieldTrialParamValueByFeature(
  235. features::kBlockPromptsIfIgnoredOften,
  236. kPromptIgnoreCountWithQuietUiKey);
  237. std::string dismissal_embargo_days_value =
  238. base::GetFieldTrialParamValueByFeature(
  239. features::kBlockPromptsIfDismissedOften,
  240. kPermissionDismissalEmbargoKey);
  241. std::string ignore_embargo_days_value =
  242. base::GetFieldTrialParamValueByFeature(
  243. features::kBlockPromptsIfIgnoredOften, kPermissionIgnoreEmbargoKey);
  244. // If converting the value fails, revert to the original value.
  245. UpdateValueFromVariation(dismissals_before_block_value,
  246. &g_dismissals_before_block,
  247. kDefaultDismissalsBeforeBlock);
  248. UpdateValueFromVariation(ignores_before_block_value, &g_ignores_before_block,
  249. kDefaultIgnoresBeforeBlock);
  250. UpdateValueFromVariation(dismissals_before_block_value_with_quiet_ui,
  251. &g_dismissals_before_block_with_quiet_ui,
  252. kDefaultDismissalsBeforeBlockWithQuietUi);
  253. UpdateValueFromVariation(ignores_before_block_value_with_quiet_ui,
  254. &g_ignores_before_block_with_quiet_ui,
  255. kDefaultIgnoresBeforeBlockWithQuietUi);
  256. UpdateValueFromVariation(dismissal_embargo_days_value,
  257. &g_dismissal_embargo_days, kDefaultEmbargoDays);
  258. UpdateValueFromVariation(ignore_embargo_days_value, &g_ignore_embargo_days,
  259. kDefaultEmbargoDays);
  260. }
  261. bool PermissionDecisionAutoBlocker::IsEmbargoed(
  262. const GURL& request_origin,
  263. ContentSettingsType permission) {
  264. return GetEmbargoResult(request_origin, permission).has_value();
  265. }
  266. absl::optional<PermissionResult>
  267. PermissionDecisionAutoBlocker::GetEmbargoResult(
  268. const GURL& request_origin,
  269. ContentSettingsType permission) {
  270. return GetEmbargoResult(settings_map_, request_origin, permission,
  271. clock_->Now());
  272. }
  273. base::Time PermissionDecisionAutoBlocker::GetEmbargoStartTime(
  274. const GURL& request_origin,
  275. ContentSettingsType permission) {
  276. DCHECK(settings_map_);
  277. std::unique_ptr<base::Value> dict =
  278. GetOriginAutoBlockerData(settings_map_, request_origin);
  279. base::Value* permission_dict = GetOrCreatePermissionDict(
  280. dict.get(), GetStringForContentType(permission));
  281. // A permission may have a record for both dismisal and ignore, return the
  282. // most recent. A permission will only actually be under one embargo, but
  283. // the record of embargo start will persist until explicitly deleted
  284. base::Time dismissal_start_time = permissions::GetEmbargoStartTime(
  285. permission_dict, features::kBlockPromptsIfDismissedOften,
  286. kPermissionDismissalEmbargoKey);
  287. base::Time ignore_start_time = permissions::GetEmbargoStartTime(
  288. permission_dict, features::kBlockPromptsIfIgnoredOften,
  289. kPermissionIgnoreEmbargoKey);
  290. return dismissal_start_time > ignore_start_time ? dismissal_start_time
  291. : ignore_start_time;
  292. }
  293. std::set<GURL> PermissionDecisionAutoBlocker::GetEmbargoedOrigins(
  294. ContentSettingsType content_type) {
  295. return GetEmbargoedOrigins(std::vector<ContentSettingsType>{content_type});
  296. }
  297. std::set<GURL> PermissionDecisionAutoBlocker::GetEmbargoedOrigins(
  298. std::vector<ContentSettingsType> content_types) {
  299. DCHECK(settings_map_);
  300. std::vector<ContentSettingsType> filtered_content_types;
  301. for (ContentSettingsType content_type : content_types) {
  302. if (IsEnabledForContentSetting(content_type))
  303. filtered_content_types.emplace_back(content_type);
  304. }
  305. if (filtered_content_types.empty())
  306. return std::set<GURL>();
  307. ContentSettingsForOneType embargo_settings;
  308. settings_map_->GetSettingsForOneType(
  309. ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA, &embargo_settings);
  310. std::set<GURL> origins;
  311. for (const auto& e : embargo_settings) {
  312. for (auto content_type : filtered_content_types) {
  313. const GURL url(e.primary_pattern.ToString());
  314. if (IsEmbargoed(url, content_type)) {
  315. origins.insert(url);
  316. break;
  317. }
  318. }
  319. }
  320. return origins;
  321. }
  322. int PermissionDecisionAutoBlocker::GetDismissCount(
  323. const GURL& url,
  324. ContentSettingsType permission) {
  325. return GetActionCount(url, permission, kPromptDismissCountKey, settings_map_);
  326. }
  327. int PermissionDecisionAutoBlocker::GetIgnoreCount(
  328. const GURL& url,
  329. ContentSettingsType permission) {
  330. return GetActionCount(url, permission, kPromptIgnoreCountKey, settings_map_);
  331. }
  332. bool PermissionDecisionAutoBlocker::RecordDismissAndEmbargo(
  333. const GURL& url,
  334. ContentSettingsType permission,
  335. bool dismissed_prompt_was_quiet) {
  336. int current_dismissal_count = RecordActionInWebsiteSettings(
  337. url, permission, kPromptDismissCountKey, settings_map_);
  338. int current_dismissal_count_with_quiet_ui =
  339. dismissed_prompt_was_quiet
  340. ? RecordActionInWebsiteSettings(url, permission,
  341. kPromptDismissCountWithQuietUiKey,
  342. settings_map_)
  343. : -1;
  344. // TODO(dominickn): ideally we would have a method
  345. // PermissionContextBase::ShouldEmbargoAfterRepeatedDismissals() to specify
  346. // if a permission is opted in. This is difficult right now because:
  347. // 1. PermissionQueueController needs to call this method at a point where it
  348. // does not have a PermissionContextBase available
  349. // 2. Not calling RecordDismissAndEmbargo means no repeated dismissal metrics
  350. // are recorded
  351. if (base::FeatureList::IsEnabled(features::kBlockPromptsIfDismissedOften)) {
  352. if (current_dismissal_count >=
  353. GetDismissalsBeforeBlockForContentSettingsType(permission)) {
  354. PlaceUnderEmbargo(url, permission, kPermissionDismissalEmbargoKey);
  355. return true;
  356. }
  357. if (current_dismissal_count_with_quiet_ui >=
  358. g_dismissals_before_block_with_quiet_ui) {
  359. DCHECK(permission == ContentSettingsType::NOTIFICATIONS ||
  360. permission == ContentSettingsType::GEOLOCATION);
  361. PlaceUnderEmbargo(url, permission, kPermissionDismissalEmbargoKey);
  362. return true;
  363. }
  364. }
  365. return false;
  366. }
  367. bool PermissionDecisionAutoBlocker::RecordIgnoreAndEmbargo(
  368. const GURL& url,
  369. ContentSettingsType permission,
  370. bool ignored_prompt_was_quiet) {
  371. int current_ignore_count = RecordActionInWebsiteSettings(
  372. url, permission, kPromptIgnoreCountKey, settings_map_);
  373. int current_ignore_count_with_quiet_ui =
  374. ignored_prompt_was_quiet
  375. ? RecordActionInWebsiteSettings(url, permission,
  376. kPromptIgnoreCountWithQuietUiKey,
  377. settings_map_)
  378. : -1;
  379. if (base::FeatureList::IsEnabled(features::kBlockPromptsIfIgnoredOften)) {
  380. if (current_ignore_count >= g_ignores_before_block) {
  381. PlaceUnderEmbargo(url, permission, kPermissionIgnoreEmbargoKey);
  382. return true;
  383. }
  384. if (current_ignore_count_with_quiet_ui >=
  385. g_ignores_before_block_with_quiet_ui) {
  386. DCHECK(permission == ContentSettingsType::NOTIFICATIONS ||
  387. permission == ContentSettingsType::GEOLOCATION);
  388. PlaceUnderEmbargo(url, permission, kPermissionIgnoreEmbargoKey);
  389. return true;
  390. }
  391. }
  392. return false;
  393. }
  394. void PermissionDecisionAutoBlocker::RemoveEmbargoAndResetCounts(
  395. const GURL& url,
  396. ContentSettingsType permission) {
  397. if (!IsEnabledForContentSetting(permission))
  398. return;
  399. std::unique_ptr<base::Value> dict =
  400. GetOriginAutoBlockerData(settings_map_, url);
  401. dict->RemoveKey(GetStringForContentType(permission));
  402. settings_map_->SetWebsiteSettingDefaultScope(
  403. url, GURL(), ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA,
  404. base::Value::FromUniquePtrValue(std::move(dict)));
  405. }
  406. void PermissionDecisionAutoBlocker::RemoveEmbargoAndResetCounts(
  407. base::RepeatingCallback<bool(const GURL& url)> filter) {
  408. std::unique_ptr<ContentSettingsForOneType> settings(
  409. new ContentSettingsForOneType);
  410. settings_map_->GetSettingsForOneType(
  411. ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA, settings.get());
  412. for (const auto& site : *settings) {
  413. GURL origin(site.primary_pattern.ToString());
  414. if (origin.is_valid() && filter.Run(origin)) {
  415. settings_map_->SetWebsiteSettingDefaultScope(
  416. origin, GURL(), ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA,
  417. base::Value());
  418. }
  419. }
  420. }
  421. // static
  422. const char*
  423. PermissionDecisionAutoBlocker::GetPromptDismissCountKeyForTesting() {
  424. return kPromptDismissCountKey;
  425. }
  426. PermissionDecisionAutoBlocker::PermissionDecisionAutoBlocker(
  427. HostContentSettingsMap* settings_map)
  428. : settings_map_(settings_map), clock_(base::DefaultClock::GetInstance()) {}
  429. PermissionDecisionAutoBlocker::~PermissionDecisionAutoBlocker() {}
  430. void PermissionDecisionAutoBlocker::PlaceUnderEmbargo(
  431. const GURL& request_origin,
  432. ContentSettingsType permission,
  433. const char* key) {
  434. std::unique_ptr<base::Value> dict =
  435. GetOriginAutoBlockerData(settings_map_, request_origin);
  436. base::Value* permission_dict = GetOrCreatePermissionDict(
  437. dict.get(), GetStringForContentType(permission));
  438. permission_dict->SetKey(
  439. key, base::Value(static_cast<double>(clock_->Now().ToInternalValue())));
  440. settings_map_->SetWebsiteSettingDefaultScope(
  441. request_origin, GURL(), ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA,
  442. base::Value::FromUniquePtrValue(std::move(dict)));
  443. }
  444. void PermissionDecisionAutoBlocker::SetClockForTesting(base::Clock* clock) {
  445. clock_ = clock;
  446. }
  447. } // namespace permissions