feat: Add Academy, Whistleblower, Incidents, Vendor, DSB, SSO, Reporting, Multi-Tenant and Industry backends
Go handlers, models, stores and migrations for all SDK modules. Updates developer portal navigation and BYOEH page. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,8 +6,8 @@
|
||||
-- Roadmaps table
|
||||
CREATE TABLE IF NOT EXISTS roadmaps (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES namespaces(id) ON DELETE SET NULL,
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES compliance_namespaces(id) ON DELETE SET NULL,
|
||||
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
@@ -93,7 +93,7 @@ CREATE TABLE IF NOT EXISTS roadmap_items (
|
||||
-- Import jobs table
|
||||
CREATE TABLE IF NOT EXISTS roadmap_import_jobs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
roadmap_id UUID REFERENCES roadmaps(id) ON DELETE SET NULL,
|
||||
|
||||
-- File info
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
-- Workshop sessions table
|
||||
CREATE TABLE IF NOT EXISTS workshop_sessions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES namespaces(id) ON DELETE SET NULL,
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES compliance_namespaces(id) ON DELETE SET NULL,
|
||||
|
||||
-- Session info
|
||||
title VARCHAR(255) NOT NULL,
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
-- Portfolios table
|
||||
CREATE TABLE IF NOT EXISTS portfolios (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES namespaces(id) ON DELETE SET NULL,
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
namespace_id UUID REFERENCES compliance_namespaces(id) ON DELETE SET NULL,
|
||||
|
||||
-- Info
|
||||
name VARCHAR(255) NOT NULL,
|
||||
|
||||
159
ai-compliance-sdk/migrations/008_academy_schema.sql
Normal file
159
ai-compliance-sdk/migrations/008_academy_schema.sql
Normal file
@@ -0,0 +1,159 @@
|
||||
-- ============================================================================
|
||||
-- Migration 008: Academy (E-Learning / Compliance Academy) Schema
|
||||
-- Compliance training courses, enrollments, and certificate management
|
||||
-- ============================================================================
|
||||
|
||||
-- Academy courses table
|
||||
CREATE TABLE IF NOT EXISTS academy_courses (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
|
||||
-- Course info
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
category VARCHAR(50) NOT NULL, -- 'dsgvo_basics', 'it_security', 'ai_literacy', 'whistleblower_protection', 'custom'
|
||||
duration_minutes INT DEFAULT 0,
|
||||
required_for_roles JSONB DEFAULT '[]', -- Array of role strings
|
||||
|
||||
-- Status
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Academy lessons table
|
||||
CREATE TABLE IF NOT EXISTS academy_lessons (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
course_id UUID NOT NULL REFERENCES academy_courses(id) ON DELETE CASCADE,
|
||||
|
||||
-- Lesson info
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
lesson_type VARCHAR(50) NOT NULL, -- 'video', 'text', 'quiz', 'interactive'
|
||||
content_url TEXT,
|
||||
duration_minutes INT DEFAULT 0,
|
||||
order_index INT DEFAULT 0,
|
||||
|
||||
-- Quiz questions (only for lesson_type = 'quiz')
|
||||
quiz_questions JSONB DEFAULT '[]', -- Array of {question, options, correct_index, explanation}
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Academy enrollments table
|
||||
CREATE TABLE IF NOT EXISTS academy_enrollments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
course_id UUID NOT NULL REFERENCES academy_courses(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL,
|
||||
|
||||
-- User info (denormalized for reporting)
|
||||
user_name VARCHAR(255) NOT NULL,
|
||||
user_email VARCHAR(255) NOT NULL,
|
||||
|
||||
-- Progress tracking
|
||||
status VARCHAR(50) DEFAULT 'not_started', -- 'not_started', 'in_progress', 'completed', 'expired'
|
||||
progress_percent INT DEFAULT 0, -- 0-100
|
||||
current_lesson_index INT DEFAULT 0,
|
||||
|
||||
-- Timestamps
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
deadline TIMESTAMPTZ,
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Academy certificates table
|
||||
CREATE TABLE IF NOT EXISTS academy_certificates (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
enrollment_id UUID NOT NULL UNIQUE REFERENCES academy_enrollments(id) ON DELETE CASCADE,
|
||||
|
||||
-- Certificate info
|
||||
user_name VARCHAR(255) NOT NULL,
|
||||
course_title VARCHAR(255) NOT NULL,
|
||||
issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
valid_until TIMESTAMPTZ,
|
||||
pdf_url TEXT,
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes
|
||||
-- ============================================================================
|
||||
|
||||
-- Course indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_academy_courses_tenant ON academy_courses(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_academy_courses_category ON academy_courses(tenant_id, category);
|
||||
CREATE INDEX IF NOT EXISTS idx_academy_courses_active ON academy_courses(tenant_id, is_active);
|
||||
|
||||
-- Lesson indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_academy_lessons_course ON academy_lessons(course_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_academy_lessons_order ON academy_lessons(course_id, order_index);
|
||||
|
||||
-- Enrollment indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_academy_enrollments_tenant ON academy_enrollments(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_academy_enrollments_course ON academy_enrollments(course_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_academy_enrollments_user ON academy_enrollments(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_academy_enrollments_status ON academy_enrollments(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_academy_enrollments_deadline ON academy_enrollments(deadline) WHERE deadline IS NOT NULL AND status NOT IN ('completed', 'expired');
|
||||
CREATE INDEX IF NOT EXISTS idx_academy_enrollments_tenant_course ON academy_enrollments(tenant_id, course_id);
|
||||
|
||||
-- Certificate indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_academy_certificates_enrollment ON academy_certificates(enrollment_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_academy_certificates_valid_until ON academy_certificates(valid_until) WHERE valid_until IS NOT NULL;
|
||||
|
||||
-- ============================================================================
|
||||
-- Triggers
|
||||
-- ============================================================================
|
||||
|
||||
-- Reuse existing update_updated_at_column function
|
||||
|
||||
-- Courses trigger
|
||||
DROP TRIGGER IF EXISTS update_academy_courses_updated_at ON academy_courses;
|
||||
CREATE TRIGGER update_academy_courses_updated_at
|
||||
BEFORE UPDATE ON academy_courses
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Lessons trigger
|
||||
DROP TRIGGER IF EXISTS update_academy_lessons_updated_at ON academy_lessons;
|
||||
CREATE TRIGGER update_academy_lessons_updated_at
|
||||
BEFORE UPDATE ON academy_lessons
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Enrollments trigger
|
||||
DROP TRIGGER IF EXISTS update_academy_enrollments_updated_at ON academy_enrollments;
|
||||
CREATE TRIGGER update_academy_enrollments_updated_at
|
||||
BEFORE UPDATE ON academy_enrollments
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Certificates trigger
|
||||
DROP TRIGGER IF EXISTS update_academy_certificates_updated_at ON academy_certificates;
|
||||
CREATE TRIGGER update_academy_certificates_updated_at
|
||||
BEFORE UPDATE ON academy_certificates
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- ============================================================================
|
||||
-- Comments
|
||||
-- ============================================================================
|
||||
|
||||
COMMENT ON TABLE academy_courses IS 'Compliance training courses (DSGVO, IT-Security, AI Literacy, Whistleblower)';
|
||||
COMMENT ON TABLE academy_lessons IS 'Individual lessons within a course (video, text, quiz, interactive)';
|
||||
COMMENT ON TABLE academy_enrollments IS 'User enrollments in courses with progress tracking';
|
||||
COMMENT ON TABLE academy_certificates IS 'Completion certificates issued for finished enrollments';
|
||||
|
||||
COMMENT ON COLUMN academy_courses.category IS 'Course category: dsgvo_basics, it_security, ai_literacy, whistleblower_protection, custom';
|
||||
COMMENT ON COLUMN academy_courses.required_for_roles IS 'JSON array of role names that are required to complete this course';
|
||||
COMMENT ON COLUMN academy_lessons.quiz_questions IS 'JSON array of quiz questions: [{question, options, correct_index, explanation}]';
|
||||
COMMENT ON COLUMN academy_enrollments.status IS 'Enrollment status: not_started, in_progress, completed, expired';
|
||||
COMMENT ON COLUMN academy_enrollments.progress_percent IS 'Course completion percentage (0-100)';
|
||||
COMMENT ON COLUMN academy_certificates.enrollment_id IS 'One-to-one relationship with enrollment (UNIQUE constraint)';
|
||||
141
ai-compliance-sdk/migrations/009_whistleblower_schema.sql
Normal file
141
ai-compliance-sdk/migrations/009_whistleblower_schema.sql
Normal file
@@ -0,0 +1,141 @@
|
||||
-- ============================================================================
|
||||
-- Migration 009: Whistleblower / Hinweisgebersystem (HinSchG)
|
||||
-- Implements the German Whistleblower Protection Act (Hinweisgeberschutzgesetz)
|
||||
-- ============================================================================
|
||||
|
||||
-- Whistleblower reports table
|
||||
CREATE TABLE IF NOT EXISTS whistleblower_reports (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
|
||||
-- Identification
|
||||
reference_number VARCHAR(20) NOT NULL UNIQUE, -- e.g. "WB-2026-0001"
|
||||
access_key VARCHAR(50) NOT NULL UNIQUE, -- for anonymous reporter access
|
||||
|
||||
-- Report content
|
||||
category VARCHAR(50) NOT NULL, -- corruption, fraud, data_protection, discrimination, environment, competition, product_safety, tax_evasion, other
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'new', -- new, acknowledged, under_review, investigation, measures_taken, closed, rejected
|
||||
title VARCHAR(500) NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
|
||||
-- Reporter info (nullable for anonymous reports)
|
||||
is_anonymous BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
reporter_name VARCHAR(255),
|
||||
reporter_email VARCHAR(255),
|
||||
reporter_phone VARCHAR(100),
|
||||
|
||||
-- HinSchG deadlines
|
||||
received_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
deadline_acknowledgment TIMESTAMPTZ NOT NULL, -- 7 days from received_at per HinSchG
|
||||
deadline_feedback TIMESTAMPTZ NOT NULL, -- 3 months from received_at per HinSchG
|
||||
|
||||
-- Status timestamps
|
||||
acknowledged_at TIMESTAMPTZ,
|
||||
closed_at TIMESTAMPTZ,
|
||||
|
||||
-- Assignment
|
||||
assigned_to UUID, -- user responsible for handling
|
||||
|
||||
-- Audit trail (JSONB array of {timestamp, action, user_id, details})
|
||||
audit_trail JSONB NOT NULL DEFAULT '[]',
|
||||
|
||||
-- Resolution
|
||||
resolution TEXT,
|
||||
|
||||
-- Timestamps
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Whistleblower messages table (anonymous communication channel)
|
||||
CREATE TABLE IF NOT EXISTS whistleblower_messages (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
report_id UUID NOT NULL REFERENCES whistleblower_reports(id) ON DELETE CASCADE,
|
||||
|
||||
-- Message
|
||||
direction VARCHAR(30) NOT NULL, -- reporter_to_admin, admin_to_reporter
|
||||
content TEXT NOT NULL,
|
||||
sent_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
read_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- Whistleblower measures table (corrective measures)
|
||||
CREATE TABLE IF NOT EXISTS whistleblower_measures (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
report_id UUID NOT NULL REFERENCES whistleblower_reports(id) ON DELETE CASCADE,
|
||||
|
||||
-- Measure details
|
||||
title VARCHAR(500) NOT NULL,
|
||||
description TEXT,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'planned', -- planned, in_progress, completed
|
||||
responsible VARCHAR(255),
|
||||
due_date TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
|
||||
-- Timestamps
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Sequence table for reference number generation
|
||||
CREATE TABLE IF NOT EXISTS whistleblower_sequences (
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
year INT NOT NULL,
|
||||
last_sequence INT NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (tenant_id, year)
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes
|
||||
-- ============================================================================
|
||||
|
||||
-- Report indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_whistleblower_reports_tenant ON whistleblower_reports(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_whistleblower_reports_access_key ON whistleblower_reports(access_key);
|
||||
CREATE INDEX IF NOT EXISTS idx_whistleblower_reports_reference ON whistleblower_reports(reference_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_whistleblower_reports_status ON whistleblower_reports(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_whistleblower_reports_category ON whistleblower_reports(tenant_id, category);
|
||||
CREATE INDEX IF NOT EXISTS idx_whistleblower_reports_received ON whistleblower_reports(tenant_id, received_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_whistleblower_reports_deadlines ON whistleblower_reports(deadline_acknowledgment, deadline_feedback)
|
||||
WHERE acknowledged_at IS NULL OR closed_at IS NULL;
|
||||
|
||||
-- Message indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_whistleblower_messages_report ON whistleblower_messages(report_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_whistleblower_messages_sent ON whistleblower_messages(report_id, sent_at);
|
||||
|
||||
-- Measure indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_whistleblower_measures_report ON whistleblower_measures(report_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_whistleblower_measures_status ON whistleblower_measures(report_id, status);
|
||||
|
||||
-- ============================================================================
|
||||
-- Triggers
|
||||
-- ============================================================================
|
||||
|
||||
-- Reuse existing update_updated_at_column function
|
||||
|
||||
-- Reports trigger
|
||||
DROP TRIGGER IF EXISTS update_whistleblower_reports_updated_at ON whistleblower_reports;
|
||||
CREATE TRIGGER update_whistleblower_reports_updated_at
|
||||
BEFORE UPDATE ON whistleblower_reports
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Measures trigger
|
||||
DROP TRIGGER IF EXISTS update_whistleblower_measures_updated_at ON whistleblower_measures;
|
||||
CREATE TRIGGER update_whistleblower_measures_updated_at
|
||||
BEFORE UPDATE ON whistleblower_measures
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- ============================================================================
|
||||
-- Comments
|
||||
-- ============================================================================
|
||||
|
||||
COMMENT ON TABLE whistleblower_reports IS 'Whistleblower reports per HinSchG (Hinweisgeberschutzgesetz)';
|
||||
COMMENT ON TABLE whistleblower_messages IS 'Anonymous communication channel between reporter and admin';
|
||||
COMMENT ON TABLE whistleblower_measures IS 'Corrective measures taken for whistleblower reports';
|
||||
COMMENT ON TABLE whistleblower_sequences IS 'Sequence numbers for reference number generation per tenant and year';
|
||||
|
||||
COMMENT ON COLUMN whistleblower_reports.reference_number IS 'Human-readable reference number (e.g. WB-2026-0001)';
|
||||
COMMENT ON COLUMN whistleblower_reports.access_key IS 'Secret key for anonymous reporter to access their report';
|
||||
COMMENT ON COLUMN whistleblower_reports.deadline_acknowledgment IS '7-day deadline per HinSchG §17 Abs. 1';
|
||||
COMMENT ON COLUMN whistleblower_reports.deadline_feedback IS '3-month deadline per HinSchG §17 Abs. 2';
|
||||
COMMENT ON COLUMN whistleblower_reports.audit_trail IS 'JSON array of audit entries tracking all actions on the report';
|
||||
111
ai-compliance-sdk/migrations/010_incidents_schema.sql
Normal file
111
ai-compliance-sdk/migrations/010_incidents_schema.sql
Normal file
@@ -0,0 +1,111 @@
|
||||
-- ============================================================================
|
||||
-- Migration 010: Incident/Breach Management Schema
|
||||
-- DSGVO Art. 33 (Authority Notification) & Art. 34 (Data Subject Notification)
|
||||
--
|
||||
-- Art. 33 requires notification of the supervisory authority within 72 hours
|
||||
-- of becoming aware of a personal data breach, unless the breach is unlikely
|
||||
-- to result in a risk to the rights and freedoms of natural persons.
|
||||
--
|
||||
-- Art. 34 requires notification of affected data subjects without undue delay
|
||||
-- when the breach is likely to result in a high risk to their rights and freedoms.
|
||||
-- ============================================================================
|
||||
|
||||
-- Incident incidents table
|
||||
CREATE TABLE IF NOT EXISTS incident_incidents (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
|
||||
-- Incident info
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
category VARCHAR(50) NOT NULL, -- data_breach, unauthorized_access, data_loss, system_compromise, phishing, ransomware, insider_threat, physical_breach, other
|
||||
status VARCHAR(50) DEFAULT 'detected', -- detected, assessment, containment, notification_required, notification_sent, remediation, closed
|
||||
severity VARCHAR(50) NOT NULL, -- critical, high, medium, low
|
||||
|
||||
-- Detection & reporting
|
||||
detected_at TIMESTAMPTZ NOT NULL,
|
||||
reported_by UUID NOT NULL,
|
||||
|
||||
-- Affected scope
|
||||
affected_data_categories JSONB DEFAULT '[]', -- e.g. ["personal_data", "health_data", "financial_data"]
|
||||
affected_data_subject_count INT DEFAULT 0,
|
||||
affected_systems JSONB DEFAULT '[]', -- e.g. ["crm", "email_server", "database"]
|
||||
|
||||
-- Assessments & notifications (JSONB embedded objects)
|
||||
risk_assessment JSONB, -- {likelihood, impact, risk_level, assessed_at, assessed_by, notes}
|
||||
authority_notification JSONB, -- {status, deadline, submitted_at, authority_name, reference_number, contact_person, notes}
|
||||
data_subject_notification JSONB, -- {required, status, sent_at, affected_count, notification_text, channel}
|
||||
|
||||
-- Resolution
|
||||
root_cause TEXT,
|
||||
lessons_learned TEXT,
|
||||
|
||||
-- Timeline (JSONB array of events)
|
||||
timeline JSONB DEFAULT '[]', -- [{timestamp, action, user_id, details}, ...]
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
closed_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- Incident measures table (corrective and preventive measures)
|
||||
CREATE TABLE IF NOT EXISTS incident_measures (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
incident_id UUID NOT NULL REFERENCES incident_incidents(id) ON DELETE CASCADE,
|
||||
|
||||
-- Measure info
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
measure_type VARCHAR(50) NOT NULL, -- immediate, long_term
|
||||
status VARCHAR(50) DEFAULT 'planned', -- planned, in_progress, completed
|
||||
responsible VARCHAR(255),
|
||||
due_date TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes
|
||||
-- ============================================================================
|
||||
|
||||
-- Incident indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_incident_incidents_tenant ON incident_incidents(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_incident_incidents_status ON incident_incidents(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_incident_incidents_severity ON incident_incidents(tenant_id, severity);
|
||||
CREATE INDEX IF NOT EXISTS idx_incident_incidents_detected_at ON incident_incidents(detected_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_incident_incidents_category ON incident_incidents(tenant_id, category);
|
||||
|
||||
-- Measure indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_incident_measures_incident ON incident_measures(incident_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_incident_measures_status ON incident_measures(incident_id, status);
|
||||
|
||||
-- ============================================================================
|
||||
-- Triggers
|
||||
-- ============================================================================
|
||||
|
||||
-- Reuse existing update_updated_at_column function
|
||||
|
||||
-- Incidents trigger
|
||||
DROP TRIGGER IF EXISTS update_incident_incidents_updated_at ON incident_incidents;
|
||||
CREATE TRIGGER update_incident_incidents_updated_at
|
||||
BEFORE UPDATE ON incident_incidents
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- ============================================================================
|
||||
-- Comments
|
||||
-- ============================================================================
|
||||
|
||||
COMMENT ON TABLE incident_incidents IS 'Security and data breach incidents per DSGVO Art. 33/34';
|
||||
COMMENT ON TABLE incident_measures IS 'Corrective and preventive measures for incidents';
|
||||
|
||||
COMMENT ON COLUMN incident_incidents.detected_at IS 'When the incident was first detected - starts the 72h Art. 33 notification clock';
|
||||
COMMENT ON COLUMN incident_incidents.authority_notification IS 'JSONB: Supervisory authority notification tracking per DSGVO Art. 33 (72h deadline)';
|
||||
COMMENT ON COLUMN incident_incidents.data_subject_notification IS 'JSONB: Data subject notification tracking per DSGVO Art. 34';
|
||||
COMMENT ON COLUMN incident_incidents.risk_assessment IS 'JSONB: Risk assessment with likelihood, impact, and auto-calculated risk level';
|
||||
COMMENT ON COLUMN incident_incidents.timeline IS 'JSONB array: Chronological record of all actions taken during incident response';
|
||||
COMMENT ON COLUMN incident_incidents.affected_data_categories IS 'JSONB array: Categories of personal data affected (e.g. health, financial)';
|
||||
COMMENT ON COLUMN incident_incidents.affected_systems IS 'JSONB array: Systems affected by the incident';
|
||||
COMMENT ON COLUMN incident_measures.measure_type IS 'immediate = short-term containment, long_term = preventive/structural fix';
|
||||
356
ai-compliance-sdk/migrations/011_vendor_compliance_schema.sql
Normal file
356
ai-compliance-sdk/migrations/011_vendor_compliance_schema.sql
Normal file
@@ -0,0 +1,356 @@
|
||||
-- ============================================================================
|
||||
-- Migration 011: Vendor Compliance Schema
|
||||
-- Vendor Management, Contract/AVV Management, Findings, Templates
|
||||
--
|
||||
-- Implements DSGVO Art. 28 (Auftragsverarbeitung) requirements:
|
||||
-- - Vendor registry with risk scoring and classification
|
||||
-- - Contract/AVV document management with AI-assisted review
|
||||
-- - Compliance findings from contract analysis
|
||||
-- - Control instances for vendor-level control assessments
|
||||
-- - Pre-filled templates for vendors, processing activities, and TOMs
|
||||
-- ============================================================================
|
||||
|
||||
-- ============================================================================
|
||||
-- Vendors (Service Provider Registry)
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vendor_vendors (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
|
||||
-- Basic info
|
||||
name VARCHAR(255) NOT NULL,
|
||||
legal_form VARCHAR(100),
|
||||
country VARCHAR(10) NOT NULL DEFAULT 'DE', -- ISO 3166-1 alpha-2
|
||||
address JSONB, -- {street, city, postalCode, country, state}
|
||||
website VARCHAR(500),
|
||||
|
||||
-- Contact
|
||||
contact_name VARCHAR(255),
|
||||
contact_email VARCHAR(255),
|
||||
contact_phone VARCHAR(100),
|
||||
contact_department VARCHAR(255),
|
||||
|
||||
-- Role & Classification
|
||||
role VARCHAR(50) NOT NULL DEFAULT 'PROCESSOR', -- PROCESSOR, CONTROLLER, JOINT_CONTROLLER, SUB_PROCESSOR, THIRD_PARTY
|
||||
service_category VARCHAR(50), -- HOSTING, CRM, ERP, ANALYTICS, etc. (19 categories)
|
||||
service_description TEXT,
|
||||
data_access_level VARCHAR(50) DEFAULT 'NONE', -- NONE, POTENTIAL, ADMINISTRATIVE, CONTENT
|
||||
|
||||
-- Processing & Compliance
|
||||
processing_locations JSONB DEFAULT '[]', -- [{country, region, isPrimary, isEU, isAdequate}]
|
||||
certifications JSONB DEFAULT '[]', -- ["ISO 27001", "SOC 2", etc.]
|
||||
|
||||
-- Risk Scoring (0-100)
|
||||
inherent_risk_score INT DEFAULT 0,
|
||||
residual_risk_score INT DEFAULT 0,
|
||||
manual_risk_adjustment INT,
|
||||
|
||||
-- Contract & Review
|
||||
review_frequency VARCHAR(50) DEFAULT 'ANNUAL', -- QUARTERLY, SEMI_ANNUAL, ANNUAL, BIENNIAL
|
||||
last_review_date TIMESTAMPTZ,
|
||||
next_review_date TIMESTAMPTZ,
|
||||
|
||||
-- Links
|
||||
processing_activity_ids JSONB DEFAULT '[]', -- UUIDs of linked processing activities
|
||||
|
||||
-- Status
|
||||
status VARCHAR(50) DEFAULT 'ACTIVE', -- ACTIVE, INACTIVE, PENDING_REVIEW, TERMINATED
|
||||
|
||||
-- Template reference (if created from template)
|
||||
template_id VARCHAR(100),
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by UUID NOT NULL
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- Contracts (including AVV/DPA)
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vendor_contracts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
vendor_id UUID NOT NULL REFERENCES vendor_vendors(id) ON DELETE CASCADE,
|
||||
|
||||
-- Document info
|
||||
file_name VARCHAR(500),
|
||||
original_name VARCHAR(500),
|
||||
mime_type VARCHAR(100),
|
||||
file_size BIGINT,
|
||||
storage_path VARCHAR(1000), -- MinIO path
|
||||
|
||||
-- Classification
|
||||
document_type VARCHAR(50) NOT NULL, -- AVV, MSA, SLA, SCC, NDA, TOM_ANNEX, CERTIFICATION, SUB_PROCESSOR_LIST
|
||||
|
||||
-- Metadata (extracted or manual)
|
||||
parties JSONB, -- [{name, role, address}]
|
||||
effective_date DATE,
|
||||
expiration_date DATE,
|
||||
auto_renewal BOOLEAN DEFAULT FALSE,
|
||||
renewal_notice_period VARCHAR(100),
|
||||
|
||||
-- Review Status
|
||||
review_status VARCHAR(50) DEFAULT 'PENDING', -- PENDING, IN_PROGRESS, COMPLETED, FAILED
|
||||
review_completed_at TIMESTAMPTZ,
|
||||
compliance_score INT, -- 0-100
|
||||
|
||||
-- Versioning
|
||||
version VARCHAR(50) DEFAULT '1.0',
|
||||
previous_version_id UUID REFERENCES vendor_contracts(id),
|
||||
|
||||
-- Content (extracted text for analysis)
|
||||
extracted_text TEXT,
|
||||
page_count INT,
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_by UUID NOT NULL
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- Findings (from contract reviews)
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vendor_findings (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
contract_id UUID REFERENCES vendor_contracts(id) ON DELETE CASCADE,
|
||||
vendor_id UUID NOT NULL REFERENCES vendor_vendors(id) ON DELETE CASCADE,
|
||||
|
||||
-- Classification
|
||||
finding_type VARCHAR(20) NOT NULL, -- OK, GAP, RISK, UNKNOWN
|
||||
category VARCHAR(50) NOT NULL, -- AVV_CONTENT, SUBPROCESSOR, INCIDENT, AUDIT_RIGHTS, DELETION, TOM, TRANSFER, LIABILITY, SLA, DATA_SUBJECT_RIGHTS, CONFIDENTIALITY, INSTRUCTION, TERMINATION, GENERAL
|
||||
severity VARCHAR(20) NOT NULL, -- LOW, MEDIUM, HIGH, CRITICAL
|
||||
|
||||
-- Content
|
||||
title VARCHAR(500) NOT NULL,
|
||||
description TEXT,
|
||||
recommendation TEXT,
|
||||
|
||||
-- Citations (from contract text)
|
||||
citations JSONB DEFAULT '[]', -- [{documentId, page, startChar, endChar, quotedText, quoteHash}]
|
||||
|
||||
-- Workflow
|
||||
status VARCHAR(50) DEFAULT 'OPEN', -- OPEN, IN_PROGRESS, RESOLVED, ACCEPTED, FALSE_POSITIVE
|
||||
assignee VARCHAR(255),
|
||||
due_date DATE,
|
||||
resolution TEXT,
|
||||
resolved_at TIMESTAMPTZ,
|
||||
resolved_by UUID,
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- Control Instances (applied controls per vendor)
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vendor_control_instances (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
|
||||
vendor_id UUID NOT NULL REFERENCES vendor_vendors(id) ON DELETE CASCADE,
|
||||
|
||||
-- Control reference
|
||||
control_id VARCHAR(100) NOT NULL, -- e.g., VND-TRF-01
|
||||
control_domain VARCHAR(50), -- TRANSFER, AUDIT, DELETION, INCIDENT, SUBPROCESSOR, TOM, CONTRACT, DATA_SUBJECT, SECURITY, GOVERNANCE
|
||||
|
||||
-- Assessment
|
||||
status VARCHAR(50) DEFAULT 'PLANNED', -- PASS, PARTIAL, FAIL, NOT_APPLICABLE, PLANNED
|
||||
evidence_ids JSONB DEFAULT '[]',
|
||||
notes TEXT,
|
||||
|
||||
-- Timing
|
||||
last_assessed_at TIMESTAMPTZ,
|
||||
last_assessed_by UUID,
|
||||
next_assessment_date TIMESTAMPTZ,
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
UNIQUE(tenant_id, vendor_id, control_id)
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- Templates (pre-filled templates for various entity types)
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS compliance_templates (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES compliance_tenants(id) ON DELETE CASCADE, -- NULL for system templates
|
||||
|
||||
-- Template info
|
||||
template_type VARCHAR(50) NOT NULL, -- VENDOR, PROCESSING_ACTIVITY, TOM, CONTROL_SET
|
||||
template_id VARCHAR(100) NOT NULL UNIQUE, -- e.g., tpl-vendor-cloud-iaas
|
||||
category VARCHAR(100), -- HR, SALES, MARKETING, CLOUD_INFRASTRUCTURE, etc.
|
||||
|
||||
-- Content
|
||||
name_de VARCHAR(500) NOT NULL,
|
||||
name_en VARCHAR(500) NOT NULL,
|
||||
description_de TEXT,
|
||||
description_en TEXT,
|
||||
|
||||
-- Template data (full template content as JSONB)
|
||||
template_data JSONB NOT NULL,
|
||||
|
||||
-- Organization
|
||||
industry VARCHAR(100), -- IT, HEALTHCARE, FINANCE, MANUFACTURING, RETAIL, etc.
|
||||
tags JSONB DEFAULT '[]',
|
||||
|
||||
-- Metadata
|
||||
is_system BOOLEAN DEFAULT FALSE, -- true = pre-installed, false = user-created
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
usage_count INT DEFAULT 0,
|
||||
|
||||
-- Audit
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes: Vendors
|
||||
-- ============================================================================
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_vendors_tenant ON vendor_vendors(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_vendors_status ON vendor_vendors(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_vendors_role ON vendor_vendors(tenant_id, role);
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_vendors_service_category ON vendor_vendors(tenant_id, service_category);
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_vendors_next_review ON vendor_vendors(next_review_date)
|
||||
WHERE next_review_date IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_vendors_template_id ON vendor_vendors(template_id)
|
||||
WHERE template_id IS NOT NULL;
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes: Contracts
|
||||
-- ============================================================================
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_contracts_tenant ON vendor_contracts(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_contracts_vendor ON vendor_contracts(vendor_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_contracts_document_type ON vendor_contracts(tenant_id, document_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_contracts_review_status ON vendor_contracts(tenant_id, review_status);
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_contracts_expiration ON vendor_contracts(expiration_date)
|
||||
WHERE expiration_date IS NOT NULL;
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes: Findings
|
||||
-- ============================================================================
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_findings_tenant ON vendor_findings(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_findings_vendor ON vendor_findings(vendor_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_findings_contract ON vendor_findings(contract_id)
|
||||
WHERE contract_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_findings_severity ON vendor_findings(tenant_id, severity);
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_findings_status ON vendor_findings(tenant_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_findings_category ON vendor_findings(tenant_id, category);
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes: Control Instances
|
||||
-- ============================================================================
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_control_instances_tenant ON vendor_control_instances(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_control_instances_vendor ON vendor_control_instances(vendor_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_control_instances_control_id ON vendor_control_instances(control_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_vendor_control_instances_status ON vendor_control_instances(tenant_id, status);
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes: Templates
|
||||
-- ============================================================================
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_compliance_templates_type ON compliance_templates(template_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_compliance_templates_category ON compliance_templates(category)
|
||||
WHERE category IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_compliance_templates_industry ON compliance_templates(industry)
|
||||
WHERE industry IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_compliance_templates_system ON compliance_templates(is_system);
|
||||
CREATE INDEX IF NOT EXISTS idx_compliance_templates_active ON compliance_templates(is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_compliance_templates_template_id ON compliance_templates(template_id);
|
||||
|
||||
-- ============================================================================
|
||||
-- Triggers
|
||||
-- ============================================================================
|
||||
|
||||
-- Reuse existing update_updated_at_column function
|
||||
|
||||
-- Vendors trigger
|
||||
DROP TRIGGER IF EXISTS update_vendor_vendors_updated_at ON vendor_vendors;
|
||||
CREATE TRIGGER update_vendor_vendors_updated_at
|
||||
BEFORE UPDATE ON vendor_vendors
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Contracts trigger
|
||||
DROP TRIGGER IF EXISTS update_vendor_contracts_updated_at ON vendor_contracts;
|
||||
CREATE TRIGGER update_vendor_contracts_updated_at
|
||||
BEFORE UPDATE ON vendor_contracts
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Findings trigger
|
||||
DROP TRIGGER IF EXISTS update_vendor_findings_updated_at ON vendor_findings;
|
||||
CREATE TRIGGER update_vendor_findings_updated_at
|
||||
BEFORE UPDATE ON vendor_findings
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Control instances trigger
|
||||
DROP TRIGGER IF EXISTS update_vendor_control_instances_updated_at ON vendor_control_instances;
|
||||
CREATE TRIGGER update_vendor_control_instances_updated_at
|
||||
BEFORE UPDATE ON vendor_control_instances
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Templates trigger
|
||||
DROP TRIGGER IF EXISTS update_compliance_templates_updated_at ON compliance_templates;
|
||||
CREATE TRIGGER update_compliance_templates_updated_at
|
||||
BEFORE UPDATE ON compliance_templates
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- ============================================================================
|
||||
-- Comments
|
||||
-- ============================================================================
|
||||
|
||||
-- Table comments
|
||||
COMMENT ON TABLE vendor_vendors IS 'Service provider registry for vendor compliance management (DSGVO Art. 28)';
|
||||
COMMENT ON TABLE vendor_contracts IS 'Contract and AVV/DPA document management with AI-assisted review';
|
||||
COMMENT ON TABLE vendor_findings IS 'Compliance findings from contract reviews and vendor assessments';
|
||||
COMMENT ON TABLE vendor_control_instances IS 'Applied controls per vendor with assessment tracking';
|
||||
COMMENT ON TABLE compliance_templates IS 'Pre-filled templates for vendors, processing activities, TOMs, and control sets';
|
||||
|
||||
-- Vendor column comments
|
||||
COMMENT ON COLUMN vendor_vendors.role IS 'DSGVO role: PROCESSOR (Art. 28), CONTROLLER, JOINT_CONTROLLER (Art. 26), SUB_PROCESSOR, THIRD_PARTY';
|
||||
COMMENT ON COLUMN vendor_vendors.data_access_level IS 'Level of access to personal data: NONE, POTENTIAL, ADMINISTRATIVE, CONTENT';
|
||||
COMMENT ON COLUMN vendor_vendors.processing_locations IS 'JSONB array: Data processing locations with EU/adequacy status for transfer assessment';
|
||||
COMMENT ON COLUMN vendor_vendors.certifications IS 'JSONB array: Vendor certifications (ISO 27001, SOC 2, etc.)';
|
||||
COMMENT ON COLUMN vendor_vendors.inherent_risk_score IS 'Risk score (0-100) before controls are applied';
|
||||
COMMENT ON COLUMN vendor_vendors.residual_risk_score IS 'Risk score (0-100) after controls are applied';
|
||||
COMMENT ON COLUMN vendor_vendors.review_frequency IS 'How often the vendor must be reviewed: QUARTERLY, SEMI_ANNUAL, ANNUAL, BIENNIAL';
|
||||
COMMENT ON COLUMN vendor_vendors.processing_activity_ids IS 'JSONB array: UUIDs linking to dsgvo_processing_activities entries';
|
||||
COMMENT ON COLUMN vendor_vendors.template_id IS 'Reference to compliance_templates.template_id if vendor was created from a template';
|
||||
|
||||
-- Contract column comments
|
||||
COMMENT ON COLUMN vendor_contracts.document_type IS 'Contract type: AVV (Auftragsverarbeitungsvertrag), MSA, SLA, SCC (Standard Contractual Clauses), NDA, TOM_ANNEX, CERTIFICATION, SUB_PROCESSOR_LIST';
|
||||
COMMENT ON COLUMN vendor_contracts.storage_path IS 'MinIO object storage path for the uploaded document';
|
||||
COMMENT ON COLUMN vendor_contracts.compliance_score IS 'AI-assessed compliance score (0-100) from contract review';
|
||||
COMMENT ON COLUMN vendor_contracts.extracted_text IS 'Full text extracted from the document for AI analysis';
|
||||
COMMENT ON COLUMN vendor_contracts.previous_version_id IS 'Self-referencing FK for contract version history';
|
||||
|
||||
-- Finding column comments
|
||||
COMMENT ON COLUMN vendor_findings.finding_type IS 'Classification: OK (compliant), GAP (missing clause), RISK (problematic clause), UNKNOWN (could not determine)';
|
||||
COMMENT ON COLUMN vendor_findings.category IS 'DSGVO Art. 28 requirement category the finding relates to';
|
||||
COMMENT ON COLUMN vendor_findings.citations IS 'JSONB array: References to specific contract text passages with page/character offsets';
|
||||
COMMENT ON COLUMN vendor_findings.status IS 'Workflow status: OPEN, IN_PROGRESS, RESOLVED, ACCEPTED (risk accepted), FALSE_POSITIVE';
|
||||
|
||||
-- Control instance column comments
|
||||
COMMENT ON COLUMN vendor_control_instances.control_id IS 'Control identifier (e.g., VND-TRF-01 for transfer controls)';
|
||||
COMMENT ON COLUMN vendor_control_instances.control_domain IS 'Control domain: TRANSFER, AUDIT, DELETION, INCIDENT, SUBPROCESSOR, TOM, CONTRACT, DATA_SUBJECT, SECURITY, GOVERNANCE';
|
||||
COMMENT ON COLUMN vendor_control_instances.status IS 'Assessment result: PASS, PARTIAL, FAIL, NOT_APPLICABLE, PLANNED';
|
||||
COMMENT ON COLUMN vendor_control_instances.evidence_ids IS 'JSONB array: References to evidence documents or contract IDs';
|
||||
|
||||
-- Template column comments
|
||||
COMMENT ON COLUMN compliance_templates.template_type IS 'Template category: VENDOR, PROCESSING_ACTIVITY, TOM, CONTROL_SET';
|
||||
COMMENT ON COLUMN compliance_templates.template_id IS 'Human-readable unique identifier (e.g., tpl-vendor-cloud-iaas)';
|
||||
COMMENT ON COLUMN compliance_templates.template_data IS 'JSONB: Full template content including all pre-filled fields';
|
||||
COMMENT ON COLUMN compliance_templates.is_system IS 'true = pre-installed system template, false = user-created tenant template';
|
||||
COMMENT ON COLUMN compliance_templates.usage_count IS 'Number of times this template has been used to create entities';
|
||||
175
ai-compliance-sdk/migrations/013_dsb_portal_schema.sql
Normal file
175
ai-compliance-sdk/migrations/013_dsb_portal_schema.sql
Normal file
@@ -0,0 +1,175 @@
|
||||
-- ============================================================================
|
||||
-- Migration 013: DSB-as-a-Service Portal Schema
|
||||
-- Datenschutzbeauftragter (Data Protection Officer) Portal
|
||||
--
|
||||
-- Provides a portal for external DSBs to manage multiple client tenants,
|
||||
-- track hours, manage tasks, and communicate.
|
||||
--
|
||||
-- Depends on: 001_rbac_schema.sql (compliance_tenants)
|
||||
-- ============================================================================
|
||||
|
||||
-- ============================================================================
|
||||
-- DSB Assignments: which DSB is assigned to which tenant
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dsb_assignments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
dsb_user_id UUID NOT NULL, -- the DSB user
|
||||
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id),
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active, paused, terminated
|
||||
contract_start DATE NOT NULL,
|
||||
contract_end DATE,
|
||||
monthly_hours_budget DECIMAL(5,1) DEFAULT 0,
|
||||
notes TEXT DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(dsb_user_id, tenant_id)
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- DSB Time Tracking
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dsb_hours (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
assignment_id UUID NOT NULL REFERENCES dsb_assignments(id) ON DELETE CASCADE,
|
||||
date DATE NOT NULL,
|
||||
hours DECIMAL(4,1) NOT NULL,
|
||||
category VARCHAR(50) NOT NULL, -- 'dsfa_review', 'consultation', 'audit', 'training', 'incident_response', 'documentation', 'meeting', 'other'
|
||||
description TEXT NOT NULL,
|
||||
billable BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- DSB Tasks / Queue
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dsb_tasks (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
assignment_id UUID NOT NULL REFERENCES dsb_assignments(id) ON DELETE CASCADE,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT DEFAULT '',
|
||||
category VARCHAR(50) NOT NULL, -- 'dsfa_review', 'dsr_response', 'incident_review', 'audit_preparation', 'policy_review', 'training', 'consultation', 'other'
|
||||
priority VARCHAR(20) NOT NULL DEFAULT 'medium', -- low, medium, high, urgent
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'open', -- open, in_progress, waiting, completed, cancelled
|
||||
due_date DATE,
|
||||
completed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- DSB Communication Log
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dsb_communications (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
assignment_id UUID NOT NULL REFERENCES dsb_assignments(id) ON DELETE CASCADE,
|
||||
direction VARCHAR(10) NOT NULL, -- 'inbound', 'outbound'
|
||||
channel VARCHAR(20) NOT NULL, -- 'email', 'phone', 'meeting', 'portal', 'letter'
|
||||
subject VARCHAR(255) NOT NULL,
|
||||
content TEXT DEFAULT '',
|
||||
participants TEXT DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes: DSB Assignments
|
||||
-- ============================================================================
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_dsb_assignments_dsb_user_id ON dsb_assignments(dsb_user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_dsb_assignments_tenant_id ON dsb_assignments(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_dsb_assignments_status ON dsb_assignments(status);
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes: DSB Hours
|
||||
-- ============================================================================
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_dsb_hours_assignment_id ON dsb_hours(assignment_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_dsb_hours_date ON dsb_hours(date);
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes: DSB Tasks
|
||||
-- ============================================================================
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_dsb_tasks_assignment_id ON dsb_tasks(assignment_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_dsb_tasks_status ON dsb_tasks(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_dsb_tasks_priority ON dsb_tasks(priority);
|
||||
CREATE INDEX IF NOT EXISTS idx_dsb_tasks_due_date ON dsb_tasks(due_date);
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes: DSB Communications
|
||||
-- ============================================================================
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_dsb_communications_assignment_id ON dsb_communications(assignment_id);
|
||||
|
||||
-- ============================================================================
|
||||
-- Triggers
|
||||
-- ============================================================================
|
||||
|
||||
-- Ensure update_updated_at_column() function exists (created in earlier migrations)
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- DSB Assignments trigger
|
||||
DROP TRIGGER IF EXISTS update_dsb_assignments_updated_at ON dsb_assignments;
|
||||
CREATE TRIGGER update_dsb_assignments_updated_at
|
||||
BEFORE UPDATE ON dsb_assignments
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- DSB Tasks trigger
|
||||
DROP TRIGGER IF EXISTS update_dsb_tasks_updated_at ON dsb_tasks;
|
||||
CREATE TRIGGER update_dsb_tasks_updated_at
|
||||
BEFORE UPDATE ON dsb_tasks
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- ============================================================================
|
||||
-- Comments
|
||||
-- ============================================================================
|
||||
|
||||
-- Table comments
|
||||
COMMENT ON TABLE dsb_assignments IS 'DSB-as-a-Service: Maps external Data Protection Officers (DSBs) to client tenants with contract details and hour budgets';
|
||||
COMMENT ON TABLE dsb_hours IS 'DSB-as-a-Service: Time tracking entries for DSB work on assigned tenants, categorized and billable';
|
||||
COMMENT ON TABLE dsb_tasks IS 'DSB-as-a-Service: Task queue for DSB work items per tenant assignment with priority and status tracking';
|
||||
COMMENT ON TABLE dsb_communications IS 'DSB-as-a-Service: Communication log between DSB and client tenant, tracking direction, channel, and content';
|
||||
|
||||
-- DSB Assignments column comments
|
||||
COMMENT ON COLUMN dsb_assignments.dsb_user_id IS 'UUID of the Data Protection Officer user account';
|
||||
COMMENT ON COLUMN dsb_assignments.tenant_id IS 'UUID of the client tenant this DSB is assigned to';
|
||||
COMMENT ON COLUMN dsb_assignments.status IS 'Assignment status: active, paused, or terminated';
|
||||
COMMENT ON COLUMN dsb_assignments.contract_start IS 'Start date of the DSB service contract';
|
||||
COMMENT ON COLUMN dsb_assignments.contract_end IS 'End date of the DSB service contract (NULL for open-ended)';
|
||||
COMMENT ON COLUMN dsb_assignments.monthly_hours_budget IS 'Monthly hour budget allocated for this tenant';
|
||||
COMMENT ON COLUMN dsb_assignments.notes IS 'Internal notes about the assignment';
|
||||
|
||||
-- DSB Hours column comments
|
||||
COMMENT ON COLUMN dsb_hours.assignment_id IS 'Reference to the DSB assignment this time entry belongs to';
|
||||
COMMENT ON COLUMN dsb_hours.date IS 'Date the work was performed';
|
||||
COMMENT ON COLUMN dsb_hours.hours IS 'Number of hours worked (e.g. 1.5)';
|
||||
COMMENT ON COLUMN dsb_hours.category IS 'Work category: dsfa_review, consultation, audit, training, incident_response, documentation, meeting, other';
|
||||
COMMENT ON COLUMN dsb_hours.description IS 'Description of work performed';
|
||||
COMMENT ON COLUMN dsb_hours.billable IS 'Whether this time entry is billable to the client';
|
||||
|
||||
-- DSB Tasks column comments
|
||||
COMMENT ON COLUMN dsb_tasks.assignment_id IS 'Reference to the DSB assignment this task belongs to';
|
||||
COMMENT ON COLUMN dsb_tasks.title IS 'Short title describing the task';
|
||||
COMMENT ON COLUMN dsb_tasks.description IS 'Detailed task description';
|
||||
COMMENT ON COLUMN dsb_tasks.category IS 'Task category: dsfa_review, dsr_response, incident_review, audit_preparation, policy_review, training, consultation, other';
|
||||
COMMENT ON COLUMN dsb_tasks.priority IS 'Task priority: low, medium, high, urgent';
|
||||
COMMENT ON COLUMN dsb_tasks.status IS 'Task status: open, in_progress, waiting, completed, cancelled';
|
||||
COMMENT ON COLUMN dsb_tasks.due_date IS 'Due date for the task (NULL if no deadline)';
|
||||
COMMENT ON COLUMN dsb_tasks.completed_at IS 'Timestamp when the task was completed';
|
||||
|
||||
-- DSB Communications column comments
|
||||
COMMENT ON COLUMN dsb_communications.assignment_id IS 'Reference to the DSB assignment this communication belongs to';
|
||||
COMMENT ON COLUMN dsb_communications.direction IS 'Communication direction: inbound (from client) or outbound (from DSB)';
|
||||
COMMENT ON COLUMN dsb_communications.channel IS 'Communication channel: email, phone, meeting, portal, letter';
|
||||
COMMENT ON COLUMN dsb_communications.subject IS 'Subject line or topic of the communication';
|
||||
COMMENT ON COLUMN dsb_communications.content IS 'Full content or summary of the communication';
|
||||
COMMENT ON COLUMN dsb_communications.participants IS 'Comma-separated list of participants';
|
||||
Reference in New Issue
Block a user