middleware

package
v0.8.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 4, 2026 License: AGPL-3.0 Imports: 32 Imported by: 0

README

LLMSafeSpaces API Middlewares

This document provides detailed technical specifications for all middleware components in the LLMSafeSpaces API system. Middlewares are executed in the order they're added to the Gin engine.

Table of Contents

  1. Core Middlewares

  2. Observability

  3. Security

  4. Infrastructure


Core Middlewares

Authentication Middleware

File: auth.go
Purpose: Handles API key/token authentication and user context propagation
Key Features:

  • Multiple token sources (Header, Cookie, Query param)
  • Role-based path exemptions
  • Contextual logging with user ID
  • Token validation via AuthService interface

Configuration (AuthConfig struct):

type AuthConfig struct {
  HeaderName       string   // Authorization header name
  QueryParamName   string   // URL query parameter name
  CookieName       string   // Cookie name
  TokenType        string   // Expected token prefix (e.g., "Bearer")
  SkipPaths        []string // Paths to exclude
  SkipPathPrefixes []string // Path prefixes to exclude
}

Usage:

router.Use(middleware.AuthMiddleware(authService, logger, middleware.AuthConfig{
  TokenType: "APIKey",
  SkipPaths: []string{"/public"},
}))
Authorization Middleware

File: auth.go
Purpose: Implements RBAC and resource-based access control
Key Features:

  • Permission/role requirements
  • Resource ownership checks
  • Integration with AuthService interface

Methods:

  • RequirePermissions(perms ...string)
  • RequireRoles(roles ...string)

Usage:

router.GET("/admin", 
  middleware.RequirePermissions("admin:read"),
  adminHandler,
)
Rate Limiting Middleware

File: rate_limit.go
Purpose: Implements distributed rate limiting
Strategies:

  1. Token Bucket (default)
  2. Fixed Window
  3. Sliding Window

Configuration (RateLimitConfig):

type RateLimitConfig struct {
  Enabled       bool
  DefaultLimit  int           // Requests per window
  DefaultWindow time.Duration // Time window
  BurstSize     int           // Token bucket capacity
  Strategy      string        // "token_bucket", "fixed_window", "sliding_window"
  ExemptRoles   []string      // Bypass roles
  CustomLimits  map[string]int // Per-client overrides
}

Headers:

  • X-RateLimit-Limit
  • X-RateLimit-Remaining
  • X-RateLimit-Reset
Request Validation Middleware

File: validation.go
Purpose: Validates request bodies against struct definitions
Features:

  • Automatic JSON validation
  • Custom validation rules
  • Sensitive data masking
  • Multi-source validation (body, query, params)

Validation Tags:

type Request struct {
  Email    string `json:"email" validate:"required,email"`
  Password string `json:"password" validate:"required,min=8"`
}

Custom Validators:

  • nohtml - Disallow HTML tags
  • iso8601 - Validate datetime format
  • alphanum_space - Only allow alphanumeric + spaces
Error Handling Middleware

File: error_handler.go
Purpose: Centralized error processing and formatting
Features:

  • APIError struct standardization
  • Stack trace management
  • Error response normalization
  • Sensitive data redaction

Error Types:

ErrorTypeAuthentication
ErrorTypeAuthorization
ErrorTypeValidation
ErrorTypeRateLimit
ErrorTypeNotFound
ErrorTypeInternal
Logging Middleware

File: logging.go
Purpose: Structured request/response logging
Features:

  • Request/response body capture
  • Sensitive field masking
  • Size-based truncation
  • Contextual logging (request ID, user ID)

Configuration (LoggingConfig):

type LoggingConfig struct {
  LogRequestBody  bool
  LogResponseBody bool
  MaxBodyLogSize  int         // Truncation threshold
  SensitiveFields []string    // Fields to mask
  SkipPaths       []string    // Excluded paths
}

Observability Middlewares

Metrics Middleware

