rate-limit 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b73af03f6ea918dd965b1f1d89b8f7131e1fbba2b94166a397c100510c4a426
4
- data.tar.gz: fcd12f564f38c214a088032b9ee912337dac4a566a1cd97c39256180e2e8e8a5
3
+ metadata.gz: b0f1786c2b5fde684e916bcc8733929b7d0d35cdc2807d8f511190f179019611
4
+ data.tar.gz: c216ce4a4f8e2e479854b61b895c87d226cceba9f8a3d6fb476852709a308ed2
5
5
  SHA512:
6
- metadata.gz: 2a9e190cda1984c96ffd6d93751e56a323d783778c170a492d0b67b151d4a85ef83c88907526f48dc0cc574bdb19880a09667f2472b4e5873d54262e8b7dd976
7
- data.tar.gz: b0529be97de2244366f3ac99f6192e7e649ff8574e15b44f51e2d56030dbcb9163238a1a03d77ed3b49d67c5669cba865c674216aa83dfe73877ab837aae02dd
6
+ metadata.gz: 25bbef68dd61abd1cb2353367af910ee5d61d2ce9f1408c66a36a2741abab62bc9b3c212552e01187c5a95121c611eeb9b16232b1b1bdcc4e486fcdc780b85e0
7
+ data.tar.gz: 5232a6c6f559c5161576b1f54d27a17b590c3b2d25376f8b6f0011d725dbf029bbc7b75e78d4afc6babeaea3a0acac4b9c3b643738f4e9730fbc5105241f208c
data/CHANGELOG.md CHANGED
@@ -7,14 +7,40 @@ and this project adheres to [Semantic Versioning].
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [v0.1.0] - 2022-09-16
10
+ ## [v0.2.0] - 2022-10-05
11
+
12
+ ### Added
13
+
14
+ - added `RateLimit::Errors::LimitExceededError#result`
15
+ - added `Worker#raise_errors`
16
+ - added `Worker#only_failures`
17
+
18
+ ### Changed
19
+
20
+ - changed `RateLimit::Result.initialize`
21
+
22
+ ### Removed
23
+
24
+ - removed `RateLimit::Result#namespace`
25
+ - removed `RateLimit::Worker#namespace`
26
+ - removed `RateLimit::Errors::LimitExceededError#namespace`
27
+ - removed `RateLimit::Errors::LimitExceededError#worker`
28
+ - removed `RateLimit.throttle_with_block!` and `Worker#throttle_with_block!` in favour of `.throttle` with `{ raise_errors: true }` option
29
+ - removed `RateLimit.throttle_only_failures_with_block!` and `Worker#throttle_only_failures_with_block!` in favour of `.throttle` with `{ only_failures: true }` option
30
+
31
+
32
+ ### Fixed
33
+
34
+ - https://github.com/catawiki/rate-limit/issues/16 String Values are not respected
35
+
36
+ ## [v0.1.0] - 2022-09-19
11
37
 
12
38
 
13
39
  ### Added
14
40
 
