Documentation
¶
Overview ¶
Package ginadapter integrates the module with gin and the wide-event logger: it runs the net/http middleware of the providers in a gin chain, registers their routes on a gin router, gates routes with authorization checks, and records authentication outcomes on the request's wide event.
It is the only package of the library that imports gin and wide-event-logger. It records on the wide event the wide-event middleware (wide-event-logger's ginadapter.NewMiddleware) put into the request context and never emits one of its own; it records IDs and decisions, never usernames, emails, names, tokens or codes. That middleware redacts every query value itself, so the code and state of OAuth callbacks stay out of your logs. Everything in it is safe for concurrent use.
Example ¶
package main
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"github.com/gin-gonic/gin"
wideevent "github.com/govengers/wide-event-logger"
wideeventgin "github.com/govengers/wide-event-logger/ginadapter"
"github.com/govengers/auth"
"github.com/govengers/auth/authorization"
"github.com/govengers/auth/ginadapter"
)
func main() {
// requireAPI stands in for session.Guard.RequireAPI: it authenticates the request from its session.
requireAPI := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
identity := &auth.Identity{Provider: "password", Subject: request.Header.Get("X-Example-User"), Active: true}
if identity.Subject == "admin-1" {
identity.Roles = []string{auth.RoleAdmin}
}
next.ServeHTTP(writer, request.WithContext(auth.WithIdentity(request.Context(), identity)))
})
}
wideEvents, err := wideeventgin.NewMiddleware(&wideeventgin.Options{Emitter: wideevent.EmitterFunc(func(*wideevent.Event) {})})
if err != nil {
fmt.Println(err)
return
}
engine := gin.New()
engine.Use(wideEvents)
api := engine.Group("/api", ginadapter.Middleware(requireAPI))
api.GET("/reports", ginadapter.Require(authorization.IsAdmin()), func(ginContext *gin.Context) {
ginContext.String(http.StatusOK, "reports")
})
for _, subject := range []string{"admin-1", "user-7"} {
request := httptest.NewRequestWithContext(context.Background(), http.MethodGet, "/api/reports", nil)
request.Header.Set("X-Example-User", subject)
recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, request)
fmt.Println(subject, recorder.Code)
}
}
Output: admin-1 200 user-7 403
Index ¶
- func Middleware(middleware func(http.Handler) http.Handler) gin.HandlerFunc
- func Observe(ctx context.Context, outcome auth.Outcome)
- func RedactQuery(we *wideevent.Event, ginContext *gin.Context)deprecated
- func RegisterRoutes(router gin.IRoutes, routes []auth.Route)
- func Require(check authorization.Check) gin.HandlerFunc
- func RequireMethodPermission(resource string) gin.HandlerFunc
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Middleware ¶
Middleware runs net/http middleware in a gin chain: when it calls its next handler, ginContext.Request becomes the (possibly enriched) request and the rest of the chain runs inside it; otherwise the chain is aborted. Like a net/http chain it wraps once, here, so state the middleware keeps across requests survives. Each request finds its gin chain through its context: a middleware that passes on a request whose context does not derive from the one it got is answered 500. Gin handlers keep writing to ginContext.Writer, so a writer the middleware wraps for its next handler is not seen.
func Observe ¶
Observe is an auth.Observer that records the outcome on the request's wide event: auth.provider, auth.action, auth.result, the client code, reason and error of Err, degraded for a success with Err, the subject and user ID of the identity (the impersonator's subject and the impersonated subject while impersonating), and the request outcome when the outcome decided it. A session.Guard reports a refresh on the same event before the request's authenticate outcome, so a refresh outcome goes under auth.refresh.* instead, without the user ID: no key is set twice. For the same reason a session.Impersonator's outcome, which follows the Guard's, goes under auth.impersonation.*. Without a wide event in ctx it records nothing.
func RedactQuery
deprecated
RedactQuery does nothing.
Deprecated: the wide-event-logger v1 middleware (ginadapter.NewMiddleware) redacts request.query itself: every value becomes "redacted" unless its parameter is in Options.QueryAllowlist. Drop RedactQuery from your pre-enricher; it stays so enrichers that mount or call it keep compiling once they move to wide-event-logger v1.
func RegisterRoutes ¶
RegisterRoutes registers each route with router.Handle(route.Method, route.Path, gin.WrapH(route.Handler)), so gin route templates (and therefore metric labels) are the provider paths. Register on the engine root. Like gin it panics on a path that conflicts with one already registered, which is a wiring mistake at startup.
func Require ¶
func Require(check authorization.Check) gin.HandlerFunc
Require runs check against the request context: allowed continues; refused aborts with auth.ResponseFor (401/403/500) and records authorization.denied / authorization.reason / authorization.error and we.Failure(err). A nil check is a wiring mistake: every request is answered 500.
func RequireMethodPermission ¶
func RequireMethodPermission(resource string) gin.HandlerFunc
RequireMethodPermission is Require(authorization.HasPermission(resource, auth.AccessForMethod(method))) evaluated per request: one gate for a route group where GET, HEAD and OPTIONS need read access on resource and every other method write access. Same recording and responses as Require.
Types ¶
This section is empty.