middleware

package
v0.1.6 Latest Latest
Warning

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

Go to latest
Published: Apr 14, 2026 License: GPL-3.0 Imports: 44 Imported by: 0

Documentation

Overview

Package middleware provides HTTP middleware for the API server. This file implements audit logging middleware for admin API endpoints.

Package middleware provides HTTP middleware for the API server. This file implements admin API key authentication middleware.

Index

Constants

View Source
const (
	AdminUserKey logger.ContextKey = "admin_user"
	AdminIDKey   logger.ContextKey = "admin_id"
	AdminRoleKey logger.ContextKey = "admin_role"
)

Admin auth context keys.

View Source
const (
	UserIDKey                        = logger.ContextKeyUserID
	RoleKey        logger.ContextKey = "role"
	RolesKey       logger.ContextKey = "roles"           // All realm roles
	EmailKey       logger.ContextKey = "email"           // User email
	UsernameKey    logger.ContextKey = "username"        // Preferred username
	TenantIDKey    logger.ContextKey = "tenant_id"       // Multi-tenant: tenant identifier
	TenantRoleKey  logger.ContextKey = "tenant_role"     // Multi-tenant: primary role within tenant
	TenantRolesKey logger.ContextKey = "tenant_roles"    // Multi-tenant: all roles within tenant
	ClaimsKey      logger.ContextKey = "keycloak_claims" // Full Keycloak claims for UserSync

)

Auth-related context keys - use logger.ContextKey for consistency.

View Source
const (
	CampaignRoleKey   logger.ContextKey = "campaign_role"
	CampaignStatusKey logger.ContextKey = "campaign_status"
	CampaignIDKey     logger.ContextKey = "campaign_id"
)

Campaign RBAC context keys.

View Source
const (
	// CSRFTokenCookieName is the name of the cookie storing the CSRF token.
	// This cookie is NOT httpOnly so JavaScript can read it.
	CSRFTokenCookieName = "csrf_token"

	// CSRFHeaderName is the header name where the CSRF token should be sent.
	CSRFHeaderName = "X-CSRF-Token"

	// CSRFTokenLength is the length of the CSRF token in bytes.
	CSRFTokenLength = 32
)
View Source
const (
	// FetchedPermissionsKey stores permissions fetched from Redis/DB (not from JWT).
	FetchedPermissionsKey logger.ContextKey = "fetched_permissions"
	// PermVersionKey stores the current permission version from Redis.
	PermVersionKey logger.ContextKey = "perm_version"
	// PermStaleKey indicates if the JWT permission version is stale.
	PermStaleKey logger.ContextKey = "perm_stale"
)

PermSyncContextKey is a context key for permission sync data.

View Source
const (
	// HeaderPermissionStale is set to "true" when JWT permission version doesn't match Redis.
	HeaderPermissionStale = "X-Permission-Stale"
	// HeaderPermissionVersion contains the current permission version from Redis.
	HeaderPermissionVersion = "X-Permission-Version"
)

Response headers for permission sync.

View Source
const (
	SecurityEventAuthFailure       = "security.auth.failure"
	SecurityEventAgentNotFound     = "security.agent.not_found"
	SecurityEventAPIKeyInvalid     = "security.apikey.invalid"
	SecurityEventAgentInactive     = "security.agent.inactive"
	SecurityEventAgentTypeMismatch = "security.agent.type_mismatch"
	SecurityEventJobAccessDenied   = "security.job.access_denied"
	SecurityEventTokenInvalid      = "security.token.invalid"
)

Security event constants for logging and metrics.

View Source
const (
	TeamIDKey     logger.ContextKey = "team_id"    // shared.ID from URL/database
	TeamSlugKey   logger.ContextKey = "team_slug"  // slug string
	TeamRoleKey   logger.ContextKey = "team_role"  // tenant.Role from membership
	MembershipKey logger.ContextKey = "membership" // *tenant.Membership
)

Tenant-related context keys for database-based multi-tenancy. These are separate from auth.go's JWT-based tenant keys.

View Source
const (
	SessionIDKey         logger.ContextKey = "session_id"
	PermissionsKey       logger.ContextKey = "permissions"
	IsAdminKey           logger.ContextKey = "is_admin"
	AuthProviderKey      logger.ContextKey = "auth_provider"
	LocalClaimsKey       logger.ContextKey = "local_claims"
	TenantMembershipsKey logger.ContextKey = "tenant_memberships"
	AccessibleTenantsKey logger.ContextKey = "accessible_tenants"
)

Additional context keys for local auth.

View Source
const (
	AuthProviderLocal = "local"
	AuthProviderOIDC  = "oidc"
)

AuthProvider values for context.

View Source
const AdminAPIKeyHeader = "X-Admin-API-Key"

AdminAPIKeyHeader is the header name for admin API key authentication.

View Source
const DefaultAccessTokenCookieName = "auth_token"

DefaultAccessTokenCookieName is the default cookie name for access tokens. This should match the frontend's auth.cookieName configuration.

View Source
const DefaultMaxBodySize = 10 << 20 // 10 MB

DefaultMaxBodySize is the default maximum request body size (10MB).

View Source
const DefaultMaxConcurrentRequests = 1000

DefaultMaxConcurrentRequests is the default maximum number of concurrent requests.

View Source
const IngestMaxBodySize = 50 << 20 // 50 MB

IngestMaxBodySize is the maximum request body size for ingest endpoints (50MB).

View Source
const LocalUserKey logger.ContextKey = "local_user"

LocalUserKey is the context key for the local user entity.

View Source
const (
	RequestIDKey = logger.ContextKeyRequestID
)

Use context keys from logger package for consistency.

Variables

View Source
var (

	// SecurityEventsTotal counts security events by type
	SecurityEventsTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "security_events_total",
			Help: "Total number of security events",
		},
		[]string{"event_type"},
	)

	// AuthFailuresTotal counts authentication failures by type
	AuthFailuresTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "auth_failures_total",
			Help: "Total number of authentication failures",
		},
		[]string{"reason"},
	)

	// BannedIPsGauge tracks currently banned IPs
	BannedIPsGauge = promauto.NewGauge(
		prometheus.GaugeOpts{
			Name: "banned_ips_current",
			Help: "Current number of banned IPs due to auth failures",
		},
	)

	// PlatformJobsSubmitted counts platform jobs submitted
	PlatformJobsSubmitted = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "platform_jobs_submitted_total",
			Help: "Total number of platform jobs submitted",
		},
		[]string{"type", "status"},
	)

	// PlatformAgentsActive tracks active platform agents
	PlatformAgentsActive = promauto.NewGauge(
		prometheus.GaugeOpts{
			Name: "platform_agents_active",
			Help: "Current number of active platform agents",
		},
	)

	// JobValidationFailures counts job validation failures by reason
	JobValidationFailures = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "job_validation_failures_total",
			Help: "Total number of job validation failures",
		},
		[]string{"reason"},
	)
)

Functions

func BodyLimit

func BodyLimit(maxBytes int64) func(http.Handler) http.Handler

BodyLimit limits the maximum size of request bodies. If maxBytes is 0, DefaultMaxBodySize is used.

func CORS

func CORS(cfg *config.CORSConfig) func(http.Handler) http.Handler

CORS adds CORS headers based on configuration. Deprecated: Use CORSWithEnvironment for production-safe CORS handling.

func CORSWithEnvironment added in v0.1.2

func CORSWithEnvironment(cfg *config.CORSConfig, appEnv string) func(http.Handler) http.Handler