File: metrics.go
Purpose: Collects API performance metrics
Tracked Metrics:

  • HTTP request duration
  • Response sizes
  • WebSocket connections
  • Code execution stats

Prometheus Metrics:

  • http_requests_total
  • http_request_duration_seconds
  • ws_connections_active
  • code_executions_total
Tracing Middleware

File: tracing.go
Purpose: Distributed tracing context management
Features:

  • Request ID generation/propagation
  • OpenTelemetry integration
  • Contextual logging
  • Latency tracking

Configuration (TracingConfig):

type TracingConfig struct {
  HeaderName         string // Context header
  PropagateHeader    bool   // Forward header
  GenerateIfMissing  bool   // Auto-generate IDs
  UseUUID            bool   // UUID vs random string
  EnableOpenTelemetry bool  // OTel integration
}
Request ID Middleware

File: request_id.go
Purpose: Unique request identification
Features:

  • UUIDv4 generation
  • Header propagation
  • Context storage
  • Validation of incoming IDs

Security Middlewares

CORS Middleware

File: cors.go
Purpose: Cross-Origin Resource Sharing control
Configuration (CORSConfig):

type CORSConfig struct {
  AllowedOrigins    []string
  AllowedMethods    []string
  AllowedHeaders    []string
  ExposedHeaders    []string
  AllowCredentials  bool
  MaxAge            int
  OptionsPassthrough bool // Custom OPTIONS handling
}
Security Headers Middleware

File: security.go
Purpose: HTTP header security hardening
Headers Set:

  • Content-Security-Policy
  • Strict-Transport-Security
  • X-Content-Type-Options
  • X-Frame-Options
  • Permissions-Policy

Configuration (SecurityConfig):

type SecurityConfig struct {
  ContentSecurityPolicy string
  RequireHTTPS         bool
  TrustedProxies       []string
  PermissionsPolicy    string
  Development          bool // Relax restrictions
}
WebSocket Security Middleware

File: security.go
Purpose: Secures WebSocket connections
Features:

  • Origin validation
  • Protocol version enforcement
  • Connection lifecycle tracking
CSP Reporting Middleware

File: security.go
Purpose: Handles Content Security Policy violation reports
Endpoint: /api/v1/csp-report
Logging: Captures full violation details


Infrastructure Middlewares

Recovery Middleware

File: recovery.go
Purpose: Panic recovery and stabilization
Features:

  • Stack trace capture
  • Broken connection detection
  • Custom recovery handlers
  • Error normalization

Configuration (RecoveryConfig):

type RecoveryConfig struct {
  IncludeStackTrace bool // Response visibility
  LogStackTrace     bool // Log visibility
  CustomRecoveryHandler func(*gin.Context, interface{})
}
Middleware Chaining

Key Considerations:

  1. Order-sensitive execution
  2. Context propagation
  3. Error handling hierarchy
  4. Performance impact

Recommended Order:

  1. Recovery
  2. Security Headers
  3. Tracing/Request ID
  4. Logging
  5. Auth
  6. Rate Limiting
  7. Validation
  8. Business Logic

Example Chain:

router.Use(
  middleware.RecoveryMiddleware(logger),
  middleware.SecurityMiddleware(logger),
  middleware.TracingMiddleware(logger),
  middleware.LoggingMiddleware(logger),
  middleware.AuthMiddleware(authService, logger),
  middleware.RateLimitMiddleware(limiter, logger, config),
)

Dependency Graph

graph TD
    A[Request ID] --> B[Logging]
    B --> C[Auth]
    C --> D[Rate Limiting]
    D --> E[Validation]
    E --> F[Business Logic]
    G[Error Handler] --> H[Recovery]
    I[Metrics] --> J[Monitoring]
    K[Tracing] --> L[Observability]

All middlewares integrate with the centralized logger and error handling systems. Security middlewares should be mounted early in the chain, while observability middlewares benefit from being first to capture full request lifecycle data.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AdminGuard

