feat(ucca): Pflichtendatenbank v2 (325 Obligations), Trigger-Engine, TOM-Control-Mapping
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 32s
CI / test-python-backend-compliance (push) Successful in 29s
CI / test-python-document-crawler (push) Successful in 20s
CI / test-python-dsms-gateway (push) Successful in 18s

- 9 Regulation-JSON-Dateien (DSGVO 80, AI Act 60, NIS2 40, BDSG 30, TTDSG 20, DSA 35, Data Act 25, EU-Maschinen 15, DORA 20)
- Condition-Tree-Engine fuer automatische Pflichtenselektion (all_of/any_of, 80+ Field-Paths)
- Generischer JSONRegulationModule-Loader mit YAML-Fallback
- Bidirektionales TOM-Control-Mapping (291 Obligation→Control, 92 Control→Obligation)
- Gap-Analyse-Engine (Compliance-%, Priority Actions, Domain Breakdown)
- ScopeDecision→UnifiedFacts Bridge fuer Auto-Profiling
- 4 neue API-Endpoints (assess-from-scope, tom-controls, gap-analysis, reverse-lookup)
- Frontend: Auto-Profiling Button, Regulation-Filter Chips, TOM-Panel, Gap-Analyse-View
- 18 Unit Tests (Condition Engine, v2 Loader, TOM Mapper)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-05 14:51:44 +01:00
parent 2540a2189a
commit 38e278ee3c
32 changed files with 22870 additions and 41 deletions

View File

