blob: b4079ded5139ebfccc180572e639dfbfdf580325 [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>
21#include <string>
22
[email protected]fd50e7b2011-11-03 09:20:2523#include "base/compiler_specific.h"
[email protected]14c1c232013-06-11 17:52:4424#include "base/containers/hash_tables.h"
avic9cec102015-12-23 00:39:2625#include "base/macros.h"
[email protected]3b63f8f42011-03-28 01:54:1526#include "base/memory/scoped_ptr.h"
[email protected]36296912012-03-20 11:08:4927#include "base/threading/non_thread_safe.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"
31
32class ExtensionFunction;
[email protected]d13950e2009-12-04 01:43:0233
[email protected]ae54db1c2012-07-11 12:33:3534namespace extensions {
[email protected]38427a12013-11-09 17:34:2035class QuotaLimitHeuristic;
[email protected]ae54db1c2012-07-11 12:33:3536
[email protected]38427a12013-11-09 17:34:2037typedef std::list<QuotaLimitHeuristic*> QuotaLimitHeuristics;
38
39// The QuotaService takes care that calls to certain extension
[email protected]36296912012-03-20 11:08:4940// functions do not exceed predefined quotas.
41//
[email protected]aab23102014-02-05 18:57:5542// The QuotaService needs to live entirely on one thread, i.e. be created,
43// called and destroyed on the same thread, due to its use of a RepeatingTimer.
[email protected]b33f0b112014-03-13 17:05:3044// It is not a KeyedService because instances exist on both the UI
[email protected]aab23102014-02-05 18:57:5545// and IO threads.
[email protected]38427a12013-11-09 17:34:2046class QuotaService : public base::NonThreadSafe {
[email protected]d13950e2009-12-04 01:43:0247 public:
48 // Some concrete heuristics (declared below) that ExtensionFunctions can
49 // use to help the service make decisions about quota violations.
50 class TimedLimit;
[email protected]d13950e2009-12-04 01:43:0251
[email protected]38427a12013-11-09 17:34:2052 QuotaService();
53 virtual ~QuotaService();
[email protected]d13950e2009-12-04 01:43:0254
55 // Decide whether the invocation of |function| with argument |args| by the
56 // extension specified by |extension_id| results in a quota limit violation.
[email protected]85231d72012-08-31 09:45:2957 // Returns an error message representing the failure if quota was exceeded,
58 // or empty-string if the request is fine and can proceed.
59 std::string Assess(const std::string& extension_id,
60 ExtensionFunction* function,
[email protected]aeca23f2013-06-21 22:34:4161 const base::ListValue* args,
[email protected]85231d72012-08-31 09:45:2962 const base::TimeTicks& event_time);
63
[email protected]d13950e2009-12-04 01:43:0264 private:
[email protected]36296912012-03-20 11:08:4965 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;
[email protected]d13950e2009-12-04 01:43:0269
kalman2610ea12014-11-05 00:18:1870 // Purge resets all accumulated data as if the service was just created.
71 // Called periodically so we don't consume an unbounded amount of memory
72 // while tracking quota.
[email protected]d13950e2009-12-04 01:43:0273 void Purge();
74 void PurgeFunctionHeuristicsMap(FunctionHeuristicsMap* map);
danakj8c3eb802015-09-24 07:53:0075 base::RepeatingTimer purge_timer_;
[email protected]d13950e2009-12-04 01:43:0276
77 // Our quota tracking state for extensions that have invoked quota limited
78 // functions. Each extension is treated separately, so extension ids are the
79 // key for the mapping. As an extension invokes functions, the map keeps
80 // track of which functions it has invoked and the heuristics for each one.
81 // Each heuristic will be evaluated and ANDed together to get a final answer.
[email protected]36296912012-03-20 11:08:4982 std::map<ExtensionId, FunctionHeuristicsMap> function_heuristics_;
[email protected]d13950e2009-12-04 01:43:0283
[email protected]38427a12013-11-09 17:34:2084 DISALLOW_COPY_AND_ASSIGN(QuotaService);
[email protected]d13950e2009-12-04 01:43:0285};
86
87// A QuotaLimitHeuristic is two things: 1, A heuristic to map extension
88// function arguments to corresponding Buckets for each input arg, and 2) a
89// heuristic for determining if a new event involving a particular item
90// (represented by its Bucket) constitutes a quota violation.
91class QuotaLimitHeuristic {
92 public:
93 // Parameters to configure the amount of tokens allotted to individual
94 // Bucket objects (see Below) and how often they are replenished.
95 struct Config {
96 // The maximum number of tokens a bucket can contain, and is refilled to
97 // every epoch.
avic9cec102015-12-23 00:39:2698 int64_t refill_token_count;
[email protected]d13950e2009-12-04 01:43:0299
100 // Specifies how frequently the bucket is logically refilled with tokens.
101 base::TimeDelta refill_interval;
102 };
103
104 // A Bucket is how the heuristic portrays an individual item (since quota
105 // limits are per item) and all associated state for an item that needs to
106 // carry through multiple calls to Apply. It "holds" tokens, which are
107 // debited and credited in response to new events involving the item being
108 // being represented. For convenience, instead of actually periodically
109 // refilling buckets they are just 'Reset' on-demand (e.g. when new events
110 // come in). So, a bucket has an expiration to denote it has becomes stale.
111 class Bucket {
112 public:
[email protected]dd1e41a2009-12-04 04:16:22113 Bucket() : num_tokens_(0) {}
[email protected]d13950e2009-12-04 01:43:02114 // Removes a token from this bucket, and returns true if the bucket had
115 // any tokens in the first place.
116 bool DeductToken() { return num_tokens_-- > 0; }
117
118 // Returns true if this bucket has tokens to deduct.
119 bool has_tokens() const { return num_tokens_ > 0; }
120
121 // Reset this bucket to specification (from internal configuration), to be
122 // valid from |start| until the first refill interval elapses and it needs
123 // to be reset again.
124 void Reset(const Config& config, const base::TimeTicks& start);
125
126 // The time at which the token count and next expiration should be reset,
127 // via a call to Reset.
128 const base::TimeTicks& expiration() { return expiration_; }
[email protected]38427a12013-11-09 17:34:20129
[email protected]d13950e2009-12-04 01:43:02130 private:
131 base::TimeTicks expiration_;
avic9cec102015-12-23 00:39:26132 int64_t num_tokens_;
[email protected]d13950e2009-12-04 01:43:02133 DISALLOW_COPY_AND_ASSIGN(Bucket);
134 };
135 typedef std::list<Bucket*> BucketList;
136
[email protected]d13950e2009-12-04 01:43:02137 // A helper interface to retrieve the bucket corresponding to |args| from
138 // the set of buckets (which is typically stored in the BucketMapper itself)
139 // for this QuotaLimitHeuristic.
140 class BucketMapper {
141 public:
[email protected]dd1e41a2009-12-04 04:16:22142 virtual ~BucketMapper() {}
[email protected]d13950e2009-12-04 01:43:02143 // In most cases, this should simply extract item IDs from the arguments
144 // (e.g for bookmark operations involving an existing item). If a problem
145 // occurs while parsing |args|, the function aborts - buckets may be non-
146 // empty). The expectation is that invalid args and associated errors are
147 // handled by the ExtensionFunction itself so we don't concern ourselves.
[email protected]aeca23f2013-06-21 22:34:41148 virtual void GetBucketsForArgs(const base::ListValue* args,
[email protected]438c97d2010-05-21 23:30:15149 BucketList* buckets) = 0;
[email protected]d13950e2009-12-04 01:43:02150 };
151
[email protected]fd50e7b2011-11-03 09:20:25152 // Maps all calls to the same bucket, regardless of |args|, for this
153 // QuotaLimitHeuristic.
154 class SingletonBucketMapper : public BucketMapper {
155 public:
156 SingletonBucketMapper() {}
dcheng9168b2f2014-10-21 12:38:24157 ~SingletonBucketMapper() override {}
158 void GetBucketsForArgs(const base::ListValue* args,
159 BucketList* buckets) override;
[email protected]fd50e7b2011-11-03 09:20:25160
161 private:
162 Bucket bucket_;
163 DISALLOW_COPY_AND_ASSIGN(SingletonBucketMapper);
164 };
165
[email protected]85231d72012-08-31 09:45:29166 // Ownership of |map| is given to the new QuotaLimitHeuristic.
167 QuotaLimitHeuristic(const Config& config,
168 BucketMapper* map,
169 const std::string& name);
[email protected]2858bbf2010-10-05 23:46:02170 virtual ~QuotaLimitHeuristic();
[email protected]d13950e2009-12-04 01:43:02171
172 // Determines if sufficient quota exists (according to the Apply
173 // implementation of a derived class) to perform an operation with |args|,
174 // based on the history of similar operations with similar arguments (which
175 // is retrieved using the BucketMapper).
[email protected]aeca23f2013-06-21 22:34:41176 bool ApplyToArgs(const base::ListValue* args,
177 const base::TimeTicks& event_time);
[email protected]d13950e2009-12-04 01:43:02178
[email protected]85231d72012-08-31 09:45:29179 // Returns an error formatted according to this heuristic.
180 std::string GetError() const;
181
[email protected]d13950e2009-12-04 01:43:02182 protected:
183 const Config& config() { return config_; }
184
185 // Determine if the new event occurring at |event_time| involving |bucket|
186 // constitutes a quota violation according to this heuristic.
187 virtual bool Apply(Bucket* bucket, const base::TimeTicks& event_time) = 0;
188
189 private:
190 friend class QuotaLimitHeuristicTest;
191
192 const Config config_;
193
[email protected]85231d72012-08-31 09:45:29194 // The mapper used in Map. Cannot be NULL.
[email protected]d13950e2009-12-04 01:43:02195 scoped_ptr<BucketMapper> bucket_mapper_;
196
[email protected]85231d72012-08-31 09:45:29197 // The name of the heuristic for formatting error messages.
198 std::string name_;
199
[email protected]d13950e2009-12-04 01:43:02200 DISALLOW_COPY_AND_ASSIGN(QuotaLimitHeuristic);
201};
202
203// A simple per-item heuristic to limit the number of events that can occur in
204// a given period of time; e.g "no more than 100 events in an hour".
[email protected]38427a12013-11-09 17:34:20205class QuotaService::TimedLimit : public QuotaLimitHeuristic {
[email protected]d13950e2009-12-04 01:43:02206 public:
[email protected]85231d72012-08-31 09:45:29207 TimedLimit(const Config& config, BucketMapper* map, const std::string& name)
208 : QuotaLimitHeuristic(config, map, name) {}
dcheng9168b2f2014-10-21 12:38:24209 bool Apply(Bucket* bucket, const base::TimeTicks& event_time) override;
[email protected]d13950e2009-12-04 01:43:02210};
211
[email protected]38427a12013-11-09 17:34:20212} // namespace extensions
213
214#endif // EXTENSIONS_BROWSER_QUOTA_SERVICE_H_