#!/usr/bin/env python3 """ GDPR Data Protection Compliance Automation Automates data mapping, ROPA creation, DPIA assessment, data subject request tracking, breach notification management, and compliance reporting. """ import json import csv import os from datetime import datetime, timedelta from pathlib import Path from dataclasses import dataclass, field, asdict from enum import Enum from typing import Optional class LawfulBasis(Enum): CONSENT = "Consent (Art. 6(1)(a))" CONTRACT = "Contract (Art. 6(1)(b))" LEGAL_OBLIGATION = "Legal Obligation (Art. 6(1)(c))" VITAL_INTERESTS = "Vital Interests (Art. 6(1)(d))" PUBLIC_TASK = "Public Task (Art. 6(1)(e))" LEGITIMATE_INTEREST = "Legitimate Interest (Art. 6(1)(f))" class DSRType(Enum): ACCESS = "Right of Access (Art. 15)" RECTIFICATION = "Right to Rectification (Art. 16)" ERASURE = "Right to Erasure (Art. 17)" RESTRICTION = "Right to Restriction (Art. 18)" PORTABILITY = "Right to Data Portability (Art. 20)" OBJECTION = "Right to Object (Art. 21)" AUTOMATED = "Automated Decision-Making (Art. 22)" class BreachSeverity(Enum): NO_RISK = "No Risk" RISK = "Risk to Rights and Freedoms" HIGH_RISK = "High Risk to Rights and Freedoms" class TransferMechanism(Enum): ADEQUACY = "Adequacy Decision (Art. 45)" SCCS = "Standard Contractual Clauses (Art. 46(2)(c))" BCRS = "Binding Corporate Rules (Art. 47)" CONSENT = "Explicit Consent (Art. 49(1)(a))" CONTRACT = "Necessary for Contract (Art. 49(1)(b))" DEROGATION = "Other Derogation (Art. 49)" ADEQUATE_COUNTRIES = [ "Andorra", "Argentina", "Canada", "Faroe Islands", "Guernsey", "Israel", "Isle of Man", "Japan", "Jersey", "New Zealand", "Republic of Korea", "Switzerland", "United Kingdom", "Uruguay", "United States (Data Privacy Framework participants)" ] @dataclass class ProcessingActivity: activity_id: str name: str purpose: str lawful_basis: str data_subjects: list personal_data_categories: list special_categories: bool = False recipients: list = field(default_factory=list) international_transfers: list = field(default_factory=list) retention_period: str = "" systems: list = field(default_factory=list) security_measures: list = field(default_factory=list) dpia_required: bool = False controller_name: str = "" processor_name: str = "" @dataclass class DataSubjectRequest: dsr_id: str request_type: str data_subject_name: str data_subject_email: str received_date: str identity_verified: bool = False identity_verified_date: str = "" deadline: str = "" status: str = "Received" systems_searched: list = field(default_factory=list) response_date: str = "" notes: str = "" def __post_init__(self): if not self.deadline and self.received_date: received = datetime.strptime(self.received_date, "%Y-%m-%d") self.deadline = (received + timedelta(days=30)).strftime("%Y-%m-%d") @dataclass class BreachRecord: breach_id: str detected_date: str detected_time: str description: str data_categories: list subjects_affected: int severity: str notification_deadline: str = "" authority_notified: bool = False authority_notification_date: str = "" subjects_notified: bool = False subjects_notification_date: str = "" root_cause: str = "" remediation: str = "" status: str = "Investigating" def __post_init__(self): if not self.notification_deadline and self.detected_date: detected = datetime.strptime(self.detected_date, "%Y-%m-%d") self.notification_deadline = (detected + timedelta(hours=72)).strftime("%Y-%m-%d %H:%M") @dataclass class DPIARecord: dpia_id: str processing_activity: str description: str necessity_assessment: str risks_identified: list mitigation_measures: list residual_risk: str dpo_consultation: bool = False authority_consultation: bool = False approved: bool = False approval_date: str = "" reviewer: str = "" class GDPRComplianceManager: """Manages GDPR compliance controls and tracking.""" def __init__(self, output_dir: str = "./gdpr_output"): self.output_dir = Path(output_dir) self.output_dir.mkdir(parents=True, exist_ok=True) self.processing_activities: list[ProcessingActivity] = [] self.dsr_log: list[DataSubjectRequest] = [] self.breach_register: list[BreachRecord] = [] self.dpia_register: list[DPIARecord] = [] def create_ropa(self, activities: list[dict]) -> list[ProcessingActivity]: """Create Records of Processing Activities (Art. 30).""" print("\n" + "=" * 70) print("RECORDS OF PROCESSING ACTIVITIES (ROPA)") print("=" * 70) for act_data in activities: activity = ProcessingActivity(**act_data) self.processing_activities.append(activity) print(f"\n [{activity.activity_id}] {activity.name}") print(f" Purpose: {activity.purpose}") print(f" Lawful Basis: {activity.lawful_basis}") print(f" Data Subjects: {', '.join(activity.data_subjects)}") print(f" Special Categories: {'Yes' if activity.special_categories else 'No'}") print(f" International Transfers: {'Yes' if activity.international_transfers else 'No'}") print(f" DPIA Required: {'Yes' if activity.dpia_required else 'No'}") # Summary statistics total = len(self.processing_activities) special = sum(1 for a in self.processing_activities if a.special_categories) transfers = sum(1 for a in self.processing_activities if a.international_transfers) dpia_needed = sum(1 for a in self.processing_activities if a.dpia_required) print(f"\n ROPA Summary:") print(f" Total Processing Activities: {total}") print(f" Special Category Processing: {special}") print(f" International Transfers: {transfers}") print(f" DPIA Required: {dpia_needed}") # Lawful basis breakdown basis_counts = {} for a in self.processing_activities: basis_counts.setdefault(a.lawful_basis, 0) basis_counts[a.lawful_basis] += 1 print(f"\n Lawful Basis Breakdown:") for basis, count in basis_counts.items(): print(f" {basis}: {count}") # Save ROPA ropa_path = self.output_dir / "ropa.json" with open(ropa_path, "w") as f: json.dump([asdict(a) for a in self.processing_activities], f, indent=2) csv_path = self.output_dir / "ropa.csv" with open(csv_path, "w", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerow([ "Activity ID", "Name", "Purpose", "Lawful Basis", "Data Subjects", "Personal Data Categories", "Special Categories", "Recipients", "International Transfers", "Retention Period", "Systems", "Security Measures", "DPIA Required" ]) for a in self.processing_activities: writer.writerow([ a.activity_id, a.name, a.purpose, a.lawful_basis, "; ".join(a.data_subjects), "; ".join(a.personal_data_categories), "Yes" if a.special_categories else "No", "; ".join(a.recipients), "; ".join(a.international_transfers), a.retention_period, "; ".join(a.systems), "; ".join(a.security_measures), "Yes" if a.dpia_required else "No" ]) print(f"\n ROPA saved to: {ropa_path}") return self.processing_activities def process_dsr(self, requests: list[dict]) -> dict: """Track and manage data subject requests.""" print("\n" + "=" * 70) print("DATA SUBJECT REQUEST TRACKER") print("=" * 70) for req_data in requests: dsr = DataSubjectRequest(**req_data) self.dsr_log.append(dsr) print(f"\n [{dsr.dsr_id}] {dsr.request_type}") print(f" Subject: {dsr.data_subject_name}") print(f" Received: {dsr.received_date}") print(f" Deadline: {dsr.deadline}") print(f" Status: {dsr.status}") print(f" Identity Verified: {'Yes' if dsr.identity_verified else 'No'}") # Overdue check today = datetime.now().strftime("%Y-%m-%d") overdue = [d for d in self.dsr_log if d.deadline < today and d.status != "Completed"] if overdue: print(f"\n ALERT: {len(overdue)} overdue DSRs!") for d in overdue: print(f" [{d.dsr_id}] {d.request_type} - Deadline: {d.deadline}") # Summary type_counts = {} for d in self.dsr_log: type_counts.setdefault(d.request_type, 0) type_counts[d.request_type] += 1 print(f"\n DSR Summary:") for rtype, count in type_counts.items(): print(f" {rtype}: {count}") print(f" Total: {len(self.dsr_log)}") print(f" Overdue: {len(overdue)}") dsr_path = self.output_dir / "dsr_log.json" with open(dsr_path, "w") as f: json.dump([asdict(d) for d in self.dsr_log], f, indent=2) print(f"\n DSR Log saved to: {dsr_path}") return {"total": len(self.dsr_log), "overdue": len(overdue), "by_type": type_counts} def manage_breach(self, breaches: list[dict]) -> list[BreachRecord]: """Manage breach register and notification tracking.""" print("\n" + "=" * 70) print("BREACH REGISTER AND NOTIFICATION TRACKER") print("=" * 70) for breach_data in breaches: breach = BreachRecord(**breach_data) self.breach_register.append(breach) print(f"\n [{breach.breach_id}] {breach.description[:60]}...") print(f" Detected: {breach.detected_date} {breach.detected_time}") print(f" Severity: {breach.severity}") print(f" Subjects Affected: {breach.subjects_affected}") print(f" Notification Deadline: {breach.notification_deadline}") print(f" Authority Notified: {'Yes' if breach.authority_notified else 'No'}") if breach.severity == BreachSeverity.HIGH_RISK.value: print(f" Subjects Notified: {'Yes' if breach.subjects_notified else 'No'}") print(f" Status: {breach.status}") breach_path = self.output_dir / "breach_register.json" with open(breach_path, "w") as f: json.dump([asdict(b) for b in self.breach_register], f, indent=2) print(f"\n Breach Register saved to: {breach_path}") return self.breach_register def conduct_dpia(self, dpias: list[dict]) -> list[DPIARecord]: """Conduct and record Data Protection Impact Assessments.""" print("\n" + "=" * 70) print("DATA PROTECTION IMPACT ASSESSMENTS (DPIAs)") print("=" * 70) for dpia_data in dpias: dpia = DPIARecord(**dpia_data) self.dpia_register.append(dpia) print(f"\n [{dpia.dpia_id}] {dpia.processing_activity}") print(f" Description: {dpia.description[:60]}...") print(f" Risks Identified: {len(dpia.risks_identified)}") print(f" Mitigation Measures: {len(dpia.mitigation_measures)}") print(f" Residual Risk: {dpia.residual_risk}") print(f" DPO Consulted: {'Yes' if dpia.dpo_consultation else 'No'}") print(f" Approved: {'Yes' if dpia.approved else 'No'}") dpia_path = self.output_dir / "dpia_register.json" with open(dpia_path, "w") as f: json.dump([asdict(d) for d in self.dpia_register], f, indent=2) print(f"\n DPIA Register saved to: {dpia_path}") return self.dpia_register def assess_compliance(self) -> dict: """Generate overall GDPR compliance assessment.""" print("\n" + "=" * 70) print("GDPR COMPLIANCE ASSESSMENT") print("=" * 70) checks = { "Article 5 - Principles": { "All processing has documented lawful basis": bool(self.processing_activities), "Purpose limitation documented for each activity": False, "Data minimization assessed": False, "Accuracy procedures in place": False, "Retention periods defined": any(a.retention_period for a in self.processing_activities), "Security measures documented": any(a.security_measures for a in self.processing_activities), }, "Article 25 - Privacy by Design": { "Development processes include privacy reviews": False, "Default privacy settings implemented": False, "Data minimization built into systems": False, }, "Article 30 - ROPA": { "Records of processing maintained": bool(self.processing_activities), "Controller details documented": any(a.controller_name for a in self.processing_activities), "All processing activities captured": len(self.processing_activities) > 0, }, "Article 32 - Security Measures": { "Encryption implemented for personal data": False, "Pseudonymization implemented where appropriate": False, "Access controls for personal data systems": False, "Regular security testing performed": False, }, "Articles 33-34 - Breach Notification": { "Breach detection capabilities in place": False, "72-hour notification process documented": False, "Breach register maintained": bool(self.breach_register), "Breach response tested": False, }, "Article 35 - DPIA": { "DPIA criteria documented": False, "DPIAs conducted for high-risk processing": bool(self.dpia_register), "DPO consulted on DPIAs": any(d.dpo_consultation for d in self.dpia_register), }, "Articles 12-22 - Data Subject Rights": { "DSR handling process documented": False, "30-day response capability": bool(self.dsr_log), "Identity verification process": any(d.identity_verified for d in self.dsr_log), "Erasure capability across systems": False, "Portability export capability": False, }, "Articles 44-49 - International Transfers": { "Cross-border transfers mapped": any(a.international_transfers for a in self.processing_activities), "Transfer mechanisms in place (SCCs/BCRs)": False, "Transfer impact assessments conducted": False, }, } total = sum(len(v) for v in checks.values()) passed = sum(sum(1 for x in v.values() if x) for v in checks.values()) pct = (passed / total * 100) if total > 0 else 0 print(f"\n Compliance Score: {passed}/{total} ({pct:.1f}%)") for article, items in checks.items(): cat_passed = sum(1 for x in items.values() if x) cat_total = len(items) print(f"\n {article} ({cat_passed}/{cat_total}):") for item, status in items.items(): icon = "[PASS]" if status else "[FAIL]" print(f" {icon} {item}") report = { "date": datetime.now().isoformat(), "compliance_percentage": pct, "checks": checks, "ropa_count": len(self.processing_activities), "dsr_count": len(self.dsr_log), "breach_count": len(self.breach_register), "dpia_count": len(self.dpia_register), } report_path = self.output_dir / "gdpr_compliance_report.json" with open(report_path, "w") as f: json.dump(report, f, indent=2) print(f"\n Compliance Report saved to: {report_path}") return report def main(): """Run GDPR compliance assessment.""" manager = GDPRComplianceManager() # ROPA sample_activities = [ { "activity_id": "PROC-001", "name": "Customer Account Management", "purpose": "Managing customer accounts and providing services", "lawful_basis": LawfulBasis.CONTRACT.value, "data_subjects": ["Customers"], "personal_data_categories": ["Name", "Email", "Phone", "Address", "Payment details"], "recipients": ["Payment processor", "CRM provider"], "international_transfers": ["US (Payment processor - SCCs)"], "retention_period": "Duration of contract + 6 years", "systems": ["CRM", "Payment gateway", "Database"], "security_measures": ["Encryption at rest", "TLS in transit", "RBAC", "MFA"], "controller_name": "Example Corp Ltd", }, { "activity_id": "PROC-002", "name": "Employee HR Processing", "purpose": "Employment administration, payroll, benefits", "lawful_basis": LawfulBasis.CONTRACT.value, "data_subjects": ["Employees", "Job applicants"], "personal_data_categories": ["Name", "Address", "DOB", "NI number", "Bank details", "Health data"], "special_categories": True, "recipients": ["Payroll provider", "Pension provider", "HMRC"], "retention_period": "Employment + 7 years", "systems": ["HRIS", "Payroll system"], "security_measures": ["Encryption", "Access controls", "Audit logging"], "dpia_required": True, "controller_name": "Example Corp Ltd", }, { "activity_id": "PROC-003", "name": "Website Analytics", "purpose": "Analyzing website usage to improve user experience", "lawful_basis": LawfulBasis.CONSENT.value, "data_subjects": ["Website visitors"], "personal_data_categories": ["IP address", "Cookie identifiers", "Browsing behavior"], "recipients": ["Analytics provider"], "international_transfers": ["US (Analytics provider - EU-US DPF)"], "retention_period": "26 months", "systems": ["Website", "Analytics platform"], "security_measures": ["IP anonymization", "Cookie consent", "Data pseudonymization"], "controller_name": "Example Corp Ltd", }, ] manager.create_ropa(sample_activities) # DSRs sample_dsrs = [ { "dsr_id": "DSR-001", "request_type": DSRType.ACCESS.value, "data_subject_name": "John Smith", "data_subject_email": "john.smith@email.com", "received_date": "2024-12-01", "identity_verified": True, "identity_verified_date": "2024-12-02", "status": "In Progress", "systems_searched": ["CRM", "Email", "Analytics"], }, { "dsr_id": "DSR-002", "request_type": DSRType.ERASURE.value, "data_subject_name": "Jane Doe", "data_subject_email": "jane.doe@email.com", "received_date": "2024-12-15", "identity_verified": True, "identity_verified_date": "2024-12-16", "status": "Completed", "response_date": "2024-12-28", }, ] manager.process_dsr(sample_dsrs) # Breaches sample_breaches = [ { "breach_id": "BREACH-001", "detected_date": "2024-11-15", "detected_time": "14:30", "description": "Unauthorized access to customer database via compromised admin credentials", "data_categories": ["Name", "Email", "Phone"], "subjects_affected": 2500, "severity": BreachSeverity.HIGH_RISK.value, "authority_notified": True, "authority_notification_date": "2024-11-16", "subjects_notified": True, "subjects_notification_date": "2024-11-18", "root_cause": "Credential compromise via phishing", "remediation": "MFA enforced, passwords reset, phishing training deployed", "status": "Closed", }, ] manager.manage_breach(sample_breaches) # DPIAs sample_dpias = [ { "dpia_id": "DPIA-001", "processing_activity": "Employee HR Processing", "description": "Processing of employee health data for occupational health and benefits administration", "necessity_assessment": "Processing is necessary for employment contract and legal obligations", "risks_identified": [ "Unauthorized access to health data", "Data breach exposing sensitive employee information", "Excessive data collection beyond what is necessary", ], "mitigation_measures": [ "Encryption of all health data at rest and in transit", "Strict RBAC limiting access to HR and OH staff only", "Audit logging of all access to health records", "Annual access reviews", "Staff training on handling special category data", ], "residual_risk": "Low", "dpo_consultation": True, "approved": True, "approval_date": "2024-06-15", "reviewer": "DPO", }, ] manager.conduct_dpia(sample_dpias) # Overall compliance assessment manager.assess_compliance() print("\n" + "=" * 70) print("GDPR COMPLIANCE ASSESSMENT COMPLETE") print("=" * 70) if __name__ == "__main__": main()