func AdminGuard() gin.HandlerFunc

AdminGuard returns a middleware that restricts access to admin users. Non-admin requests receive 404 (not 403) to avoid revealing route existence.

func AuthMiddleware

func AuthMiddleware(authService apiinterfaces.AuthService, log pkginterfaces.LoggerInterface, config ...AuthConfig) gin.HandlerFunc

AuthMiddleware returns a middleware that handles authentication

func AuthorizationMiddleware

func AuthorizationMiddleware(authService apiinterfaces.AuthService, log pkginterfaces.LoggerInterface) gin.HandlerFunc

AuthorizationMiddleware returns a middleware that handles authorization

func CSPReportingMiddleware

func CSPReportingMiddleware(log interfaces.LoggerInterface) gin.HandlerFunc

CSPReportingMiddleware returns a middleware that handles CSP violation reports

func ErrorHandlerMiddleware

func ErrorHandlerMiddleware(log interfaces.LoggerInterface, config ...ErrorHandlerConfig) gin.HandlerFunc

ErrorHandlerMiddleware returns a middleware that handles errors

func FeatureGuard

func FeatureGuard(reader orgPlanReader, feature string) gin.HandlerFunc

FeatureGuard returns Gin middleware that denies the request when the org identified by ":id" is on a plan that does not include the named feature. Feature names map 1:1 to the cases in billing.IsFeatureAllowed ("policies", "audit", "sso", "custom_credentials").

FeatureGuard MUST run after OrgAdminGuard or OrgMemberGuard so that the caller has already been authenticated; it does not perform its own membership check. It performs a GetOrg lookup to read the plan; for admin-only routes the extra query is acceptable (low request volume).

Unknown feature names are allowed (fail-open) to preserve forward compatibility with new features added in billing.IsFeatureAllowed.

func HandleAPIError

func HandleAPIError(c *gin.Context, err error)

HandleAPIError handles an API error in a handler

func LoggingMiddleware

func LoggingMiddleware(log interfaces.LoggerInterface, config ...LoggingConfig) gin.HandlerFunc

func MetricsMiddleware

func MetricsMiddleware(metricsService interfaces.MetricsService) gin.HandlerFunc

MetricsMiddleware returns a middleware that collects metrics

func OrgAdminGuard

func OrgAdminGuard(store orgMemberChecker) gin.HandlerFunc

OrgAdminGuard returns Gin middleware that verifies the caller is an admin (role='admin') of the org identified by ":id". Returns 403 for non-admins and members of soft-deleted orgs.

func OrgMemberGuard

func OrgMemberGuard(store orgMemberChecker) gin.HandlerFunc

OrgMemberGuard returns Gin middleware that verifies the caller is a member of the org identified by the ":id" path parameter. Returns 403 for unauthorized callers (including members of soft-deleted orgs).

func PerRouteRateLimitMiddleware added in v0.4.0

PerRouteRateLimitMiddleware applies stricter rate limits to specific routes on top of the global RateLimitMiddleware. For paths not listed in cfg.Routes, it is a no-op (the global limiter handles them).

Identity resolution mirrors the global middleware: API-key if available (set by AuthMiddleware on authenticated routes), else client IP. Anonymous endpoints like /account/recover always fall back to IP — this matches the threat model (per-source throttling).

The underlying RateLimiterService.Allow(key, rate, burst) keys buckets on `key` alone, so this middleware MUST prefix the key with the route to get bucket isolation between routes (see ratelimit.go:Allow). The prefix is the FULL gin route pattern (e.g. "/api/v1/account/recover"), not the request URL — parameterised routes (/secrets/:id) share one bucket per route, which is the intended behavior (a user scanning IDs is rate-limited as one).

func RecoveryMiddleware

func RecoveryMiddleware(log interfaces.LoggerInterface, config ...RecoveryConfig) gin.HandlerFunc

RecoveryMiddleware returns a middleware that recovers from panics

func RequirePermissions

