/** * Ergänzt die Rechtsgrundlage jeder Erklärung um die DSGVO-Grundlage. * * node scripts/enrich-legal-basis.js --dry-run * node scripts/enrich-legal-basis.js * * Art. 13 Abs. 1 lit. c DSGVO verlangt „die Rechtsgrundlagen der Verarbeitung". * Gemeint ist die Erlaubnisnorm der DSGVO selbst – die Fachnorm allein (etwa * „§ 64 LBO") benennt nur die Aufgabe, nicht die datenschutzrechtliche * Grundlage. Für Behörden in Schleswig-Holstein ist das: * * hoheitliches Handeln Art. 6 Abs. 1 lit. e DSGVO i. V. m. § 3 LDSG SH * Beschäftigtendaten Art. 6 Abs. 1 lit. b DSGVO i. V. m. § 15 LDSG SH * Einwilligung Art. 6 Abs. 1 lit. a DSGVO * Vertrag Art. 6 Abs. 1 lit. b DSGVO * * Der vom Amt angegebene Text bleibt unverändert erhalten und wird nur * darunter gesetzt. Erklärungen ohne jede Angabe werden NICHT ergänzt – dort * eine Grundlage zu erfinden wäre etwas anderes als eine vorhandene zu * vervollständigen. Sie stehen im Bericht. * * Der Lauf ist wiederholbar: Bereits ergänzte Texte werden übersprungen. * Rückgängig machen lässt er sich mit `npm run import-csv`, das die * Rohwerte der Liste wiederherstellt. */ const fs = require("fs") const path = require("path") const { PrismaClient } = require("@prisma/client") const prisma = new PrismaClient() const JSON_DIR = path.join(process.cwd(), "data", "policies") const GRUNDLAGEN = { hoheitlich: { kopf: "**Art. 6 Abs. 1 lit. e DSGVO** i. V. m. **§ 3 LDSG SH** – Verarbeitung zur Wahrnehmung einer Aufgabe, die im öffentlichen Interesse liegt oder in Ausübung öffentlicher Gewalt erfolgt.", label: "hoheitliche Aufgabe", }, beschaeftigung: { kopf: "**Art. 6 Abs. 1 lit. b DSGVO** i. V. m. **§ 15 LDSG SH** – Verarbeitung für Zwecke des Beschäftigungsverhältnisses.", label: "Beschäftigungsverhältnis", }, einwilligung: { kopf: "**Art. 6 Abs. 1 lit. a DSGVO** – Einwilligung der betroffenen Person. Die Einwilligung kann jederzeit mit Wirkung für die Zukunft widerrufen werden (Art. 7 Abs. 3 DSGVO).", label: "Einwilligung", }, vertrag: { kopf: "**Art. 6 Abs. 1 lit. b DSGVO** – Erfüllung eines Vertrags oder Durchführung vorvertraglicher Maßnahmen.", label: "Vertrag", }, } /** Liest Fachbereich und Fachdienst aus der Bezeichnung der Tätigkeit. */ function organisation(beschreibung) { const lies = (feld) => { const treffer = new RegExp(`^${feld}: (.+)$`, "m").exec(beschreibung || "") return treffer ? treffer[1].trim() : "" } return { bereich: lies("Fachbereich"), dienst: lies("Fachdienst") } } /** Ordnet einer Erklärung die zutreffende DSGVO-Grundlage zu. */ function einordnen(policy) { const norm = (policy.legalBasis || "").toLowerCase() const { bereich, dienst } = organisation(policy.processingDescription) // Personalangelegenheiten zuerst: Sie nennen oft BGB/TVöD und würden sonst // als reine Vertragsverhältnisse durchgehen. if (dienst === "Personalamt" || bereich === "LVB") return "beschaeftigung" if (norm.includes("einwilligung")) return "einwilligung" // Privatrechtliches Handeln der Gemeinde if (/(^|[^a-z])(miet|pacht|kauf)vertrag/.test(norm)) return "vertrag" if (norm.includes("§ 311b bgb")) return "vertrag" if (norm.includes("vereinbarung der eigentümer")) return "vertrag" return "hoheitlich" } async function main() { const dryRun = process.argv.includes("--dry-run") const policies = await prisma.privacyPolicy.findMany({ orderBy: { title: "asc" } }) const geplant = [] const ohneAngabe = [] const schonErgaenzt = [] for (const policy of policies) { const bestand = (policy.legalBasis || "").trim() if (!bestand) { ohneAngabe.push(policy) continue } if (bestand.includes("DSGVO")) { schonErgaenzt.push(policy) continue } const art = einordnen(policy) const neu = `${GRUNDLAGEN[art].kopf}\n\nKonkrete Rechtsgrundlage: ${bestand}` geplant.push({ policy, art, alt: bestand, neu }) } const nachArt = {} geplant.forEach((g) => { nachArt[g.art] = nachArt[g.art] || [] nachArt[g.art].push(g) }) console.log(`Erklärungen gesamt: ${policies.length}`) console.log(`Zu ergänzen: ${geplant.length}`) console.log(`Bereits ergänzt: ${schonErgaenzt.length}`) console.log(`Ohne jede Angabe: ${ohneAngabe.length} (werden nicht angefasst)`) console.log("") for (const [art, liste] of Object.entries(nachArt)) { console.log(`=== ${GRUNDLAGEN[art].label} (${liste.length}) ===`) console.log(` ${GRUNDLAGEN[art].kopf.replace(/\*\*/g, "")}`) if (art !== "hoheitlich") { liste.forEach((g) => console.log(` ${g.policy.title} – „${g.alt}"`)) } console.log("") } console.log(`=== Ohne Rechtsgrundlage – bleiben offen (${ohneAngabe.length}) ===`) ohneAngabe.forEach((p) => console.log(` ${p.title}`)) console.log("") if (dryRun) { console.log("--dry-run: nichts geschrieben.") if (geplant.length) { console.log("") console.log("Beispiel:") console.log(" vorher : " + geplant[0].alt) console.log(" nachher: " + geplant[0].neu.replace(/\n/g, "\n ")) } return } for (const { policy, neu } of geplant) { await prisma.privacyPolicy.update({ where: { id: policy.id }, data: { legalBasis: neu }, }) // JSON-Ablage nachziehen, soweit vorhanden und beschreibbar const datei = path.join(JSON_DIR, `${policy.slug}.json`) try { if (fs.existsSync(datei)) { const inhalt = JSON.parse(fs.readFileSync(datei, "utf8")) inhalt.legalBasis = neu fs.writeFileSync(datei, JSON.stringify(inhalt, null, 2), "utf8") } } catch { // Die Datenbank ist maßgeblich; die Ablage ist nur Nachweis. } } console.log(`Ergänzt: ${geplant.length}`) } main() .catch((e) => { console.error(e) process.exitCode = 1 }) .finally(() => prisma.$disconnect())