fix(admin-v2): Restore complete admin-v2 application
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>
This commit is contained in:
395
admin-v2/app/(admin)/compliance/tom/page.tsx
Normal file
395
admin-v2/app/(admin)/compliance/tom/page.tsx
Normal file
@@ -0,0 +1,395 @@
|
||||
'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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user