func RequirePermissions(permissions ...string) gin.HandlerFunc

RequirePermissions returns a middleware that requires specific permissions

func SecurityMiddleware

func SecurityMiddleware(log interfaces.LoggerInterface, config ...SecurityConfig) gin.HandlerFunc

SecurityMiddleware returns a middleware that adds security headers

func TracingMiddleware

func TracingMiddleware(log interfaces.LoggerInterface, config ...TracingConfig) gin.HandlerFunc

TracingMiddleware returns a middleware that adds request tracing

func Turnstile added in v0.2.0

func Turnstile(cfg TurnstileConfig) gin.HandlerFunc

Turnstile returns a middleware that enforces CAPTCHA validation.

func ValidateRequest

func ValidateRequest(c *gin.Context, model interface{}) error

ValidateRequest validates a request body against a model

func ValidationMiddleware

func ValidationMiddleware(log interfaces.LoggerInterface, config ...ValidationConfig) gin.HandlerFunc

ValidationMiddleware returns a middleware that validates request bodies

func WebSocketMetricsMiddleware

func WebSocketMetricsMiddleware(metricsService interfaces.MetricsService) gin.HandlerFunc

WebSocketMetricsMiddleware returns a middleware that tracks WebSocket connections

func WorkspaceAccessMiddleware

func WorkspaceAccessMiddleware(svc workspaceAccessService) gin.HandlerFunc

WorkspaceAccessMiddleware is the single ownership gate for /:id workspace routes (design 0041 D1). It resolves the workspace once, runs the CheckOwnership authorisation (D5 creator-membership + D6 org-admin), and on success stores the metadata in the request context so downstream handlers and service methods can reuse it without a second DB hit.

Error mapping follows verifyOwner semantics exactly: NotFound → 404, Forbidden → 403, Internal/bare errors → 500. The middleware never rewrites an infrastructure failure as 403 — fail-closed here means "deny", not "pretend the user is unauthorized".

func WorkspaceMetaFromContext

func WorkspaceMetaFromContext(c *gin.Context) (meta *types.WorkspaceMetadata, ok bool)

WorkspaceMetaFromContext returns the metadata stored by WorkspaceAccessMiddleware. The ok flag is false when the middleware did not run (e.g. the route is mounted outside an idGroup) — callers must handle that case explicitly rather than relying on a non-nil meta.

The canonical store is c.Request.Context() (set under types.ContextKeyWorkspaceMeta) so the same value is visible to service-layer code reading a plain context.Context. This accessor is kept for handler ergonomics and delegates to types.WorkspaceMetaFromCtx.

Types

type AuthConfig

type AuthConfig struct {
	// HeaderName is the name of the header containing the authentication token
	HeaderName string

	// QueryParamName is the name of the query parameter containing the authentication token
	QueryParamName string

	// CookieName is the name of the cookie containing the authentication token
	CookieName string

	// TokenType is the type of token (e.g., "Bearer")
	TokenType string

	// SkipPaths are paths that should not be authenticated
	SkipPaths []string

	// SkipPathPrefixes are path prefixes that should not be authenticated
	SkipPathPrefixes []string
}

AuthConfig defines configuration for the authentication middleware

func DefaultAuthConfig

func DefaultAuthConfig() AuthConfig

DefaultAuthConfig returns the default authentication configuration

type ErrorHandlerConfig

type ErrorHandlerConfig struct {
	// IncludeStackTrace indicates whether to include stack traces in error responses
	IncludeStackTrace bool

	// LogStackTrace indicates whether to log stack traces
	LogStackTrace bool

	// MaxBodyLogSize is the maximum size of request/response bodies to log
	MaxBodyLogSize int

	// SensitiveFields are JSON fields that should be redacted in request/response bodies
	SensitiveFields []string
}

ErrorHandlerConfig defines configuration for the error handler middleware

func DefaultErrorHandlerConfig

func DefaultErrorHandlerConfig() ErrorHandlerConfig