CORSWithEnvironment creates a CORS middleware that enforces environment-specific rules. In production (APP_ENV=production), wildcard "*" origins are rejected at config validation. This function adds runtime enforcement as a defense-in-depth measure.

func CSRF

func CSRF(cfg CSRFConfig) func(http.Handler) http.Handler

CSRF returns a middleware that validates CSRF tokens using Double Submit Cookie pattern. It compares the token in the cookie with the token sent in the X-CSRF-Token header. Safe methods (GET, HEAD, OPTIONS) are exempt from CSRF validation.

func CSRFOptional

func CSRFOptional(cfg CSRFConfig) func(http.Handler) http.Handler

CSRFOptional is like CSRF but only validates if a CSRF cookie is present. Useful for endpoints that can be called both with and without cookies.

func CampaignRoleResolver added in v0.1.3

func CampaignRoleResolver(roleQuerier CampaignRoleQuerier) func(http.Handler) http.Handler

CampaignRoleResolver creates a middleware that resolves the user's campaign role from the database and stores it in the request context. It uses the {id} URL parameter for the campaign ID. Admin users bypass the DB query entirely.

func ClearCSRFTokenCookie

func ClearCSRFTokenCookie(w http.ResponseWriter, cfg CSRFConfig)

ClearCSRFTokenCookie removes the CSRF token cookie.

func ConcurrencyLimit added in v0.1.2

func ConcurrencyLimit(maxConcurrent int) func(http.Handler) http.Handler

ConcurrencyLimit creates a middleware that limits the number of concurrent requests using a semaphore pattern. When the limit is reached, new requests receive a 503 Service Unavailable response.

A maxConcurrent value of 0 or less disables the limit.

func ContextLogger added in v0.1.2

func ContextLogger(log *logger.Logger) func(http.Handler) http.Handler

ContextLogger injects a request-scoped logger into the request context. The logger is enriched with request_id and user_id (when available). Services can retrieve it via logger.FromContext(ctx) for automatic request correlation in logs.

func Decompress

func Decompress(config *DecompressConfig) func(http.Handler) http.Handler

Decompress middleware decompresses request bodies based on Content-Encoding header. Supports gzip and zstd compression.

This middleware should be placed BEFORE body limit middleware to properly limit the decompressed size, not the compressed size.

Example:

router.Use(middleware.Decompress(nil))
router.Use(middleware.BodyLimit(50 * 1024 * 1024)) // 50MB decompressed limit

func DecompressForIngest

func DecompressForIngest() func(http.Handler) http.Handler

DecompressForIngest is a variant specifically for ingest endpoints. It has a higher size limit (100MB) to accommodate large scan reports. SECURITY: Maintains zipbomb protection with higher limits.

func DistributedRateLimit

func DistributedRateLimit(cfg DistributedRateLimitConfig) func(http.Handler) http.Handler

DistributedRateLimit creates middleware using Redis-backed rate limiting. Essential for production multi-instance deployments where in-memory rate limiting is insufficient.

Example usage:

rateLimiter, _ := redis.NewRateLimiter(client, "api", 100, time.Minute, log)
adapter := redis.NewMiddlewareAdapter(rateLimiter)
router.Use(middleware.DistributedRateLimit(middleware.DistributedRateLimitConfig{
    Limiter: adapter,
    Logger:  log,
}))

func EndpointKeyFunc

func EndpointKeyFunc(r *http.Request) string

EndpointKeyFunc returns a key function that includes the endpoint. Useful for per-endpoint rate limiting.

func ExtractResourceIDFromPath

func ExtractResourceIDFromPath(path, prefix string) string

ExtractResourceIDFromPath extracts a resource ID from the URL path. Useful for DELETE operations where the ID might not be in chi params yet.

func GenerateCSRFToken

func GenerateCSRFToken() (string, error)

GenerateCSRFToken generates a cryptographically secure CSRF token.

func GetAccessibleTenants

func GetAccessibleTenants(ctx context.Context) []string

GetAccessibleTenants extracts the list of tenant IDs the user has access to.

func GetAdminID

func GetAdminID(ctx context.Context) string

GetAdminID extracts the admin ID from context.

func GetAdminRole

func GetAdminRole(ctx context.Context) string

GetAdminRole extracts the admin role from context.

func GetAdminUser

func GetAdminUser(ctx context.Context) *admin.AdminUser

GetAdminUser extracts the admin user from context.

func GetAuthProvider

func GetAuthProvider(ctx context.Context) string

GetAuthProvider extracts the auth provider from context.

func GetCampaignID added in v0.1.3

func GetCampaignID(ctx context.Context) string

GetCampaignID returns the campaign ID from context.

func GetCampaignRole added in v0.1.3

func GetCampaignRole(ctx context.Context) pentest.CampaignRole

GetCampaignRole returns the user's campaign role from context.

func GetCampaignStatus added in v0.1.3

func GetCampaignStatus(ctx context.Context) pentest.CampaignStatus

GetCampaignStatus returns the campaign status from context.

func GetClaims

func GetClaims(ctx context.Context) *keycloak.Claims

GetClaims extracts the full Keycloak claims from context.

func GetCurrentPermVersion

func GetCurrentPermVersion(ctx context.Context) int

GetCurrentPermVersion returns the current permission version from Redis.

func GetEmail

func GetEmail(ctx context.Context) string

GetEmail extracts the user email from context.

func GetFetchedPermissions

func GetFetchedPermissions(ctx context.Context) []string

GetFetchedPermissions returns permissions fetched from Redis/DB.

func GetLocalClaims

func GetLocalClaims(ctx context.Context) *jwt.Claims

GetLocalClaims extracts local JWT claims from context.

func GetLocalUser

func GetLocalUser(ctx context.Context) *user.User

GetLocalUser extracts the local user entity from context.

func GetLocalUserID

func GetLocalUserID(ctx context.Context) shared.ID

GetLocalUserID extracts the local user ID from context. Returns zero ID if no local user is in context.

func GetPermissions

func GetPermissions(ctx context.Context) []string

GetPermissions extracts the permissions from context.

func GetRLSTx

func GetRLSTx(ctx context.Context) *sql.Tx

GetRLSTx retrieves the RLS transaction from context. Returns nil if no transaction is set.

func GetRequestID

func GetRequestID(ctx context.Context) string

GetRequestID extracts the request ID from context.

func GetRole

func GetRole(ctx context.Context) string

GetRole extracts the primary role from context.

func GetRoles

func GetRoles(ctx context.Context) []string

GetRoles extracts all realm roles from context.

func GetSessionID

func GetSessionID(ctx context.Context) string

GetSessionID extracts the session ID from context.

func GetTeamID

func GetTeamID(ctx context.Context) shared.ID

GetTeamID extracts the team ID (shared.ID) from context.

func GetTeamMembership

func GetTeamMembership(ctx context.Context) *tenant.Membership

GetTeamMembership extracts the membership from context.

func GetTeamRole

func GetTeamRole(ctx context.Context) tenant.Role

GetTeamRole extracts the user's role in the team from context.

func GetTeamSlug

func GetTeamSlug(ctx context.Context) string

GetTeamSlug extracts the team slug from context.

func GetTenantID

func GetTenantID(ctx context.Context) string

GetTenantID extracts the tenant ID from context.

func GetTenantIDFromContext

func GetTenantIDFromContext(ctx context.Context) (shared.ID, bool)

GetTenantIDFromContext extracts tenant ID from context. This is a helper that wraps the existing middleware function.

func GetTenantMemberships

