Module: VelocityLimiter
- Included in:
- RailsBase::AdminRiskyMfaSend, RailsBase::Authentication::SendLoginMfaToUser, RailsBase::Authentication::SendVerificationEmail, RailsBase::NameChange
- Defined in:
- lib/velocity_limiter.rb
Instance Method Summary collapse
- #_velocity_limiter_params_validator! ⇒ Object
- #cache_delineator ⇒ Object
- #cache_key ⇒ Object
- #velocity_frame ⇒ Object
- #velocity_limit_message(metadata:) ⇒ Object
- #velocity_limit_reached? ⇒ Boolean
- #velocity_max ⇒ Object
- #velocity_max_in_frame ⇒ Object
- #vl_metadata(vl_arr: vl_read) ⇒ Object
- #vl_read ⇒ Object
- #vl_time ⇒ Object
- #vl_write!(write) ⇒ Object
Instance Method Details
#_velocity_limiter_params_validator! ⇒ Object
21 22 23 24 25 26 27 |
# File 'lib/velocity_limiter.rb', line 21 def _velocity_limiter_params_validator! raise "Parent overloaded velocity_max_in_frame. Expected to be a ActiveSupport::Duration" unless velocity_max_in_frame.is_a? ActiveSupport::Duration raise "Parent overloaded velocity_frame. Expected to be a ActiveSupport::Duration" unless velocity_frame.is_a? ActiveSupport::Duration raise "Parent overloaded velocity_max. Expected to be a Integer" unless velocity_max.is_a? Integer raise "Parent overloaded velocity_max. Expected to be a Integer greater than 1" if velocity_max < 1 raise "Parent overloaded cache_key. Expected to be a String" unless cache_key.is_a? String end |
#cache_delineator ⇒ Object
42 43 44 |
# File 'lib/velocity_limiter.rb', line 42 def cache_delineator ',' end |
#cache_key ⇒ Object
93 94 95 |
# File 'lib/velocity_limiter.rb', line 93 def cache_key raise "cache_key must be defined in the parent class" end |
#velocity_frame ⇒ Object
35 36 |
# File 'lib/velocity_limiter.rb', line 35 def velocity_frame end |
#velocity_limit_message(metadata:) ⇒ Object
38 39 40 |
# File 'lib/velocity_limiter.rb', line 38 def (metadata:) "Velocity limit reached for SMS verification. You may try again in #{[:to_words]}" end |
#velocity_limit_reached? ⇒ Boolean
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# File 'lib/velocity_limiter.rb', line 2 def velocity_limit_reached? _velocity_limiter_params_validator! = log(level: :info, msg: "#{cache_key} has attempted #{self.class.name} #{[:within_attempts_count]} times since #{[:threshold]}") if [:velocity_reached] log(level: :warn, msg: "#{cache_key} has been velocity limited. #{[:within_attempts_count]} attempts since #{[:threshold]}. MAX allowed is #{velocity_max}") log(level: :warn, msg: "#{cache_key} may try again in #{[:to_words]} :: #{[:attempt_again_at]}. Will fully reset at #{[:fully_reset_time]}") msg = (metadata: ) return {reached: true, msg: msg} end vl_write!([:vl_write]) { reached: false } end |
#velocity_max ⇒ Object
32 33 |
# File 'lib/velocity_limiter.rb', line 32 def velocity_max end |
#velocity_max_in_frame ⇒ Object
29 30 |
# File 'lib/velocity_limiter.rb', line 29 def velocity_max_in_frame end |
#vl_metadata(vl_arr: vl_read) ⇒ Object
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/velocity_limiter.rb', line 50 def (vl_arr: vl_read) threshold = vl_time - velocity_max_in_frame within_attempts = vl_arr.select do |time| time >= threshold end attempt_again_at = within_attempts.first ? (within_attempts.first + velocity_max_in_frame) : Time.zone.now obj = {} obj[:vl_write] = [within_attempts, vl_time].flatten obj[:fully_reset_time] = (within_attempts.last || Time.zone.now) + velocity_max_in_frame obj[:attempt_again_at] = attempt_again_at obj[:velocity_reached] = within_attempts.count >= velocity_max obj[:within_attempts_arr] = within_attempts obj[:within_attempts_count] = within_attempts.count obj[:attempts_remaining] = velocity_max - obj[:vl_write].count obj[:threshold] = threshold obj[:velocity_max] = velocity_max obj[:velocity_frame] = velocity_frame obj[:velocity_max_in_frame] = velocity_max_in_frame obj[:to_words] = distance_of_time_in_words(Time.zone.now, attempt_again_at, include_seconds: true) obj end |
#vl_read ⇒ Object
74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/velocity_limiter.rb', line 74 def vl_read json = Rails.cache.fetch(cache_key) || '' begin array = json.split(cache_delineator).map { |time| Time.zone.parse time } rescue StandardError => e log(level: :error, msg: "Failed to parse json strings into time. #{json}") array = [] end log(level: :info, msg: "Read from #{cache_key} :: #{array}") array end |
#vl_time ⇒ Object
46 47 48 |
# File 'lib/velocity_limiter.rb', line 46 def vl_time @vl_time ||= Time.zone.now end |
#vl_write!(write) ⇒ Object
87 88 89 90 91 |
# File 'lib/velocity_limiter.rb', line 87 def vl_write!(write) cache_write = write.map(&:to_s).join(cache_delineator) log(level: :info, msg: "Writing [#{cache_write}] to #{cache_key}") Rails.cache.write(cache_key, cache_write, expires_in: velocity_frame) end |