DefaultErrorHandlerConfig returns the default error handler configuration

type LoggingConfig

type LoggingConfig struct {
	// LogRequestBody indicates whether to log request bodies
	LogRequestBody bool

	// LogResponseBody indicates whether to log response bodies
	LogResponseBody bool

	// MaxBodyLogSize is the maximum size of request/response bodies to log
	MaxBodyLogSize int

	// SensitiveFields are JSON fields that should be redacted in request/response bodies.
	// Field-name matching is exact (case-sensitive). See pkg/utilities/masking.go.
	//
	// G25: "value" is intentionally included. The secrets endpoint carries
	// plaintext credentials in the "value" field; even though /api/v1/secrets/*
	// is in SkipPathPrefixes (defense in depth — bodies never logged at all
	// for that path), other endpoints may also pass through sensitive values
	// in a "value" field (env-var updates, settings updates with a secret
	// subtype, etc.). Masking "value" globally errs on the side of caution;
	// legitimate non-secret uses (e.g. settings PUT {"value":"20Gi"}) become
	// "********" in logs, which is acceptable for log readability.
	SensitiveFields []string

	// SkipPaths are exact paths that should not be logged at all
	// (typical use: liveness/readiness probes that flood logs).
	SkipPaths []string

	// SkipPathPrefixes are URL path prefixes that should not be logged
	// (G25). Prefix matching (not exact) so a single entry like
	// "/api/v1/secrets/" catches every secrets sub-path
	// (/api/v1/secrets/:id, /api/v1/secrets/:id/reveal, etc.). Bodies
	// on these paths can carry plaintext credentials in non-standard
	// fields; the safest policy is to not log them at all.
	SkipPathPrefixes []string
}

func DefaultLoggingConfig

func DefaultLoggingConfig() LoggingConfig

DefaultLoggingConfig returns the default logging configuration

type MeteringMiddleware

type MeteringMiddleware struct {
	// contains filtered or unexported fields
}

func (*MeteringMiddleware) Handler

func (m *MeteringMiddleware) Handler() gin.HandlerFunc

type PerRouteRateLimitConfig added in v0.4.0

type PerRouteRateLimitConfig struct {
	Enabled bool
	Routes  map[string]RouteRateLimit
}

PerRouteRateLimitConfig configures the per-route rate limiter. It is INTENTIONALLY separate from the global RateLimitConfig: the global limiter applies a wide budget across every endpoint; this layer adds STRICTER limits to specific paths that warrant them (G35 — /account/recover; G41 — /secrets/:id/reveal; future endpoints).

The two layers do NOT share buckets: this middleware keys its buckets by "<path>:<hashed-identity>" while the global middleware keys by "<hashed-identity>" alone. This isolation is the whole point — without it, a user could spend 99 of their 100 global requests on /recover before any per-endpoint gate tripped.

type RateLimitConfig

type RateLimitConfig struct {
	Enabled       bool
	DefaultLimit  int
	DefaultWindow time.Duration
	BurstSize     int
	Strategy      string
	ExemptPaths   []string // path prefixes exempt from rate limiting (e.g. SSE endpoints)
	CustomLimits  map[string]int
	CustomBursts  map[string]int
}

func DefaultRateLimitConfig

func DefaultRateLimitConfig() RateLimitConfig

type RecoveryConfig

type RecoveryConfig struct {
	// IncludeStackTrace indicates whether to include stack traces in error responses
	IncludeStackTrace bool

	// LogStackTrace indicates whether to log stack traces
	LogStackTrace bool

	// CustomRecoveryHandler is a custom function to handle recovery
	CustomRecoveryHandler func(*gin.Context, interface{})
}

RecoveryConfig defines configuration for the recovery middleware

func DefaultRecoveryConfig

func DefaultRecoveryConfig() RecoveryConfig

DefaultRecoveryConfig returns the default recovery configuration

type RouteRateLimit added in v0.4.0

