Production hardening: security, resilience, observability, and compliance

Password complexity: custom validator requiring uppercase, lowercase, digit (min 8 chars)
Token expiry: 90-day token lifetime with refresh endpoint (60-90 day renewal window)
Health check: /api/health/ now pings Postgres + Redis, returns 503 on failure
Audit logging: async audit_log table for auth events (login, register, delete, etc.)
Circuit breaker: APNs/FCM push sends wrapped with 5-failure threshold, 30s recovery
FK indexes: 27 missing foreign key indexes across all tables (migration 017)
CSP header: default-src 'none'; frame-ancestors 'none'
Gzip compression: level 5 with media endpoint skipper
Prometheus metrics: /metrics endpoint using existing monitoring service
External timeouts: 15s push, 30s SMTP, context timeouts on all external calls

Migrations: 016 (token created_at), 017 (FK indexes), 018 (audit_log)
Tests: circuit breaker (15), audit service (8), token refresh (7), health (4),
       middleware expiry (5), validator (new)
This commit is contained in:
Trey T
2026-03-26 14:05:28 -05:00
parent 4abc57535e
commit b679f28e55
30 changed files with 2077 additions and 47 deletions

View File

@@ -4,6 +4,7 @@ import (
"net/http"
"reflect"
"strings"
"unicode"
"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
@@ -27,9 +28,34 @@ func NewCustomValidator() *CustomValidator {
return name
})
// Register custom password complexity validator
v.RegisterValidation("password_complexity", validatePasswordComplexity)
return &CustomValidator{validator: v}
}
// validatePasswordComplexity checks that a password contains at least one
// uppercase letter, one lowercase letter, and one digit.
// Minimum length is enforced separately via the "min" tag.
func validatePasswordComplexity(fl validator.FieldLevel) bool {
password := fl.Field().String()
var hasUpper, hasLower, hasDigit bool
for _, ch := range password {
switch {
case unicode.IsUpper(ch):
hasUpper = true
case unicode.IsLower(ch):
hasLower = true
case unicode.IsDigit(ch):
hasDigit = true
}
if hasUpper && hasLower && hasDigit {
return true
}
}
return hasUpper && hasLower && hasDigit
}
// Validate implements echo.Validator interface
func (cv *CustomValidator) Validate(i interface{}) error {
if err := cv.validator.Struct(i); err != nil {
@@ -96,6 +122,8 @@ func formatMessage(fe validator.FieldError) string {
return "Must be a valid URL"
case "uuid":
return "Must be a valid UUID"
case "password_complexity":
return "Password must be at least 8 characters with at least one uppercase letter, one lowercase letter, and one digit"
default:
return "Invalid value"
}