1 // Copyright 2013 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.
5 // The QuotaService uses heuristics to limit abusive requests
6 // 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.
14 #ifndef EXTENSIONS_BROWSER_QUOTA_SERVICE_H_
15 #define EXTENSIONS_BROWSER_QUOTA_SERVICE_H_
21 #include "base/compiler_specific.h"
22 #include "base/containers/hash_tables.h"
23 #include "base/memory/scoped_ptr.h"
24 #include "base/threading/non_thread_safe.h"
25 #include "base/time/time.h"
26 #include "base/timer/timer.h"
27 #include "base/values.h"
29 class ExtensionFunction
;
31 namespace extensions
{
32 class QuotaLimitHeuristic
;
33 class TestResetQuotaFunction
;
35 typedef std::list
<QuotaLimitHeuristic
*> QuotaLimitHeuristics
;
37 // The QuotaService takes care that calls to certain extension
38 // functions do not exceed predefined quotas.
40 // The QuotaService needs to live entirely on one thread, i.e. be created,
41 // called and destroyed on the same thread, due to its use of a RepeatingTimer.
42 // It is not a BrowserContextKeyedService because instances exist on both the UI
44 class QuotaService
: public base::NonThreadSafe
{
46 // Some concrete heuristics (declared below) that ExtensionFunctions can
47 // use to help the service make decisions about quota violations.
52 virtual ~QuotaService();
54 // Decide whether the invocation of |function| with argument |args| by the
55 // extension specified by |extension_id| results in a quota limit violation.
56 // Returns an error message representing the failure if quota was exceeded,
57 // or empty-string if the request is fine and can proceed.
58 std::string
Assess(const std::string
& extension_id
,
59 ExtensionFunction
* function
,
60 const base::ListValue
* args
,
61 const base::TimeTicks
& event_time
);
64 friend class extensions::TestResetQuotaFunction
;
65 typedef std::string ExtensionId
;
66 typedef std::string FunctionName
;
67 // All QuotaLimitHeuristic instances in this map are owned by us.
68 typedef std::map
<FunctionName
, QuotaLimitHeuristics
> FunctionHeuristicsMap
;
70 // Purge resets all accumulated data (except |violation_errors_|) as if the
71 // service was just created. Called periodically so we don't consume an
72 // unbounded amount of memory while tracking quota. Yes, this could mean an
73 // extension gets away with murder if it is timed right, but the extensions
74 // we are trying to limit are ones that consistently violate, so we'll
75 // converge to the correct set.
77 void PurgeFunctionHeuristicsMap(FunctionHeuristicsMap
* map
);
78 base::RepeatingTimer
<QuotaService
> purge_timer_
;
80 // Our quota tracking state for extensions that have invoked quota limited
81 // functions. Each extension is treated separately, so extension ids are the
82 // key for the mapping. As an extension invokes functions, the map keeps
83 // track of which functions it has invoked and the heuristics for each one.
84 // Each heuristic will be evaluated and ANDed together to get a final answer.
85 std::map
<ExtensionId
, FunctionHeuristicsMap
> function_heuristics_
;
87 // For now, as soon as an extension violates quota, we don't allow it to
88 // make any more requests to quota limited functions. This provides a quick
89 // lookup for these extensions that is only stored in memory.
90 typedef std::map
<std::string
, std::string
> ViolationErrorMap
;
91 ViolationErrorMap violation_errors_
;
93 DISALLOW_COPY_AND_ASSIGN(QuotaService
);
96 // A QuotaLimitHeuristic is two things: 1, A heuristic to map extension
97 // function arguments to corresponding Buckets for each input arg, and 2) a
98 // heuristic for determining if a new event involving a particular item
99 // (represented by its Bucket) constitutes a quota violation.
100 class QuotaLimitHeuristic
{
102 // Parameters to configure the amount of tokens allotted to individual
103 // Bucket objects (see Below) and how often they are replenished.
105 // The maximum number of tokens a bucket can contain, and is refilled to
107 int64 refill_token_count
;
109 // Specifies how frequently the bucket is logically refilled with tokens.
110 base::TimeDelta refill_interval
;
113 // A Bucket is how the heuristic portrays an individual item (since quota
114 // limits are per item) and all associated state for an item that needs to
115 // carry through multiple calls to Apply. It "holds" tokens, which are
116 // debited and credited in response to new events involving the item being
117 // being represented. For convenience, instead of actually periodically
118 // refilling buckets they are just 'Reset' on-demand (e.g. when new events
119 // come in). So, a bucket has an expiration to denote it has becomes stale.
122 Bucket() : num_tokens_(0) {}
123 // Removes a token from this bucket, and returns true if the bucket had
124 // any tokens in the first place.
125 bool DeductToken() { return num_tokens_
-- > 0; }
127 // Returns true if this bucket has tokens to deduct.
128 bool has_tokens() const { return num_tokens_
> 0; }
130 // Reset this bucket to specification (from internal configuration), to be
131 // valid from |start| until the first refill interval elapses and it needs
132 // to be reset again.
133 void Reset(const Config
& config
, const base::TimeTicks
& start
);
135 // The time at which the token count and next expiration should be reset,
136 // via a call to Reset.
137 const base::TimeTicks
& expiration() { return expiration_
; }
140 base::TimeTicks expiration_
;
142 DISALLOW_COPY_AND_ASSIGN(Bucket
);
144 typedef std::list
<Bucket
*> BucketList
;
146 // A helper interface to retrieve the bucket corresponding to |args| from
147 // the set of buckets (which is typically stored in the BucketMapper itself)
148 // for this QuotaLimitHeuristic.
151 virtual ~BucketMapper() {}
152 // In most cases, this should simply extract item IDs from the arguments
153 // (e.g for bookmark operations involving an existing item). If a problem
154 // occurs while parsing |args|, the function aborts - buckets may be non-
155 // empty). The expectation is that invalid args and associated errors are
156 // handled by the ExtensionFunction itself so we don't concern ourselves.
157 virtual void GetBucketsForArgs(const base::ListValue
* args
,
158 BucketList
* buckets
) = 0;
161 // Maps all calls to the same bucket, regardless of |args|, for this
162 // QuotaLimitHeuristic.
163 class SingletonBucketMapper
: public BucketMapper
{
165 SingletonBucketMapper() {}
166 virtual ~SingletonBucketMapper() {}
167 virtual void GetBucketsForArgs(const base::ListValue
* args
,
168 BucketList
* buckets
) OVERRIDE
;
172 DISALLOW_COPY_AND_ASSIGN(SingletonBucketMapper
);
175 // Ownership of |map| is given to the new QuotaLimitHeuristic.
176 QuotaLimitHeuristic(const Config
& config
,
178 const std::string
& name
);
179 virtual ~QuotaLimitHeuristic();
181 // Determines if sufficient quota exists (according to the Apply
182 // implementation of a derived class) to perform an operation with |args|,
183 // based on the history of similar operations with similar arguments (which
184 // is retrieved using the BucketMapper).
185 bool ApplyToArgs(const base::ListValue
* args
,
186 const base::TimeTicks
& event_time
);
188 // Returns an error formatted according to this heuristic.
189 std::string
GetError() const;
192 const Config
& config() { return config_
; }
194 // Determine if the new event occurring at |event_time| involving |bucket|
195 // constitutes a quota violation according to this heuristic.
196 virtual bool Apply(Bucket
* bucket
, const base::TimeTicks
& event_time
) = 0;
199 friend class QuotaLimitHeuristicTest
;
201 const Config config_
;
203 // The mapper used in Map. Cannot be NULL.
204 scoped_ptr
<BucketMapper
> bucket_mapper_
;
206 // The name of the heuristic for formatting error messages.
209 DISALLOW_COPY_AND_ASSIGN(QuotaLimitHeuristic
);
212 // A simple per-item heuristic to limit the number of events that can occur in
213 // a given period of time; e.g "no more than 100 events in an hour".
214 class QuotaService::TimedLimit
: public QuotaLimitHeuristic
{
216 TimedLimit(const Config
& config
, BucketMapper
* map
, const std::string
& name
)
217 : QuotaLimitHeuristic(config
, map
, name
) {}
218 virtual bool Apply(Bucket
* bucket
,
219 const base::TimeTicks
& event_time
) OVERRIDE
;
222 // A per-item heuristic to limit the number of events that can occur in a
223 // period of time over a sustained longer interval. E.g "no more than two
224 // events per minute, sustained over 10 minutes".
225 class QuotaService::SustainedLimit
: public QuotaLimitHeuristic
{
227 SustainedLimit(const base::TimeDelta
& sustain
,
228 const Config
& config
,
230 const std::string
& name
);
231 virtual bool Apply(Bucket
* bucket
,
232 const base::TimeTicks
& event_time
) OVERRIDE
;
235 // Specifies how long exhaustion of buckets is allowed to continue before
237 const int64 repeat_exhaustion_allowance_
;
238 int64 num_available_repeat_exhaustions_
;
241 } // namespace extensions
243 #endif // EXTENSIONS_BROWSER_QUOTA_SERVICE_H_