- Database: kanzlai.audit_log table with RLS, append-only policies (no UPDATE/DELETE), indexes for entity, user, and time queries - Backend: AuditService.Log() with context-based tenant/user/IP/UA extraction, wired into all 7 services (case, deadline, appointment, document, note, party, tenant) - API: GET /api/audit-log with entity_type, entity_id, user_id, from/to date, and pagination filters - Frontend: Protokoll tab on case detail page with chronological audit entries, diff preview, and pagination Required by § 50 BRAO and DSGVO Art. 5(2).
55 lines
1.2 KiB
Go
55 lines
1.2 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type contextKey string
|
|
|
|
const (
|
|
userIDKey contextKey = "user_id"
|
|
tenantIDKey contextKey = "tenant_id"
|
|
ipKey contextKey = "ip_address"
|
|
userAgentKey contextKey = "user_agent"
|
|
)
|
|
|
|
func ContextWithUserID(ctx context.Context, userID uuid.UUID) context.Context {
|
|
return context.WithValue(ctx, userIDKey, userID)
|
|
}
|
|
|
|
func ContextWithTenantID(ctx context.Context, tenantID uuid.UUID) context.Context {
|
|
return context.WithValue(ctx, tenantIDKey, tenantID)
|
|
}
|
|
|
|
func UserFromContext(ctx context.Context) (uuid.UUID, bool) {
|
|
id, ok := ctx.Value(userIDKey).(uuid.UUID)
|
|
return id, ok
|
|
}
|
|
|
|
func TenantFromContext(ctx context.Context) (uuid.UUID, bool) {
|
|
id, ok := ctx.Value(tenantIDKey).(uuid.UUID)
|
|
return id, ok
|
|
}
|
|
|
|
func ContextWithRequestInfo(ctx context.Context, ip, userAgent string) context.Context {
|
|
ctx = context.WithValue(ctx, ipKey, ip)
|
|
ctx = context.WithValue(ctx, userAgentKey, userAgent)
|
|
return ctx
|
|
}
|
|
|
|
func IPFromContext(ctx context.Context) *string {
|
|
if v, ok := ctx.Value(ipKey).(string); ok && v != "" {
|
|
return &v
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func UserAgentFromContext(ctx context.Context) *string {
|
|
if v, ok := ctx.Value(userAgentKey).(string); ok && v != "" {
|
|
return &v
|
|
}
|
|
return nil
|
|
}
|