[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:
168
consent-service/internal/handlers/gdpr.go
Normal file
168
consent-service/internal/handlers/gdpr.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/breakpilot/consent-service/internal/middleware"
|
||||
"github.com/breakpilot/consent-service/internal/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// ========================================
|
||||
// GDPR / DATA SUBJECT RIGHTS
|
||||
// ========================================
|
||||
|
||||
// GetMyData returns all data we have about the user
|
||||
func (h *Handler) GetMyData(c *gin.Context) {
|
||||
userID, err := middleware.GetUserID(c)
|
||||
if err != nil || userID == uuid.Nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid user"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ipAddress := middleware.GetClientIP(c)
|
||||
userAgent := middleware.GetUserAgent(c)
|
||||
|
||||
// Get user info
|
||||
var user models.User
|
||||
err = h.db.Pool.QueryRow(ctx, `
|
||||
SELECT id, external_id, email, role, created_at, updated_at
|
||||
FROM users WHERE id = $1
|
||||
`, userID).Scan(&user.ID, &user.ExternalID, &user.Email, &user.Role, &user.CreatedAt, &user.UpdatedAt)
|
||||
|
||||
// Get consents
|
||||
consentRows, _ := h.db.Pool.Query(ctx, `
|
||||
SELECT uc.consented, uc.consented_at, ld.type, ld.name, dv.version
|
||||
FROM user_consents uc
|
||||
JOIN document_versions dv ON uc.document_version_id = dv.id
|
||||
JOIN legal_documents ld ON dv.document_id = ld.id
|
||||
WHERE uc.user_id = $1
|
||||
`, userID)
|
||||
defer consentRows.Close()
|
||||
|
||||
var consents []map[string]interface{}
|
||||
for consentRows.Next() {
|
||||
var consented bool
|
||||
var consentedAt time.Time
|
||||
var docType, docName, version string
|
||||
consentRows.Scan(&consented, &consentedAt, &docType, &docName, &version)
|
||||
consents = append(consents, map[string]interface{}{
|
||||
"document_type": docType,
|
||||
"document_name": docName,
|
||||
"version": version,
|
||||
"consented": consented,
|
||||
"consented_at": consentedAt,
|
||||
})
|
||||
}
|
||||
|
||||
// Get cookie consents
|
||||
cookieRows, _ := h.db.Pool.Query(ctx, `
|
||||
SELECT cat.name, cc.consented, cc.updated_at
|
||||
FROM cookie_consents cc
|
||||
JOIN cookie_categories cat ON cc.category_id = cat.id
|
||||
WHERE cc.user_id = $1
|
||||
`, userID)
|
||||
defer cookieRows.Close()
|
||||
|
||||
var cookieConsents []map[string]interface{}
|
||||
for cookieRows.Next() {
|
||||
var name string
|
||||
var consented bool
|
||||
var updatedAt time.Time
|
||||
cookieRows.Scan(&name, &consented, &updatedAt)
|
||||
cookieConsents = append(cookieConsents, map[string]interface{}{
|
||||
"category": name,
|
||||
"consented": consented,
|
||||
"updated_at": updatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
// Log data access
|
||||
h.logAudit(ctx, &userID, "data_access", "user", &userID, nil, ipAddress, userAgent)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"user": map[string]interface{}{
|
||||
"id": user.ID,
|
||||
"email": user.Email,
|
||||
"created_at": user.CreatedAt,
|
||||
},
|
||||
"consents": consents,
|
||||
"cookie_consents": cookieConsents,
|
||||
"exported_at": time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
// RequestDataExport creates a data export request
|
||||
func (h *Handler) RequestDataExport(c *gin.Context) {
|
||||
userID, err := middleware.GetUserID(c)
|
||||
if err != nil || userID == uuid.Nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid user"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ipAddress := middleware.GetClientIP(c)
|
||||
userAgent := middleware.GetUserAgent(c)
|
||||
|
||||
var requestID uuid.UUID
|
||||
err = h.db.Pool.QueryRow(ctx, `
|
||||
INSERT INTO data_export_requests (user_id, status)
|
||||
VALUES ($1, 'pending')
|
||||
RETURNING id
|
||||
`, userID).Scan(&requestID)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create export request"})
|
||||
return
|
||||
}
|
||||
|
||||
// Log to audit trail
|
||||
h.logAudit(ctx, &userID, "data_export_requested", "export_request", &requestID, nil, ipAddress, userAgent)
|
||||
|
||||
c.JSON(http.StatusAccepted, gin.H{
|
||||
"message": "Export request created. You will be notified when ready.",
|
||||
"request_id": requestID,
|
||||
})
|
||||
}
|
||||
|
||||
// RequestDataDeletion creates a data deletion request
|
||||
func (h *Handler) RequestDataDeletion(c *gin.Context) {
|
||||
userID, err := middleware.GetUserID(c)
|
||||
if err != nil || userID == uuid.Nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid user"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
c.ShouldBindJSON(&req)
|
||||
|
||||
ctx := context.Background()
|
||||
ipAddress := middleware.GetClientIP(c)
|
||||
userAgent := middleware.GetUserAgent(c)
|
||||
|
||||
var requestID uuid.UUID
|
||||
err = h.db.Pool.QueryRow(ctx, `
|
||||
INSERT INTO data_deletion_requests (user_id, status, reason)
|
||||
VALUES ($1, 'pending', $2)
|
||||
RETURNING id
|
||||
`, userID, req.Reason).Scan(&requestID)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create deletion request"})
|
||||
return
|
||||
}
|
||||
|
||||
// Log to audit trail
|
||||
h.logAudit(ctx, &userID, "data_deletion_requested", "deletion_request", &requestID, nil, ipAddress, userAgent)
|
||||
|
||||
c.JSON(http.StatusAccepted, gin.H{
|
||||
"message": "Deletion request created. We will process your request within 30 days.",
|
||||
"request_id": requestID,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user