skill added
This commit is contained in:
+83
-5
@@ -26,6 +26,13 @@
|
||||
(username [last-seen #:mutable] [last-cookie-renewal #:mutable])
|
||||
#:transparent)
|
||||
(struct failures ([attempts #:mutable] [started #:mutable]) #:transparent)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Hold configured users, trusted proxies, and volatile auth state.
|
||||
; pre : Constructor fields contain normalized and parsed internal values.
|
||||
; post : Creating or recognizing a value does not change external state.
|
||||
; result : auth-manager? recognizes values used by the authentication API.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(struct auth-manager
|
||||
(users trusted-proxies session-seconds sessions failed lock)
|
||||
#:transparent)
|
||||
@@ -45,6 +52,12 @@
|
||||
(define failure-window-seconds 300)
|
||||
(define maximum-failures 5)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Create an Argon2id password hash for configuration storage.
|
||||
; pre : Password is a string containing at least twelve characters.
|
||||
; post : No module state is changed.
|
||||
; result : A salted Argon2id hash encoded as a string.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (make-password-hash password)
|
||||
(unless (and (string? password)
|
||||
(>= (string-length password) 12))
|
||||
@@ -56,6 +69,12 @@
|
||||
(string->bytes/utf-8 password)
|
||||
password-parameters))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Verify a password against an encoded Argon2id hash.
|
||||
; pre : Password and encoded are arbitrary values.
|
||||
; post : No module state is changed.
|
||||
; result : #t only when both values are strings and the password matches.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (password-hash-valid? password encoded)
|
||||
(and (string? password)
|
||||
(string? encoded)
|
||||
@@ -135,6 +154,12 @@
|
||||
(string-trim (last (string-split forwarded ",")))
|
||||
peer))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Report whether browser authentication is configured.
|
||||
; pre : Manager is an auth-manager.
|
||||
; post : Manager remains unchanged.
|
||||
; result : #t when at least one configured user can log in, otherwise #f.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (auth-enabled? manager)
|
||||
(positive? (hash-count (auth-manager-users manager))))
|
||||
|
||||
@@ -150,6 +175,14 @@
|
||||
(auth-manager-session-seconds manager))
|
||||
(hash-remove! (auth-manager-sessions manager) token)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Resolve the browser user represented by a request cookie.
|
||||
; pre : Manager is an auth-manager and request is an HTTP request.
|
||||
; post : Expired sessions are removed and a valid session's last-seen time
|
||||
; is updated.
|
||||
; result : "anonymous" when authentication is disabled, the normalized
|
||||
; username for a valid session, or #f when login is required.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (auth-request-user manager request)
|
||||
(cond
|
||||
((not (auth-enabled? manager)) "anonymous")
|
||||
@@ -187,7 +220,16 @@
|
||||
address
|
||||
(failures 1 now))))
|
||||
|
||||
;; Returns a new token, #f for invalid credentials, or 'rate-limited.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Authenticate credentials and start a browser session.
|
||||
; pre : Manager is an auth-manager, request is an HTTP request, and
|
||||
; username and password are strings.
|
||||
; post : A valid login creates a new session; a failed login updates the
|
||||
; rate-limit state for the effective client address.
|
||||
; result : A new opaque token, #f for invalid credentials, or 'rate-limited.
|
||||
; internals: Unknown users follow the same Argon2id verification path as known
|
||||
; users to reduce username-dependent timing differences.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (auth-login! manager request username password)
|
||||
(define address (request-address manager request))
|
||||
(define now (current-seconds))
|
||||
@@ -217,6 +259,12 @@
|
||||
(record-failure! manager address now)
|
||||
#f)))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : End the browser session named by the request cookie.
|
||||
; pre : Manager is an auth-manager and request is an HTTP request.
|
||||
; post : The matching server-side session is removed when it exists.
|
||||
; result : Void.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (auth-logout! manager request)
|
||||
(let ((token (request-session-token request)))
|
||||
(when token
|
||||
@@ -225,6 +273,13 @@
|
||||
(lambda ()
|
||||
(hash-remove! (auth-manager-sessions manager) token))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Encode an authenticated session token as a browser cookie.
|
||||
; pre : Manager is an auth-manager and token is a session token string.
|
||||
; post : Manager remains unchanged.
|
||||
; result : A Secure, HttpOnly, SameSite=Strict Set-Cookie value whose Max-Age
|
||||
; equals the configured session lifetime.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (auth-session-cookie manager token)
|
||||
(string->bytes/utf-8
|
||||
(format
|
||||
@@ -233,9 +288,17 @@
|
||||
token
|
||||
(auth-manager-session-seconds manager))))
|
||||
|
||||
;; Return a refreshed cookie at most once per half session lifetime. The
|
||||
;; server-side inactivity timer is updated on every authenticated request, but
|
||||
;; limiting Set-Cookie avoids rewriting it for every one-second player poll.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Renew an actively used browser cookie at a bounded frequency.
|
||||
; pre : Manager is an auth-manager and request is an HTTP request.
|
||||
; post : Expired sessions are removed. When renewal is due, the session's
|
||||
; last-cookie-renewal time is advanced.
|
||||
; result : A fresh Set-Cookie value after half the configured lifetime has
|
||||
; elapsed, otherwise #f.
|
||||
; internals: The server idle timer moves on every authenticated request, while
|
||||
; this half-life threshold prevents the one-second player poll from
|
||||
; returning Set-Cookie every second.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (auth-renewal-cookie manager request)
|
||||
(and (auth-enabled? manager)
|
||||
(let ((token (request-session-token request))
|
||||
@@ -243,7 +306,7 @@
|
||||
(and token
|
||||
(call-with-semaphore
|
||||
(auth-manager-lock manager)
|
||||
(lambda ()
|
||||
(λ ()
|
||||
(prune-sessions! manager now)
|
||||
(define value
|
||||
(hash-ref (auth-manager-sessions manager) token #f))
|
||||
@@ -257,12 +320,27 @@
|
||||
(set-session-last-cookie-renewal! value now)
|
||||
(auth-session-cookie manager token)))))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Encode deletion of the browser session cookie.
|
||||
; pre : None.
|
||||
; post : No module state is changed.
|
||||
; result : A Secure, HttpOnly, SameSite=Strict Set-Cookie value with Max-Age 0.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (auth-expired-cookie)
|
||||
(string->bytes/utf-8
|
||||
(format
|
||||
"~a=; Path=/; Max-Age=0; Secure; HttpOnly; SameSite=Strict"
|
||||
session-cookie-name)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Create the authentication and session manager used by the server.
|
||||
; pre : User-pairs contains username and Argon2id-hash pairs, trusted proxy
|
||||
; values are IP addresses or CIDR networks, and session-seconds is a
|
||||
; positive exact integer.
|
||||
; post : No external state is changed; session and rate-limit tables start
|
||||
; empty.
|
||||
; result : A new auth-manager with normalized usernames and parsed networks.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (make-auth-manager user-pairs
|
||||
#:trusted-proxies
|
||||
[trusted-proxy-values '("127.0.0.0/8" "::1/128")]
|
||||
|
||||
Reference in New Issue
Block a user