feat: add in-memory rate limiting for login endpoint

Rate-limit POST /admin/login at 10 attempts per 60 seconds per IP.
Stored in Volumen.login_attempts so tests can reset the counter.
This commit is contained in:
2026-06-25 21:33:16 +02:00
parent 2348352fab
commit 140b16b55a
3 changed files with 25 additions and 0 deletions
+17
View File
@@ -13,6 +13,9 @@ module Volumen
# Sinatra application: the public JSON API at /api/volumen and the
# server-rendered admin at /admin.
class Server < Sinatra::Base
MAX_LOGIN_ATTEMPTS = 10
LOGIN_WINDOW = 60 # seconds
configure do
set :show_exceptions, false
set :raise_errors, false
@@ -102,6 +105,7 @@ module Volumen
post "/admin/login" do
check_csrf!
check_login_rate_limit!
user = users.authenticate(params["username"], params["password"])
if user
session[:user] = user.username
@@ -387,6 +391,19 @@ module Volumen
halt(403, "Invalid CSRF token")
end
def check_login_rate_limit!
ip = request.ip.to_s
now = Time.now.to_f
cutoff = now - LOGIN_WINDOW
attempts = (Volumen.login_attempts[ip] ||= []).select { |t| t > cutoff }
if attempts.length >= MAX_LOGIN_ATTEMPTS
halt(429, "Too many login attempts. Please wait and try again.")
end
attempts << now
Volumen.login_attempts[ip] = attempts
end
def render_settings(error: nil, notice: nil)
@error = error
@notice = notice