Documentation ¶
Overview ¶
The middleware package defines what a middleware is in trails and a set of basic middlewares.
The available middlewares are: - CORS - CurrentUser - ForceHTTPS - InjectSession - LogRequest - RateLimit - RequestID
Due to the amount of configuration required, middleware does not provide a default middleware chain Instead, the following can be copy-pasted:
vs := middleware.NewVisitors() adpts := []middleware.Adapter{ middleware.RateLimit(vs), middleware.ForceHTTPS(env), middleware.RequestID(requestIDKey), middleware.LogRequest(log), middleware.InjectSession(sessionStore, sessionKey), middleware.CurrentUser(responder, userStore, userKey), }
Index ¶
- func Chain(handler http.Handler, adapters ...Adapter) http.Handler
- func GetIPAddress(hm http.Header) string
- func NoopAdapter(h http.Handler) http.Handler
- func ReportPanic(env string) func(http.HandlerFunc) http.HandlerFunc
- type Adapter
- func CORS(base string) Adapter
- func CurrentUser(d *resp.Responder, storer UserStorer) Adapter
- func ForceHTTPS(env trails.Environment) Adapter
- func InjectIPAddress() Adapter
- func InjectSession(store session.SessionStorer) Adapter
- func LogRequest(ls *slog.Logger) Adapter
- func RateLimit(visitors *Visitors) Adapter
- func RequestID() Adapter
- func RequireAuthed(loginUrl, logoffUrl string) Adapter
- func RequireUnauthed() Adapter
- type AuthorizeApplicator
- type LogRequestRecord
- type User
- type UserStorer
- type Visitor
- type Visitors
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func GetIPAddress ¶
GetIPAddress parses "X-Forward-For" and "X-Real-Ip" headers for the IP address from the request.
GetIPAddress skips addresses from non-public ranges.
func NoopAdapter ¶
NoopAdapter is a pass-through Adapter, often returned by Adapters available in this package when they are misconfigured.
func ReportPanic ¶
func ReportPanic(env string) func(http.HandlerFunc) http.HandlerFunc
ReportPanic encloses the env and returns a function that when called, wraps the passed in http.HandlerFunc in sentryhttp.HandleFunc in order to recover and report panics.
Types ¶
type Adapter ¶
An Adapter enables chaining middlewares together.
func CORS ¶
CORS sets "Access-Control-Allowed" style headers on a response. The handler including this middleware must also handle the http.MethodOptions method and not just the HTTP method it's designed for.
If base is it's zero-value, NoopAdapter returns and this middleware does nothing.
func CurrentUser ¶
func CurrentUser(d *resp.Responder, storer UserStorer) Adapter
CurrentUser uses storer and the session.Session stored in a *http.Request.Context to check the current user has access and then stashes the current user in the *http.Request.Context under the trails.CurrentUserKey.
CurrentUser uses a *resp.Responder to handle cases where a current user cannot be retrieved or does not have access.
In such a case, CurrentUser toggles between *resp.Responder.Redirect or *resp.Responder.Json, choosing the latter if the *http.Request "Accept header MIME type is "application/json".
func ForceHTTPS ¶
func ForceHTTPS(env trails.Environment) Adapter
ForceHTTPS redirects HTTP requests to HTTPS if the environment is not "development".
The "X-Forwarded-Proto" is used to check whether HTTP was requested due to a trails application running behind a proxy.
TODO(dlk): configurable headers to check.
func InjectIPAddress ¶
func InjectIPAddress() Adapter
InjectIPAddress grabs the IP address in the *http.Request.Header and promotes it to *http.Request.Context under trails.IpAddrKey.
func InjectSession ¶
func InjectSession(store session.SessionStorer) Adapter
InjectSession stores the session associated with the *http.Request in *http.Request.Context.
If store is its zero-value, NoopAdapter returns and this middleware does nothing.
func LogRequest ¶
LogRequest logs the a LogRequestRecord using the provided handler.
For the LogRequestRecord.URI, LogRequest masks query params matching these keys with trails.LogMaskVal: - password
If handler is nil, NoopAdapter returns and this middleware does nothing.
func RateLimit ¶
RateLimit encloses the Visitors map and serves the http.Handler
NOTE: cribbed from https://www.alexedwards.net/blog/how-to-rate-limit-http-requests
If we need anything more sophisticated, check https://github.com/didip/tollbooth
func RequestID ¶
func RequestID() Adapter
RequestID adds a UUID to the request context using trails.RequestIDKey.
TODO(dlk): use "X-Request-ID" or similar header for UUID value.
func RequireAuthed ¶ added in v0.3.2
RequireAuthed returns a middleware.Adapter that checks whether a User is authenticated, and requires they be authenticated. When the User is authenticated, then RequireAuthed hands off to the next part of the middleware chain.
Authenticated means a User is set in the request context with the provided key.
When the User is not authenticated, and the request's "Accept" header has "application/json" in it, RequireUnauthed writes 401 to the client. If the request does not have that value in it's header, RequireAuthed redirects to the provided login URL.
The URL originally requested is appended to as a "next" query param when the request method is GET and the endpoint is not the logoff URL.
func RequireUnauthed ¶ added in v0.3.2
func RequireUnauthed() Adapter
RequireUnauthed returns a middleware.Adapter that checks whether a user is authenticated and requires they not be authenticated. When they are not authenticated, RequireUnauthed hands off to the next part of the middleware chain.
Authenticated means a User is set in the request context with the provided key.
When the User is authenticated, and the request's "Accept" header has "application/json" in it, RequireUnauthed writes 400 to the client. If the request does not have that value in it's header, RequireUnauthed redirect to User's HomePath.
type AuthorizeApplicator ¶ added in v0.5.0
type AuthorizeApplicator[T any] struct { // contains filtered or unexported fields }
An AuthorizeApplicator constructs Adapters that apply custom authorization rules for users, as specified by type T.
func NewAuthorizeApplicator ¶ added in v0.5.0
func NewAuthorizeApplicator[T any](d *resp.Responder) AuthorizeApplicator[T]
NewAuthorizeApplicator constructs an AuthorizeApplicator for type T. Apply methods for the constructed AuthorizeApplicator will use the Responder for redirects. Apply methods will use trails.CurrentUserKey to pull a user out of the request Context.
func (AuthorizeApplicator[T]) Apply ¶ added in v0.5.0
func (aa AuthorizeApplicator[T]) Apply(fn func(user T) (string, bool)) Adapter
Apply wraps a custom function validating the authorization of a user, whose type is specified by T.
Apply retrieves the value for the trails.CurrentUserKey from the request Context. Apply should not be used in a situation where the http.Request.Context in some cases stores the requisite value and others does not.
The provided custom function returns either true and an empty string - meaning the user is authorized - or false and a valid URL as a string.
If the custom function returns true, Apply passes the request to the next handler in the middleware stack.
If the custom function returns false, Apply does not pass the request to the next handler in the middleware stack.
Instead, Apply takes one of two actions depending on the "Accept" HTTP header of the request.
- By default, Apply writes 401.
- If "text/html" appears in the "Accept" header, though, Apply sets a "no access" flash on the session and redirects to the URL the custom function returns.
If fn is nil, Apply returns a NoopAdapter.
type LogRequestRecord ¶ added in v0.7.0
type LogRequestRecord struct { BodySize int `json:"bodySize"` Duration int64 `json:"duration"` Host string `json:"host"` ID string `json:"id"` IPAddr string `json:"remoteAddr"` Method string `json:"method"` Path string `json:"path"` Protocol string `json:"protocol"` Referrer string `json:"referrer"` ReqContentLen int `json:"contentLength"` ReqContentType string `json:"contentType"` Scheme string `json:"scheme"` SessionID string `json:"sessionId"` Status int `json:"status"` URI string `json:"uri"` UserAgent string `json:"userAgent"` }
A LogRequestRecord represents the fields that a LogRequest
type UserStorer ¶
UserStorer defines how to retrieve a User by an ID in the context of middleware.
type Visitors ¶
A Visitors maps a Visitor to an IP address.
func NewVisitors ¶
func NewVisitors() *Visitors