15
- - [https://github.com/catawiki/rate-limit/pull/11] `RateLimit::Result` class
16
- - [https://github.com/catawiki/rate-limit/pull/12] `RateLimit::Worker` class
17
- - [https://github.com/catawiki/rate-limit/pull/13] `RateLimit::Config#on_success` and `RateLimit::Config#on_failure`
41
+ - https://github.com/catawiki/rate-limit/pull/11 `RateLimit::Result` class
42
+ - https://github.com/catawiki/rate-limit/pull/12 `RateLimit::Worker` class
43
+ - https://github.com/catawiki/rate-limit/pull/13 `RateLimit::Config#on_success` and `RateLimit::Config#on_failure`
18
44
 
19
45
  ### Changed
20
46
 
@@ -26,8 +52,8 @@ and this project adheres to [Semantic Versioning].
26
52
 
27
53
  ### Fixed
28
54
 
29
- - [https://github.com/catawiki/rate-limit/issues/7] Symbol topic names does not load the correct limits
30
- - [https://github.com/catawiki/rate-limit/issues/6] Main Module (RateLimit) fails to autoload
55
+ - https://github.com/catawiki/rate-limit/issues/7 Symbol topic names does not load the correct limits
56
+ - https://github.com/catawiki/rate-limit/issues/6 Main Module (RateLimit) fails to autoload
31
57
 
32
58
 
33
59
  ## v0.0.1 - 2022-09-09
@@ -40,4 +66,5 @@ and this project adheres to [Semantic Versioning].
40
66
  <!-- versions -->
41
67
 
42
68
  [Unreleased]: https://github.com/catawiki/rate-limit/compare/v0.1.0...HEAD
43
- [v0.1.0]: https://github.com/catawiki/rate-limit/compare/v0.0.1...v0.1.0
69
+ [v0.2.0]: https://github.com/catawiki/rate-limit/compare/v0.1.0...v0.2.0
70
+ [v0.1.0]: https://github.com/catawiki/rate-limit/compare/v0.0.1...v0.1.0
data/README.md CHANGED
@@ -3,12 +3,12 @@
3
3
 
4
4
  Protect your Ruby apps from bad actors. RateLimit allows you to set permissions as to whether certain number of feature calls are valid or not for a specific entity (user, phone number, email address, etc...).
5
5
 
6
- This gem mainly provides brute-force protection by throttling attepmts for a specific entity id (i.e user_id). However it could also be used to throttle based on ip address (we recommend that you consider using [Rack::Attack](https://github.com/rack/rack-attack) for more optimized ip throttling)
6
+ This gem mainly provides brute-force protection by throttling attempts for a specific entity id (i.e user_id). However it could also be used to throttle based on ip address (we recommend that you consider using [Rack::Attack](https://github.com/rack/rack-attack) for more optimized ip throttling)
7
7
 
8
8
  #### Common Use Cases
9
9
  * [Login] Brute-force attempts for a spefic account
10
- * [SMS Spam] Brute-force attempts for requesting Phone Verification SMS for a spefic user_id
11
- * [SMS Spam] Brute-force attempts for requesting Phone Verification SMS for a spefic phone_number
10
+ * [SMS Spam] Brute-force attempts for requesting Phone Verification SMS for a specific user_id
11
+ * [SMS Spam] Brute-force attempts for requesting Phone Verification SMS for a specific phone_number
12
12
  * [Verifications] Brute-force attempts for entering verification codes
13
13
  * [Redeem] Brute-force attempts to redeem voucher codes from a specific account
14
14
 
@@ -28,85 +28,32 @@ Or install it yourself as:
28
28
 
29
29
  $ gem install rate-limit
30
30
 
31
- ## Usage
31
+ ## Basic Usage
32
32
 
33
- #### Basic `RateLimit.throttle`
33
+ ### [`RateLimit.throttle`](https://github.com/catawiki/rate-limit/wiki/Throttling)
34
+ The throttle method expects the following options
34
35
 
35
- ```ruby
36
- result = RateLimit.throttle(topic: :login, namespace: :user_id, value: id)
36
+ | Option | Description | Examples |
37
+ | ---------------- | ------------------------------------------------------------------------------- | ------------------------------------- |
38
+ | topic | The topic you would like to throttle | "login", "send_sms", "redeem_voucher" |
39
+ | value | The identifier of the unique entity that is throttled based on the topic limits | user_id, phone_number, voucher_code |
37
40
 
38
- if result.success?
39
- # Do something
40
- end
41
- ```
42
- or
43
41
 
42
+ The `throttle` method checks if the given value did exceed the defined limits for the given topic. If the limit is exceeded then it returns [RateLimit::Result](https://github.com/catawiki/rate-limit/wiki/RateLimit::Result) Object, where `result.success?` will be `false`. Otherwise, it increments the attempts counter in the cache and sets `result.success?` to `true`.
43
+
44
+ #### Example
44
45
  ```ruby
45
- result = RateLimit.throttle(topic: :login, namespace: :user_id, value: id)
46
+ result = RateLimit.throttle(topic: :login, value: 123)
46
47
 
47
48
  if result.success?
48
49
  # Do something
49
50
  end
50
51
  ```
51
52
 
52
- #### Basic with exception `RateLimit.throttle!`
53
-
54
- ```ruby
55
- begin
56
- RateLimit.throttle_with_block!(topic: :send_sms, namespace: :user_id, value: id) do
57
- # Logic goes Here
58
- end
59
- rescue RateLimit::Errors::LimitExceededError => e
60
- # Error Handling Logic goes here
61
- e.topic # :login
62
- e.namespace # :user_id
63
- e.value # id
64
- e.threshold # 2
65
- e.interval # 60
66
- end
67
- ```
68
-
69
- #### Advanced
70
-
71
- ```ruby
72
- throttler = RateLimit::Worker.new(topic: :login, namespace: :user_id, value: id)
73
-
74
- begin
75
- throttler.throttle_with_block! do
76
- # Logic goes Here
77
- end
78
- rescue RateLimit::Errors::LimitExceededError => e
79
- # Error Handling Logic goes here
80
- end
81
- ```
82
-
83
- #### Manual
84
53
 
85
- ```ruby
86
- throttler = RateLimit::Worker.new(topic: :login, namespace: :user_id, value: id)
87
-
88
- unless throttler.limit_exceeded?
89
- # Logic goes Here
90
-
91
- throttler.increment_counters
92
- end
93
- ```
94
-
95
- #### Nested throttles
96
-
97
- ```ruby
98
- begin
99
- RateLimit.throttle_with_block!(topic: :send_sms, namespace: :user_id, value: id) do
100
- RateLimit.throttle_with_block!(topic: :send_sms, namespace: :phone_number, value: number) do
101
- # Logic goes Here
102
- end
103
- end
104
- rescue RateLimit::Errors::LimitExceededError => e
105
- # Error Handling Logic goes here
106
- end
107
- ```
54
+ Please check the [Wiki](https://github.com/catawiki/rate-limit/wiki) for advanced throttling and options.
108
55
 
109
- ### Config
56
+ ## [Configuration](https://github.com/catawiki/rate-limit/wiki/Configuration)
110
57
 
111
58
  Customize the configuration by adding the following block to `config/initializers/rate_limit.rb`
112
59
 
@@ -119,11 +66,11 @@ RateLimit.configure do |config|
119
66
  config.limits_file_path = 'config/rate-limit.yml'
120
67
  config.on_success = proc { |result|
121
68
  # Success Logic Goes HERE
122
- # result.topic, result.namespace, result.value
69
+ # result.topic, result.value
123
70
  }
124
71
  config.on_failure = proc { |result|
125
72
  # Failure Logic Goes HERE
126
- # result.topic, result.namespace, result.value, result.threshold, result.interval
73
+ # result.topic, result.value, result.threshold, result.interval
127
74
  }
128
75
  end
129
76
  ```
@@ -4,21 +4,7 @@ module RateLimit
4
4
  module Base
5
5
  def throttle(**args)
6
6
  worker = Worker.new(**args)
7
- worker.throttle
8
- worker.result
9
- end
10
-
11
- def throttle_with_block!(**args, &block)
12
- worker = Worker.new(**args)
13
-
14
- worker.throttle_with_block!(&block)
15
- worker.result
16
- end
17
-
18
- def throttle_only_failures_with_block!(**args, &block)
19
- worker = Worker.new(**args)
20
-
21
- worker.throttle_only_failures_with_block!(&block)
7
+ worker.throttle { yield if block_given? }
22
8
  worker.result
23
9
  end
24
10
 
@@ -3,18 +3,18 @@
3
3
  module RateLimit
4
4
  module Errors
5
5
  class LimitExceededError < StandardError
6
- attr_reader :window
6
+ attr_reader :result
7
7
 
8
- delegate :topic, :namespace, :value, :threshold, :interval, to: :window
8
+ delegate :topic, :value, :threshold, :interval, to: :result
9
9
 
10
- def initialize(window)
11
- @window = window
10
+ def initialize(result)
11
+ @result = result
12
12
 
13
13
  super(custom_message)
14
14
  end
15
15
 
16
16
  def custom_message
17
- "#{topic}: #{namespace} has exceeded #{threshold} in #{interval} seconds"
17
+ "#{result.topic}: has exceeded #{result.threshold} in #{result.interval} seconds"
18
18
  end
19
19
  end
20
20
  end
@@ -3,20 +3,26 @@
3
3
  module RateLimit
4
4
  class Result
5
5
  # Attributes
6
- attr_accessor :topic, :namespace, :value, :threshold, :interval
6
+ attr_accessor :topic, :value, :threshold, :interval
7
7
 
8
8
  # Methods
9
- def initialize(worker, success)
10
- @topic = worker.topic
11
- @namespace = worker.namespace
12
- @value = worker.value
13
- @threshold = worker.exceeded_window&.threshold
14
- @interval = worker.exceeded_window&.interval
15
- @success = success
9
+ def initialize(topic:, value:)
10
+ @topic = topic
11
+ @value = value
16
12
  end
17
13
 
18
14
  def success?
19
15
  @success
20
16
  end
17
+
18
+ def success!
19
+ @success = true
20
+ end
21
+
22
+ def failure!(worker)
23
+ @success = false
24
+ @threshold = worker.exceeded_window&.threshold
25
+ @interval = worker.exceeded_window&.interval
26
+ end
21
27
  end
22
28
  end
@@ -5,29 +5,12 @@ module RateLimit
5
5
  def throttle
6
6
  return failure! if reloaded_limit_exceeded?
7
7
 
8
- success!
9
- end
10
-
11
- def throttle_with_block!
12
- if reloaded_limit_exceeded?
13
- failure!
14
- raise Errors::LimitExceededError, exceeded_window
15
- end
16
-
17
- yield
18
-
19
- success!
20
- end
21
-
22
- def throttle_only_failures_with_block!
23
- return failure! if reloaded_limit_exceeded?
8
+ yield if block_given?
24
9
 
25
- begin
26
- yield
27
- rescue StandardError => e
28
- success!
29
- raise e
30
- end
10
+ return success! unless only_failures
11
+ rescue StandardError => e
12
+ success! unless e.is_a?(Errors::LimitExceededError)
13
+ raise e
31
14
  end
32
15
  end
33
16
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RateLimit
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
@@ -4,7 +4,7 @@ module RateLimit
4
4
  class Window
5
5
  attr_accessor :worker, :limit
6
6
 
7
- delegate :topic, :namespace, :value, to: :worker
7
+ delegate :topic, :value, to: :worker
8
8
  delegate :threshold, :interval, to: :limit
9
9
 
10
10
  def initialize(worker, limit)
@@ -13,7 +13,7 @@ module RateLimit
13
13
  end
14
14
 
15
15
  def key
16
- @key ||= [topic, namespace, value, interval].join(':')
16
+ @key ||= [topic, value, interval].join(':')
17
17
  end
18
18
 
19
19
  def cached_counter
@@ -4,13 +4,15 @@ module RateLimit
4
4
  class Worker
5
5
  include Throttler
6
6
 
7
- attr_accessor :topic, :namespace, :value, :limits, :windows, :exceeded_window, :result
8
-
9
- def initialize(topic:, value:, namespace: nil)
10
- @topic = topic.to_s
11
- @value = value.to_i
12
- @namespace = namespace&.to_s
13
- @windows = Window.find_all(worker: self, topic: @topic)
7
+ attr_accessor :topic, :value, :limits, :windows, :exceeded_window, :result, :raise_errors, :only_failures
8
+
9
+ def initialize(topic:, value:, raise_errors: false, only_failures: false)
10
+ @topic = topic.to_s
11
+ @value = value.to_s
12
+ @windows = Window.find_all(worker: self, topic: @topic)
13
+ @result = Result.new(topic: @topic, value: @value)
14
+ @raise_errors = raise_errors
15
+ @only_failures = only_failures
14
16
  end
15
17
 
16
18
  def increment_cache_counter
@@ -33,13 +35,15 @@ module RateLimit
33
35
 
34
36
  def success!
35
37
  increment_cache_counter
36
- @result = Result.new(self, true)
38
+ result.success!
37
39
  RateLimit.config.success_callback(result)
38
40
  end
39
41
 
40
42
  def failure!
41
- @result = Result.new(self, false)
43
+ result.failure!(self)
42
44
  RateLimit.config.failure_callback(result)
45
+
46
+ raise Errors::LimitExceededError, result if raise_errors
43
47
  end
44
48
  end
45
49
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rate-limit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mohamed Motaweh
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-19 00:00:00.000000000 Z
11
+ date: 2022-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport