package api import ( "net/http" "github.com/gin-gonic/gin" ) // Checkpoint represents a checkpoint definition type Checkpoint struct { ID string `json:"id"` Step string `json:"step"` Name string `json:"name"` Type string `json:"type"` BlocksProgress bool `json:"blocksProgress"` RequiresReview string `json:"requiresReview"` AutoValidate bool `json:"autoValidate"` Description string `json:"description"` } // CheckpointHandler handles checkpoint-related requests type CheckpointHandler struct { checkpoints map[string]Checkpoint } // NewCheckpointHandler creates a new checkpoint handler func NewCheckpointHandler() *CheckpointHandler { return &CheckpointHandler{ checkpoints: initCheckpoints(), } } func initCheckpoints() map[string]Checkpoint { return map[string]Checkpoint{ "CP-UC": { ID: "CP-UC", Step: "use-case-workshop", Name: "Use Case Erfassung", Type: "REQUIRED", BlocksProgress: true, RequiresReview: "NONE", AutoValidate: true, Description: "Mindestens ein Use Case muss erfasst sein", }, "CP-SCAN": { ID: "CP-SCAN", Step: "screening", Name: "System Screening", Type: "REQUIRED", BlocksProgress: true, RequiresReview: "NONE", AutoValidate: true, Description: "SBOM und Security Scan müssen abgeschlossen sein", }, "CP-MOD": { ID: "CP-MOD", Step: "modules", Name: "Modul-Zuweisung", Type: "REQUIRED", BlocksProgress: true, RequiresReview: "NONE", AutoValidate: true, Description: "Mindestens ein Compliance-Modul muss zugewiesen sein", }, "CP-REQ": { ID: "CP-REQ", Step: "requirements", Name: "Anforderungen", Type: "REQUIRED", BlocksProgress: true, RequiresReview: "NONE", AutoValidate: true, Description: "Anforderungen müssen aus Regulierungen abgeleitet sein", }, "CP-CTRL": { ID: "CP-CTRL", Step: "controls", Name: "Controls", Type: "REQUIRED", BlocksProgress: true, RequiresReview: "NONE", AutoValidate: true, Description: "Controls müssen den Anforderungen zugeordnet sein", }, "CP-EVI": { ID: "CP-EVI", Step: "evidence", Name: "Nachweise", Type: "REQUIRED", BlocksProgress: true, RequiresReview: "NONE", AutoValidate: true, Description: "Nachweise für Controls müssen dokumentiert sein", }, "CP-CHK": { ID: "CP-CHK", Step: "audit-checklist", Name: "Audit Checklist", Type: "REQUIRED", BlocksProgress: true, RequiresReview: "NONE", AutoValidate: true, Description: "Prüfliste muss generiert und überprüft sein", }, "CP-RISK": { ID: "CP-RISK", Step: "risks", Name: "Risikobewertung", Type: "REQUIRED", BlocksProgress: true, RequiresReview: "NONE", AutoValidate: true, Description: "Kritische Risiken müssen Mitigationsmaßnahmen haben", }, "CP-AI": { ID: "CP-AI", Step: "ai-act", Name: "AI Act Klassifizierung", Type: "REQUIRED", BlocksProgress: true, RequiresReview: "LEGAL", AutoValidate: false, Description: "KI-System muss klassifiziert sein", }, "CP-OBL": { ID: "CP-OBL", Step: "obligations", Name: "Pflichtenübersicht", Type: "REQUIRED", BlocksProgress: true, RequiresReview: "NONE", AutoValidate: true, Description: "Rechtliche Pflichten müssen identifiziert sein", }, "CP-DSFA": { ID: "CP-DSFA", Step: "dsfa", Name: "DSFA", Type: "RECOMMENDED", BlocksProgress: false, RequiresReview: "DSB", AutoValidate: false, Description: "Datenschutz-Folgenabschätzung muss erstellt und genehmigt sein", }, "CP-TOM": { ID: "CP-TOM", Step: "tom", Name: "TOMs", Type: "REQUIRED", BlocksProgress: true, RequiresReview: "NONE", AutoValidate: true, Description: "Technische und organisatorische Maßnahmen müssen definiert sein", }, "CP-VVT": { ID: "CP-VVT", Step: "vvt", Name: "Verarbeitungsverzeichnis", Type: "REQUIRED", BlocksProgress: true, RequiresReview: "DSB", AutoValidate: false, Description: "Verarbeitungsverzeichnis muss vollständig sein", }, } } // GetAll returns all checkpoint definitions func (h *CheckpointHandler) GetAll(c *gin.Context) { tenantID := c.Query("tenantId") checkpointList := make([]Checkpoint, 0, len(h.checkpoints)) for _, cp := range h.checkpoints { checkpointList = append(checkpointList, cp) } SuccessResponse(c, gin.H{ "tenantId": tenantID, "checkpoints": checkpointList, }) } // Validate validates a specific checkpoint func (h *CheckpointHandler) Validate(c *gin.Context) { var req struct { TenantID string `json:"tenantId" binding:"required"` CheckpointID string `json:"checkpointId" binding:"required"` Data map[string]interface{} `json:"data"` } if err := c.ShouldBindJSON(&req); err != nil { ErrorResponse(c, http.StatusBadRequest, err.Error(), "INVALID_REQUEST") return } checkpoint, ok := h.checkpoints[req.CheckpointID] if !ok { ErrorResponse(c, http.StatusNotFound, "Checkpoint not found", "CHECKPOINT_NOT_FOUND") return } // Perform validation based on checkpoint ID result := h.validateCheckpoint(checkpoint, req.Data) SuccessResponse(c, result) } func (h *CheckpointHandler) validateCheckpoint(checkpoint Checkpoint, data map[string]interface{}) CheckpointResult { result := CheckpointResult{ CheckpointID: checkpoint.ID, Passed: true, ValidatedAt: now(), ValidatedBy: "SYSTEM", Errors: []ValidationError{}, Warnings: []ValidationError{}, } // Validation logic based on checkpoint switch checkpoint.ID { case "CP-UC": useCases, _ := data["useCases"].([]interface{}) if len(useCases) == 0 { result.Passed = false result.Errors = append(result.Errors, ValidationError{ RuleID: "uc-min-count", Field: "useCases", Message: "Mindestens ein Use Case muss erstellt werden", Severity: "ERROR", }) } case "CP-SCAN": screening, _ := data["screening"].(map[string]interface{}) if screening == nil || screening["status"] != "COMPLETED" { result.Passed = false result.Errors = append(result.Errors, ValidationError{ RuleID: "scan-complete", Field: "screening", Message: "Security Scan muss abgeschlossen sein", Severity: "ERROR", }) } case "CP-MOD": modules, _ := data["modules"].([]interface{}) if len(modules) == 0 { result.Passed = false result.Errors = append(result.Errors, ValidationError{ RuleID: "mod-min-count", Field: "modules", Message: "Mindestens ein Modul muss zugewiesen werden", Severity: "ERROR", }) } case "CP-RISK": risks, _ := data["risks"].([]interface{}) criticalUnmitigated := 0 for _, r := range risks { risk, ok := r.(map[string]interface{}) if !ok { continue } severity, _ := risk["severity"].(string) if severity == "CRITICAL" || severity == "HIGH" { mitigations, _ := risk["mitigation"].([]interface{}) if len(mitigations) == 0 { criticalUnmitigated++ } } } if criticalUnmitigated > 0 { result.Passed = false result.Errors = append(result.Errors, ValidationError{ RuleID: "critical-risks-mitigated", Field: "risks", Message: "Kritische Risiken ohne Mitigationsmaßnahmen gefunden", Severity: "ERROR", }) } case "CP-DSFA": dsfa, _ := data["dsfa"].(map[string]interface{}) if dsfa == nil { result.Passed = false result.Errors = append(result.Errors, ValidationError{ RuleID: "dsfa-exists", Field: "dsfa", Message: "DSFA muss erstellt werden", Severity: "ERROR", }) } else if dsfa["status"] != "APPROVED" { result.Warnings = append(result.Warnings, ValidationError{ RuleID: "dsfa-approved", Field: "dsfa", Message: "DSFA sollte vom DSB genehmigt werden", Severity: "WARNING", }) } case "CP-TOM": toms, _ := data["toms"].([]interface{}) if len(toms) == 0 { result.Passed = false result.Errors = append(result.Errors, ValidationError{ RuleID: "tom-min-count", Field: "toms", Message: "Mindestens eine TOM muss definiert werden", Severity: "ERROR", }) } case "CP-VVT": vvt, _ := data["vvt"].([]interface{}) if len(vvt) == 0 { result.Passed = false result.Errors = append(result.Errors, ValidationError{ RuleID: "vvt-min-count", Field: "vvt", Message: "Mindestens eine Verarbeitungstätigkeit muss dokumentiert werden", Severity: "ERROR", }) } } return result }