Initial commit: breakpilot-compliance - Compliance SDK Platform
Services: Admin-Compliance, Backend-Compliance, AI-Compliance-SDK, Consent-SDK, Developer-Portal, PCA-Platform, DSMS Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
337
ai-compliance-sdk/internal/audit/trail_builder.go
Normal file
337
ai-compliance-sdk/internal/audit/trail_builder.go
Normal file
@@ -0,0 +1,337 @@
|
||||
package audit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// TrailBuilder helps construct structured audit entries
|
||||
type TrailBuilder struct {
|
||||
store *Store
|
||||
}
|
||||
|
||||
// NewTrailBuilder creates a new trail builder
|
||||
func NewTrailBuilder(store *Store) *TrailBuilder {
|
||||
return &TrailBuilder{store: store}
|
||||
}
|
||||
|
||||
// LLMEntryBuilder builds LLM audit entries
|
||||
type LLMEntryBuilder struct {
|
||||
entry *LLMAuditEntry
|
||||
store *Store
|
||||
}
|
||||
|
||||
// NewLLMEntry creates a new LLM audit entry builder
|
||||
func (tb *TrailBuilder) NewLLMEntry() *LLMEntryBuilder {
|
||||
return &LLMEntryBuilder{
|
||||
entry: &LLMAuditEntry{
|
||||
ID: uuid.New(),
|
||||
PIITypesDetected: []string{},
|
||||
PolicyViolations: []string{},
|
||||
DataCategoriesAccessed: []string{},
|
||||
RequestMetadata: make(map[string]any),
|
||||
CreatedAt: time.Now().UTC(),
|
||||
},
|
||||
store: tb.store,
|
||||
}
|
||||
}
|
||||
|
||||
// WithTenant sets the tenant ID
|
||||
func (b *LLMEntryBuilder) WithTenant(tenantID uuid.UUID) *LLMEntryBuilder {
|
||||
b.entry.TenantID = tenantID
|
||||
return b
|
||||
}
|
||||
|
||||
// WithNamespace sets the namespace ID
|
||||
func (b *LLMEntryBuilder) WithNamespace(namespaceID uuid.UUID) *LLMEntryBuilder {
|
||||
b.entry.NamespaceID = &namespaceID
|
||||
return b
|
||||
}
|
||||
|
||||
// WithUser sets the user ID
|
||||
func (b *LLMEntryBuilder) WithUser(userID uuid.UUID) *LLMEntryBuilder {
|
||||
b.entry.UserID = userID
|
||||
return b
|
||||
}
|
||||
|
||||
// WithSession sets the session ID
|
||||
func (b *LLMEntryBuilder) WithSession(sessionID string) *LLMEntryBuilder {
|
||||
b.entry.SessionID = sessionID
|
||||
return b
|
||||
}
|
||||
|
||||
// WithOperation sets the operation type
|
||||
func (b *LLMEntryBuilder) WithOperation(operation string) *LLMEntryBuilder {
|
||||
b.entry.Operation = operation
|
||||
return b
|
||||
}
|
||||
|
||||
// WithModel sets the model used
|
||||
func (b *LLMEntryBuilder) WithModel(model, provider string) *LLMEntryBuilder {
|
||||
b.entry.ModelUsed = model
|
||||
b.entry.Provider = provider
|
||||
return b
|
||||
}
|
||||
|
||||
// WithPrompt sets prompt-related fields
|
||||
func (b *LLMEntryBuilder) WithPrompt(hash string, length int) *LLMEntryBuilder {
|
||||
b.entry.PromptHash = hash
|
||||
b.entry.PromptLength = length
|
||||
return b
|
||||
}
|
||||
|
||||
// WithResponse sets response-related fields
|
||||
func (b *LLMEntryBuilder) WithResponse(length int) *LLMEntryBuilder {
|
||||
b.entry.ResponseLength = length
|
||||
return b
|
||||
}
|
||||
|
||||
// WithUsage sets token usage and duration
|
||||
func (b *LLMEntryBuilder) WithUsage(tokens int, durationMS int) *LLMEntryBuilder {
|
||||
b.entry.TokensUsed = tokens
|
||||
b.entry.DurationMS = durationMS
|
||||
return b
|
||||
}
|
||||
|
||||
// WithPII sets PII detection fields
|
||||
func (b *LLMEntryBuilder) WithPII(detected bool, types []string, redacted bool) *LLMEntryBuilder {
|
||||
b.entry.PIIDetected = detected
|
||||
b.entry.PIITypesDetected = types
|
||||
b.entry.PIIRedacted = redacted
|
||||
return b
|
||||
}
|
||||
|
||||
// WithPolicy sets policy-related fields
|
||||
func (b *LLMEntryBuilder) WithPolicy(policyID *uuid.UUID, violations []string) *LLMEntryBuilder {
|
||||
b.entry.PolicyID = policyID
|
||||
b.entry.PolicyViolations = violations
|
||||
return b
|
||||
}
|
||||
|
||||
// WithDataCategories sets accessed data categories
|
||||
func (b *LLMEntryBuilder) WithDataCategories(categories []string) *LLMEntryBuilder {
|
||||
b.entry.DataCategoriesAccessed = categories
|
||||
return b
|
||||
}
|
||||
|
||||
// WithError sets error message
|
||||
func (b *LLMEntryBuilder) WithError(errMsg string) *LLMEntryBuilder {
|
||||
b.entry.ErrorMessage = errMsg
|
||||
return b
|
||||
}
|
||||
|
||||
// WithMetadata sets request metadata
|
||||
func (b *LLMEntryBuilder) WithMetadata(metadata map[string]any) *LLMEntryBuilder {
|
||||
b.entry.RequestMetadata = metadata
|
||||
return b
|
||||
}
|
||||
|
||||
// AddMetadata adds a key-value pair to metadata
|
||||
func (b *LLMEntryBuilder) AddMetadata(key string, value any) *LLMEntryBuilder {
|
||||
if b.entry.RequestMetadata == nil {
|
||||
b.entry.RequestMetadata = make(map[string]any)
|
||||
}
|
||||
b.entry.RequestMetadata[key] = value
|
||||
return b
|
||||
}
|
||||
|
||||
// Build returns the built entry
|
||||
func (b *LLMEntryBuilder) Build() *LLMAuditEntry {
|
||||
return b.entry
|
||||
}
|
||||
|
||||
// Save persists the entry to the database
|
||||
func (b *LLMEntryBuilder) Save(ctx context.Context) error {
|
||||
return b.store.CreateLLMAuditEntry(ctx, b.entry)
|
||||
}
|
||||
|
||||
// GeneralEntryBuilder builds general audit entries
|
||||
type GeneralEntryBuilder struct {
|
||||
entry *GeneralAuditEntry
|
||||
store *Store
|
||||
}
|
||||
|
||||
// NewGeneralEntry creates a new general audit entry builder
|
||||
func (tb *TrailBuilder) NewGeneralEntry() *GeneralEntryBuilder {
|
||||
return &GeneralEntryBuilder{
|
||||
entry: &GeneralAuditEntry{
|
||||
ID: uuid.New(),
|
||||
OldValues: make(map[string]any),
|
||||
NewValues: make(map[string]any),
|
||||
CreatedAt: time.Now().UTC(),
|
||||
},
|
||||
store: tb.store,
|
||||
}
|
||||
}
|
||||
|
||||
// WithTenant sets the tenant ID
|
||||
func (b *GeneralEntryBuilder) WithTenant(tenantID uuid.UUID) *GeneralEntryBuilder {
|
||||
b.entry.TenantID = tenantID
|
||||
return b
|
||||
}
|
||||
|
||||
// WithNamespace sets the namespace ID
|
||||
func (b *GeneralEntryBuilder) WithNamespace(namespaceID uuid.UUID) *GeneralEntryBuilder {
|
||||
b.entry.NamespaceID = &namespaceID
|
||||
return b
|
||||
}
|
||||
|
||||
// WithUser sets the user ID
|
||||
func (b *GeneralEntryBuilder) WithUser(userID uuid.UUID) *GeneralEntryBuilder {
|
||||
b.entry.UserID = userID
|
||||
return b
|
||||
}
|
||||
|
||||
// WithAction sets the action
|
||||
func (b *GeneralEntryBuilder) WithAction(action string) *GeneralEntryBuilder {
|
||||
b.entry.Action = action
|
||||
return b
|
||||
}
|
||||
|
||||
// WithResource sets the resource type and ID
|
||||
func (b *GeneralEntryBuilder) WithResource(resourceType string, resourceID *uuid.UUID) *GeneralEntryBuilder {
|
||||
b.entry.ResourceType = resourceType
|
||||
b.entry.ResourceID = resourceID
|
||||
return b
|
||||
}
|
||||
|
||||
// WithOldValues sets the old values
|
||||
func (b *GeneralEntryBuilder) WithOldValues(values map[string]any) *GeneralEntryBuilder {
|
||||
b.entry.OldValues = values
|
||||
return b
|
||||
}
|
||||
|
||||
// WithNewValues sets the new values
|
||||
func (b *GeneralEntryBuilder) WithNewValues(values map[string]any) *GeneralEntryBuilder {
|
||||
b.entry.NewValues = values
|
||||
return b
|
||||
}
|
||||
|
||||
// WithClient sets client information
|
||||
func (b *GeneralEntryBuilder) WithClient(ipAddress, userAgent string) *GeneralEntryBuilder {
|
||||
b.entry.IPAddress = ipAddress
|
||||
b.entry.UserAgent = userAgent
|
||||
return b
|
||||
}
|
||||
|
||||
// WithReason sets the reason for the action
|
||||
func (b *GeneralEntryBuilder) WithReason(reason string) *GeneralEntryBuilder {
|
||||
b.entry.Reason = reason
|
||||
return b
|
||||
}
|
||||
|
||||
// Build returns the built entry
|
||||
func (b *GeneralEntryBuilder) Build() *GeneralAuditEntry {
|
||||
return b.entry
|
||||
}
|
||||
|
||||
// Save persists the entry to the database
|
||||
func (b *GeneralEntryBuilder) Save(ctx context.Context) error {
|
||||
return b.store.CreateGeneralAuditEntry(ctx, b.entry)
|
||||
}
|
||||
|
||||
// Common audit action types
|
||||
const (
|
||||
ActionCreate = "create"
|
||||
ActionUpdate = "update"
|
||||
ActionDelete = "delete"
|
||||
ActionRead = "read"
|
||||
ActionExport = "export"
|
||||
ActionGrant = "grant"
|
||||
ActionRevoke = "revoke"
|
||||
ActionLogin = "login"
|
||||
ActionLogout = "logout"
|
||||
ActionFailed = "failed"
|
||||
)
|
||||
|
||||
// Common resource types
|
||||
const (
|
||||
ResourceTenant = "tenant"
|
||||
ResourceNamespace = "namespace"
|
||||
ResourceRole = "role"
|
||||
ResourceUserRole = "user_role"
|
||||
ResourcePolicy = "llm_policy"
|
||||
ResourceAPIKey = "api_key"
|
||||
ResourceEvidence = "evidence"
|
||||
ResourceControl = "control"
|
||||
)
|
||||
|
||||
// Convenience methods for common operations
|
||||
|
||||
// LogRoleAssignment creates an audit entry for role assignment
|
||||
func (tb *TrailBuilder) LogRoleAssignment(ctx context.Context, tenantID, userID, targetUserID, roleID uuid.UUID, grantedBy uuid.UUID, ipAddress, userAgent string) error {
|
||||
return tb.NewGeneralEntry().
|
||||
WithTenant(tenantID).
|
||||
WithUser(grantedBy).
|
||||
WithAction(ActionGrant).
|
||||
WithResource(ResourceUserRole, &roleID).
|
||||
WithNewValues(map[string]any{
|
||||
"target_user_id": targetUserID.String(),
|
||||
"role_id": roleID.String(),
|
||||
}).
|
||||
WithClient(ipAddress, userAgent).
|
||||
Save(ctx)
|
||||
}
|
||||
|
||||
// LogRoleRevocation creates an audit entry for role revocation
|
||||
func (tb *TrailBuilder) LogRoleRevocation(ctx context.Context, tenantID, userID, targetUserID, roleID uuid.UUID, revokedBy uuid.UUID, reason, ipAddress, userAgent string) error {
|
||||
return tb.NewGeneralEntry().
|
||||
WithTenant(tenantID).
|
||||
WithUser(revokedBy).
|
||||
WithAction(ActionRevoke).
|
||||
WithResource(ResourceUserRole, &roleID).
|
||||
WithOldValues(map[string]any{
|
||||
"target_user_id": targetUserID.String(),
|
||||
"role_id": roleID.String(),
|
||||
}).
|
||||
WithReason(reason).
|
||||
WithClient(ipAddress, userAgent).
|
||||
Save(ctx)
|
||||
}
|
||||
|
||||
// LogPolicyChange creates an audit entry for LLM policy changes
|
||||
func (tb *TrailBuilder) LogPolicyChange(ctx context.Context, tenantID, userID, policyID uuid.UUID, action string, oldValues, newValues map[string]any, ipAddress, userAgent string) error {
|
||||
return tb.NewGeneralEntry().
|
||||
WithTenant(tenantID).
|
||||
WithUser(userID).
|
||||
WithAction(action).
|
||||
WithResource(ResourcePolicy, &policyID).
|
||||
WithOldValues(oldValues).
|
||||
WithNewValues(newValues).
|
||||
WithClient(ipAddress, userAgent).
|
||||
Save(ctx)
|
||||
}
|
||||
|
||||
// LogNamespaceAccess creates an audit entry for namespace access
|
||||
func (tb *TrailBuilder) LogNamespaceAccess(ctx context.Context, tenantID, userID, namespaceID uuid.UUID, action string, ipAddress, userAgent string) error {
|
||||
return tb.NewGeneralEntry().
|
||||
WithTenant(tenantID).
|
||||
WithUser(userID).
|
||||
WithNamespace(namespaceID).
|
||||
WithAction(action).
|
||||
WithResource(ResourceNamespace, &namespaceID).
|
||||
WithClient(ipAddress, userAgent).
|
||||
Save(ctx)
|
||||
}
|
||||
|
||||
// LogDataExport creates an audit entry for data export
|
||||
func (tb *TrailBuilder) LogDataExport(ctx context.Context, tenantID, userID uuid.UUID, namespaceID *uuid.UUID, resourceType, format string, recordCount int, ipAddress, userAgent string) error {
|
||||
builder := tb.NewGeneralEntry().
|
||||
WithTenant(tenantID).
|
||||
WithUser(userID).
|
||||
WithAction(ActionExport).
|
||||
WithResource(resourceType, nil).
|
||||
WithNewValues(map[string]any{
|
||||
"format": format,
|
||||
"record_count": recordCount,
|
||||
}).
|
||||
WithClient(ipAddress, userAgent)
|
||||
|
||||
if namespaceID != nil {
|
||||
builder.WithNamespace(*namespaceID)
|
||||
}
|
||||
|
||||
return builder.Save(ctx)
|
||||
}
|
||||
Reference in New Issue
Block a user