[split-required] [guardrail-change] Enforce 500 LOC budget across all services

Install LOC guardrails (check-loc.sh, architecture.md, pre-commit hook)
and split all 44 files exceeding 500 LOC into domain-focused modules:

- consent-service (Go): models, handlers, services, database splits
- backend-core (Python): security_api, rbac_api, pdf_service, auth splits
- admin-core (TypeScript): 5 page.tsx + sidebar extractions
- pitch-deck (TypeScript): 6 slides, 3 UI components, engine.ts splits
- voice-service (Python): enhanced_task_orchestrator split

Result: 0 violations, 36 exempted (pipeline, tests, pure-data files).
Go build verified clean. No behavior changes — pure structural splits.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-27 00:09:30 +02:00
parent 5ef039a6bc
commit 92c86ec6ba
162 changed files with 23853 additions and 23034 deletions

View File

@@ -6,7 +6,6 @@ import (
"strings"
"github.com/breakpilot/consent-service/internal/middleware"
"github.com/breakpilot/consent-service/internal/models"
"github.com/breakpilot/consent-service/internal/services"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
@@ -292,366 +291,6 @@ func (h *OAuthHandler) Introspect(c *gin.Context) {
})
}
// ========================================
// 2FA (TOTP) Endpoints
// ========================================
// Setup2FA initiates 2FA setup
// POST /auth/2fa/setup
func (h *OAuthHandler) Setup2FA(c *gin.Context) {
userID, err := middleware.GetUserID(c)
if err != nil || userID == uuid.Nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
return
}
// Get user email
ctx := context.Background()
user, err := h.authService.GetUserByID(ctx, userID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user"})
return
}
// Setup 2FA
response, err := h.totpService.Setup2FA(ctx, userID, user.Email)
if err != nil {
switch err {
case services.ErrTOTPAlreadyEnabled:
c.JSON(http.StatusConflict, gin.H{"error": "2FA is already enabled for this account"})
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to setup 2FA"})
}
return
}
c.JSON(http.StatusOK, response)
}
// Verify2FASetup verifies the 2FA setup with a code
// POST /auth/2fa/verify-setup
func (h *OAuthHandler) Verify2FASetup(c *gin.Context) {
userID, err := middleware.GetUserID(c)
if err != nil || userID == uuid.Nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
return
}
var req models.Verify2FARequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
return
}
ctx := context.Background()
err = h.totpService.Verify2FASetup(ctx, userID, req.Code)
if err != nil {
switch err {
case services.ErrTOTPAlreadyEnabled:
c.JSON(http.StatusConflict, gin.H{"error": "2FA is already enabled"})
case services.ErrTOTPInvalidCode:
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid 2FA code"})
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to verify 2FA setup"})
}
return
}
c.JSON(http.StatusOK, gin.H{"message": "2FA enabled successfully"})
}
// Verify2FAChallenge verifies a 2FA challenge during login
// POST /auth/2fa/verify
func (h *OAuthHandler) Verify2FAChallenge(c *gin.Context) {
var req models.Verify2FAChallengeRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
return
}
ctx := context.Background()
var userID *uuid.UUID
var err error
if req.RecoveryCode != "" {
// Verify with recovery code
userID, err = h.totpService.VerifyChallengeWithRecoveryCode(ctx, req.ChallengeID, req.RecoveryCode)
} else {
// Verify with TOTP code
userID, err = h.totpService.VerifyChallenge(ctx, req.ChallengeID, req.Code)
}
if err != nil {
switch err {
case services.ErrTOTPChallengeExpired:
c.JSON(http.StatusGone, gin.H{"error": "2FA challenge has expired"})
case services.ErrTOTPInvalidCode:
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid 2FA code"})
case services.ErrRecoveryCodeInvalid:
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid recovery code"})
default:
c.JSON(http.StatusBadRequest, gin.H{"error": "2FA verification failed"})
}
return
}
// Get user and generate tokens
user, err := h.authService.GetUserByID(ctx, *userID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user"})
return
}
// Generate access token
accessToken, err := h.authService.GenerateAccessToken(user)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
return
}
// Generate refresh token
refreshToken, refreshTokenHash, err := h.authService.GenerateRefreshToken()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate refresh token"})
return
}
// Store session
ipAddress := middleware.GetClientIP(c)
userAgent := middleware.GetUserAgent(c)
// We need direct DB access for this, or we need to add a method to AuthService
// For now, we'll return the tokens and let the caller handle session storage
c.JSON(http.StatusOK, gin.H{
"access_token": accessToken,
"refresh_token": refreshToken,
"token_type": "Bearer",
"expires_in": 3600,
"user": map[string]interface{}{
"id": user.ID,
"email": user.Email,
"name": user.Name,
"role": user.Role,
},
"_session_hash": refreshTokenHash,
"_ip": ipAddress,
"_user_agent": userAgent,
})
}
// Disable2FA disables 2FA for the current user
// POST /auth/2fa/disable
func (h *OAuthHandler) Disable2FA(c *gin.Context) {
userID, err := middleware.GetUserID(c)
if err != nil || userID == uuid.Nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
return
}
var req models.Verify2FARequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
return
}
ctx := context.Background()
err = h.totpService.Disable2FA(ctx, userID, req.Code)
if err != nil {
switch err {
case services.ErrTOTPNotEnabled:
c.JSON(http.StatusNotFound, gin.H{"error": "2FA is not enabled"})
case services.ErrTOTPInvalidCode:
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid 2FA code"})
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to disable 2FA"})
}
return
}
c.JSON(http.StatusOK, gin.H{"message": "2FA disabled successfully"})
}
// Get2FAStatus returns the 2FA status for the current user
// GET /auth/2fa/status
func (h *OAuthHandler) Get2FAStatus(c *gin.Context) {
userID, err := middleware.GetUserID(c)
if err != nil || userID == uuid.Nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
return
}
ctx := context.Background()
status, err := h.totpService.GetStatus(ctx, userID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get 2FA status"})
return
}
c.JSON(http.StatusOK, status)
}
// RegenerateRecoveryCodes generates new recovery codes
// POST /auth/2fa/recovery-codes
func (h *OAuthHandler) RegenerateRecoveryCodes(c *gin.Context) {
userID, err := middleware.GetUserID(c)
if err != nil || userID == uuid.Nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
return
}
var req models.Verify2FARequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
return
}
ctx := context.Background()
codes, err := h.totpService.RegenerateRecoveryCodes(ctx, userID, req.Code)
if err != nil {
switch err {
case services.ErrTOTPNotEnabled:
c.JSON(http.StatusNotFound, gin.H{"error": "2FA is not enabled"})
case services.ErrTOTPInvalidCode:
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid 2FA code"})
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to regenerate recovery codes"})
}
return
}
c.JSON(http.StatusOK, gin.H{"recovery_codes": codes})
}
// ========================================
// Enhanced Login with 2FA
// ========================================
// LoginWith2FA handles login with optional 2FA
// POST /auth/login
func (h *OAuthHandler) LoginWith2FA(c *gin.Context) {
var req models.LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
return
}
ctx := context.Background()
ipAddress := middleware.GetClientIP(c)
userAgent := middleware.GetUserAgent(c)
// Attempt login
response, err := h.authService.Login(ctx, &req, ipAddress, userAgent)
if err != nil {
switch err {
case services.ErrInvalidCredentials:
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid email or password"})
case services.ErrAccountLocked:
c.JSON(http.StatusForbidden, gin.H{"error": "Account is temporarily locked"})
case services.ErrAccountSuspended:
c.JSON(http.StatusForbidden, gin.H{"error": "Account is suspended"})
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": "Login failed"})
}
return
}
// Check if 2FA is enabled
twoFactorEnabled, _ := h.totpService.IsTwoFactorEnabled(ctx, response.User.ID)
if twoFactorEnabled {
// Create 2FA challenge
challengeID, err := h.totpService.CreateChallenge(ctx, response.User.ID, ipAddress, userAgent)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create 2FA challenge"})
return
}
// Return 2FA required response
c.JSON(http.StatusOK, gin.H{
"requires_2fa": true,
"challenge_id": challengeID,
"message": "2FA verification required",
})
return
}
// No 2FA required, return tokens
c.JSON(http.StatusOK, gin.H{
"requires_2fa": false,
"access_token": response.AccessToken,
"refresh_token": response.RefreshToken,
"token_type": "Bearer",
"expires_in": response.ExpiresIn,
"user": map[string]interface{}{
"id": response.User.ID,
"email": response.User.Email,
"name": response.User.Name,
"role": response.User.Role,
},
})
}
// ========================================
// Registration with mandatory 2FA setup
// ========================================
// RegisterWith2FA handles registration with mandatory 2FA setup
// POST /auth/register
func (h *OAuthHandler) RegisterWith2FA(c *gin.Context) {
var req models.RegisterRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
return
}
ctx := context.Background()
// Validate password strength
if len(req.Password) < 8 {
c.JSON(http.StatusBadRequest, gin.H{"error": "Password must be at least 8 characters"})
return
}
// Register user
user, verificationToken, err := h.authService.Register(ctx, &req)
if err != nil {
switch err {
case services.ErrUserExists:
c.JSON(http.StatusConflict, gin.H{"error": "A user with this email already exists"})
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": "Registration failed"})
}
return
}
// Setup 2FA immediately
twoFAResponse, err := h.totpService.Setup2FA(ctx, user.ID, user.Email)
if err != nil {
// Non-fatal - user can set up 2FA later, but log it
c.JSON(http.StatusCreated, gin.H{
"message": "Registration successful. Please verify your email.",
"user_id": user.ID,
"verification_token": verificationToken, // In production, this would be sent via email
"two_factor_setup": nil,
"two_factor_error": "Failed to initialize 2FA. Please set it up in your account settings.",
})
return
}
c.JSON(http.StatusCreated, gin.H{
"message": "Registration successful. Please verify your email and complete 2FA setup.",
"user_id": user.ID,
"verification_token": verificationToken, // In production, this would be sent via email
"two_factor_setup": map[string]interface{}{
"secret": twoFAResponse.Secret,
"qr_code": twoFAResponse.QRCodeDataURL,
"recovery_codes": twoFAResponse.RecoveryCodes,
"setup_required": true,
"setup_endpoint": "/auth/2fa/verify-setup",
},
})
}
// ========================================
// OAuth Client Management (Admin)
// ========================================