func GetTenantMemberships(ctx context.Context) []jwt.TenantMembership

GetTenantMemberships extracts tenant memberships from context.

func GetTenantRole

func GetTenantRole(ctx context.Context) string

GetTenantRole extracts the primary tenant role from context.

func GetTenantRoles

func GetTenantRoles(ctx context.Context) []string

GetTenantRoles extracts all tenant roles from context.

func GetUserID

func GetUserID(ctx context.Context) string

GetUserID extracts the user ID from context.

func GetUserTenantRole

func GetUserTenantRole(ctx context.Context, tenantID string) string

GetUserTenantRole returns the user's role in a specific tenant.

func GetUsername

func GetUsername(ctx context.Context) string

GetUsername extracts the preferred username from context.

func HandleBodyLimitError

func HandleBodyLimitError(w http.ResponseWriter, _ *http.Request)

BodyLimitHandler is an error handler for body limit exceeded. Use this in your error handling middleware to catch http.MaxBytesError.

func HasAnyPermission

func HasAnyPermission(ctx context.Context, permissions ...string) bool

HasAnyPermission checks if the user has any of the specified permissions.

func HasPermission

func HasPermission(ctx context.Context, permission string) bool

HasPermission checks if the user has a specific permission. Owner/Admin (IsAdmin flag in JWT) bypass permission checks - they have almost all permissions. Member/Viewer/Custom roles: permissions fetched from DB (not in JWT). For owner-only operations, use IsOwner() or RequireOwner() middleware.

func HasPermissionFromCache

func HasPermissionFromCache(ctx context.Context, permission string) bool

HasPermissionFromCache checks if user has a permission using fetched permissions. Unlike HasPermission which uses JWT permissions + IsAdmin bypass, this always uses the latest permissions from cache.

NOTE: For backward compatibility, use HasPermission which still works. This function is for explicit cache-based permission checks.

func HasTenantAccess

func HasTenantAccess(ctx context.Context, tenantID string) bool

HasTenantAccess checks if the user has access to a specific tenant.

func HasTenantRole

func HasTenantRole(ctx context.Context, tenantID string, requiredRole string) bool

HasTenantRole checks if user has a specific role (or higher) in a tenant.

func IsAdmin

func IsAdmin(ctx context.Context) bool

IsAdmin checks if the user has admin flag set (owner or admin role). When true, the user bypasses general permission checks. For owner-only operations (team delete, billing), use IsOwner() instead.

func IsOwner

func IsOwner(ctx context.Context) bool

IsOwner checks if the user has the owner role. Use this for owner-only operations like team deletion, billing management.

func IsPermissionStale

func IsPermissionStale(ctx context.Context) bool

IsPermissionStale returns true if the JWT permission version doesn't match Redis.

func IsPlatformAdmin

func IsPlatformAdmin(ctx context.Context) bool

IsPlatformAdmin checks if the user has platform admin privileges. Platform admins can manage platform-wide resources like platform agents. This is typically granted to OpenCTEM staff or system administrators.

Platform admin is determined by: 1. Having "platform_admin" role in RealmAccess (for OIDC/Keycloak) 2. Being the system admin (for local auth - checks specific user IDs or emails)

func Logger

func Logger(log *logger.Logger) func(http.Handler) http.Handler

Logger logs HTTP requests. This is the simple version that logs all requests.

func LoggerWithConfig

func LoggerWithConfig(log *logger.Logger, cfg LoggerConfig) func(http.Handler) http.Handler

LoggerWithConfig logs HTTP requests with configurable behavior. Use this for production to skip health checks and reduce log volume.

func Metrics

func Metrics() func(http.Handler) http.Handler

Metrics returns the Prometheus metrics middleware.

func MustGetAdminUser

func MustGetAdminUser(ctx context.Context) *admin.AdminUser

MustGetAdminUser extracts admin user from context or panics if not found. Use this in handlers protected by Authenticate() middleware.

func MustGetTenantID

func MustGetTenantID(ctx context.Context) string

MustGetTenantID extracts tenant ID from context or panics if not found. Use this in handlers protected by RequireTenant() middleware. Panics indicate a programming error (missing middleware), not a user error.

func OptionalUnifiedAuth

func OptionalUnifiedAuth(cfg UnifiedAuthConfig) func(http.Handler) http.Handler

OptionalUnifiedAuth creates an optional authentication middleware. It extracts claims if present but doesn't require authentication.

func PlatformAdminRLSMiddleware

func PlatformAdminRLSMiddleware(db *sql.DB, log *logger.Logger) func(http.Handler) http.Handler

PlatformAdminRLSMiddleware sets the platform admin bypass for RLS. This middleware should be used for platform admin routes that need cross-tenant access.

Usage:

adminRouter.Use(PlatformAdminAuthMiddleware)
adminRouter.Use(PlatformAdminRLSMiddleware(db, log))
adminRouter.Get("/all-findings", handler.ListAllFindings)

func RLSContextMiddleware

func RLSContextMiddleware(db *sql.DB, log *logger.Logger) func(http.Handler) http.Handler

RLSContextMiddleware sets the PostgreSQL session variable for Row Level Security. This middleware should be applied AFTER authentication middleware.

How it works:

  1. Extracts tenant_id from the authenticated context
  2. Begins a database transaction
  3. Sets `app.current_tenant_id` session variable for RLS policies
  4. Stores the transaction in context for handlers to use
  5. Commits/rollbacks based on response status

Usage in routes:

router.Use(authMiddleware)
router.Use(RLSContextMiddleware(db, log))
router.Get("/findings", handler.ListFindings)  // RLS automatically filters

func RateLimit

func RateLimit(cfg *config.RateLimitConfig, log *logger.Logger) func(http.Handler) http.Handler

RateLimit creates a rate limiting middleware from config. Note: For proper cleanup, use RateLimitWithStop instead.

func RateLimitWithStop

func RateLimitWithStop(cfg *config.RateLimitConfig, log *logger.Logger) (func(http.Handler) http.Handler, func())

RateLimitWithStop creates a rate limiting middleware and returns a stop function. The stop function should be called during graceful shutdown.

func RecordAuthFailure

func RecordAuthFailure(reason string)

RecordAuthFailure increments the auth failure counter.

func RecordJobSubmitted

func RecordJobSubmitted(jobType, status string)

RecordJobSubmitted increments the job submitted counter.

func RecordJobValidationFailure

func RecordJobValidationFailure(reason string)

RecordJobValidationFailure increments the validation failure counter.

func RecordSecurityEvent

func RecordSecurityEvent(eventType string)

RecordSecurityEvent increments the security event counter.

func Recovery

func Recovery(log *logger.Logger) func(http.Handler) http.Handler

Recovery recovers from panics and returns a 500 error. In production, stack traces are omitted from logs to prevent information leakage.

func RecoveryWithConfig

func RecoveryWithConfig(log *logger.Logger, isProduction bool) func(http.Handler) http.Handler

RecoveryWithConfig is like Recovery but accepts a production mode flag. SECURITY: In production, stack traces are omitted to prevent sensitive path/code information from being exposed in logs.

func RequestID

func RequestID() func(http.Handler) http.Handler

RequestID adds a unique request ID to each request.

func Require

func Require(perm permission.Permission) func(http.Handler) http.Handler

Require creates a middleware that requires a specific permission. Uses permission.Permission constants for type safety.

Example:

r.POST("/", middleware.Require(permission.AssetsWrite)(handler))
r.DELETE("/{id}", middleware.Require(permission.AssetsDelete)(handler))

