Class: VectorMCP::Security::AuthenticationRateLimiter
- Inherits:
-
Object
- Object
- VectorMCP::Security::AuthenticationRateLimiter
- Defined in:
- lib/vector_mcp/security/authentication_rate_limiter.rb
Overview
In-process limiter for repeated authentication failures.
Attempts are tracked independently by transport-provided remote address and by a SHA-256 fingerprint of the presented credential. Raw credentials are never retained. Storage is bounded to avoid turning the limiter into a memory-exhaustion primitive.
Constant Summary collapse
- DEFAULT_MAX_ATTEMPTS =
10- DEFAULT_WINDOW_SECONDS =
60- DEFAULT_MAX_ENTRIES =
10_000- CREDENTIAL_HEADERS =
%w[Authorization X-API-Key X-JWT-Token].freeze
- CREDENTIAL_PARAMS =
%w[api_key apikey jwt_token token].freeze
Instance Attribute Summary collapse
-
#max_attempts ⇒ Object
readonly
Returns the value of attribute max_attempts.
-
#max_entries ⇒ Object
readonly
Returns the value of attribute max_entries.
-
#window_seconds ⇒ Object
readonly
Returns the value of attribute window_seconds.
Instance Method Summary collapse
-
#check!(request) ⇒ void
Atomically reserve an attempt, raising when any request identifier is blocked.
-
#initialize(max_attempts: DEFAULT_MAX_ATTEMPTS, window_seconds: DEFAULT_WINDOW_SECONDS, max_entries: DEFAULT_MAX_ENTRIES, clock: nil) ⇒ AuthenticationRateLimiter
constructor
A new instance of AuthenticationRateLimiter.
-
#record_failure(request) ⇒ void
Record an unsuccessful authentication result for the reserved attempt.
-
#record_success(request) ⇒ void
Clear failure history after successful authentication.
-
#status ⇒ Hash
Non-sensitive limiter status.
Constructor Details
#initialize(max_attempts: DEFAULT_MAX_ATTEMPTS, window_seconds: DEFAULT_WINDOW_SECONDS, max_entries: DEFAULT_MAX_ENTRIES, clock: nil) ⇒ AuthenticationRateLimiter
Returns a new instance of AuthenticationRateLimiter.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/vector_mcp/security/authentication_rate_limiter.rb', line 29 def initialize(max_attempts: DEFAULT_MAX_ATTEMPTS, window_seconds: DEFAULT_WINDOW_SECONDS, max_entries: DEFAULT_MAX_ENTRIES, clock: nil) validate_positive_integer!(:max_attempts, max_attempts) validate_positive_number!(:window_seconds, window_seconds) validate_positive_integer!(:max_entries, max_entries) @max_attempts = max_attempts @window_seconds = window_seconds @max_entries = max_entries @clock = clock || -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) } @attempts = {} @mutex = Mutex.new @logger = VectorMCP.logger_for("security.auth_rate_limit") end |
Instance Attribute Details
#max_attempts ⇒ Object (readonly)
Returns the value of attribute max_attempts.
23 24 25 |
# File 'lib/vector_mcp/security/authentication_rate_limiter.rb', line 23 def max_attempts @max_attempts end |
#max_entries ⇒ Object (readonly)
Returns the value of attribute max_entries.
23 24 25 |
# File 'lib/vector_mcp/security/authentication_rate_limiter.rb', line 23 def max_entries @max_entries end |
#window_seconds ⇒ Object (readonly)
Returns the value of attribute window_seconds.
23 24 25 |
# File 'lib/vector_mcp/security/authentication_rate_limiter.rb', line 23 def window_seconds @window_seconds end |
Instance Method Details
#check!(request) ⇒ void
This method returns an undefined value.
Atomically reserve an attempt, raising when any request identifier is blocked.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/vector_mcp/security/authentication_rate_limiter.rb', line 50 def check!(request) context = VectorMCP::RequestContext.coerce(request) current_time = now identifiers = request_identifiers(context) retry_after = @mutex.synchronize do blocked_for = identifiers.filter_map do |identifier| = (identifier, current_time) next unless .length >= @max_attempts .first + @window_seconds - current_time end.max unless blocked_for identifiers.each do |identifier| = (identifier, current_time) << current_time store(identifier, ) end end blocked_for end return unless retry_after seconds = [retry_after.ceil, 1].max log_rate_limit(context, seconds) raise VectorMCP::RateLimitExceededError.new(retry_after: seconds) end |
#record_failure(request) ⇒ void
This method returns an undefined value.
Record an unsuccessful authentication result for the reserved attempt.
80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/vector_mcp/security/authentication_rate_limiter.rb', line 80 def record_failure(request) context = VectorMCP::RequestContext.coerce(request) current_time = now threshold_reached = @mutex.synchronize do request_identifiers(context).filter_map do |identifier| = (identifier, current_time) identifier_scope(identifier) if .length == @max_attempts end end log_failure_threshold(context, threshold_reached) unless threshold_reached.empty? end |
#record_success(request) ⇒ void
This method returns an undefined value.
Clear failure history after successful authentication.
96 97 98 99 |
# File 'lib/vector_mcp/security/authentication_rate_limiter.rb', line 96 def record_success(request) identifiers = request_identifiers(VectorMCP::RequestContext.coerce(request)) @mutex.synchronize { identifiers.each { |identifier| @attempts.delete(identifier) } } end |
#status ⇒ Hash
Returns non-sensitive limiter status.
102 103 104 105 106 107 108 109 110 111 |
# File 'lib/vector_mcp/security/authentication_rate_limiter.rb', line 102 def status tracked_entries = @mutex.synchronize { @attempts.length } { enabled: true, max_attempts: @max_attempts, window_seconds: @window_seconds, max_entries: @max_entries, tracked_entries: tracked_entries } end |