blob: ef71f3c4c36fc679500b5b98a78d3ac1dc05a43a [file] [log] [blame]
[email protected]38427a12013-11-09 17:34:201// Copyright 2013 The Chromium Authors. All rights reserved.
[email protected]d13950e2009-12-04 01:43:022// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]38427a12013-11-09 17:34:205// The QuotaService uses heuristics to limit abusive requests
[email protected]d13950e2009-12-04 01:43:026// made by extensions. In this model 'items' (e.g individual bookmarks) are
7// represented by a 'Bucket' that holds state for that item for one single
8// interval of time. The interval of time is defined as 'how long we need to
9// watch an item (for a particular heuristic) before making a decision about
10// quota violations'. A heuristic is two functions: one mapping input
11// arguments to a unique Bucket (the BucketMapper), and another to determine
12// if a new request involving such an item at a given time is a violation.
13
[email protected]38427a12013-11-09 17:34:2014#ifndef EXTENSIONS_BROWSER_QUOTA_SERVICE_H_
15#define EXTENSIONS_BROWSER_QUOTA_SERVICE_H_
[email protected]d13950e2009-12-04 01:43:0216
avic9cec102015-12-23 00:39:2617#include <stdint.h>
18
[email protected]d13950e2009-12-04 01:43:0219#include <list>
20#include <map>
dchengf5d241082016-04-21 03:43:1121#include <memory>
[email protected]d13950e2009-12-04 01:43:0222#include <string>
23
[email protected]fd50e7b2011-11-03 09:20:2524#include "base/compiler_specific.h"
[email protected]14c1c232013-06-11 17:52:4425#include "base/containers/hash_tables.h"
avic9cec102015-12-23 00:39:2626#include "base/macros.h"
gab370841f22017-06-01 14:38:2627#include "base/threading/thread_checker.h"
[email protected]41a17c52013-06-28 00:27:5328#include "base/time/time.h"
29#include "base/timer/timer.h"
[email protected]d13950e2009-12-04 01:43:0230#include "base/values.h"
lazyboy0222b362016-11-12 02:00:0931#include "extensions/common/extension_id.h"
[email protected]d13950e2009-12-04 01:43:0232
33class ExtensionFunction;
[email protected]d13950e2009-12-04 01:43:0234
[email protected]ae54db1c2012-07-11 12:33:3535namespace extensions {
[email protected]38427a12013-11-09 17:34:2036class QuotaLimitHeuristic;
[email protected]ae54db1c2012-07-11 12:33:3537
avi14f29742016-11-03 23:39:5038using QuotaLimitHeuristics = std::list<std::unique_ptr<QuotaLimitHeuristic>>;
[email protected]38427a12013-11-09 17:34:2039
40// The QuotaService takes care that calls to certain extension
[email protected]36296912012-03-20 11:08:4941// functions do not exceed predefined quotas.
42//
[email protected]aab23102014-02-05 18:57:5543// The QuotaService needs to live entirely on one thread, i.e. be created,
44// called and destroyed on the same thread, due to its use of a RepeatingTimer.
[email protected]b33f0b112014-03-13 17:05:3045// It is not a KeyedService because instances exist on both the UI
[email protected]aab23102014-02-05 18:57:5546// and IO threads.
gab370841f22017-06-01 14:38:2647class QuotaService {
[email protected]d13950e2009-12-04 01:43:0248 public:
49 // Some concrete heuristics (declared below) that ExtensionFunctions can
50 // use to help the service make decisions about quota violations.
51 class TimedLimit;
[email protected]d13950e2009-12-04 01:43:0252
[email protected]38427a12013-11-09 17:34:2053 QuotaService();
54 virtual ~QuotaService();
[email protected]d13950e2009-12-04 01:43:0255
56 // Decide whether the invocation of |function| with argument |args| by the
57 // extension specified by |extension_id| results in a quota limit violation.
[email protected]85231d72012-08-31 09:45:2958 // Returns an error message representing the failure if quota was exceeded,
59 // or empty-string if the request is fine and can proceed.
60 std::string Assess(const std::string& extension_id,
61 ExtensionFunction* function,
[email protected]aeca23f2013-06-21 22:34:4162 const base::ListValue* args,
[email protected]85231d72012-08-31 09:45:2963 const base::TimeTicks& event_time);
64
fdoray4cfe32a2016-06-29 19:45:5265 // An active ScopedDisablePurgeForTesting prevents QuotaService's constructor
66 // from starting a purge timer.
67 class ScopedDisablePurgeForTesting {
68 public:
69 ScopedDisablePurgeForTesting();
70 ~ScopedDisablePurgeForTesting();
71
72 private:
73 DISALLOW_COPY_AND_ASSIGN(ScopedDisablePurgeForTesting);
74 };
75
[email protected]d13950e2009-12-04 01:43:0276 private:
avi14f29742016-11-03 23:39:5077 using FunctionName = std::string;
[email protected]36296912012-03-20 11:08:4978 // All QuotaLimitHeuristic instances in this map are owned by us.
avi14f29742016-11-03 23:39:5079 using FunctionHeuristicsMap = std::map<FunctionName, QuotaLimitHeuristics>;
[email protected]d13950e2009-12-04 01:43:0280
kalman2610ea12014-11-05 00:18:1881 // Purge resets all accumulated data as if the service was just created.
82 // Called periodically so we don't consume an unbounded amount of memory
83 // while tracking quota.
[email protected]d13950e2009-12-04 01:43:0284 void Purge();
danakj8c3eb802015-09-24 07:53:0085 base::RepeatingTimer purge_timer_;
[email protected]d13950e2009-12-04 01:43:0286
87 // Our quota tracking state for extensions that have invoked quota limited
88 // functions. Each extension is treated separately, so extension ids are the
89 // key for the mapping. As an extension invokes functions, the map keeps
90 // track of which functions it has invoked and the heuristics for each one.
91 // Each heuristic will be evaluated and ANDed together to get a final answer.
[email protected]36296912012-03-20 11:08:4992 std::map<ExtensionId, FunctionHeuristicsMap> function_heuristics_;
[email protected]d13950e2009-12-04 01:43:0293
gab370841f22017-06-01 14:38:2694 THREAD_CHECKER(thread_checker_);
95
[email protected]38427a12013-11-09 17:34:2096 DISALLOW_COPY_AND_ASSIGN(QuotaService);
[email protected]d13950e2009-12-04 01:43:0297};
98
99// A QuotaLimitHeuristic is two things: 1, A heuristic to map extension
100// function arguments to corresponding Buckets for each input arg, and 2) a
101// heuristic for determining if a new event involving a particular item
102// (represented by its Bucket) constitutes a quota violation.
103class QuotaLimitHeuristic {
104 public:
105 // Parameters to configure the amount of tokens allotted to individual
106 // Bucket objects (see Below) and how often they are replenished.
107 struct Config {
108 // The maximum number of tokens a bucket can contain, and is refilled to
109 // every epoch.
avic9cec102015-12-23 00:39:26110 int64_t refill_token_count;
[email protected]d13950e2009-12-04 01:43:02111
112 // Specifies how frequently the bucket is logically refilled with tokens.
113 base::TimeDelta refill_interval;
114 };
115
116 // A Bucket is how the heuristic portrays an individual item (since quota
117 // limits are per item) and all associated state for an item that needs to
118 // carry through multiple calls to Apply. It "holds" tokens, which are
119 // debited and credited in response to new events involving the item being
120 // being represented. For convenience, instead of actually periodically
121 // refilling buckets they are just 'Reset' on-demand (e.g. when new events
122 // come in). So, a bucket has an expiration to denote it has becomes stale.
123 class Bucket {
124 public:
[email protected]dd1e41a2009-12-04 04:16:22125 Bucket() : num_tokens_(0) {}
[email protected]d13950e2009-12-04 01:43:02126 // Removes a token from this bucket, and returns true if the bucket had
127 // any tokens in the first place.
128 bool DeductToken() { return num_tokens_-- > 0; }
129
130 // Returns true if this bucket has tokens to deduct.
131 bool has_tokens() const { return num_tokens_ > 0; }
132
133 // Reset this bucket to specification (from internal configuration), to be
134 // valid from |start| until the first refill interval elapses and it needs
135 // to be reset again.
136 void Reset(const Config& config, const base::TimeTicks& start);
137
138 // The time at which the token count and next expiration should be reset,
139 // via a call to Reset.
140 const base::TimeTicks& expiration() { return expiration_; }
[email protected]38427a12013-11-09 17:34:20141
[email protected]d13950e2009-12-04 01:43:02142 private:
143 base::TimeTicks expiration_;
avic9cec102015-12-23 00:39:26144 int64_t num_tokens_;
[email protected]d13950e2009-12-04 01:43:02145 DISALLOW_COPY_AND_ASSIGN(Bucket);
146 };
avi14f29742016-11-03 23:39:50147 using BucketList = std::list<Bucket*>;
[email protected]d13950e2009-12-04 01:43:02148
[email protected]d13950e2009-12-04 01:43:02149 // A helper interface to retrieve the bucket corresponding to |args| from
150 // the set of buckets (which is typically stored in the BucketMapper itself)
151 // for this QuotaLimitHeuristic.
152 class BucketMapper {
153 public:
[email protected]dd1e41a2009-12-04 04:16:22154 virtual ~BucketMapper() {}
[email protected]d13950e2009-12-04 01:43:02155 // In most cases, this should simply extract item IDs from the arguments
156 // (e.g for bookmark operations involving an existing item). If a problem
157 // occurs while parsing |args|, the function aborts - buckets may be non-
158 // empty). The expectation is that invalid args and associated errors are
159 // handled by the ExtensionFunction itself so we don't concern ourselves.
[email protected]aeca23f2013-06-21 22:34:41160 virtual void GetBucketsForArgs(const base::ListValue* args,
[email protected]438c97d2010-05-21 23:30:15161 BucketList* buckets) = 0;
[email protected]d13950e2009-12-04 01:43:02162 };
163
[email protected]fd50e7b2011-11-03 09:20:25164 // Maps all calls to the same bucket, regardless of |args|, for this
165 // QuotaLimitHeuristic.
166 class SingletonBucketMapper : public BucketMapper {
167 public:
168 SingletonBucketMapper() {}
dcheng9168b2f2014-10-21 12:38:24169 ~SingletonBucketMapper() override {}
170 void GetBucketsForArgs(const base::ListValue* args,
171 BucketList* buckets) override;
[email protected]fd50e7b2011-11-03 09:20:25172
173 private:
174 Bucket bucket_;
175 DISALLOW_COPY_AND_ASSIGN(SingletonBucketMapper);
176 };
177
[email protected]85231d72012-08-31 09:45:29178 // Ownership of |map| is given to the new QuotaLimitHeuristic.
179 QuotaLimitHeuristic(const Config& config,
180 BucketMapper* map,
181 const std::string& name);
[email protected]2858bbf2010-10-05 23:46:02182 virtual ~QuotaLimitHeuristic();
[email protected]d13950e2009-12-04 01:43:02183
184 // Determines if sufficient quota exists (according to the Apply
185 // implementation of a derived class) to perform an operation with |args|,
186 // based on the history of similar operations with similar arguments (which
187 // is retrieved using the BucketMapper).
[email protected]aeca23f2013-06-21 22:34:41188 bool ApplyToArgs(const base::ListValue* args,
189 const base::TimeTicks& event_time);
[email protected]d13950e2009-12-04 01:43:02190
[email protected]85231d72012-08-31 09:45:29191 // Returns an error formatted according to this heuristic.
192 std::string GetError() const;
193
[email protected]d13950e2009-12-04 01:43:02194 protected:
195 const Config& config() { return config_; }
196
197 // Determine if the new event occurring at |event_time| involving |bucket|
198 // constitutes a quota violation according to this heuristic.
199 virtual bool Apply(Bucket* bucket, const base::TimeTicks& event_time) = 0;
200
201 private:
202 friend class QuotaLimitHeuristicTest;
203
204 const Config config_;
205
[email protected]85231d72012-08-31 09:45:29206 // The mapper used in Map. Cannot be NULL.
dchengf5d241082016-04-21 03:43:11207 std::unique_ptr<BucketMapper> bucket_mapper_;
[email protected]d13950e2009-12-04 01:43:02208
[email protected]85231d72012-08-31 09:45:29209 // The name of the heuristic for formatting error messages.
210 std::string name_;
211
[email protected]d13950e2009-12-04 01:43:02212 DISALLOW_COPY_AND_ASSIGN(QuotaLimitHeuristic);
213};
214
215// A simple per-item heuristic to limit the number of events that can occur in
216// a given period of time; e.g "no more than 100 events in an hour".
[email protected]38427a12013-11-09 17:34:20217class QuotaService::TimedLimit : public QuotaLimitHeuristic {
[email protected]d13950e2009-12-04 01:43:02218 public:
[email protected]85231d72012-08-31 09:45:29219 TimedLimit(const Config& config, BucketMapper* map, const std::string& name)
220 : QuotaLimitHeuristic(config, map, name) {}
dcheng9168b2f2014-10-21 12:38:24221 bool Apply(Bucket* bucket, const base::TimeTicks& event_time) override;
[email protected]d13950e2009-12-04 01:43:02222};
223
[email protected]38427a12013-11-09 17:34:20224} // namespace extensions
225
226#endif // EXTENSIONS_BROWSER_QUOTA_SERVICE_H_