type RouteRateLimit struct {
	Limit  int
	Burst  int
	Window time.Duration
}

RouteRateLimit is the per-route limit configuration applied by PerRouteRateLimitMiddleware to a specific path (matched by gin's FullPath, e.g. "/api/v1/account/recover").

Semantics (intentionally correct, unlike the global limiter's pre-existing per-second confusion): `Limit` is the maximum number of requests per `Window` per identity (API-key or IP). The middleware converts to a per-second refill rate internally (`Limit / Window.Seconds()`) so a config of {Limit: 20, Window: 1m} actually enforces 20 per minute, not 20 per second.

type SecurityConfig

type SecurityConfig struct {
	// AllowedOrigins is a list of allowed origins for CORS
	AllowedOrigins []string

	// AllowedMethods is a list of allowed HTTP methods for CORS
	AllowedMethods []string

	// AllowedHeaders is a list of allowed HTTP headers for CORS
	AllowedHeaders []string

	// ExposedHeaders is a list of headers that can be exposed to the client
	ExposedHeaders []string

	// AllowCredentials indicates whether the request can include user credentials
	AllowCredentials bool

	// MaxAge indicates how long the results of a preflight request can be cached
	MaxAge int

	// TrustedProxies is a list of trusted proxy IP addresses
	TrustedProxies []string

	// ContentSecurityPolicy is the Content-Security-Policy header value
	ContentSecurityPolicy string

	// ReferrerPolicy is the Referrer-Policy header value
	ReferrerPolicy string

	// PermissionsPolicy is the Permissions-Policy header value
	PermissionsPolicy string

	// RequireHTTPS indicates whether to require HTTPS
	RequireHTTPS bool

	// AllowHTTPSDowngrade indicates whether to allow HTTPS downgrade in development
	AllowHTTPSDowngrade bool

	// Development indicates whether the application is running in development mode
	Development bool
}

SecurityConfig defines configuration for the security middleware

func DefaultSecurityConfig

func DefaultSecurityConfig() SecurityConfig

DefaultSecurityConfig returns the default security configuration

type TracingConfig

type TracingConfig struct {
	// HeaderName is the name of the header to use for the request ID
	HeaderName string

	// PropagateHeader indicates whether to propagate the request ID in the response header
	PropagateHeader bool

	// GenerateIfMissing indicates whether to generate a request ID if one is not provided
	GenerateIfMissing bool

	// UseUUID indicates whether to use UUID for generated request IDs
	UseUUID bool

	// TracerName is the name of the tracer to use
	TracerName string

	// EnableOpenTelemetry indicates whether to use OpenTelemetry for tracing
	EnableOpenTelemetry bool
}

TracingConfig defines configuration for the tracing middleware

func DefaultTracingConfig

func DefaultTracingConfig() TracingConfig

DefaultTracingConfig returns the default tracing configuration

type TurnstileConfig added in v0.2.0

type TurnstileConfig struct {
	SecretKey string
	VerifyURL string
	// Optional HTTP client override — tests substitute a stub.
	HTTPClient *http.Client
	// Optional logger override — tests use a nop; production wires the
	// service's zap logger.
	Logger *zap.Logger
}

TurnstileConfig is the minimum surface the middleware needs. Zero-value SecretKey or VerifyURL results in a permanently-failing middleware (fail-closed).

type ValidationConfig

type ValidationConfig struct {
	// CustomValidators is a map of custom validation functions
	CustomValidators map[string]validator.Func

	// CustomErrorMessages is a map of custom error messages for validation tags
	CustomErrorMessages map[string]string

	// ValidateQueryParams indicates whether to validate query parameters
	ValidateQueryParams bool

	// ValidatePathParams indicates whether to validate path parameters
	ValidatePathParams bool
}

ValidationConfig defines configuration for the validation middleware

func DefaultValidationConfig

func DefaultValidationConfig() ValidationConfig

DefaultValidationConfig returns the default validation configuration

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL