The admin-v2 application was incomplete in the repository. This commit restores all missing components: - Admin pages (76 pages): dashboard, ai, compliance, dsgvo, education, infrastructure, communication, development, onboarding, rbac - SDK pages (45 pages): tom, dsfa, vvt, loeschfristen, einwilligungen, vendor-compliance, tom-generator, dsr, and more - Developer portal (25 pages): API docs, SDK guides, frameworks - All components, lib files, hooks, and types - Updated package.json with all dependencies The issue was caused by incomplete initial repository state - the full admin-v2 codebase existed in backend/admin-v2 and docs-src/admin-v2 but was never fully synced to the main admin-v2 directory. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
396 lines
15 KiB
TypeScript
396 lines
15 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* TOM - Technische und Organisatorische Massnahmen
|
|
*
|
|
* Art. 32 DSGVO - Sicherheit der Verarbeitung
|
|
*/
|
|
|
|
import { useState } from 'react'
|
|
import { PagePurpose } from '@/components/common/PagePurpose'
|
|
|
|
interface TOMCategory {
|
|
id: string
|
|
title: string
|
|
article: string
|
|
description: string
|
|
measures: {
|
|
name: string
|
|
description: string
|
|
status: 'implemented' | 'partial' | 'planned' | 'not_applicable'
|
|
evidence?: string
|
|
lastReview?: string
|
|
}[]
|
|
}
|
|
|
|
export default function TOMPage() {
|
|
const [expandedCategory, setExpandedCategory] = useState<string | null>('encryption')
|
|
|
|
const tomCategories: TOMCategory[] = [
|
|
{
|
|
id: 'encryption',
|
|
title: 'Verschluesselung',
|
|
article: 'Art. 32 Abs. 1 lit. a',
|
|
description: 'Pseudonymisierung und Verschluesselung personenbezogener Daten',
|
|
measures: [
|
|
{
|
|
name: 'TLS 1.3 fuer alle Verbindungen',
|
|
description: 'Alle HTTP-Verbindungen werden ueber HTTPS mit TLS 1.3 verschluesselt',
|
|
status: 'implemented',
|
|
evidence: 'SSL Labs A+ Rating, Nginx Config',
|
|
lastReview: '2024-12-01'
|
|
},
|
|
{
|
|
name: 'Verschluesselung ruhender Daten',
|
|
description: 'PostgreSQL-Datenbank mit AES-256 Verschluesselung (pgcrypto)',
|
|
status: 'implemented',
|
|
evidence: 'PostgreSQL Config, Encryption Keys in Vault',
|
|
lastReview: '2024-12-01'
|
|
},
|
|
{
|
|
name: 'E-Mail-Verschluesselung',
|
|
description: 'Optionale PGP-Verschluesselung fuer sensible E-Mails',
|
|
status: 'partial',
|
|
evidence: 'PGP-Keys verfuegbar, nicht fuer alle Empfaenger',
|
|
},
|
|
{
|
|
name: 'Backup-Verschluesselung',
|
|
description: 'Alle Backups werden mit AES-256 verschluesselt gespeichert',
|
|
status: 'implemented',
|
|
evidence: 'restic Backup Config',
|
|
lastReview: '2024-11-15'
|
|
},
|
|
]
|
|
},
|
|
{
|
|
id: 'access_control',
|
|
title: 'Zugriffskontrolle',
|
|
article: 'Art. 32 Abs. 1 lit. b',
|
|
description: 'Faehigkeit, Vertraulichkeit und Integritaet auf Dauer sicherzustellen',
|
|
measures: [
|
|
{
|
|
name: 'Role-Based Access Control (RBAC)',
|
|
description: 'Zugriff basierend auf Rollen: user, admin, data_protection_officer',
|
|
status: 'implemented',
|
|
evidence: 'consent-service/internal/middleware/auth.go',
|
|
lastReview: '2024-12-01'
|
|
},
|
|
{
|
|
name: 'Multi-Faktor-Authentifizierung',
|
|
description: '2FA fuer Admin-Zugaenge (TOTP)',
|
|
status: 'implemented',
|
|
evidence: 'Auth-Service Implementation',
|
|
lastReview: '2024-12-01'
|
|
},
|
|
{
|
|
name: 'Passwort-Policy',
|
|
description: 'Min. 12 Zeichen, Komplexitaetsanforderungen, bcrypt-Hashing',
|
|
status: 'implemented',
|
|
evidence: 'consent-service/internal/services/auth_service.go',
|
|
lastReview: '2024-12-01'
|
|
},
|
|
{
|
|
name: 'Session-Management',
|
|
description: 'JWT mit 24h Ablauf, Refresh-Token-Rotation',
|
|
status: 'implemented',
|
|
evidence: 'JWT Config, Token-Rotation Logic',
|
|
lastReview: '2024-12-01'
|
|
},
|
|
{
|
|
name: 'IP-Whitelisting Admin',
|
|
description: 'Admin-Zugang nur von definierten IP-Bereichen',
|
|
status: 'planned',
|
|
},
|
|
]
|
|
},
|
|
{
|
|
id: 'availability',
|
|
title: 'Verfuegbarkeit & Belastbarkeit',
|
|
article: 'Art. 32 Abs. 1 lit. b',
|
|
description: 'Faehigkeit, Verfuegbarkeit und Belastbarkeit der Systeme sicherzustellen',
|
|
measures: [
|
|
{
|
|
name: 'Automatische Backups',
|
|
description: 'Taegliche inkrementelle Backups, woechentliche Vollbackups',
|
|
status: 'implemented',
|
|
evidence: 'restic + cron Jobs',
|
|
lastReview: '2024-11-15'
|
|
},
|
|
{
|
|
name: 'Disaster Recovery Plan',
|
|
description: 'Dokumentierter Wiederherstellungsplan mit RTO < 4h',
|
|
status: 'partial',
|
|
evidence: 'DR-Dokumentation in Arbeit',
|
|
},
|
|
{
|
|
name: 'Health Monitoring',
|
|
description: 'Prometheus + Grafana fuer System-Monitoring',
|
|
status: 'implemented',
|
|
evidence: 'Monitoring Stack deployed',
|
|
lastReview: '2024-12-01'
|
|
},
|
|
{
|
|
name: 'Rate Limiting',
|
|
description: 'API Rate Limiting zum Schutz vor DDoS',
|
|
status: 'implemented',
|
|
evidence: 'Nginx Rate Limit Config',
|
|
lastReview: '2024-12-01'
|
|
},
|
|
]
|
|
},
|
|
{
|
|
id: 'restore',
|
|
title: 'Wiederherstellung',
|
|
article: 'Art. 32 Abs. 1 lit. c',
|
|
description: 'Rasche Wiederherstellung nach physischem oder technischem Zwischenfall',
|
|
measures: [
|
|
{
|
|
name: 'Backup-Restore-Tests',
|
|
description: 'Quartalsweise Tests der Backup-Wiederherstellung',
|
|
status: 'partial',
|
|
evidence: 'Letzter Test: 2024-10-15',
|
|
},
|
|
{
|
|
name: 'Dokumentierte Recovery-Prozeduren',
|
|
description: 'Schritt-fuer-Schritt Anleitungen fuer verschiedene Szenarien',
|
|
status: 'implemented',
|
|
evidence: 'docs/disaster-recovery/',
|
|
lastReview: '2024-11-01'
|
|
},
|
|
{
|
|
name: 'Redundante Datenhaltung',
|
|
description: 'Datenbank-Replikation auf zweitem Server',
|
|
status: 'planned',
|
|
},
|
|
]
|
|
},
|
|
{
|
|
id: 'review',
|
|
title: 'Regelmaessige Ueberpruefung',
|
|
article: 'Art. 32 Abs. 1 lit. d',
|
|
description: 'Verfahren zur regelmaessigen Ueberpruefung, Bewertung und Evaluierung',
|
|
measures: [
|
|
{
|
|
name: 'Security Audits',
|
|
description: 'Jaehrliche externe Security-Audits',
|
|
status: 'implemented',
|
|
evidence: 'Letzter Audit: 2024-09',
|
|
lastReview: '2024-09-15'
|
|
},
|
|
{
|
|
name: 'Penetration Tests',
|
|
description: 'Jaehrliche Penetrationstests durch externen Dienstleister',
|
|
status: 'partial',
|
|
evidence: 'Naechster Test geplant: Q1 2025',
|
|
},
|
|
{
|
|
name: 'Vulnerability Scanning',
|
|
description: 'Woechentliche automatisierte Schwachstellen-Scans',
|
|
status: 'implemented',
|
|
evidence: 'GitHub Dependabot + Trivy',
|
|
lastReview: '2024-12-01'
|
|
},
|
|
{
|
|
name: 'TOM-Review',
|
|
description: 'Jaehrliche Ueberpruefung aller TOMs',
|
|
status: 'implemented',
|
|
evidence: 'Diese Seite',
|
|
lastReview: '2024-12-01'
|
|
},
|
|
]
|
|
},
|
|
{
|
|
id: 'logging',
|
|
title: 'Protokollierung & Audit-Trail',
|
|
article: 'Art. 32 Abs. 2',
|
|
description: 'Nachweis der Einhaltung durch Protokollierung',
|
|
measures: [
|
|
{
|
|
name: 'Zugriffs-Logging',
|
|
description: 'Protokollierung aller Zugriffe auf personenbezogene Daten',
|
|
status: 'implemented',
|
|
evidence: 'consent-service Audit-Logs',
|
|
lastReview: '2024-12-01'
|
|
},
|
|
{
|
|
name: 'Aenderungs-Historie',
|
|
description: 'Vollstaendige Historie aller Datenänderungen',
|
|
status: 'implemented',
|
|
evidence: 'audit_logs Tabelle in PostgreSQL',
|
|
lastReview: '2024-12-01'
|
|
},
|
|
{
|
|
name: 'Admin-Aktionen-Log',
|
|
description: 'Protokollierung aller administrativen Aktionen',
|
|
status: 'implemented',
|
|
evidence: 'Admin Action Logger',
|
|
lastReview: '2024-12-01'
|
|
},
|
|
{
|
|
name: 'Log-Aufbewahrung',
|
|
description: 'Logs werden 2 Jahre aufbewahrt, dann automatisch geloescht',
|
|
status: 'implemented',
|
|
evidence: 'Log Retention Policy',
|
|
lastReview: '2024-11-01'
|
|
},
|
|
]
|
|
},
|
|
]
|
|
|
|
const getStatusBadge = (status: string) => {
|
|
switch (status) {
|
|
case 'implemented':
|
|
return <span className="px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800">Umgesetzt</span>
|
|
case 'partial':
|
|
return <span className="px-2 py-1 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800">Teilweise</span>
|
|
case 'planned':
|
|
return <span className="px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800">Geplant</span>
|
|
case 'not_applicable':
|
|
return <span className="px-2 py-1 rounded-full text-xs font-medium bg-slate-100 text-slate-600">N/A</span>
|
|
default:
|
|
return null
|
|
}
|
|
}
|
|
|
|
const calculateCategoryScore = (category: TOMCategory) => {
|
|
const total = category.measures.length
|
|
const implemented = category.measures.filter(m => m.status === 'implemented').length
|
|
const partial = category.measures.filter(m => m.status === 'partial').length
|
|
return Math.round(((implemented + partial * 0.5) / total) * 100)
|
|
}
|
|
|
|
const calculateOverallScore = () => {
|
|
let totalMeasures = 0
|
|
let implementedScore = 0
|
|
tomCategories.forEach(cat => {
|
|
cat.measures.forEach(m => {
|
|
totalMeasures++
|
|
if (m.status === 'implemented') implementedScore += 1
|
|
else if (m.status === 'partial') implementedScore += 0.5
|
|
})
|
|
})
|
|
return Math.round((implementedScore / totalMeasures) * 100)
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
<PagePurpose
|
|
title="Technische & Organisatorische Massnahmen (TOMs)"
|
|
purpose="Dokumentation aller Sicherheitsmassnahmen gemaess Art. 32 DSGVO. Diese Seite dient als Nachweis fuer Auditoren und den DSB."
|
|
audience={['DSB', 'IT-Sicherheit', 'Auditoren', 'Geschaeftsfuehrung']}
|
|
gdprArticles={['Art. 32 (Sicherheit der Verarbeitung)']}
|
|
architecture={{
|
|
services: ['consent-service (Go)', 'backend (Python)', 'Nginx', 'PostgreSQL'],
|
|
databases: ['PostgreSQL (verschluesselt)', 'MinIO (Backups)'],
|
|
}}
|
|
relatedPages={[
|
|
{ name: 'DSMS', href: '/compliance/dsms', description: 'Datenschutz-Management-System' },
|
|
{ name: 'Security', href: '/infrastructure/security', description: 'DevSecOps Dashboard' },
|
|
{ name: 'Audit', href: '/compliance/audit', description: 'Audit-Dokumentation' },
|
|
]}
|
|
collapsible={true}
|
|
defaultCollapsed={true}
|
|
/>
|
|
|
|
{/* Overall Score */}
|
|
<div className="bg-white rounded-xl border border-slate-200 p-6 mb-6">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h2 className="text-lg font-semibold text-slate-900">TOM-Umsetzungsgrad</h2>
|
|
<p className="text-sm text-slate-500 mt-1">Gesamtfortschritt aller technischen und organisatorischen Massnahmen</p>
|
|
</div>
|
|
<div className="text-right">
|
|
<div className={`text-4xl font-bold ${calculateOverallScore() >= 80 ? 'text-green-600' : calculateOverallScore() >= 50 ? 'text-yellow-600' : 'text-red-600'}`}>
|
|
{calculateOverallScore()}%
|
|
</div>
|
|
<div className="text-sm text-slate-500">Umgesetzt</div>
|
|
</div>
|
|
</div>
|
|
<div className="mt-4 h-3 bg-slate-100 rounded-full overflow-hidden">
|
|
<div
|
|
className={`h-full transition-all ${calculateOverallScore() >= 80 ? 'bg-green-500' : calculateOverallScore() >= 50 ? 'bg-yellow-500' : 'bg-red-500'}`}
|
|
style={{ width: `${calculateOverallScore()}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* TOM Categories */}
|
|
<div className="space-y-4">
|
|
{tomCategories.map((category) => (
|
|
<div key={category.id} className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
|
<button
|
|
onClick={() => setExpandedCategory(expandedCategory === category.id ? null : category.id)}
|
|
className="w-full px-6 py-4 flex items-center justify-between hover:bg-slate-50 transition-colors"
|
|
>
|
|
<div className="flex items-center gap-4">
|
|
<div className={`w-12 h-12 rounded-lg flex items-center justify-center text-lg font-bold ${
|
|
calculateCategoryScore(category) >= 80 ? 'bg-green-100 text-green-700' :
|
|
calculateCategoryScore(category) >= 50 ? 'bg-yellow-100 text-yellow-700' :
|
|
'bg-red-100 text-red-700'
|
|
}`}>
|
|
{calculateCategoryScore(category)}%
|
|
</div>
|
|
<div className="text-left">
|
|
<h3 className="font-semibold text-slate-900">{category.title}</h3>
|
|
<p className="text-sm text-slate-500">{category.article} - {category.description}</p>
|
|
</div>
|
|
</div>
|
|
<svg
|
|
className={`w-5 h-5 text-slate-400 transition-transform ${expandedCategory === category.id ? 'rotate-180' : ''}`}
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
|
</svg>
|
|
</button>
|
|
|
|
{expandedCategory === category.id && (
|
|
<div className="px-6 pb-6 border-t border-slate-100">
|
|
<div className="mt-4 space-y-3">
|
|
{category.measures.map((measure, idx) => (
|
|
<div key={idx} className="p-4 bg-slate-50 rounded-lg">
|
|
<div className="flex items-start justify-between mb-2">
|
|
<h4 className="font-medium text-slate-900">{measure.name}</h4>
|
|
{getStatusBadge(measure.status)}
|
|
</div>
|
|
<p className="text-sm text-slate-600 mb-2">{measure.description}</p>
|
|
{(measure.evidence || measure.lastReview) && (
|
|
<div className="flex flex-wrap gap-4 text-xs text-slate-500">
|
|
{measure.evidence && (
|
|
<span>Nachweis: <span className="font-mono">{measure.evidence}</span></span>
|
|
)}
|
|
{measure.lastReview && (
|
|
<span>Letzte Pruefung: {measure.lastReview}</span>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Info */}
|
|
<div className="mt-6 bg-purple-50 border border-purple-200 rounded-xl p-4">
|
|
<div className="flex gap-3">
|
|
<svg className="w-5 h-5 text-purple-600 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
<div>
|
|
<h4 className="font-semibold text-purple-900">Dokumentationspflicht</h4>
|
|
<p className="text-sm text-purple-800 mt-1">
|
|
Gemaess Art. 32 Abs. 1 DSGVO muessen geeignete technische und organisatorische Massnahmen
|
|
implementiert werden, um ein dem Risiko angemessenes Schutzniveau zu gewaehrleisten.
|
|
Diese Dokumentation dient als Nachweis fuer Aufsichtsbehoerden.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|