490 lines
21 KiB
Go
490 lines
21 KiB
Go
package http
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/config"
|
|
httpmw "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/http/middleware"
|
|
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/interface/http/handler/admin"
|
|
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/interface/http/handler/gateway"
|
|
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/interface/http/handler/healthcheck"
|
|
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/interface/http/handler/plugin"
|
|
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/interface/http/handler/site"
|
|
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/interface/http/handler/tenant"
|
|
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/interface/http/handler/user"
|
|
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/interface/http/middleware"
|
|
)
|
|
|
|
// Server represents the HTTP server
|
|
type Server struct {
|
|
server *http.Server
|
|
logger *zap.Logger
|
|
jwtMiddleware *httpmw.JWTMiddleware
|
|
apikeyMiddleware *httpmw.APIKeyMiddleware
|
|
rateLimitMiddlewares *httpmw.RateLimitMiddlewares // CWE-770: Registration and auth endpoints rate limiting
|
|
securityHeadersMiddleware *httpmw.SecurityHeadersMiddleware
|
|
requestSizeLimitMw *httpmw.RequestSizeLimitMiddleware
|
|
config *config.Config
|
|
healthHandler *healthcheck.Handler
|
|
registerHandler *gateway.RegisterHandler
|
|
loginHandler *gateway.LoginHandler
|
|
refreshTokenHandler *gateway.RefreshTokenHandler
|
|
helloHandler *gateway.HelloHandler
|
|
meHandler *gateway.MeHandler
|
|
createTenantHandler *tenant.CreateHandler
|
|
getTenantHandler *tenant.GetHandler
|
|
createUserHandler *user.CreateHandler
|
|
getUserHandler *user.GetHandler
|
|
createSiteHandler *site.CreateHandler
|
|
getSiteHandler *site.GetHandler
|
|
listSitesHandler *site.ListHandler
|
|
deleteSiteHandler *site.DeleteHandler
|
|
rotateSiteAPIKeyHandler *site.RotateAPIKeyHandler
|
|
verifySiteHandler *site.VerifySiteHandler
|
|
pluginStatusHandler *plugin.StatusHandler
|
|
pluginVerifyHandler *plugin.PluginVerifyHandler
|
|
pluginVersionHandler *plugin.VersionHandler
|
|
syncPagesHandler *plugin.SyncPagesHandler
|
|
searchPagesHandler *plugin.SearchPagesHandler
|
|
deletePagesHandler *plugin.DeletePagesHandler
|
|
syncStatusHandler *plugin.SyncStatusHandler
|
|
unlockAccountHandler *admin.UnlockAccountHandler
|
|
accountStatusHandler *admin.AccountStatusHandler
|
|
}
|
|
|
|
// ProvideServer creates a new HTTP server
|
|
func ProvideServer(
|
|
cfg *config.Config,
|
|
logger *zap.Logger,
|
|
jwtMiddleware *httpmw.JWTMiddleware,
|
|
apikeyMiddleware *httpmw.APIKeyMiddleware,
|
|
rateLimitMiddlewares *httpmw.RateLimitMiddlewares,
|
|
securityHeadersMiddleware *httpmw.SecurityHeadersMiddleware,
|
|
requestSizeLimitMw *httpmw.RequestSizeLimitMiddleware,
|
|
healthHandler *healthcheck.Handler,
|
|
registerHandler *gateway.RegisterHandler,
|
|
loginHandler *gateway.LoginHandler,
|
|
refreshTokenHandler *gateway.RefreshTokenHandler,
|
|
helloHandler *gateway.HelloHandler,
|
|
meHandler *gateway.MeHandler,
|
|
createTenantHandler *tenant.CreateHandler,
|
|
getTenantHandler *tenant.GetHandler,
|
|
createUserHandler *user.CreateHandler,
|
|
getUserHandler *user.GetHandler,
|
|
createSiteHandler *site.CreateHandler,
|
|
getSiteHandler *site.GetHandler,
|
|
listSitesHandler *site.ListHandler,
|
|
deleteSiteHandler *site.DeleteHandler,
|
|
rotateSiteAPIKeyHandler *site.RotateAPIKeyHandler,
|
|
verifySiteHandler *site.VerifySiteHandler,
|
|
pluginStatusHandler *plugin.StatusHandler,
|
|
pluginVerifyHandler *plugin.PluginVerifyHandler,
|
|
pluginVersionHandler *plugin.VersionHandler,
|
|
syncPagesHandler *plugin.SyncPagesHandler,
|
|
searchPagesHandler *plugin.SearchPagesHandler,
|
|
deletePagesHandler *plugin.DeletePagesHandler,
|
|
syncStatusHandler *plugin.SyncStatusHandler,
|
|
unlockAccountHandler *admin.UnlockAccountHandler,
|
|
accountStatusHandler *admin.AccountStatusHandler,
|
|
) *Server {
|
|
mux := http.NewServeMux()
|
|
|
|
s := &Server{
|
|
logger: logger,
|
|
jwtMiddleware: jwtMiddleware,
|
|
apikeyMiddleware: apikeyMiddleware,
|
|
rateLimitMiddlewares: rateLimitMiddlewares,
|
|
securityHeadersMiddleware: securityHeadersMiddleware,
|
|
requestSizeLimitMw: requestSizeLimitMw,
|
|
config: cfg,
|
|
healthHandler: healthHandler,
|
|
registerHandler: registerHandler,
|
|
loginHandler: loginHandler,
|
|
refreshTokenHandler: refreshTokenHandler,
|
|
helloHandler: helloHandler,
|
|
meHandler: meHandler,
|
|
createTenantHandler: createTenantHandler,
|
|
getTenantHandler: getTenantHandler,
|
|
createUserHandler: createUserHandler,
|
|
getUserHandler: getUserHandler,
|
|
createSiteHandler: createSiteHandler,
|
|
getSiteHandler: getSiteHandler,
|
|
listSitesHandler: listSitesHandler,
|
|
deleteSiteHandler: deleteSiteHandler,
|
|
rotateSiteAPIKeyHandler: rotateSiteAPIKeyHandler,
|
|
verifySiteHandler: verifySiteHandler,
|
|
pluginStatusHandler: pluginStatusHandler,
|
|
pluginVerifyHandler: pluginVerifyHandler,
|
|
pluginVersionHandler: pluginVersionHandler,
|
|
syncPagesHandler: syncPagesHandler,
|
|
searchPagesHandler: searchPagesHandler,
|
|
deletePagesHandler: deletePagesHandler,
|
|
syncStatusHandler: syncStatusHandler,
|
|
unlockAccountHandler: unlockAccountHandler,
|
|
accountStatusHandler: accountStatusHandler,
|
|
}
|
|
|
|
// Register routes
|
|
s.registerRoutes(mux)
|
|
|
|
// Create HTTP server
|
|
// CWE-770: Configure timeouts to prevent resource exhaustion
|
|
s.server = &http.Server{
|
|
Addr: fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port),
|
|
Handler: s.applyMiddleware(mux),
|
|
ReadTimeout: cfg.HTTP.ReadTimeout,
|
|
WriteTimeout: cfg.HTTP.WriteTimeout,
|
|
IdleTimeout: cfg.HTTP.IdleTimeout,
|
|
}
|
|
|
|
logger.Info("✓ HTTP server configured",
|
|
zap.String("address", s.server.Addr),
|
|
zap.Duration("read_timeout", cfg.HTTP.ReadTimeout),
|
|
zap.Duration("write_timeout", cfg.HTTP.WriteTimeout),
|
|
zap.Int64("max_body_size", cfg.HTTP.MaxRequestBodySize))
|
|
|
|
return s
|
|
}
|
|
|
|
// registerRoutes registers all HTTP routes
|
|
func (s *Server) registerRoutes(mux *http.ServeMux) {
|
|
// ===== PUBLIC ROUTES (No authentication, no tenant) =====
|
|
// Health check
|
|
mux.HandleFunc("GET /health", s.healthHandler.Handle)
|
|
|
|
// Version endpoint - public API for checking backend version
|
|
mux.HandleFunc("GET /api/v1/version", s.pluginVersionHandler.Handle)
|
|
|
|
// Public gateway routes (registration, login, etc.)
|
|
// CWE-770: Apply request size limits and rate limiting
|
|
// Apply small size limit (1MB) for registration/login endpoints
|
|
if s.config.RateLimit.RegistrationEnabled {
|
|
mux.HandleFunc("POST /api/v1/register",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyRegistrationRateLimit(s.registerHandler.Handle)),
|
|
).ServeHTTP)
|
|
} else {
|
|
mux.HandleFunc("POST /api/v1/register",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.registerHandler.Handle),
|
|
).ServeHTTP)
|
|
}
|
|
mux.HandleFunc("POST /api/v1/login",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.loginHandler.Handle),
|
|
).ServeHTTP)
|
|
mux.HandleFunc("POST /api/v1/refresh",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.refreshTokenHandler.Handle),
|
|
).ServeHTTP)
|
|
|
|
// ===== AUTHENTICATED ROUTES (JWT only, no tenant context) =====
|
|
// Gateway routes
|
|
// CWE-770: Apply small size limit (1MB) and generic rate limiting for hello endpoint
|
|
if s.config.RateLimit.GenericEnabled {
|
|
mux.HandleFunc("POST /api/v1/hello",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyAuthOnlyWithGenericRateLimit(s.helloHandler.Handle)),
|
|
).ServeHTTP)
|
|
} else {
|
|
mux.HandleFunc("POST /api/v1/hello",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyAuthOnly(s.helloHandler.Handle)),
|
|
).ServeHTTP)
|
|
}
|
|
|
|
// CWE-770: Apply generic rate limiting to /me endpoint to prevent profile enumeration and DoS
|
|
if s.config.RateLimit.GenericEnabled {
|
|
mux.HandleFunc("GET /api/v1/me",
|
|
s.applyAuthOnlyWithGenericRateLimit(s.meHandler.Handle))
|
|
} else {
|
|
mux.HandleFunc("GET /api/v1/me", s.applyAuthOnly(s.meHandler.Handle))
|
|
}
|
|
|
|
// Tenant management routes - these operate at system/admin level
|
|
// CWE-770: Apply small size limit (1MB) and generic rate limiting for tenant creation
|
|
if s.config.RateLimit.GenericEnabled {
|
|
mux.HandleFunc("POST /api/v1/tenants",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyAuthOnlyWithGenericRateLimit(s.createTenantHandler.Handle)),
|
|
).ServeHTTP)
|
|
mux.HandleFunc("GET /api/v1/tenants/{id}", s.applyAuthOnlyWithGenericRateLimit(s.getTenantHandler.HandleByID))
|
|
mux.HandleFunc("GET /api/v1/tenants/slug/{slug}", s.applyAuthOnlyWithGenericRateLimit(s.getTenantHandler.HandleBySlug))
|
|
} else {
|
|
mux.HandleFunc("POST /api/v1/tenants",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyAuthOnly(s.createTenantHandler.Handle)),
|
|
).ServeHTTP)
|
|
mux.HandleFunc("GET /api/v1/tenants/{id}", s.applyAuthOnly(s.getTenantHandler.HandleByID))
|
|
mux.HandleFunc("GET /api/v1/tenants/slug/{slug}", s.applyAuthOnly(s.getTenantHandler.HandleBySlug))
|
|
}
|
|
|
|
// ===== TENANT-SCOPED ROUTES (JWT + Tenant context) =====
|
|
// User routes - these operate within a tenant context
|
|
// CWE-770: Apply small size limit (1MB) and generic rate limiting for user creation
|
|
if s.config.RateLimit.GenericEnabled {
|
|
mux.HandleFunc("POST /api/v1/users",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyAuthAndTenantWithGenericRateLimit(s.createUserHandler.Handle)),
|
|
).ServeHTTP)
|
|
mux.HandleFunc("GET /api/v1/users/{id}", s.applyAuthAndTenantWithGenericRateLimit(s.getUserHandler.Handle))
|
|
} else {
|
|
mux.HandleFunc("POST /api/v1/users",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyAuthAndTenant(s.createUserHandler.Handle)),
|
|
).ServeHTTP)
|
|
mux.HandleFunc("GET /api/v1/users/{id}", s.applyAuthAndTenant(s.getUserHandler.Handle))
|
|
}
|
|
|
|
// Site management routes - JWT authenticated, tenant-scoped
|
|
// CWE-770: Apply small size limit (1MB) and generic rate limiting for site management
|
|
if s.config.RateLimit.GenericEnabled {
|
|
mux.HandleFunc("POST /api/v1/sites",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyAuthAndTenantWithGenericRateLimit(s.createSiteHandler.Handle)),
|
|
).ServeHTTP)
|
|
mux.HandleFunc("GET /api/v1/sites", s.applyAuthAndTenantWithGenericRateLimit(s.listSitesHandler.Handle))
|
|
mux.HandleFunc("GET /api/v1/sites/{id}", s.applyAuthAndTenantWithGenericRateLimit(s.getSiteHandler.Handle))
|
|
mux.HandleFunc("DELETE /api/v1/sites/{id}", s.applyAuthAndTenantWithGenericRateLimit(s.deleteSiteHandler.Handle))
|
|
mux.HandleFunc("POST /api/v1/sites/{id}/rotate-api-key",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyAuthAndTenantWithGenericRateLimit(s.rotateSiteAPIKeyHandler.Handle)),
|
|
).ServeHTTP)
|
|
mux.HandleFunc("POST /api/v1/sites/{id}/verify",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyAuthAndTenantWithGenericRateLimit(s.verifySiteHandler.Handle)),
|
|
).ServeHTTP)
|
|
} else {
|
|
mux.HandleFunc("POST /api/v1/sites",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyAuthAndTenant(s.createSiteHandler.Handle)),
|
|
).ServeHTTP)
|
|
mux.HandleFunc("GET /api/v1/sites", s.applyAuthAndTenant(s.listSitesHandler.Handle))
|
|
mux.HandleFunc("GET /api/v1/sites/{id}", s.applyAuthAndTenant(s.getSiteHandler.Handle))
|
|
mux.HandleFunc("DELETE /api/v1/sites/{id}", s.applyAuthAndTenant(s.deleteSiteHandler.Handle))
|
|
mux.HandleFunc("POST /api/v1/sites/{id}/rotate-api-key",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyAuthAndTenant(s.rotateSiteAPIKeyHandler.Handle)),
|
|
).ServeHTTP)
|
|
mux.HandleFunc("POST /api/v1/sites/{id}/verify",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyAuthAndTenant(s.verifySiteHandler.Handle)),
|
|
).ServeHTTP)
|
|
}
|
|
|
|
// ===== ADMIN ROUTES (JWT authenticated) =====
|
|
// CWE-307: Admin endpoints for account lockout management
|
|
// CWE-770: Apply small size limit (1MB) and generic rate limiting for admin endpoints
|
|
if s.config.RateLimit.GenericEnabled {
|
|
mux.HandleFunc("POST /api/v1/admin/unlock-account",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyAuthOnlyWithGenericRateLimit(s.unlockAccountHandler.Handle)),
|
|
).ServeHTTP)
|
|
mux.HandleFunc("GET /api/v1/admin/account-status", s.applyAuthOnlyWithGenericRateLimit(s.accountStatusHandler.Handle))
|
|
} else {
|
|
mux.HandleFunc("POST /api/v1/admin/unlock-account",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyAuthOnly(s.unlockAccountHandler.Handle)),
|
|
).ServeHTTP)
|
|
mux.HandleFunc("GET /api/v1/admin/account-status", s.applyAuthOnly(s.accountStatusHandler.Handle))
|
|
}
|
|
|
|
// ===== WORDPRESS PLUGIN API ROUTES (API Key authentication) =====
|
|
// CWE-770: Apply lenient site-based rate limiting to protect core business endpoints
|
|
// Default: 1000 requests/hour per site (very lenient for high-volume legitimate traffic)
|
|
|
|
if s.config.RateLimit.PluginAPIEnabled {
|
|
// Plugin status/verification - with rate limiting
|
|
mux.HandleFunc("GET /api/v1/plugin/status", s.applyAPIKeyAuthWithPluginRateLimit(s.pluginStatusHandler.Handle))
|
|
|
|
// Plugin domain verification endpoint
|
|
mux.HandleFunc("POST /api/v1/plugin/verify",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyAPIKeyAuthWithPluginRateLimit(s.pluginVerifyHandler.Handle)),
|
|
).ServeHTTP)
|
|
|
|
// Page sync and search routes
|
|
// CWE-770: Apply larger size limit (50MB) for page sync (bulk operations) + rate limiting
|
|
mux.HandleFunc("POST /api/v1/plugin/pages/sync",
|
|
s.requestSizeLimitMw.LimitLarge()(
|
|
http.HandlerFunc(s.applyAPIKeyAuthWithPluginRateLimit(s.syncPagesHandler.Handle)),
|
|
).ServeHTTP)
|
|
// Apply medium limit (5MB) for search and delete operations + rate limiting
|
|
mux.HandleFunc("POST /api/v1/plugin/pages/search",
|
|
s.requestSizeLimitMw.LimitMedium()(
|
|
http.HandlerFunc(s.applyAPIKeyAuthWithPluginRateLimit(s.searchPagesHandler.Handle)),
|
|
).ServeHTTP)
|
|
mux.HandleFunc("DELETE /api/v1/plugin/pages",
|
|
s.requestSizeLimitMw.LimitMedium()(
|
|
http.HandlerFunc(s.applyAPIKeyAuthWithPluginRateLimit(s.deletePagesHandler.Handle)),
|
|
).ServeHTTP)
|
|
mux.HandleFunc("DELETE /api/v1/plugin/pages/all", s.applyAPIKeyAuthWithPluginRateLimit(s.deletePagesHandler.HandleDeleteAll))
|
|
mux.HandleFunc("GET /api/v1/plugin/pages/status", s.applyAPIKeyAuthWithPluginRateLimit(s.syncStatusHandler.Handle))
|
|
mux.HandleFunc("GET /api/v1/plugin/pages/{page_id}", s.applyAPIKeyAuthWithPluginRateLimit(s.syncStatusHandler.HandleGetPageDetails))
|
|
} else {
|
|
// Plugin endpoints without rate limiting (not recommended for production)
|
|
mux.HandleFunc("GET /api/v1/plugin/status", s.applyAPIKeyAuth(s.pluginStatusHandler.Handle))
|
|
|
|
// Plugin domain verification endpoint
|
|
mux.HandleFunc("POST /api/v1/plugin/verify",
|
|
s.requestSizeLimitMw.LimitSmall()(
|
|
http.HandlerFunc(s.applyAPIKeyAuth(s.pluginVerifyHandler.Handle)),
|
|
).ServeHTTP)
|
|
|
|
mux.HandleFunc("POST /api/v1/plugin/pages/sync",
|
|
s.requestSizeLimitMw.LimitLarge()(
|
|
http.HandlerFunc(s.applyAPIKeyAuth(s.syncPagesHandler.Handle)),
|
|
).ServeHTTP)
|
|
mux.HandleFunc("POST /api/v1/plugin/pages/search",
|
|
s.requestSizeLimitMw.LimitMedium()(
|
|
http.HandlerFunc(s.applyAPIKeyAuth(s.searchPagesHandler.Handle)),
|
|
).ServeHTTP)
|
|
mux.HandleFunc("DELETE /api/v1/plugin/pages",
|
|
s.requestSizeLimitMw.LimitMedium()(
|
|
http.HandlerFunc(s.applyAPIKeyAuth(s.deletePagesHandler.Handle)),
|
|
).ServeHTTP)
|
|
mux.HandleFunc("DELETE /api/v1/plugin/pages/all", s.applyAPIKeyAuth(s.deletePagesHandler.HandleDeleteAll))
|
|
mux.HandleFunc("GET /api/v1/plugin/pages/status", s.applyAPIKeyAuth(s.syncStatusHandler.Handle))
|
|
mux.HandleFunc("GET /api/v1/plugin/pages/{page_id}", s.applyAPIKeyAuth(s.syncStatusHandler.HandleGetPageDetails))
|
|
}
|
|
}
|
|
|
|
// applyMiddleware applies global middleware to all routes
|
|
func (s *Server) applyMiddleware(handler http.Handler) http.Handler {
|
|
// Apply middleware in order (innermost to outermost)
|
|
// 1. Logger middleware (logging)
|
|
// 2. Security headers middleware (CWE-693: Protection Mechanism Failure)
|
|
handler = middleware.LoggerMiddleware(s.logger)(handler)
|
|
handler = s.securityHeadersMiddleware.Handler(handler)
|
|
return handler
|
|
}
|
|
|
|
// applyAuthOnly applies only JWT authentication middleware (no tenant)
|
|
func (s *Server) applyAuthOnly(handler http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
// Chain: JWT validation -> Auth check -> Handler
|
|
s.jwtMiddleware.Handler(
|
|
s.jwtMiddleware.RequireAuth(
|
|
http.HandlerFunc(handler),
|
|
),
|
|
).ServeHTTP(w, r)
|
|
}
|
|
}
|
|
|
|
// applyAuthOnlyWithGenericRateLimit applies JWT authentication + generic rate limiting (CWE-770)
|
|
// Used for authenticated CRUD endpoints (tenant/user/site management, admin, /me, /hello)
|
|
// Applies user-based rate limiting (extracted from JWT context)
|
|
func (s *Server) applyAuthOnlyWithGenericRateLimit(handler http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
// Chain: JWT validation -> Auth check -> Generic rate limit (user-based) -> Handler
|
|
s.jwtMiddleware.Handler(
|
|
s.jwtMiddleware.RequireAuth(
|
|
s.rateLimitMiddlewares.Generic.HandlerWithUserKey(
|
|
http.HandlerFunc(handler),
|
|
),
|
|
),
|
|
).ServeHTTP(w, r)
|
|
}
|
|
}
|
|
|
|
// applyAuthAndTenant applies JWT authentication + tenant middleware
|
|
func (s *Server) applyAuthAndTenant(handler http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
// Chain: JWT validation -> Auth check -> Tenant -> Handler
|
|
s.jwtMiddleware.Handler(
|
|
s.jwtMiddleware.RequireAuth(
|
|
middleware.TenantMiddleware()(
|
|
http.HandlerFunc(handler),
|
|
),
|
|
),
|
|
).ServeHTTP(w, r)
|
|
}
|
|
}
|
|
|
|
// applyAuthAndTenantWithGenericRateLimit applies JWT authentication + tenant + generic rate limiting (CWE-770)
|
|
// Used for tenant-scoped CRUD endpoints (user/site management)
|
|
// Applies user-based rate limiting (extracted from JWT context)
|
|
func (s *Server) applyAuthAndTenantWithGenericRateLimit(handler http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
// Chain: JWT validation -> Auth check -> Tenant -> Generic rate limit (user-based) -> Handler
|
|
s.jwtMiddleware.Handler(
|
|
s.jwtMiddleware.RequireAuth(
|
|
middleware.TenantMiddleware()(
|
|
s.rateLimitMiddlewares.Generic.HandlerWithUserKey(
|
|
http.HandlerFunc(handler),
|
|
),
|
|
),
|
|
),
|
|
).ServeHTTP(w, r)
|
|
}
|
|
}
|
|
|
|
// applyAPIKeyAuth applies API key authentication middleware (for WordPress plugin)
|
|
func (s *Server) applyAPIKeyAuth(handler http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
// Chain: API key validation -> Require API key -> Handler
|
|
s.apikeyMiddleware.Handler(
|
|
s.apikeyMiddleware.RequireAPIKey(
|
|
http.HandlerFunc(handler),
|
|
),
|
|
).ServeHTTP(w, r)
|
|
}
|
|
}
|
|
|
|
// applyAPIKeyAuthWithPluginRateLimit applies API key authentication + plugin API rate limiting (CWE-770)
|
|
// Used for WordPress Plugin API endpoints (core business endpoints)
|
|
// Applies site-based rate limiting (extracted from API key context)
|
|
func (s *Server) applyAPIKeyAuthWithPluginRateLimit(handler http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
// Chain: API key validation -> Require API key -> Plugin rate limit (site-based) -> Handler
|
|
s.apikeyMiddleware.Handler(
|
|
s.apikeyMiddleware.RequireAPIKey(
|
|
s.rateLimitMiddlewares.PluginAPI.HandlerWithSiteKey(
|
|
http.HandlerFunc(handler),
|
|
),
|
|
),
|
|
).ServeHTTP(w, r)
|
|
}
|
|
}
|
|
|
|
// applyRegistrationRateLimit applies rate limiting middleware for registration (CWE-770)
|
|
func (s *Server) applyRegistrationRateLimit(handler http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
// Chain: Rate limit check -> Handler
|
|
s.rateLimitMiddlewares.Registration.Handler(
|
|
http.HandlerFunc(handler),
|
|
).ServeHTTP(w, r)
|
|
}
|
|
}
|
|
|
|
// Start starts the HTTP server
|
|
func (s *Server) Start() error {
|
|
s.logger.Info("")
|
|
s.logger.Info("🚀 MaplePress Backend is ready!")
|
|
s.logger.Info("",
|
|
zap.String("address", s.server.Addr),
|
|
zap.String("url", fmt.Sprintf("http://localhost:%s", s.server.Addr[len(s.server.Addr)-4:])))
|
|
s.logger.Info("")
|
|
|
|
if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
return fmt.Errorf("failed to start server: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Shutdown gracefully shuts down the HTTP server
|
|
func (s *Server) Shutdown(ctx context.Context) error {
|
|
s.logger.Info("shutting down HTTP server")
|
|
|
|
if err := s.server.Shutdown(ctx); err != nil {
|
|
return fmt.Errorf("failed to shutdown server: %w", err)
|
|
}
|
|
|
|
s.logger.Info("HTTP server shut down successfully")
|
|
return nil
|
|
}
|