@@ -1,6 +1,7 @@
package handlers
import (
"fmt"
"net/http"
"strconv"
"time"
@@ -14,23 +15,60 @@ import (
// ObligationsHandlers handles API requests for the generic obligations framework
type ObligationsHandlers struct {
registry *ucca.ObligationsRegistry
store *ucca.ObligationsStore // Optional: for persisting assessments
registry *ucca.ObligationsRegistry
store *ucca.ObligationsStore // Optional: for persisting assessments
tomIndex *ucca.TOMControlIndex
tomMapper *ucca.TOMObligationMapper
gapAnalyzer *ucca.TOMGapAnalyzer
}
// NewObligationsHandlers creates a new ObligationsHandlers instance
func NewObligationsHandlers() *ObligationsHandlers {
return &ObligationsHandlers{
h := &ObligationsHandlers{
registry: ucca.NewObligationsRegistry(),
}
h.initTOM()
return h
}
// NewObligationsHandlersWithStore creates a new ObligationsHandlers with a store
func NewObligationsHandlersWithStore(store *ucca.ObligationsStore) *ObligationsHandlers {
return &ObligationsHandlers{
h := &ObligationsHandlers{
registry: ucca.NewObligationsRegistry(),
store: store,
}
h.initTOM()
return h
}
// initTOM initializes TOM control index, mapper, and gap analyzer
func (h *ObligationsHandlers) initTOM() {
tomIndex, err := ucca.LoadTOMControls()
if err != nil {
fmt.Printf("Warning: Could not load TOM controls: %v\n", err)
return
}
h.tomIndex = tomIndex
// Try to load v2 TOM mapping
mapping, err := ucca.LoadV2TOMMapping()
if err != nil {
// Build mapping from v2 regulation files
regs, err2 := ucca.LoadAllV2Regulations()
if err2 == nil {
var allObligations []ucca.V2Obligation
for _, reg := range regs {
allObligations = append(allObligations, reg.Obligations...)
}
h.tomMapper = ucca.NewTOMObligationMapperFromObligations(tomIndex, allObligations)
}
} else {
h.tomMapper = ucca.NewTOMObligationMapper(tomIndex, mapping)
}
if h.tomMapper != nil {
h.gapAnalyzer = ucca.NewTOMGapAnalyzer(h.tomMapper, tomIndex)
}
}
// RegisterRoutes registers all obligations-related routes
@@ -56,6 +94,14 @@ func (h *ObligationsHandlers) RegisterRoutes(r *gin.RouterGroup) {
// Quick check endpoint (no persistence)
obligations.POST("/quick-check", h.QuickCheck)
// v2: Scope-based assessment
obligations.POST("/assess-from-scope", h.AssessFromScope)
// v2: TOM Control endpoints
obligations.GET("/:id/tom-controls", h.GetTOMControlsForObligation)
obligations.POST("/gap-analysis", h.GapAnalysis)
obligations.GET("/tom-controls/:controlId/obligations", h.GetObligationsForControl)
}
}
@@ -423,6 +469,106 @@ func (h *ObligationsHandlers) QuickCheck(c *gin.Context) {
})
}
// AssessFromScope assesses obligations from a ScopeDecision
// POST /sdk/v1/ucca/obligations/assess-from-scope
func (h *ObligationsHandlers) AssessFromScope(c *gin.Context) {
tenantID := rbac.GetTenantID(c)
if tenantID == uuid.Nil {
tenantID = uuid.New()
}
var scope ucca.ScopeDecision
if err := c.ShouldBindJSON(&scope); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()})
return
}
// Convert scope to facts
facts := ucca.MapScopeToFacts(&scope)
// Evaluate
overview := h.registry.EvaluateAll(tenantID, facts, "")
// Enrich with TOM control requirements if available
if h.tomMapper != nil {
overview.TOMControlRequirements = h.tomMapper.DeriveControlsFromObligations(overview.Obligations)
}
var warnings []string
if len(overview.ApplicableRegulations) == 0 {
warnings = append(warnings, "Keine anwendbaren Regulierungen gefunden. Pruefen Sie die Scope-Angaben.")
}
c.JSON(http.StatusOK, ucca.ObligationsAssessResponse{
Overview: overview,
Warnings: warnings,
})
}
// GetTOMControlsForObligation returns TOM controls linked to an obligation
// GET /sdk/v1/ucca/obligations/:id/tom-controls
func (h *ObligationsHandlers) GetTOMControlsForObligation(c *gin.Context) {
obligationID := c.Param("id")
if h.tomMapper == nil {
c.JSON(http.StatusNotImplemented, gin.H{"error": "TOM mapping not available"})
return
}
controls := h.tomMapper.GetControlsForObligation(obligationID)
controlIDs := h.tomMapper.GetControlIDsForObligation(obligationID)
c.JSON(http.StatusOK, gin.H{
"obligation_id": obligationID,
"control_ids": controlIDs,
"controls": controls,
"count": len(controls),
})
}
// GapAnalysis performs a TOM control gap analysis
// POST /sdk/v1/ucca/obligations/gap-analysis
func (h *ObligationsHandlers) GapAnalysis(c *gin.Context) {
if h.gapAnalyzer == nil {
c.JSON(http.StatusNotImplemented, gin.H{"error": "Gap analysis not available"})
return
}
var req ucca.GapAnalysisRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()})
return
}
result := h.gapAnalyzer.Analyze(&req)
c.JSON(http.StatusOK, result)
}
// GetObligationsForControl returns obligations linked to a TOM control
// GET /sdk/v1/ucca/obligations/tom-controls/:controlId/obligations
func (h *ObligationsHandlers) GetObligationsForControl(c *gin.Context) {
controlID := c.Param("controlId")
if h.tomMapper == nil {
c.JSON(http.StatusNotImplemented, gin.H{"error": "TOM mapping not available"})
return
}
obligationIDs := h.tomMapper.GetObligationsForControl(controlID)
var control *ucca.TOMControl
if h.tomIndex != nil {
control, _ = h.tomIndex.GetControl(controlID)
}
c.JSON(http.StatusOK, gin.H{
"control_id": controlID,
"control": control,
"obligation_ids": obligationIDs,
"count": len(obligationIDs),
})
}
// ============================================================================
// Helper Functions
// ============================================================================