func RequireActiveMembershipFromJWT added in v0.1.6

func RequireActiveMembershipFromJWT(reader MembershipReader) func(http.Handler) http.Handler

RequireActiveMembershipFromJWT is the JWT-claim variant of RequireMembership. It is used by token-based tenant routes (e.g. /api/v1/me/*, /api/v1/notifications, /api/v1/api-keys) where the tenant ID comes from the access-token claim instead of the URL path.

Without this, suspended members with a still-valid JWT could keep hitting JWT-claim-scoped endpoints until their token expires, even though tenant-scoped URL routes are blocked.

Like RequireMembership it accepts the MembershipReader interface so the same Redis-backed cache can serve both URL- and JWT-tenant routes.

Must be used after KeycloakAuth, UserSync, and RequireTenant middleware.

func RequireAdmin

func RequireAdmin() func(http.Handler) http.Handler

RequireAdmin creates a middleware that requires admin access (owner or admin role).

func RequireAll

func RequireAll(perms ...permission.Permission) func(http.Handler) http.Handler

RequireAll creates a middleware that requires all of the specified permissions. Uses permission.Permission constants for type safety.

Example:

r.POST("/", middleware.RequireAll(permission.AssetsWrite, permission.RepositoriesWrite)(handler))

func RequireAny

func RequireAny(perms ...permission.Permission) func(http.Handler) http.Handler

RequireAny creates a middleware that requires any of the specified permissions. Uses permission.Permission constants for type safety.

Example:

r.GET("/", middleware.RequireAny(permission.AssetsRead, permission.RepositoriesRead)(handler))

func RequireCampaignRole added in v0.1.3

func RequireCampaignRole(allowed ...pentest.CampaignRole) func(http.Handler) http.Handler

RequireCampaignRole creates a middleware that checks the user's campaign role. Admin/owner bypasses the check. If no campaign role is set (not a member), returns 404 to avoid revealing campaign existence.

func RequireCampaignWritable added in v0.1.3

func RequireCampaignWritable(allowExistingUpdates bool) func(http.Handler) http.Handler

RequireCampaignWritable creates a middleware that blocks writes on locked campaigns. allowExistingUpdates: if true, on_hold campaigns allow updating existing items.

func RequireDeletePermission

func RequireDeletePermission() func(http.Handler) http.Handler

RequireDeletePermission allows owner and admin roles only.

func RequireLocalUser

func RequireLocalUser() func(http.Handler) http.Handler

RequireLocalUser ensures a local user exists in context. Use this for endpoints that require user data from the local database.

func RequireMembership

func RequireMembership(reader MembershipReader) func(http.Handler) http.Handler

RequireMembership verifies the authenticated user is a member of the tenant AND that the membership is currently active (not suspended). This is the canonical enforcement point for member suspension — it runs on every tenant-scoped request, so suspension takes effect immediately even if the user is holding a still-valid JWT.

The reader is typically a Redis-backed cache wrapping the tenant repo, so the per-request cost is a Redis GET on cache hit (the common case) instead of a DB round trip.

Must be used after KeycloakAuth, UserSync, and TenantContext middleware.

func RequireMinTeamRole

func RequireMinTeamRole(minRole tenant.Role) func(http.Handler) http.Handler

RequireMinTeamRole checks if the user has at least the minimum role level. Uses role hierarchy: owner > admin > member > viewer.

func RequireMinTenantRole

func RequireMinTenantRole(minRole tenant.Role) func(http.Handler) http.Handler

RequireMinTenantRole checks if the user has at least the minimum role level. Uses role hierarchy: owner > admin > member > viewer.

func RequireOwner

func RequireOwner() func(http.Handler) http.Handler

RequireOwner creates a middleware that requires owner role. Use this for sensitive operations that only the owner should perform: - TeamDelete: Deleting the tenant - BillingManage: Managing billing settings - GroupsDelete, PermissionSetsDelete, AssignmentRulesDelete: Deleting access control resources

func RequirePlatformAdmin

func RequirePlatformAdmin() func(http.Handler) http.Handler

RequirePlatformAdmin creates a middleware that requires platform admin access. Platform admins can manage OpenCTEM's shared infrastructure like platform agents. This is for OpenCTEM operators, not regular tenant admins.

func RequireRole

func RequireRole(roles ...string) func(http.Handler) http.Handler

RequireRole checks if the user has one of the required roles.

func RequireTeamAdmin

func RequireTeamAdmin() func(http.Handler) http.Handler

RequireTeamAdmin checks if the user has admin permissions in the team.

func RequireTeamOwner

func RequireTeamOwner() func(http.Handler) http.Handler

RequireTeamOwner checks if the user is the owner of the team.

func RequireTeamRole

func RequireTeamRole(roles ...tenant.Role) func(http.Handler) http.Handler

RequireTeamRole checks if the user has one of the required roles in the team. Must be used after RequireMembership middleware.

func RequireTeamWrite

func RequireTeamWrite() func(http.Handler) http.Handler

RequireTeamWrite checks if the user has write permissions in the team.

func RequireTenant

func RequireTenant() func(http.Handler) http.Handler

RequireTenant ensures the request has a valid tenant ID.

func RequireTenantRole

func RequireTenantRole(roles ...tenant.Role) func(http.Handler) http.Handler

RequireTenantRole checks if the user has one of the required roles within the tenant. Uses role from JWT token claims.

func RequireWritePermission

func RequireWritePermission() func(http.Handler) http.Handler

RequireWritePermission allows owner, admin, and member roles.

func SecurityHeaders

func SecurityHeaders() func(http.Handler) http.Handler

SecurityHeaders adds security-related HTTP headers.

func SecurityHeadersWithConfig

func SecurityHeadersWithConfig(cfg SecurityHeadersConfig) func(http.Handler) http.Handler

SecurityHeadersWithConfig adds security headers with custom configuration.

func SetAuditResourceName

func SetAuditResourceName(ctx context.Context, name string)

SetAuditResourceName sets the resource name in the current request's audit log. Call this from handlers to provide more context for audit logs.

func SetCSRFTokenCookie

func SetCSRFTokenCookie(w http.ResponseWriter, token string, cfg CSRFConfig)

SetCSRFTokenCookie sets the CSRF token in a JavaScript-readable cookie. This is NOT httpOnly so that frontend JavaScript can read and send it in headers.

func SetCampaignContext added in v0.1.3

func SetCampaignContext(ctx context.Context, role pentest.CampaignRole, status pentest.CampaignStatus, campaignID string) context.Context

SetCampaignContext returns a context with campaign role, status, and ID set.

func SetTeamDBContext

func SetTeamDBContext(db *sql.DB) func(http.Handler) http.Handler

SetTeamDBContext sets the team context for database RLS.

func TenantContext

func TenantContext(tenantRepo tenant.Repository) func(http.Handler) http.Handler

TenantContext extracts tenant ID from URL path parameter and adds to context. It expects the path parameter to be named "tenant" (e.g., /tenants/{tenant}/assets). The {tenant} can be either a tenant ID (UUID) or tenant slug.

func Timeout

func Timeout(timeout time.Duration) func(http.Handler) http.Handler

Timeout adds a timeout to each request context. If the handler takes longer than the timeout, the request is canceled.

func Tracing added in v0.1.2

func Tracing(serviceName string) func(http.Handler) http.Handler

Tracing returns an OpenTelemetry tracing middleware that creates spans for each HTTP request and propagates trace context.

func UnifiedAuth

func UnifiedAuth(cfg UnifiedAuthConfig) func(http.Handler) http.Handler

UnifiedAuth creates an authentication middleware that supports both local and OIDC authentication. The middleware tries to validate tokens based on the configured auth provider: - "local": Only validates local JWT tokens - "oidc": Only validates Keycloak/OIDC tokens - "hybrid": Tries local first, then falls back to OIDC

Token extraction order: 1. Authorization header (Bearer <token>) 2. Query parameter "token" (for SSE/EventSource which can't send headers)

func UserKeyFunc

func UserKeyFunc(r *http.Request) string

UserKeyFunc returns a key function that uses authenticated user ID. Falls back to IP address for unauthenticated requests.

func UserSync

func UserSync(userService *app.UserService, log *logger.Logger) func(http.Handler) http.Handler

UserSync middleware syncs authenticated users to the local database. It runs AFTER auth middleware (UnifiedAuth or KeycloakAuth) and creates/updates user records. Performance optimization: - First request: creates user if not exists - Subsequent requests: only updates last_login_at every 24 hours This enables: - Local user management independent of Keycloak - User-specific data (preferences, profile) stored locally - Future migration away from Keycloak Supports both local auth (LocalClaimsKey) and OIDC auth (ClaimsKey).

Types

type AITriageRateLimitConfig

type AITriageRateLimitConfig struct {
	// TriageRequestsPerMin is the max triage requests per minute per tenant.
	// Default: 10 (AI calls are expensive)
	TriageRequestsPerMin int
	// CleanupInterval for visitor entries.
	// Default: 1 minute
	CleanupInterval time.Duration
}

AITriageRateLimitConfig configures AI triage rate limits.

func DefaultAITriageRateLimitConfig

func DefaultAITriageRateLimitConfig() AITriageRateLimitConfig

DefaultAITriageRateLimitConfig returns secure defaults for AI triage rate limiting.

type AITriageRateLimiter

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

AITriageRateLimiter provides rate limiting for AI triage endpoints. This prevents abuse of expensive LLM API calls.

func NewAITriageRateLimiter

func NewAITriageRateLimiter(cfg AITriageRateLimitConfig, log *logger.Logger) *AITriageRateLimiter

NewAITriageRateLimiter creates a rate limiter for AI triage endpoints.

func (*AITriageRateLimiter) RequestMiddleware

func (a *AITriageRateLimiter) RequestMiddleware() func(http.Handler) http.Handler

RequestMiddleware returns middleware for AI triage request endpoints (POST). Uses tenant ID as the rate limit key for per-tenant limiting.

func (*AITriageRateLimiter) Stop

func (a *AITriageRateLimiter) Stop()

Stop gracefully shuts down the rate limiter.

type AdminAuthMiddleware

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

AdminAuthMiddleware provides authentication for admin API endpoints.

func NewAdminAuthMiddleware

func NewAdminAuthMiddleware(adminRepo admin.Repository, log *logger.Logger) *AdminAuthMiddleware

NewAdminAuthMiddleware creates a new AdminAuthMiddleware.

func (*AdminAuthMiddleware) Authenticate

func (m *AdminAuthMiddleware) Authenticate(next http.Handler) http.Handler

Authenticate validates the admin API key and adds admin info to context. Use this middleware for all admin endpoints.

func (*AdminAuthMiddleware) RequirePermission

func (m *AdminAuthMiddleware) RequirePermission(action string) func(http.Handler) http.Handler

RequirePermission creates middleware that requires a specific permission. Use after Authenticate middleware.

func (*AdminAuthMiddleware) RequireRole

func (m *AdminAuthMiddleware) RequireRole(roles ...admin.AdminRole) func(http.Handler) http.Handler

RequireRole creates middleware that requires a specific admin role. Use after Authenticate middleware.

type AdminMappingRateLimitConfig

type AdminMappingRateLimitConfig struct {
	// WriteRequestsPerMin is the max write requests per minute per admin.
	// Default: 10 (as per RFC 2026-02-02-asset-types-cleanup.md)
	WriteRequestsPerMin int
	// CleanupInterval for visitor entries.
	// Default: 1 minute
	CleanupInterval time.Duration
}

AdminMappingRateLimitConfig configures admin mapping rate limits.

func DefaultAdminMappingRateLimitConfig

func DefaultAdminMappingRateLimitConfig() AdminMappingRateLimitConfig

DefaultAdminMappingRateLimitConfig returns secure defaults for admin mapping rate limiting.

type AdminMappingRateLimiter

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

AdminMappingRateLimiter provides rate limiting for admin target mapping endpoints. This prevents abuse of admin configuration APIs.

func NewAdminMappingRateLimiter

func NewAdminMappingRateLimiter(cfg AdminMappingRateLimitConfig, log *logger.Logger) *AdminMappingRateLimiter

NewAdminMappingRateLimiter creates a rate limiter for admin target mapping endpoints.

func (*AdminMappingRateLimiter) Stop

func (a *AdminMappingRateLimiter) Stop()

Stop gracefully shuts down the rate limiter.

func (*AdminMappingRateLimiter) WriteMiddleware

func (a *AdminMappingRateLimiter) WriteMiddleware() func(http.Handler) http.Handler

WriteMiddleware returns middleware for admin mapping write endpoints (POST/PATCH/DELETE). Uses admin ID (from context) or IP as the rate limit key.

type AnalyticsRateLimitConfig

type AnalyticsRateLimitConfig struct {
	// ListRequestsPerMin is the max list requests per minute per tenant.
	// Default: 60 (1 per second)
	ListRequestsPerMin int
	// AggregatedRequestsPerMin is the max aggregated stats requests per minute per tenant.
	// Default: 30 (more expensive queries)
	AggregatedRequestsPerMin int
	// CleanupInterval for visitor entries.
	// Default: 1 minute
	CleanupInterval time.Duration
}

AnalyticsRateLimitConfig configures analytics-specific rate limits.

func DefaultAnalyticsRateLimitConfig

func DefaultAnalyticsRateLimitConfig() AnalyticsRateLimitConfig

DefaultAnalyticsRateLimitConfig returns secure defaults for analytics rate limiting.

type AnalyticsRateLimiter

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

AnalyticsRateLimiter provides rate limiting for agent analytics endpoints. This prevents abuse and ensures fair resource usage for analytics queries.

func NewAnalyticsRateLimiter

func NewAnalyticsRateLimiter(cfg AnalyticsRateLimitConfig, log *logger.Logger) *AnalyticsRateLimiter

NewAnalyticsRateLimiter creates a rate limiter for analytics endpoints.

func (*AnalyticsRateLimiter) AggregatedMiddleware

func (a *AnalyticsRateLimiter) AggregatedMiddleware() func(http.Handler) http.Handler

AggregatedMiddleware returns middleware for aggregated stats endpoints. Uses tenant ID (or user ID for admin) as the rate limit key. More restrictive as aggregated queries are more expensive.

func (*AnalyticsRateLimiter) ListMiddleware

func (a *AnalyticsRateLimiter) ListMiddleware() func(http.Handler) http.Handler

ListMiddleware returns middleware for analytics list endpoints. Uses tenant ID (or user ID for admin) as the rate limit key.

func (*AnalyticsRateLimiter) Stop

func (a *AnalyticsRateLimiter) Stop()

Stop gracefully shuts down all rate limiters.

type AuditMiddleware

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

AuditMiddleware provides audit logging for admin API endpoints.

func NewAuditMiddleware

func NewAuditMiddleware(auditRepo admin.AuditLogRepository, log *logger.Logger) *AuditMiddleware

NewAuditMiddleware creates a new AuditMiddleware.

func (*AuditMiddleware) AuditAction

func (m *AuditMiddleware) AuditAction(action string) func(http.Handler) http.Handler

AuditAction creates a simpler audit middleware for actions without URL params.

func (*AuditMiddleware) AuditAdminCreate

func (m *AuditMiddleware) AuditAdminCreate() func(http.Handler) http.Handler

AuditAdminCreate returns middleware for admin user creation.

func (*AuditMiddleware) AuditAdminDelete

func (m *AuditMiddleware) AuditAdminDelete() func(http.Handler) http.Handler

AuditAdminDelete returns middleware for admin user deletion.

func (*AuditMiddleware) AuditAdminUpdate

func (m *AuditMiddleware) AuditAdminUpdate() func(http.Handler) http.Handler

AuditAdminUpdate returns middleware for admin user updates.

func (*AuditMiddleware) AuditAgentCreate

func (m *AuditMiddleware) AuditAgentCreate() func(http.Handler) http.Handler

AuditAgentCreate returns middleware for platform agent creation.

func (*AuditMiddleware) AuditAgentDelete

func (m *AuditMiddleware) AuditAgentDelete() func(http.Handler) http.Handler

AuditAgentDelete returns middleware for platform agent deletion.

func (*AuditMiddleware) AuditAgentDisable

func (m *AuditMiddleware) AuditAgentDisable() func(http.Handler) http.Handler

AuditAgentDisable returns middleware for disabling platform agents.

func (*AuditMiddleware) AuditAgentEnable

func (m *AuditMiddleware) AuditAgentEnable() func(http.Handler) http.Handler

AuditAgentEnable returns middleware for enabling platform agents.

func (*AuditMiddleware) AuditAgentUpdate

func (m *AuditMiddleware) AuditAgentUpdate() func(http.Handler) http.Handler

AuditAgentUpdate returns middleware for platform agent updates.

func (*AuditMiddleware) AuditJobCancel

func (m *AuditMiddleware) AuditJobCancel() func(http.Handler) http.Handler

AuditJobCancel returns middleware for platform job cancellation.

func (*AuditMiddleware) AuditLog

func (m *AuditMiddleware) AuditLog(action, resourceType, resourceIDParam string) func(http.Handler) http.Handler

AuditLog creates middleware that logs admin actions to the audit log. Should be used after AdminAuthMiddleware.Authenticate().

Parameters:

  • action: The action being performed (e.g., "agent.create", "token.revoke")
  • resourceType: The type of resource (e.g., "agent", "token")
  • resourceIDParam: The chi URL param name for resource ID (e.g., "id", "agentID")

func (*AuditMiddleware) AuditResourceAction

func (m *AuditMiddleware) AuditResourceAction(action, resourceType string) func(http.Handler) http.Handler

AuditResourceAction creates audit middleware for resource-specific actions.

func (*AuditMiddleware) AuditTargetMappingCreate

func (m *AuditMiddleware) AuditTargetMappingCreate() func(http.Handler) http.Handler

AuditTargetMappingCreate returns middleware for target mapping creation.

func (*AuditMiddleware) AuditTargetMappingDelete

func (m *AuditMiddleware) AuditTargetMappingDelete() func(http.Handler) http.Handler

AuditTargetMappingDelete returns middleware for target mapping deletion.

func (*AuditMiddleware) AuditTargetMappingUpdate

func (m *AuditMiddleware) AuditTargetMappingUpdate() func(http.Handler) http.Handler

AuditTargetMappingUpdate returns middleware for target mapping updates.

func (*AuditMiddleware) AuditTokenCreate

func (m *AuditMiddleware) AuditTokenCreate() func(http.Handler) http.Handler

AuditTokenCreate returns middleware for bootstrap token creation.

func (*AuditMiddleware) AuditTokenRevoke

func (m *AuditMiddleware) AuditTokenRevoke() func(http.Handler) http.Handler

AuditTokenRevoke returns middleware for bootstrap token revocation.

func (*AuditMiddleware) LogAuthFailure

func (m *AuditMiddleware) LogAuthFailure(r *http.Request, reason string)

LogAuthFailure logs a failed authentication attempt.

func (*AuditMiddleware) LogAuthSuccess

func (m *AuditMiddleware) LogAuthSuccess(r *http.Request, adminUser *admin.AdminUser)

LogAuthSuccess logs a successful authentication.

type AuthFailureLimiter

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

AuthFailureLimiter tracks auth failures and blocks IPs after too many failures. This provides protection against brute-force attacks on platform agent credentials.

func NewAuthFailureLimiter

func NewAuthFailureLimiter(cfg AuthFailureLimiterConfig, log *logger.Logger) *AuthFailureLimiter

NewAuthFailureLimiter creates a new auth failure limiter.

func (*AuthFailureLimiter) GetStats

func (afl *AuthFailureLimiter) GetStats() (trackedIPs int, bannedIPs int)

GetStats returns current limiter stats.

func (*AuthFailureLimiter) IsBanned

func (afl *AuthFailureLimiter) IsBanned(ip string) bool

IsBanned checks if an IP is currently banned.

func (*AuthFailureLimiter) RecordFailure

func (afl *AuthFailureLimiter) RecordFailure(ip string) bool

RecordFailure records an auth failure for an IP. Returns true if the IP is now banned.

func (*AuthFailureLimiter) RecordSuccess

func (afl *AuthFailureLimiter) RecordSuccess(ip string)

RecordSuccess clears failure count for an IP (on successful auth).

func (*AuthFailureLimiter) Stop

func (afl *AuthFailureLimiter) Stop()

Stop stops the cleanup goroutine.

type AuthFailureLimiterConfig

type AuthFailureLimiterConfig struct {
	// MaxFailures is the max auth failures before IP is banned.
	// Default: 5
	MaxFailures int
	// BanDuration is how long an IP is banned after max failures.
	// Default: 15 minutes
	BanDuration time.Duration
	// WindowDuration is the time window for counting failures.
	// Default: 5 minutes
	WindowDuration time.Duration
	// CleanupInterval for removing old entries.
	// Default: 1 minute
	CleanupInterval time.Duration
}

AuthFailureLimiterConfig configures the auth failure limiter.

func DefaultAuthFailureLimiterConfig

func DefaultAuthFailureLimiterConfig() AuthFailureLimiterConfig

DefaultAuthFailureLimiterConfig returns secure defaults.

type AuthRateLimitConfig

type AuthRateLimitConfig struct {
	// LoginRatePerMin is the max login attempts per minute per IP.
	// Default: 5
	LoginRatePerMin int
	// RegisterRatePerMin is the max registration attempts per minute per IP.
	// Default: 3
	RegisterRatePerMin int
	// PasswordResetRatePerMin is the max password reset/forgot attempts per minute per IP.
	// Default: 3
	PasswordResetRatePerMin int
	// TokenExchangeRatePerMin is the max token exchange/refresh attempts per minute per IP.
	// Higher than login because token exchange requires a valid refresh token (not brute-forceable)
	// and is used for tenant switching which may happen frequently.
	// Default: 20
	TokenExchangeRatePerMin int
	// CleanupInterval for visitor entries.
	// Default: 1 minute
	CleanupInterval time.Duration
}

AuthRateLimitConfig configures auth-specific rate limits.

func DefaultAuthRateLimitConfig

func DefaultAuthRateLimitConfig() AuthRateLimitConfig

DefaultAuthRateLimitConfig returns secure defaults for auth rate limiting.

type AuthRateLimiter

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

AuthRateLimiter provides stricter rate limiting for authentication endpoints. This is critical for preventing brute-force attacks.

func NewAuthRateLimiter

func NewAuthRateLimiter(cfg AuthRateLimitConfig, log *logger.Logger) *AuthRateLimiter

NewAuthRateLimiter creates a rate limiter specialized for authentication endpoints.

func (*AuthRateLimiter) LoginMiddleware

func (a *AuthRateLimiter) LoginMiddleware() func(http.Handler) http.Handler

LoginMiddleware returns middleware for login endpoints. Applies strict rate limiting to prevent brute-force attacks.

func (*AuthRateLimiter) PasswordMiddleware

func (a *AuthRateLimiter) PasswordMiddleware() func(http.Handler) http.Handler

PasswordMiddleware returns middleware for password reset/forgot endpoints.

func (*AuthRateLimiter) RegisterMiddleware

func (a *AuthRateLimiter) RegisterMiddleware() func(http.Handler) http.Handler

RegisterMiddleware returns middleware for registration endpoints.

func (*AuthRateLimiter) Stop

func (a *AuthRateLimiter) Stop()

Stop gracefully shuts down all rate limiters.

func (*AuthRateLimiter) TokenExchangeMiddleware added in v0.1.2

func (a *AuthRateLimiter) TokenExchangeMiddleware() func(http.Handler) http.Handler

TokenExchangeMiddleware returns middleware for token exchange/refresh endpoints. Uses a higher rate limit than login because token exchange requires a valid refresh token (not brute-forceable) and is used for tenant switching.

type CSRFConfig

type CSRFConfig struct {
	// Secure sets the Secure flag on the CSRF cookie.
	Secure bool
	// Domain sets the Domain for the CSRF cookie.
	Domain string
	// SameSite sets the SameSite policy.
	SameSite http.SameSite
	// Path sets the cookie path.
	Path string
	// TTL is the token validity period.
	TTL time.Duration
	// Logger for CSRF operations.
	Logger *logger.Logger
}

CSRFConfig holds CSRF middleware configuration.

func NewCSRFConfig

func NewCSRFConfig(cfg config.AuthConfig, log *logger.Logger) CSRFConfig

NewCSRFConfig creates a CSRFConfig from AuthConfig.

type CampaignRoleQuerier added in v0.1.3

type CampaignRoleQuerier interface {
	GetUserRole(ctx context.Context, tenantID, campaignID, userID string) (pentest.CampaignRole, error)
}

CampaignRoleQuerier resolves a user's campaign role from the database.

type CampaignStatusQuerier added in v0.1.3

type CampaignStatusQuerier interface {
	GetCampaignStatus(ctx context.Context, tenantID, campaignID string) (pentest.CampaignStatus, error)
}

CampaignStatusQuerier resolves a campaign's status. Optional — if nil, status not set in context.

type DecompressConfig

type DecompressConfig struct {
	// MaxDecompressedSize is the maximum size of decompressed body.
	// Default: 50MB
	MaxDecompressedSize int64

	// MaxCompressedSize is the maximum size of compressed input.
	// Default: 10MB (prevents reading huge compressed payloads)
	MaxCompressedSize int64

	// MaxCompressionRatio is the maximum allowed compression ratio.
	// If decompressed/compressed > this ratio, reject as potential zipbomb.
	// Default: 100 (100:1 ratio)
	MaxCompressionRatio float64

	// AllowedEncodings specifies which encodings are allowed.
	// Default: ["gzip", "zstd"]
	AllowedEncodings []string
}

DecompressConfig configures the decompression middleware.

func DefaultDecompressConfig

func DefaultDecompressConfig() *DecompressConfig

DefaultDecompressConfig returns the default configuration.

type DistributedRateLimitConfig

type DistributedRateLimitConfig struct {
	// Limiter is the Redis-backed rate limiter adapter.
	Limiter *redisinfra.MiddlewareAdapter
	// KeyFunc extracts the rate limit key from the request.
	// Defaults to using client IP.
	KeyFunc func(r *http.Request) string
	// Logger for rate limit events.
	Logger *logger.Logger
	// SkipFunc optionally skips rate limiting for certain requests.
	SkipFunc func(r *http.Request) bool
}

DistributedRateLimitConfig configures the distributed rate limit middleware.

type FindingActivityRateLimitConfig

type FindingActivityRateLimitConfig struct {
	// ListRequestsPerMin is the max list activity requests per minute per user.
	// Default: 60
	ListRequestsPerMin int
	// CleanupInterval for visitor entries.
	// Default: 1 minute
	CleanupInterval time.Duration
}

FindingActivityRateLimitConfig configures finding activity rate limits.

func DefaultFindingActivityRateLimitConfig

func DefaultFindingActivityRateLimitConfig() FindingActivityRateLimitConfig

DefaultFindingActivityRateLimitConfig returns secure defaults.

type FindingActivityRateLimiter

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

FindingActivityRateLimiter provides rate limiting for finding activity endpoints. This prevents enumeration attacks and DoS on activity APIs.

func NewFindingActivityRateLimiter

func NewFindingActivityRateLimiter(cfg FindingActivityRateLimitConfig, log *logger.Logger) *FindingActivityRateLimiter

NewFindingActivityRateLimiter creates a rate limiter for finding activity endpoints.

func (*FindingActivityRateLimiter) ListMiddleware

func (f *FindingActivityRateLimiter) ListMiddleware() func(http.Handler) http.Handler

ListMiddleware returns middleware for activity list endpoints. Uses user ID (or IP) as the rate limit key for per-user limiting.

func (*FindingActivityRateLimiter) Stop

func (f *FindingActivityRateLimiter) Stop()

Stop gracefully shuts down the rate limiter.

type LoggerConfig

type LoggerConfig struct {
	// SkipPaths are paths that should not be logged (e.g., health checks)
	SkipPaths []string

	// SkipSuccessful skips logging for successful requests (2xx status codes)
	// Useful for high-traffic endpoints where you only care about errors
	SkipSuccessful bool

	// SlowRequestThreshold logs requests slower than this as warnings
	// Set to 0 to disable slow request logging
	SlowRequestThreshold time.Duration
}

LoggerConfig configures HTTP request logging behavior.

func DefaultLoggerConfig

func DefaultLoggerConfig() LoggerConfig

DefaultLoggerConfig returns default logging configuration.

type MembershipReader added in v0.1.6

type MembershipReader interface {
	GetMembership(ctx context.Context, userID, tenantID shared.ID) (*tenant.Membership, error)
}

MembershipReader is the minimal lookup contract the membership-aware middleware needs. Both the raw tenant.Repository and the cached app.MembershipCacheService implement it via structural typing, so the middleware can transparently switch between direct DB access and a Redis-backed cache without knowing the difference.

type PermissionSyncMiddleware

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

PermissionSyncMiddleware handles permission synchronization and stale detection. It enriches the request context with permissions from Redis cache and sets the X-Permission-Stale header when JWT version doesn't match Redis version.

func NewPermissionSyncMiddleware

func NewPermissionSyncMiddleware(
	permCache *app.PermissionCacheService,
	permVersion *app.PermissionVersionService,
	log *logger.Logger,
) *PermissionSyncMiddleware

NewPermissionSyncMiddleware creates a new permission sync middleware.

func (*PermissionSyncMiddleware) EnrichPermissions

func (m *PermissionSyncMiddleware) EnrichPermissions(next http.Handler) http.Handler

EnrichPermissions fetches permissions from Redis cache and adds them to context. Also checks for stale permissions and sets the X-Permission-Stale header.

This middleware should be placed AFTER UnifiedAuth in the middleware chain. It uses the user ID and tenant ID from the context set by UnifiedAuth.

Flow: 1. Get tenant ID and user ID from context 2. Get current permission version from Redis 3. Compare with JWT's perm_version (if present) 4. If mismatch: set X-Permission-Stale header 5. Fetch permissions from Redis cache (or DB fallback) 6. Store permissions in context for handlers

type PlatformRegistrationRateLimitConfig

type PlatformRegistrationRateLimitConfig struct {
	// RegistrationRatePerMin is the max registration attempts per minute per IP.
	// Default: 5 (very strict for bootstrap token protection)
	RegistrationRatePerMin int
	// CleanupInterval for visitor entries.
	// Default: 1 minute
	CleanupInterval time.Duration
}

PlatformRegistrationRateLimitConfig configures platform registration rate limits.

func DefaultPlatformRegistrationRateLimitConfig

func DefaultPlatformRegistrationRateLimitConfig() PlatformRegistrationRateLimitConfig

DefaultPlatformRegistrationRateLimitConfig returns secure defaults.

type PlatformRegistrationRateLimiter

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

PlatformRegistrationRateLimiter provides strict rate limiting for platform agent registration. This prevents brute-force attacks on bootstrap tokens.

func NewPlatformRegistrationRateLimiter

func NewPlatformRegistrationRateLimiter(cfg PlatformRegistrationRateLimitConfig, log *logger.Logger) *PlatformRegistrationRateLimiter

NewPlatformRegistrationRateLimiter creates a rate limiter for platform agent registration.

func (*PlatformRegistrationRateLimiter) Middleware

Middleware returns the platform registration rate limiting middleware.

func (*PlatformRegistrationRateLimiter) Stop

Stop gracefully shuts down the rate limiter.

type RateLimiter

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

RateLimiter implements a per-IP rate limiter.

func NewRateLimiter

func NewRateLimiter(cfg *config.RateLimitConfig, log *logger.Logger) *RateLimiter

NewRateLimiter creates a new rate limiter.

func (*RateLimiter) Middleware

func (rl *RateLimiter) Middleware() func(http.Handler) http.Handler

Middleware returns the rate limiting middleware.

func (*RateLimiter) Stop

func (rl *RateLimiter) Stop()

Stop stops the cleanup goroutine and waits for it to exit. Safe to call multiple times.

type ReadEndpointRateLimitConfig added in v0.1.3

type ReadEndpointRateLimitConfig struct {
	// ReadRequestsPerMin is the max GET requests per minute per user.
	// Default: 120 (2 per second — generous for normal usage, blocks scraping)
	ReadRequestsPerMin int
	// CleanupInterval for visitor entries.
	// Default: 1 minute
	CleanupInterval time.Duration
}

ReadEndpointRateLimitConfig configures read endpoint rate limits.

func DefaultReadEndpointRateLimitConfig added in v0.1.3

func DefaultReadEndpointRateLimitConfig() ReadEndpointRateLimitConfig

DefaultReadEndpointRateLimitConfig returns secure defaults for read endpoint rate limiting.

type ReadEndpointRateLimiter added in v0.1.3

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

ReadEndpointRateLimiter provides per-user rate limiting for read/list endpoints. This prevents enumeration attacks and excessive polling on GET endpoints across all authenticated API routes.

func NewReadEndpointRateLimiter added in v0.1.3

func NewReadEndpointRateLimiter(cfg ReadEndpointRateLimitConfig, log *logger.Logger) *ReadEndpointRateLimiter

NewReadEndpointRateLimiter creates a rate limiter for read/list endpoints.

func (*ReadEndpointRateLimiter) Middleware added in v0.1.3

func (rel *ReadEndpointRateLimiter) Middleware() func(http.Handler) http.Handler

Middleware returns middleware that rate-limits GET requests per user. Non-GET requests pass through without rate limiting.

func (*ReadEndpointRateLimiter) Stop added in v0.1.3

func (rel *ReadEndpointRateLimiter) Stop()

Stop gracefully shuts down the rate limiter.

type SecurityHeadersConfig

type SecurityHeadersConfig struct {
	// HSTSEnabled enables HTTP Strict Transport Security.
	// Should be true in production with HTTPS.
	HSTSEnabled bool
	// HSTSMaxAge is the max-age for HSTS in seconds (default: 1 year).
	HSTSMaxAge int
	// HSTSIncludeSubdomains includes subdomains in HSTS.
	HSTSIncludeSubdomains bool
}

SecurityHeadersConfig configures security headers.

type TriggerRateLimitConfig

type TriggerRateLimitConfig struct {
	// PipelineTriggersPerMin is the max pipeline triggers per minute per tenant.
	// Default: 30
	PipelineTriggersPerMin int
	// ScanTriggersPerMin is the max scan triggers per minute per tenant.
	// Default: 20
	ScanTriggersPerMin int
	// QuickScanTriggersPerMin is the max quick scan triggers per minute per tenant.
	// Default: 10 (stricter as quick scans can be resource-intensive)
	QuickScanTriggersPerMin int
	// CleanupInterval for visitor entries.
	// Default: 1 minute
	CleanupInterval time.Duration
}

TriggerRateLimitConfig configures trigger-specific rate limits.

func DefaultTriggerRateLimitConfig

func DefaultTriggerRateLimitConfig() TriggerRateLimitConfig

DefaultTriggerRateLimitConfig returns secure defaults for trigger rate limiting.

type TriggerRateLimiter

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

TriggerRateLimiter provides rate limiting for pipeline and scan trigger endpoints. This prevents abuse and ensures fair resource usage.

func NewTriggerRateLimiter

func NewTriggerRateLimiter(cfg TriggerRateLimitConfig, log *logger.Logger) *TriggerRateLimiter

NewTriggerRateLimiter creates a rate limiter specialized for trigger endpoints.

func (*TriggerRateLimiter) PipelineMiddleware

func (t *TriggerRateLimiter) PipelineMiddleware() func(http.Handler) http.Handler

PipelineMiddleware returns middleware for pipeline trigger endpoints. Uses tenant ID as the rate limit key for per-tenant limiting.

func (*TriggerRateLimiter) QuickScanMiddleware

func (t *TriggerRateLimiter) QuickScanMiddleware() func(http.Handler) http.Handler

QuickScanMiddleware returns middleware for quick scan trigger endpoints. Uses tenant ID as the rate limit key for per-tenant limiting.

func (*TriggerRateLimiter) ScanMiddleware

func (t *TriggerRateLimiter) ScanMiddleware() func(http.Handler) http.Handler

ScanMiddleware returns middleware for scan trigger endpoints. Uses tenant ID as the rate limit key for per-tenant limiting.

func (*TriggerRateLimiter) Stop

func (t *TriggerRateLimiter) Stop()

Stop gracefully shuts down all rate limiters.

type UnifiedAuthConfig

type UnifiedAuthConfig struct {
	Provider              config.AuthProvider
	LocalValidator        *jwt.Generator
	OIDCValidator         *keycloak.Validator
	Logger                *logger.Logger
	SessionTimeoutMinutes int // Session timeout in minutes (0 = disabled)
}

UnifiedAuthConfig holds configuration for unified auth middleware.

Jump to

Keyboard shortcuts

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