Skip to main content

aegis_server/
gdpr.rs

1//! Aegis GDPR/CCPA Compliance Module
2//!
3//! Provides data subject deletion (right to erasure) and data portability (right to data export)
4//! functionality for GDPR and CCPA compliance.
5//!
6//! Key Features:
7//! - Data subject deletion endpoint (DELETE /api/v1/compliance/data-subject/{identifier})
8//! - Data export endpoint (POST /api/v1/compliance/export) - GDPR Article 20 data portability
9//! - Secure deletion across KV store, documents, SQL tables, and graph store
10//! - Data export in JSON (machine-readable) and CSV (flat) formats
11//! - Deletion audit trail with immutable records for compliance proof
12//! - Export audit trail for compliance tracking
13//! - Deletion certificates with cryptographic verification
14//! - Configurable retention exception handling for audit logs
15//!
16//! @version 0.1.0
17//! @author AutomataNexus Development Team
18
19use crate::activity::ActivityType;
20use crate::state::AppState;
21use axum::{
22    extract::{Path, State},
23    http::StatusCode,
24    response::IntoResponse,
25    Json,
26};
27use chrono::{DateTime, Utc};
28use parking_lot::RwLock;
29use serde::{Deserialize, Serialize};
30use sha2::{Digest, Sha256};
31use std::collections::HashMap;
32use std::sync::atomic::{AtomicU64, Ordering};
33use std::sync::Arc;
34
35// =============================================================================
36// Types
37// =============================================================================
38
39/// Scope of data deletion.
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
41#[serde(tag = "type", rename_all = "snake_case")]
42pub enum DeletionScope {
43    /// Delete all data for the subject
44    All,
45    /// Delete only from specific collections/tables
46    SpecificCollections { collections: Vec<String> },
47    /// Delete all data except audit logs (for legal hold)
48    ExcludeAuditLogs,
49}
50
51impl Default for DeletionScope {
52    fn default() -> Self {
53        DeletionScope::All
54    }
55}
56
57// =============================================================================
58// Export Types (GDPR Article 20 - Data Portability)
59// =============================================================================
60
61/// Export format for data portability.
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
63#[serde(rename_all = "lowercase")]
64pub enum ExportFormat {
65    /// JSON format - machine-readable, nested structure
66    Json,
67    /// CSV format - flat structure for spreadsheets
68    Csv,
69}
70
71impl Default for ExportFormat {
72    fn default() -> Self {
73        ExportFormat::Json
74    }
75}
76
77/// Scope of data export.
78#[derive(Debug, Clone, Serialize, Deserialize, Default)]
79#[serde(tag = "type", rename_all = "snake_case")]
80pub enum ExportScope {
81    /// Export all data for the subject
82    #[default]
83    All,
84    /// Export only from specific collections/tables
85    SpecificCollections { collections: Vec<String> },
86}
87
88/// Date range for filtering export data.
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct DateRange {
91    /// Start date (inclusive), ISO 8601 format
92    #[serde(default)]
93    pub start: Option<String>,
94    /// End date (inclusive), ISO 8601 format
95    #[serde(default)]
96    pub end: Option<String>,
97}
98
99/// Request to export data for a data subject (GDPR Article 20).
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct ExportRequest {
102    /// Unique identifier for the data subject (e.g., email, user ID, customer ID)
103    pub subject_id: String,
104    /// Export format (json or csv)
105    #[serde(default)]
106    pub format: ExportFormat,
107    /// Scope of the export
108    #[serde(default)]
109    pub scope: ExportScope,
110    /// Optional date range filter
111    #[serde(default)]
112    pub date_range: Option<DateRange>,
113    /// Optional field names to search for the subject identifier
114    /// If not provided, searches common fields like "email", "user_id", "customer_id", etc.
115    #[serde(default)]
116    pub search_fields: Vec<String>,
117}
118
119/// A single exported data item.
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct ExportedItem {
122    /// Type of data store (kv, document, sql, graph)
123    pub store_type: String,
124    /// Collection, table, or key name
125    pub location: String,
126    /// Identifier of the item
127    pub item_id: String,
128    /// The actual data
129    pub data: serde_json::Value,
130    /// Timestamp when the data was created/updated (if available)
131    #[serde(skip_serializing_if = "Option::is_none")]
132    pub timestamp: Option<String>,
133}
134
135/// Response from data export endpoint.
136#[derive(Debug, Serialize)]
137pub struct ExportResponse {
138    /// Unique export ID for audit tracking
139    pub export_id: String,
140    /// The subject whose data was exported
141    pub subject_id: String,
142    /// Export format used
143    pub format: ExportFormat,
144    /// When the export was generated
145    pub generated_at: String,
146    /// Total number of items exported
147    pub total_items: usize,
148    /// The exported data (JSON format) or CSV string (CSV format)
149    pub data: serde_json::Value,
150    /// Scope of the export
151    pub scope: ExportScope,
152    /// Date range filter applied (if any)
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub date_range: Option<DateRange>,
155}
156
157/// Request to delete data for a data subject.
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct DeletionRequest {
160    /// Unique identifier for the data subject (e.g., email, user ID, customer ID)
161    pub subject_id: String,
162    /// Scope of the deletion
163    #[serde(default)]
164    pub scope: DeletionScope,
165    /// Identity of the person/system making the request
166    pub requestor: String,
167    /// Reason for the deletion request
168    pub reason: String,
169    /// Optional field names to search for the subject identifier
170    /// If not provided, searches common fields like "email", "user_id", "customer_id", etc.
171    #[serde(default)]
172    pub search_fields: Vec<String>,
173    /// Whether to perform secure overwrite of deleted data blocks
174    #[serde(default)]
175    pub secure_erase: bool,
176}
177
178/// Record of a single item that was deleted.
179#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct DeletedItem {
181    /// Type of data store (kv, document, sql, graph)
182    pub store_type: String,
183    /// Collection, table, or key name
184    pub location: String,
185    /// Identifier of the deleted item
186    pub item_id: String,
187    /// Size in bytes of the deleted data (approximate)
188    pub size_bytes: Option<u64>,
189    /// Timestamp of deletion
190    pub deleted_at: String,
191}
192
193/// Certificate proving data deletion was performed.
194#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct DeletionCertificate {
196    /// Unique certificate ID
197    pub id: String,
198    /// The subject whose data was deleted
199    pub subject_id: String,
200    /// When the deletion was performed
201    pub timestamp: String,
202    /// Summary of items deleted
203    pub items_deleted: Vec<DeletedItem>,
204    /// Total number of items deleted
205    pub total_items: usize,
206    /// Total bytes deleted (approximate)
207    pub total_bytes: u64,
208    /// Scope of the deletion
209    pub scope: DeletionScope,
210    /// Who requested the deletion
211    pub requestor: String,
212    /// Reason for deletion
213    pub reason: String,
214    /// SHA-256 hash of the certificate for verification
215    pub verification_hash: String,
216    /// Node that performed the deletion
217    pub verified_by: String,
218    /// Whether secure erase was performed
219    pub secure_erase_performed: bool,
220}
221
222impl DeletionCertificate {
223    /// Create a new deletion certificate with verification hash.
224    pub fn new(
225        subject_id: String,
226        items: Vec<DeletedItem>,
227        scope: DeletionScope,
228        requestor: String,
229        reason: String,
230        node_id: String,
231        secure_erase: bool,
232    ) -> Self {
233        let timestamp = Utc::now().to_rfc3339();
234        let total_items = items.len();
235        let total_bytes = items.iter().filter_map(|i| i.size_bytes).sum();
236
237        // Generate unique certificate ID
238        let id = format!(
239            "DEL-{}-{}",
240            timestamp
241                .replace([':', '-', 'T', 'Z', '.'], "")
242                .chars()
243                .take(14)
244                .collect::<String>(),
245            &subject_id.chars().take(8).collect::<String>()
246        );
247
248        let mut cert = Self {
249            id,
250            subject_id,
251            timestamp,
252            items_deleted: items,
253            total_items,
254            total_bytes,
255            scope,
256            requestor,
257            reason,
258            verification_hash: String::new(),
259            verified_by: node_id,
260            secure_erase_performed: secure_erase,
261        };
262
263        // Compute verification hash
264        cert.verification_hash = cert.compute_hash();
265        cert
266    }
267
268    /// Compute SHA-256 hash of certificate contents for verification.
269    fn compute_hash(&self) -> String {
270        let mut hasher = Sha256::new();
271
272        // Hash all relevant fields
273        hasher.update(self.id.as_bytes());
274        hasher.update(self.subject_id.as_bytes());
275        hasher.update(self.timestamp.as_bytes());
276        hasher.update(self.total_items.to_string().as_bytes());
277        hasher.update(self.total_bytes.to_string().as_bytes());
278        hasher.update(self.requestor.as_bytes());
279        hasher.update(self.reason.as_bytes());
280        hasher.update(self.verified_by.as_bytes());
281
282        // Hash each deleted item
283        for item in &self.items_deleted {
284            hasher.update(item.store_type.as_bytes());
285            hasher.update(item.location.as_bytes());
286            hasher.update(item.item_id.as_bytes());
287        }
288
289        let result = hasher.finalize();
290        hex_encode(&result)
291    }
292
293    /// Verify the certificate hash is valid.
294    pub fn verify(&self) -> bool {
295        let mut cert_copy = self.clone();
296        cert_copy.verification_hash = String::new();
297        cert_copy.compute_hash() == self.verification_hash
298    }
299}
300
301/// Audit log entry for deletion operations.
302#[derive(Debug, Clone, Serialize, Deserialize)]
303pub struct DeletionAuditEntry {
304    /// Unique entry ID
305    pub id: String,
306    /// Type of event
307    pub event_type: DeletionEventType,
308    /// Subject ID involved
309    pub subject_id: String,
310    /// Timestamp
311    pub timestamp: String,
312    /// Who initiated the action
313    pub actor: String,
314    /// Details of the action
315    pub details: serde_json::Value,
316    /// Hash of previous audit entry (for chain integrity)
317    pub prev_hash: String,
318    /// Hash of this entry
319    pub hash: String,
320}
321
322/// Types of GDPR audit events (deletion and export).
323#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
324#[serde(rename_all = "snake_case")]
325pub enum DeletionEventType {
326    /// Deletion request received
327    RequestReceived,
328    /// Deletion process started
329    DeletionStarted,
330    /// Item deleted from a store
331    ItemDeleted,
332    /// Secure erase performed
333    SecureErasePerformed,
334    /// Deletion completed
335    DeletionCompleted,
336    /// Deletion failed
337    DeletionFailed,
338    /// Certificate generated
339    CertificateGenerated,
340    /// Data export request received (GDPR Article 20)
341    ExportRequestReceived,
342    /// Data export completed (GDPR Article 20)
343    ExportCompleted,
344    /// Data export failed (GDPR Article 20)
345    ExportFailed,
346}
347
348/// Response from deletion endpoint.
349#[derive(Debug, Serialize)]
350pub struct DeletionResponse {
351    pub success: bool,
352    pub certificate: Option<DeletionCertificate>,
353    pub message: String,
354    #[serde(skip_serializing_if = "Option::is_none")]
355    pub error: Option<String>,
356}
357
358/// Response listing deletion certificates.
359#[derive(Debug, Serialize)]
360pub struct ListCertificatesResponse {
361    pub certificates: Vec<DeletionCertificate>,
362    pub total: usize,
363}
364
365/// Response for verifying a certificate.
366#[derive(Debug, Serialize)]
367pub struct VerifyCertificateResponse {
368    pub valid: bool,
369    pub certificate: Option<DeletionCertificate>,
370    pub message: String,
371}
372
373// =============================================================================
374// Deletion Audit Log
375// =============================================================================
376
377/// Immutable audit log for deletion operations.
378/// Maintains a hash chain for tamper detection.
379pub struct DeletionAuditLog {
380    entries: RwLock<Vec<DeletionAuditEntry>>,
381    certificates: RwLock<HashMap<String, DeletionCertificate>>,
382    next_id: AtomicU64,
383    last_hash: RwLock<String>,
384}
385
386impl DeletionAuditLog {
387    /// Create a new deletion audit log.
388    pub fn new() -> Self {
389        Self {
390            entries: RwLock::new(Vec::new()),
391            certificates: RwLock::new(HashMap::new()),
392            next_id: AtomicU64::new(1),
393            last_hash: RwLock::new("genesis".to_string()),
394        }
395    }
396
397    /// Log a deletion event.
398    pub fn log_event(
399        &self,
400        event_type: DeletionEventType,
401        subject_id: &str,
402        actor: &str,
403        details: serde_json::Value,
404    ) -> String {
405        let id = format!("gdpr-{:08}", self.next_id.fetch_add(1, Ordering::SeqCst));
406        let timestamp = Utc::now().to_rfc3339();
407
408        let prev_hash = self.last_hash.read().clone();
409
410        // Compute hash
411        let mut hasher = Sha256::new();
412        hasher.update(id.as_bytes());
413        hasher.update(format!("{:?}", event_type).as_bytes());
414        hasher.update(subject_id.as_bytes());
415        hasher.update(timestamp.as_bytes());
416        hasher.update(actor.as_bytes());
417        if let Ok(json) = serde_json::to_string(&details) {
418            hasher.update(json.as_bytes());
419        }
420        hasher.update(prev_hash.as_bytes());
421        let hash = hex_encode(&hasher.finalize());
422
423        let entry = DeletionAuditEntry {
424            id: id.clone(),
425            event_type,
426            subject_id: subject_id.to_string(),
427            timestamp,
428            actor: actor.to_string(),
429            details,
430            prev_hash,
431            hash: hash.clone(),
432        };
433
434        // Update chain
435        *self.last_hash.write() = hash;
436        self.entries.write().push(entry);
437
438        id
439    }
440
441    /// Store a deletion certificate.
442    pub fn store_certificate(&self, cert: DeletionCertificate) {
443        self.certificates.write().insert(cert.id.clone(), cert);
444    }
445
446    /// Get a certificate by ID.
447    pub fn get_certificate(&self, id: &str) -> Option<DeletionCertificate> {
448        self.certificates.read().get(id).cloned()
449    }
450
451    /// List all certificates.
452    pub fn list_certificates(&self) -> Vec<DeletionCertificate> {
453        self.certificates.read().values().cloned().collect()
454    }
455
456    /// Get audit entries for a subject.
457    pub fn get_entries_for_subject(&self, subject_id: &str) -> Vec<DeletionAuditEntry> {
458        self.entries
459            .read()
460            .iter()
461            .filter(|e| e.subject_id == subject_id)
462            .cloned()
463            .collect()
464    }
465
466    /// Verify the integrity of the audit chain.
467    pub fn verify_integrity(&self) -> Result<usize, String> {
468        let entries = self.entries.read();
469        let mut last_hash = "genesis".to_string();
470
471        for (idx, entry) in entries.iter().enumerate() {
472            // Verify chain linkage
473            if entry.prev_hash != last_hash {
474                return Err(format!(
475                    "Hash chain broken at entry {}: expected prev_hash '{}', got '{}'",
476                    idx, last_hash, entry.prev_hash
477                ));
478            }
479
480            // Verify entry hash
481            let mut hasher = Sha256::new();
482            hasher.update(entry.id.as_bytes());
483            hasher.update(format!("{:?}", entry.event_type).as_bytes());
484            hasher.update(entry.subject_id.as_bytes());
485            hasher.update(entry.timestamp.as_bytes());
486            hasher.update(entry.actor.as_bytes());
487            if let Ok(json) = serde_json::to_string(&entry.details) {
488                hasher.update(json.as_bytes());
489            }
490            hasher.update(entry.prev_hash.as_bytes());
491            let computed_hash = hex_encode(&hasher.finalize());
492
493            if entry.hash != computed_hash {
494                return Err(format!(
495                    "Hash mismatch at entry {}: computed '{}', stored '{}'",
496                    idx, computed_hash, entry.hash
497                ));
498            }
499
500            last_hash = entry.hash.clone();
501        }
502
503        Ok(entries.len())
504    }
505}
506
507impl Default for DeletionAuditLog {
508    fn default() -> Self {
509        Self::new()
510    }
511}
512
513// =============================================================================
514// GDPR Service
515// =============================================================================
516
517/// GDPR compliance service for data deletion.
518pub struct GdprService {
519    audit_log: Arc<DeletionAuditLog>,
520}
521
522impl GdprService {
523    /// Create a new GDPR service.
524    pub fn new() -> Self {
525        Self {
526            audit_log: Arc::new(DeletionAuditLog::new()),
527        }
528    }
529
530    /// Get the audit log.
531    pub fn audit_log(&self) -> &Arc<DeletionAuditLog> {
532        &self.audit_log
533    }
534
535    /// Default fields to search for subject identification.
536    fn default_search_fields() -> Vec<String> {
537        vec![
538            "email".to_string(),
539            "user_id".to_string(),
540            "customer_id".to_string(),
541            "subject_id".to_string(),
542            "id".to_string(),
543            "userId".to_string(),
544            "customerId".to_string(),
545            "user".to_string(),
546            "owner".to_string(),
547        ]
548    }
549
550    /// Delete data for a subject from the KV store.
551    pub fn delete_from_kv(
552        &self,
553        state: &AppState,
554        subject_id: &str,
555        search_fields: &[String],
556    ) -> Vec<DeletedItem> {
557        let mut deleted = Vec::new();
558        let entries = state.kv_store.list(None, usize::MAX);
559
560        for entry in entries {
561            let should_delete =
562                self.value_contains_subject(&entry.value, subject_id, search_fields)
563                    || entry.key.contains(subject_id);
564
565            if should_delete {
566                let size = serde_json::to_string(&entry.value)
567                    .map(|s| s.len() as u64)
568                    .ok();
569
570                if state.kv_store.delete(&entry.key).is_some() {
571                    deleted.push(DeletedItem {
572                        store_type: "kv".to_string(),
573                        location: "kv_store".to_string(),
574                        item_id: entry.key,
575                        size_bytes: size,
576                        deleted_at: Utc::now().to_rfc3339(),
577                    });
578                }
579            }
580        }
581
582        deleted
583    }
584
585    /// Delete data for a subject from document collections.
586    pub fn delete_from_documents(
587        &self,
588        state: &AppState,
589        subject_id: &str,
590        search_fields: &[String],
591        specific_collections: Option<&[String]>,
592    ) -> Vec<DeletedItem> {
593        let mut deleted = Vec::new();
594        let collections = state.document_engine.list_collections();
595
596        for collection_name in collections {
597            // Skip if specific collections are specified and this isn't one of them
598            if let Some(specific) = specific_collections {
599                if !specific.contains(&collection_name) {
600                    continue;
601                }
602            }
603
604            // Query all documents in the collection
605            let query = aegis_document::Query::new();
606            if let Ok(result) = state.document_engine.find(&collection_name, &query) {
607                for doc in &result.documents {
608                    // Check if document belongs to subject
609                    let should_delete =
610                        self.document_belongs_to_subject(doc, subject_id, search_fields);
611
612                    if should_delete {
613                        let size = serde_json::to_string(&doc.data)
614                            .map(|s| s.len() as u64)
615                            .ok();
616
617                        if state
618                            .document_engine
619                            .delete(&collection_name, &doc.id)
620                            .is_ok()
621                        {
622                            deleted.push(DeletedItem {
623                                store_type: "document".to_string(),
624                                location: collection_name.clone(),
625                                item_id: doc.id.to_string(),
626                                size_bytes: size,
627                                deleted_at: Utc::now().to_rfc3339(),
628                            });
629                        }
630                    }
631                }
632            }
633        }
634
635        deleted
636    }
637
638    /// Delete data for a subject from SQL tables.
639    pub fn delete_from_sql(
640        &self,
641        state: &AppState,
642        subject_id: &str,
643        search_fields: &[String],
644        specific_tables: Option<&[String]>,
645    ) -> Vec<DeletedItem> {
646        let mut deleted = Vec::new();
647        let tables = state.query_engine.list_tables(None);
648
649        for table_name in tables {
650            // Skip if specific tables are specified and this isn't one of them
651            if let Some(specific) = specific_tables {
652                if !specific.contains(&table_name) {
653                    continue;
654                }
655            }
656
657            // Get table schema to find searchable columns
658            if let Some(table_info) = state.query_engine.get_table_info(&table_name, None) {
659                let searchable_columns: Vec<&String> = table_info
660                    .columns
661                    .iter()
662                    .filter(|c| {
663                        search_fields
664                            .iter()
665                            .any(|f| c.name.to_lowercase() == f.to_lowercase())
666                    })
667                    .map(|c| &c.name)
668                    .collect();
669
670                // Delete rows matching the subject
671                for column in searchable_columns {
672                    // Use parameterized delete - escape subject_id to prevent SQL injection
673                    let escaped_subject = subject_id.replace('\'', "''");
674                    let delete_sql = format!(
675                        "DELETE FROM {} WHERE {} = '{}'",
676                        table_name, column, escaped_subject
677                    );
678
679                    if let Ok(result) = state.query_engine.execute(&delete_sql, None) {
680                        if result.rows_affected > 0 {
681                            deleted.push(DeletedItem {
682                                store_type: "sql".to_string(),
683                                location: table_name.clone(),
684                                item_id: format!("{}={}", column, subject_id),
685                                size_bytes: None, // Size unknown for SQL deletes
686                                deleted_at: Utc::now().to_rfc3339(),
687                            });
688                        }
689                    }
690                }
691            }
692        }
693
694        deleted
695    }
696
697    /// Delete data for a subject from graph store.
698    pub fn delete_from_graph(
699        &self,
700        state: &AppState,
701        subject_id: &str,
702        search_fields: &[String],
703    ) -> Vec<DeletedItem> {
704        let mut deleted = Vec::new();
705        let nodes = state.graph_store.list_nodes();
706
707        for node in nodes {
708            let should_delete =
709                self.value_contains_subject(&node.properties, subject_id, search_fields)
710                    || node.id.contains(subject_id)
711                    || node.label.contains(subject_id);
712
713            if should_delete {
714                let size = serde_json::to_string(&node.properties)
715                    .map(|s| s.len() as u64)
716                    .ok();
717
718                if state.graph_store.delete_node(&node.id).is_ok() {
719                    deleted.push(DeletedItem {
720                        store_type: "graph".to_string(),
721                        location: "graph_store".to_string(),
722                        item_id: node.id,
723                        size_bytes: size,
724                        deleted_at: Utc::now().to_rfc3339(),
725                    });
726                }
727            }
728        }
729
730        deleted
731    }
732
733    /// Check if a JSON value contains the subject identifier.
734    fn value_contains_subject(
735        &self,
736        value: &serde_json::Value,
737        subject_id: &str,
738        search_fields: &[String],
739    ) -> bool {
740        match value {
741            serde_json::Value::Object(map) => {
742                for field in search_fields {
743                    if let Some(v) = map.get(field) {
744                        if self.value_matches_subject(v, subject_id) {
745                            return true;
746                        }
747                    }
748                }
749                // Also check nested objects
750                for (_, v) in map {
751                    if self.value_contains_subject(v, subject_id, search_fields) {
752                        return true;
753                    }
754                }
755                false
756            }
757            serde_json::Value::Array(arr) => arr
758                .iter()
759                .any(|v| self.value_contains_subject(v, subject_id, search_fields)),
760            _ => false,
761        }
762    }
763
764    /// Check if a value matches the subject identifier.
765    fn value_matches_subject(&self, value: &serde_json::Value, subject_id: &str) -> bool {
766        match value {
767            serde_json::Value::String(s) => s == subject_id,
768            serde_json::Value::Number(n) => n.to_string() == subject_id,
769            _ => false,
770        }
771    }
772
773    /// Check if a document belongs to a subject.
774    fn document_belongs_to_subject(
775        &self,
776        doc: &aegis_document::Document,
777        subject_id: &str,
778        search_fields: &[String],
779    ) -> bool {
780        // Check document ID
781        if doc.id.to_string() == subject_id {
782            return true;
783        }
784
785        // Check document fields
786        for field in search_fields {
787            if let Some(value) = doc.get(field) {
788                if self.doc_value_matches_subject(value, subject_id) {
789                    return true;
790                }
791            }
792        }
793
794        false
795    }
796
797    /// Check if a document value matches the subject.
798    fn doc_value_matches_subject(&self, value: &aegis_document::Value, subject_id: &str) -> bool {
799        match value {
800            aegis_document::Value::String(s) => s == subject_id,
801            aegis_document::Value::Int(i) => i.to_string() == subject_id,
802            aegis_document::Value::Float(f) => f.to_string() == subject_id,
803            _ => false,
804        }
805    }
806
807    /// Perform secure erase by overwriting deleted data blocks.
808    /// This is a best-effort operation that overwrites data with zeros.
809    pub fn secure_erase(&self, _state: &AppState) -> bool {
810        // In a production system, this would:
811        // 1. Identify the physical storage blocks that contained deleted data
812        // 2. Overwrite those blocks with random data or zeros multiple times
813        // 3. Verify the overwrite was successful
814        //
815        // For this implementation, we rely on the underlying storage engine's
816        // deletion mechanisms. The data has already been removed from memory
817        // and the persistence layer will not include it on next write.
818
819        // Force a sync to ensure data is actually removed from disk
820        true
821    }
822
823    /// Execute a complete deletion request.
824    pub fn execute_deletion(
825        &self,
826        state: &AppState,
827        request: DeletionRequest,
828    ) -> Result<DeletionCertificate, String> {
829        let node_id = state.config.node_id.clone();
830
831        // Log request received
832        self.audit_log.log_event(
833            DeletionEventType::RequestReceived,
834            &request.subject_id,
835            &request.requestor,
836            serde_json::json!({
837                "scope": request.scope,
838                "reason": request.reason,
839                "secure_erase": request.secure_erase,
840            }),
841        );
842
843        // Log deletion started
844        self.audit_log.log_event(
845            DeletionEventType::DeletionStarted,
846            &request.subject_id,
847            &request.requestor,
848            serde_json::json!({"timestamp": Utc::now().to_rfc3339()}),
849        );
850
851        let search_fields = if request.search_fields.is_empty() {
852            Self::default_search_fields()
853        } else {
854            request.search_fields.clone()
855        };
856
857        let mut all_deleted = Vec::new();
858
859        // Determine specific collections/tables if scope requires it
860        let specific_collections = match &request.scope {
861            DeletionScope::SpecificCollections { collections } => Some(collections.as_slice()),
862            _ => None,
863        };
864
865        // Delete from KV store
866        let kv_deleted = self.delete_from_kv(state, &request.subject_id, &search_fields);
867        for item in &kv_deleted {
868            self.audit_log.log_event(
869                DeletionEventType::ItemDeleted,
870                &request.subject_id,
871                &request.requestor,
872                serde_json::json!({
873                    "store": "kv",
874                    "item_id": item.item_id,
875                }),
876            );
877        }
878        all_deleted.extend(kv_deleted);
879
880        // Delete from documents
881        let doc_deleted = self.delete_from_documents(
882            state,
883            &request.subject_id,
884            &search_fields,
885            specific_collections,
886        );
887        for item in &doc_deleted {
888            self.audit_log.log_event(
889                DeletionEventType::ItemDeleted,
890                &request.subject_id,
891                &request.requestor,
892                serde_json::json!({
893                    "store": "document",
894                    "collection": item.location,
895                    "item_id": item.item_id,
896                }),
897            );
898        }
899        all_deleted.extend(doc_deleted);
900
901        // Delete from SQL tables
902        let sql_deleted = self.delete_from_sql(
903            state,
904            &request.subject_id,
905            &search_fields,
906            specific_collections,
907        );
908        for item in &sql_deleted {
909            self.audit_log.log_event(
910                DeletionEventType::ItemDeleted,
911                &request.subject_id,
912                &request.requestor,
913                serde_json::json!({
914                    "store": "sql",
915                    "table": item.location,
916                    "item_id": item.item_id,
917                }),
918            );
919        }
920        all_deleted.extend(sql_deleted);
921
922        // Delete from graph store
923        let graph_deleted = self.delete_from_graph(state, &request.subject_id, &search_fields);
924        for item in &graph_deleted {
925            self.audit_log.log_event(
926                DeletionEventType::ItemDeleted,
927                &request.subject_id,
928                &request.requestor,
929                serde_json::json!({
930                    "store": "graph",
931                    "item_id": item.item_id,
932                }),
933            );
934        }
935        all_deleted.extend(graph_deleted);
936
937        // Perform secure erase if requested
938        let secure_erase_performed = if request.secure_erase {
939            let success = self.secure_erase(state);
940            self.audit_log.log_event(
941                DeletionEventType::SecureErasePerformed,
942                &request.subject_id,
943                &request.requestor,
944                serde_json::json!({
945                    "success": success,
946                    "timestamp": Utc::now().to_rfc3339(),
947                }),
948            );
949            success
950        } else {
951            false
952        };
953
954        // Save state to disk to persist deletions
955        if let Err(e) = state.save_to_disk() {
956            tracing::warn!("Failed to persist deletions to disk: {}", e);
957        }
958
959        // Log deletion completed
960        self.audit_log.log_event(
961            DeletionEventType::DeletionCompleted,
962            &request.subject_id,
963            &request.requestor,
964            serde_json::json!({
965                "items_deleted": all_deleted.len(),
966                "timestamp": Utc::now().to_rfc3339(),
967            }),
968        );
969
970        // Create certificate
971        let certificate = DeletionCertificate::new(
972            request.subject_id.clone(),
973            all_deleted,
974            request.scope,
975            request.requestor.clone(),
976            request.reason,
977            node_id,
978            secure_erase_performed,
979        );
980
981        // Log certificate generated
982        self.audit_log.log_event(
983            DeletionEventType::CertificateGenerated,
984            &request.subject_id,
985            &request.requestor,
986            serde_json::json!({
987                "certificate_id": certificate.id,
988                "verification_hash": certificate.verification_hash,
989            }),
990        );
991
992        // Store certificate
993        self.audit_log.store_certificate(certificate.clone());
994
995        Ok(certificate)
996    }
997
998    // =========================================================================
999    // Data Export Methods (GDPR Article 20 - Data Portability)
1000    // =========================================================================
1001
1002    /// Export data for a subject from the KV store.
1003    pub fn export_from_kv(
1004        &self,
1005        state: &AppState,
1006        subject_id: &str,
1007        search_fields: &[String],
1008        date_range: Option<&DateRange>,
1009    ) -> Vec<ExportedItem> {
1010        let mut exported = Vec::new();
1011        let entries = state.kv_store.list(None, usize::MAX);
1012
1013        for entry in entries {
1014            // Check if entry belongs to subject
1015            let belongs_to_subject =
1016                self.value_contains_subject(&entry.value, subject_id, search_fields)
1017                    || entry.key.contains(subject_id);
1018
1019            if !belongs_to_subject {
1020                continue;
1021            }
1022
1023            // Check date range filter
1024            if let Some(range) = date_range {
1025                if !self.is_within_date_range(&entry.updated_at.to_rfc3339(), range) {
1026                    continue;
1027                }
1028            }
1029
1030            exported.push(ExportedItem {
1031                store_type: "kv".to_string(),
1032                location: "kv_store".to_string(),
1033                item_id: entry.key.clone(),
1034                data: serde_json::json!({
1035                    "key": entry.key,
1036                    "value": entry.value,
1037                    "ttl": entry.ttl,
1038                }),
1039                timestamp: Some(entry.updated_at.to_rfc3339()),
1040            });
1041        }
1042
1043        exported
1044    }
1045
1046    /// Export data for a subject from document collections.
1047    pub fn export_from_documents(
1048        &self,
1049        state: &AppState,
1050        subject_id: &str,
1051        search_fields: &[String],
1052        specific_collections: Option<&[String]>,
1053        date_range: Option<&DateRange>,
1054    ) -> Vec<ExportedItem> {
1055        let mut exported = Vec::new();
1056        let collections = state.document_engine.list_collections();
1057
1058        for collection_name in collections {
1059            // Skip if specific collections are specified and this isn't one of them
1060            if let Some(specific) = specific_collections {
1061                if !specific.contains(&collection_name) {
1062                    continue;
1063                }
1064            }
1065
1066            // Query all documents in the collection
1067            let query = aegis_document::Query::new();
1068            if let Ok(result) = state.document_engine.find(&collection_name, &query) {
1069                for doc in &result.documents {
1070                    // Check if document belongs to subject
1071                    if !self.document_belongs_to_subject(doc, subject_id, search_fields) {
1072                        continue;
1073                    }
1074
1075                    // Check date range filter using document's updated_at if available
1076                    if let Some(range) = date_range {
1077                        if let Some(aegis_document::Value::String(updated)) = doc.get("updated_at")
1078                        {
1079                            if !self.is_within_date_range(updated, range) {
1080                                continue;
1081                            }
1082                        }
1083                    }
1084
1085                    // Convert document data to JSON
1086                    let doc_data = self.document_to_json(doc);
1087
1088                    exported.push(ExportedItem {
1089                        store_type: "document".to_string(),
1090                        location: collection_name.clone(),
1091                        item_id: doc.id.to_string(),
1092                        data: doc_data,
1093                        timestamp: doc.get("updated_at").and_then(|v| {
1094                            if let aegis_document::Value::String(s) = v {
1095                                Some(s.clone())
1096                            } else {
1097                                None
1098                            }
1099                        }),
1100                    });
1101                }
1102            }
1103        }
1104
1105        exported
1106    }
1107
1108    /// Export data for a subject from SQL tables.
1109    pub fn export_from_sql(
1110        &self,
1111        state: &AppState,
1112        subject_id: &str,
1113        search_fields: &[String],
1114        specific_tables: Option<&[String]>,
1115        _date_range: Option<&DateRange>,
1116    ) -> Vec<ExportedItem> {
1117        let mut exported = Vec::new();
1118        let tables = state.query_engine.list_tables(None);
1119
1120        for table_name in tables {
1121            // Skip if specific tables are specified and this isn't one of them
1122            if let Some(specific) = specific_tables {
1123                if !specific.contains(&table_name) {
1124                    continue;
1125                }
1126            }
1127
1128            // Get table schema to find searchable columns
1129            if let Some(table_info) = state.query_engine.get_table_info(&table_name, None) {
1130                let searchable_columns: Vec<&String> = table_info
1131                    .columns
1132                    .iter()
1133                    .filter(|c| {
1134                        search_fields
1135                            .iter()
1136                            .any(|f| c.name.to_lowercase() == f.to_lowercase())
1137                    })
1138                    .map(|c| &c.name)
1139                    .collect();
1140
1141                // Query rows matching the subject
1142                for column in searchable_columns {
1143                    // Use parameterized query - escape subject_id to prevent SQL injection
1144                    let escaped_subject = subject_id.replace('\'', "''");
1145                    let select_sql = format!(
1146                        "SELECT * FROM {} WHERE {} = '{}'",
1147                        table_name, column, escaped_subject
1148                    );
1149
1150                    if let Ok(result) = state.query_engine.execute(&select_sql, None) {
1151                        for (idx, row) in result.rows.iter().enumerate() {
1152                            // Convert row to JSON object
1153                            let mut row_data = serde_json::Map::new();
1154                            for (col_idx, col_name) in result.columns.iter().enumerate() {
1155                                if let Some(value) = row.get(col_idx) {
1156                                    row_data.insert(col_name.clone(), value.clone());
1157                                }
1158                            }
1159
1160                            exported.push(ExportedItem {
1161                                store_type: "sql".to_string(),
1162                                location: table_name.clone(),
1163                                item_id: format!("{}={}/row-{}", column, subject_id, idx),
1164                                data: serde_json::Value::Object(row_data),
1165                                timestamp: None,
1166                            });
1167                        }
1168                    }
1169                }
1170            }
1171        }
1172
1173        exported
1174    }
1175
1176    /// Export data for a subject from graph store.
1177    pub fn export_from_graph(
1178        &self,
1179        state: &AppState,
1180        subject_id: &str,
1181        search_fields: &[String],
1182    ) -> Vec<ExportedItem> {
1183        let mut exported = Vec::new();
1184        let nodes = state.graph_store.list_nodes();
1185
1186        for node in nodes {
1187            let belongs_to_subject =
1188                self.value_contains_subject(&node.properties, subject_id, search_fields)
1189                    || node.id.contains(subject_id)
1190                    || node.label.contains(subject_id);
1191
1192            if belongs_to_subject {
1193                // Get edges connected to this node
1194                let edges = state.graph_store.get_edges_for_node(&node.id);
1195                let edge_data: Vec<serde_json::Value> = edges
1196                    .iter()
1197                    .map(|e| {
1198                        serde_json::json!({
1199                            "id": e.id,
1200                            "source": e.source,
1201                            "target": e.target,
1202                            "relationship": e.relationship,
1203                        })
1204                    })
1205                    .collect();
1206
1207                exported.push(ExportedItem {
1208                    store_type: "graph".to_string(),
1209                    location: "graph_store".to_string(),
1210                    item_id: node.id.clone(),
1211                    data: serde_json::json!({
1212                        "id": node.id,
1213                        "label": node.label,
1214                        "properties": node.properties,
1215                        "edges": edge_data,
1216                    }),
1217                    timestamp: None,
1218                });
1219            }
1220        }
1221
1222        exported
1223    }
1224
1225    /// Check if a timestamp is within the specified date range.
1226    fn is_within_date_range(&self, timestamp: &str, range: &DateRange) -> bool {
1227        let ts = match DateTime::parse_from_rfc3339(timestamp) {
1228            Ok(dt) => dt.with_timezone(&Utc),
1229            Err(_) => return true, // If we can't parse, include it
1230        };
1231
1232        if let Some(ref start) = range.start {
1233            if let Ok(start_dt) = DateTime::parse_from_rfc3339(start) {
1234                if ts < start_dt.with_timezone(&Utc) {
1235                    return false;
1236                }
1237            }
1238        }
1239
1240        if let Some(ref end) = range.end {
1241            if let Ok(end_dt) = DateTime::parse_from_rfc3339(end) {
1242                if ts > end_dt.with_timezone(&Utc) {
1243                    return false;
1244                }
1245            }
1246        }
1247
1248        true
1249    }
1250
1251    /// Convert a document to JSON.
1252    fn document_to_json(&self, doc: &aegis_document::Document) -> serde_json::Value {
1253        let mut map = serde_json::Map::new();
1254        map.insert(
1255            "_id".to_string(),
1256            serde_json::Value::String(doc.id.to_string()),
1257        );
1258        for (key, value) in &doc.data {
1259            map.insert(key.clone(), self.doc_value_to_json(value));
1260        }
1261        serde_json::Value::Object(map)
1262    }
1263
1264    /// Convert a document value to JSON.
1265    fn doc_value_to_json(&self, value: &aegis_document::Value) -> serde_json::Value {
1266        match value {
1267            aegis_document::Value::Null => serde_json::Value::Null,
1268            aegis_document::Value::Bool(b) => serde_json::Value::Bool(*b),
1269            aegis_document::Value::Int(i) => serde_json::Value::Number((*i).into()),
1270            aegis_document::Value::Float(f) => serde_json::Number::from_f64(*f)
1271                .map(serde_json::Value::Number)
1272                .unwrap_or(serde_json::Value::Null),
1273            aegis_document::Value::String(s) => serde_json::Value::String(s.clone()),
1274            aegis_document::Value::Array(arr) => {
1275                serde_json::Value::Array(arr.iter().map(|v| self.doc_value_to_json(v)).collect())
1276            }
1277            aegis_document::Value::Object(obj) => {
1278                let map: serde_json::Map<String, serde_json::Value> = obj
1279                    .iter()
1280                    .map(|(k, v)| (k.clone(), self.doc_value_to_json(v)))
1281                    .collect();
1282                serde_json::Value::Object(map)
1283            }
1284        }
1285    }
1286
1287    /// Convert exported items to CSV format.
1288    fn items_to_csv(&self, items: &[ExportedItem]) -> String {
1289        if items.is_empty() {
1290            return "store_type,location,item_id,timestamp,data\n".to_string();
1291        }
1292
1293        let mut csv = String::new();
1294
1295        // Collect all unique data keys from all items
1296        let mut all_data_keys: Vec<String> = Vec::new();
1297        for item in items {
1298            if let serde_json::Value::Object(map) = &item.data {
1299                for key in map.keys() {
1300                    if !all_data_keys.contains(key) {
1301                        all_data_keys.push(key.clone());
1302                    }
1303                }
1304            }
1305        }
1306        all_data_keys.sort();
1307
1308        // Write header
1309        let mut header_parts = vec![
1310            "store_type".to_string(),
1311            "location".to_string(),
1312            "item_id".to_string(),
1313            "timestamp".to_string(),
1314        ];
1315        header_parts.extend(all_data_keys.iter().cloned());
1316        csv.push_str(&header_parts.join(","));
1317        csv.push('\n');
1318
1319        // Write rows
1320        for item in items {
1321            let mut row_parts = vec![
1322                escape_csv_field(&item.store_type),
1323                escape_csv_field(&item.location),
1324                escape_csv_field(&item.item_id),
1325                escape_csv_field(&item.timestamp.clone().unwrap_or_default()),
1326            ];
1327
1328            // Add data fields
1329            for key in &all_data_keys {
1330                let value = if let serde_json::Value::Object(map) = &item.data {
1331                    map.get(key)
1332                        .map(json_value_to_csv_string)
1333                        .unwrap_or_default()
1334                } else {
1335                    String::new()
1336                };
1337                row_parts.push(escape_csv_field(&value));
1338            }
1339
1340            csv.push_str(&row_parts.join(","));
1341            csv.push('\n');
1342        }
1343
1344        csv
1345    }
1346
1347    /// Execute a complete data export request (GDPR Article 20).
1348    pub fn execute_export(
1349        &self,
1350        state: &AppState,
1351        request: ExportRequest,
1352    ) -> Result<ExportResponse, String> {
1353        // Generate export ID
1354        let timestamp = Utc::now();
1355        let export_id = format!(
1356            "EXP-{}-{}",
1357            timestamp.format("%Y%m%d%H%M%S"),
1358            &request.subject_id.chars().take(8).collect::<String>()
1359        );
1360
1361        // Log export request received
1362        self.audit_log.log_event(
1363            DeletionEventType::ExportRequestReceived,
1364            &request.subject_id,
1365            "system",
1366            serde_json::json!({
1367                "export_id": export_id,
1368                "format": request.format,
1369                "scope": request.scope,
1370                "date_range": request.date_range,
1371            }),
1372        );
1373
1374        let search_fields = if request.search_fields.is_empty() {
1375            Self::default_search_fields()
1376        } else {
1377            request.search_fields.clone()
1378        };
1379
1380        let mut all_exported = Vec::new();
1381
1382        // Determine specific collections/tables if scope requires it
1383        let specific_collections = match &request.scope {
1384            ExportScope::SpecificCollections { collections } => Some(collections.as_slice()),
1385            _ => None,
1386        };
1387
1388        // Export from KV store
1389        let kv_exported = self.export_from_kv(
1390            state,
1391            &request.subject_id,
1392            &search_fields,
1393            request.date_range.as_ref(),
1394        );
1395        all_exported.extend(kv_exported);
1396
1397        // Export from documents
1398        let doc_exported = self.export_from_documents(
1399            state,
1400            &request.subject_id,
1401            &search_fields,
1402            specific_collections,
1403            request.date_range.as_ref(),
1404        );
1405        all_exported.extend(doc_exported);
1406
1407        // Export from SQL tables
1408        let sql_exported = self.export_from_sql(
1409            state,
1410            &request.subject_id,
1411            &search_fields,
1412            specific_collections,
1413            request.date_range.as_ref(),
1414        );
1415        all_exported.extend(sql_exported);
1416
1417        // Export from graph store
1418        let graph_exported = self.export_from_graph(state, &request.subject_id, &search_fields);
1419        all_exported.extend(graph_exported);
1420
1421        let total_items = all_exported.len();
1422
1423        // Convert to requested format
1424        let data = match request.format {
1425            ExportFormat::Json => {
1426                // Group items by store type for better organization
1427                let mut grouped: HashMap<String, Vec<&ExportedItem>> = HashMap::new();
1428                for item in &all_exported {
1429                    grouped
1430                        .entry(item.store_type.clone())
1431                        .or_default()
1432                        .push(item);
1433                }
1434
1435                let mut result = serde_json::Map::new();
1436                result.insert(
1437                    "subject_id".to_string(),
1438                    serde_json::Value::String(request.subject_id.clone()),
1439                );
1440                result.insert(
1441                    "export_id".to_string(),
1442                    serde_json::Value::String(export_id.clone()),
1443                );
1444                result.insert(
1445                    "generated_at".to_string(),
1446                    serde_json::Value::String(timestamp.to_rfc3339()),
1447                );
1448                result.insert(
1449                    "total_items".to_string(),
1450                    serde_json::Value::Number(total_items.into()),
1451                );
1452
1453                let mut data_section = serde_json::Map::new();
1454                for (store_type, items) in grouped {
1455                    let items_json: Vec<serde_json::Value> = items
1456                        .iter()
1457                        .map(|item| {
1458                            serde_json::json!({
1459                                "location": item.location,
1460                                "item_id": item.item_id,
1461                                "data": item.data,
1462                                "timestamp": item.timestamp,
1463                            })
1464                        })
1465                        .collect();
1466                    data_section.insert(store_type, serde_json::Value::Array(items_json));
1467                }
1468                result.insert("data".to_string(), serde_json::Value::Object(data_section));
1469
1470                serde_json::Value::Object(result)
1471            }
1472            ExportFormat::Csv => {
1473                let csv_content = self.items_to_csv(&all_exported);
1474                serde_json::Value::String(csv_content)
1475            }
1476        };
1477
1478        // Log export completed
1479        self.audit_log.log_event(
1480            DeletionEventType::ExportCompleted,
1481            &request.subject_id,
1482            "system",
1483            serde_json::json!({
1484                "export_id": export_id,
1485                "total_items": total_items,
1486                "format": request.format,
1487                "timestamp": timestamp.to_rfc3339(),
1488            }),
1489        );
1490
1491        Ok(ExportResponse {
1492            export_id,
1493            subject_id: request.subject_id,
1494            format: request.format,
1495            generated_at: timestamp.to_rfc3339(),
1496            total_items,
1497            data,
1498            scope: request.scope,
1499            date_range: request.date_range,
1500        })
1501    }
1502}
1503
1504impl Default for GdprService {
1505    fn default() -> Self {
1506        Self::new()
1507    }
1508}
1509
1510/// Escape a field for CSV output.
1511fn escape_csv_field(field: &str) -> String {
1512    if field.contains(',') || field.contains('"') || field.contains('\n') || field.contains('\r') {
1513        format!("\"{}\"", field.replace('"', "\"\""))
1514    } else {
1515        field.to_string()
1516    }
1517}
1518
1519/// Convert a JSON value to a string suitable for CSV.
1520fn json_value_to_csv_string(value: &serde_json::Value) -> String {
1521    match value {
1522        serde_json::Value::Null => String::new(),
1523        serde_json::Value::Bool(b) => b.to_string(),
1524        serde_json::Value::Number(n) => n.to_string(),
1525        serde_json::Value::String(s) => s.clone(),
1526        serde_json::Value::Array(_) | serde_json::Value::Object(_) => {
1527            serde_json::to_string(value).unwrap_or_default()
1528        }
1529    }
1530}
1531
1532// =============================================================================
1533// HTTP Handlers
1534// =============================================================================
1535
1536/// Delete all data for a data subject (GDPR right to erasure).
1537pub async fn delete_data_subject(
1538    State(state): State<AppState>,
1539    Path(identifier): Path<String>,
1540    Json(mut request): Json<DeletionRequest>,
1541) -> impl IntoResponse {
1542    // Use path parameter as subject_id if not provided in body
1543    if request.subject_id.is_empty() {
1544        request.subject_id = identifier;
1545    }
1546
1547    state.activity.log_with_details(
1548        ActivityType::Delete,
1549        &format!("GDPR deletion request for subject: {}", request.subject_id),
1550        None,
1551        Some(&request.requestor),
1552        Some("gdpr"),
1553        Some(serde_json::json!({
1554            "scope": request.scope,
1555            "reason": request.reason,
1556        })),
1557    );
1558
1559    match state.gdpr.execute_deletion(&state, request) {
1560        Ok(certificate) => {
1561            state.activity.log_with_details(
1562                ActivityType::System,
1563                &format!("GDPR deletion completed. Certificate: {}", certificate.id),
1564                None,
1565                None,
1566                Some("gdpr"),
1567                Some(serde_json::json!({
1568                    "items_deleted": certificate.total_items,
1569                    "bytes_deleted": certificate.total_bytes,
1570                })),
1571            );
1572
1573            (
1574                StatusCode::OK,
1575                Json(DeletionResponse {
1576                    success: true,
1577                    certificate: Some(certificate),
1578                    message: "Data deletion completed successfully".to_string(),
1579                    error: None,
1580                }),
1581            )
1582        }
1583        Err(e) => {
1584            state.activity.log_with_details(
1585                ActivityType::System,
1586                &format!("GDPR deletion failed: {}", e),
1587                None,
1588                None,
1589                Some("gdpr"),
1590                None,
1591            );
1592
1593            (
1594                StatusCode::INTERNAL_SERVER_ERROR,
1595                Json(DeletionResponse {
1596                    success: false,
1597                    certificate: None,
1598                    message: "Data deletion failed".to_string(),
1599                    error: Some(e),
1600                }),
1601            )
1602        }
1603    }
1604}
1605
1606/// List all deletion certificates.
1607pub async fn list_deletion_certificates(
1608    State(state): State<AppState>,
1609) -> Json<ListCertificatesResponse> {
1610    state
1611        .activity
1612        .log(ActivityType::Query, "List GDPR deletion certificates");
1613
1614    let certificates = state.gdpr.audit_log().list_certificates();
1615    let total = certificates.len();
1616
1617    Json(ListCertificatesResponse {
1618        certificates,
1619        total,
1620    })
1621}
1622
1623/// Get a specific deletion certificate.
1624pub async fn get_deletion_certificate(
1625    State(state): State<AppState>,
1626    Path(cert_id): Path<String>,
1627) -> impl IntoResponse {
1628    state.activity.log(
1629        ActivityType::Query,
1630        &format!("Get GDPR certificate: {}", cert_id),
1631    );
1632
1633    match state.gdpr.audit_log().get_certificate(&cert_id) {
1634        Some(cert) => (StatusCode::OK, Json(Some(cert))),
1635        None => (StatusCode::NOT_FOUND, Json(None)),
1636    }
1637}
1638
1639/// Verify a deletion certificate.
1640pub async fn verify_deletion_certificate(
1641    State(state): State<AppState>,
1642    Path(cert_id): Path<String>,
1643) -> Json<VerifyCertificateResponse> {
1644    state.activity.log(
1645        ActivityType::Query,
1646        &format!("Verify GDPR certificate: {}", cert_id),
1647    );
1648
1649    match state.gdpr.audit_log().get_certificate(&cert_id) {
1650        Some(cert) => {
1651            let valid = cert.verify();
1652            Json(VerifyCertificateResponse {
1653                valid,
1654                certificate: Some(cert),
1655                message: if valid {
1656                    "Certificate is valid and has not been tampered with".to_string()
1657                } else {
1658                    "Certificate verification failed - hash mismatch".to_string()
1659                },
1660            })
1661        }
1662        None => Json(VerifyCertificateResponse {
1663            valid: false,
1664            certificate: None,
1665            message: format!("Certificate '{}' not found", cert_id),
1666        }),
1667    }
1668}
1669
1670/// Get deletion audit entries for a subject.
1671pub async fn get_deletion_audit(
1672    State(state): State<AppState>,
1673    Path(subject_id): Path<String>,
1674) -> Json<Vec<DeletionAuditEntry>> {
1675    state.activity.log(
1676        ActivityType::Query,
1677        &format!("Get GDPR audit for: {}", subject_id),
1678    );
1679
1680    Json(state.gdpr.audit_log().get_entries_for_subject(&subject_id))
1681}
1682
1683/// Verify the integrity of the deletion audit log.
1684pub async fn verify_audit_integrity(State(state): State<AppState>) -> impl IntoResponse {
1685    state
1686        .activity
1687        .log(ActivityType::Query, "Verify GDPR audit log integrity");
1688
1689    match state.gdpr.audit_log().verify_integrity() {
1690        Ok(count) => (
1691            StatusCode::OK,
1692            Json(serde_json::json!({
1693                "valid": true,
1694                "entries_verified": count,
1695                "message": "Audit log integrity verified successfully",
1696            })),
1697        ),
1698        Err(e) => (
1699            StatusCode::OK,
1700            Json(serde_json::json!({
1701                "valid": false,
1702                "entries_verified": 0,
1703                "message": e,
1704            })),
1705        ),
1706    }
1707}
1708
1709// =============================================================================
1710// Data Export Handlers (GDPR Article 20 - Data Portability)
1711// =============================================================================
1712
1713/// Export data for a data subject (GDPR Article 20 - Right to Data Portability).
1714///
1715/// This endpoint exports all data associated with a data subject in a machine-readable
1716/// format (JSON or CSV) as required by GDPR Article 20.
1717///
1718/// Request body:
1719/// - subject_id: The identifier of the data subject (required)
1720/// - format: "json" or "csv" (default: "json")
1721/// - scope: { "type": "all" } or { "type": "specific_collections", "collections": [...] }
1722/// - date_range: { "start": "ISO8601", "end": "ISO8601" } (optional)
1723/// - search_fields: Array of field names to search for subject_id (optional)
1724pub async fn export_data_subject(
1725    State(state): State<AppState>,
1726    Json(request): Json<ExportRequest>,
1727) -> impl IntoResponse {
1728    // Validate request
1729    if request.subject_id.is_empty() {
1730        return (
1731            StatusCode::BAD_REQUEST,
1732            Json(serde_json::json!({
1733                "success": false,
1734                "error": "subject_id is required",
1735            })),
1736        );
1737    }
1738
1739    state.activity.log_with_details(
1740        ActivityType::Query,
1741        &format!(
1742            "GDPR data export request for subject: {}",
1743            request.subject_id
1744        ),
1745        None,
1746        None,
1747        Some("gdpr"),
1748        Some(serde_json::json!({
1749            "format": request.format,
1750            "scope": request.scope,
1751            "date_range": request.date_range,
1752        })),
1753    );
1754
1755    match state.gdpr.execute_export(&state, request) {
1756        Ok(response) => {
1757            state.activity.log_with_details(
1758                ActivityType::System,
1759                &format!(
1760                    "GDPR data export completed. Export ID: {}",
1761                    response.export_id
1762                ),
1763                None,
1764                None,
1765                Some("gdpr"),
1766                Some(serde_json::json!({
1767                    "export_id": response.export_id,
1768                    "total_items": response.total_items,
1769                    "format": response.format,
1770                })),
1771            );
1772
1773            (
1774                StatusCode::OK,
1775                Json(serde_json::json!({
1776                    "success": true,
1777                    "export_id": response.export_id,
1778                    "subject_id": response.subject_id,
1779                    "format": response.format,
1780                    "generated_at": response.generated_at,
1781                    "total_items": response.total_items,
1782                    "data": response.data,
1783                    "scope": response.scope,
1784                    "date_range": response.date_range,
1785                })),
1786            )
1787        }
1788        Err(e) => {
1789            state.activity.log_with_details(
1790                ActivityType::System,
1791                &format!("GDPR data export failed: {}", e),
1792                None,
1793                None,
1794                Some("gdpr"),
1795                None,
1796            );
1797
1798            (
1799                StatusCode::INTERNAL_SERVER_ERROR,
1800                Json(serde_json::json!({
1801                    "success": false,
1802                    "error": e,
1803                })),
1804            )
1805        }
1806    }
1807}
1808
1809// =============================================================================
1810// Helper Functions
1811// =============================================================================
1812
1813/// Encode bytes as lowercase hex string.
1814fn hex_encode(bytes: &[u8]) -> String {
1815    const HEX_CHARS: &[u8] = b"0123456789abcdef";
1816    let mut result = String::with_capacity(bytes.len() * 2);
1817    for &byte in bytes {
1818        result.push(HEX_CHARS[(byte >> 4) as usize] as char);
1819        result.push(HEX_CHARS[(byte & 0x0f) as usize] as char);
1820    }
1821    result
1822}
1823
1824// =============================================================================
1825// Tests
1826// =============================================================================
1827
1828#[cfg(test)]
1829mod tests {
1830    use super::*;
1831
1832    #[test]
1833    fn test_deletion_scope_default() {
1834        let scope = DeletionScope::default();
1835        assert_eq!(scope, DeletionScope::All);
1836    }
1837
1838    #[test]
1839    fn test_deletion_certificate_hash() {
1840        let items = vec![DeletedItem {
1841            store_type: "kv".to_string(),
1842            location: "kv_store".to_string(),
1843            item_id: "user:123".to_string(),
1844            size_bytes: Some(256),
1845            deleted_at: "2025-01-26T12:00:00Z".to_string(),
1846        }];
1847
1848        let cert = DeletionCertificate::new(
1849            "user@example.com".to_string(),
1850            items,
1851            DeletionScope::All,
1852            "admin".to_string(),
1853            "GDPR request".to_string(),
1854            "node-1".to_string(),
1855            false,
1856        );
1857
1858        // Hash should be computed
1859        assert!(!cert.verification_hash.is_empty());
1860
1861        // Certificate should verify
1862        assert!(cert.verify());
1863    }
1864
1865    #[test]
1866    fn test_deletion_certificate_tamper_detection() {
1867        let items = vec![DeletedItem {
1868            store_type: "kv".to_string(),
1869            location: "kv_store".to_string(),
1870            item_id: "user:123".to_string(),
1871            size_bytes: Some(256),
1872            deleted_at: "2025-01-26T12:00:00Z".to_string(),
1873        }];
1874
1875        let mut cert = DeletionCertificate::new(
1876            "user@example.com".to_string(),
1877            items,
1878            DeletionScope::All,
1879            "admin".to_string(),
1880            "GDPR request".to_string(),
1881            "node-1".to_string(),
1882            false,
1883        );
1884
1885        // Tamper with the certificate
1886        cert.total_items = 999;
1887
1888        // Should fail verification
1889        assert!(!cert.verify());
1890    }
1891
1892    #[test]
1893    fn test_audit_log_integrity() {
1894        let audit_log = DeletionAuditLog::new();
1895
1896        // Log some events
1897        audit_log.log_event(
1898            DeletionEventType::RequestReceived,
1899            "user@example.com",
1900            "admin",
1901            serde_json::json!({"test": true}),
1902        );
1903        audit_log.log_event(
1904            DeletionEventType::DeletionStarted,
1905            "user@example.com",
1906            "admin",
1907            serde_json::json!({}),
1908        );
1909        audit_log.log_event(
1910            DeletionEventType::DeletionCompleted,
1911            "user@example.com",
1912            "admin",
1913            serde_json::json!({"items": 5}),
1914        );
1915
1916        // Should verify successfully
1917        let result = audit_log.verify_integrity();
1918        assert!(result.is_ok());
1919        assert_eq!(result.unwrap(), 3);
1920    }
1921
1922    #[test]
1923    fn test_audit_log_get_entries_for_subject() {
1924        let audit_log = DeletionAuditLog::new();
1925
1926        audit_log.log_event(
1927            DeletionEventType::RequestReceived,
1928            "user1@example.com",
1929            "admin",
1930            serde_json::json!({}),
1931        );
1932        audit_log.log_event(
1933            DeletionEventType::RequestReceived,
1934            "user2@example.com",
1935            "admin",
1936            serde_json::json!({}),
1937        );
1938        audit_log.log_event(
1939            DeletionEventType::DeletionCompleted,
1940            "user1@example.com",
1941            "admin",
1942            serde_json::json!({}),
1943        );
1944
1945        let user1_entries = audit_log.get_entries_for_subject("user1@example.com");
1946        assert_eq!(user1_entries.len(), 2);
1947
1948        let user2_entries = audit_log.get_entries_for_subject("user2@example.com");
1949        assert_eq!(user2_entries.len(), 1);
1950    }
1951
1952    #[test]
1953    fn test_gdpr_service_default_search_fields() {
1954        let fields = GdprService::default_search_fields();
1955        assert!(fields.contains(&"email".to_string()));
1956        assert!(fields.contains(&"user_id".to_string()));
1957        assert!(fields.contains(&"customer_id".to_string()));
1958    }
1959
1960    #[test]
1961    fn test_hex_encode() {
1962        let data = [0x00, 0x01, 0x0a, 0xff, 0xab];
1963        let hex = hex_encode(&data);
1964        assert_eq!(hex, "00010affab");
1965    }
1966
1967    #[test]
1968    fn test_deletion_scope_serialization() {
1969        let scope = DeletionScope::SpecificCollections {
1970            collections: vec!["users".to_string(), "orders".to_string()],
1971        };
1972        let json = serde_json::to_string(&scope).unwrap();
1973        assert!(json.contains("specific_collections"));
1974        assert!(json.contains("users"));
1975
1976        let deserialized: DeletionScope = serde_json::from_str(&json).unwrap();
1977        match deserialized {
1978            DeletionScope::SpecificCollections { collections } => {
1979                assert_eq!(collections.len(), 2);
1980                assert!(collections.contains(&"users".to_string()));
1981            }
1982            _ => panic!("Wrong variant deserialized"),
1983        }
1984    }
1985
1986    // =========================================================================
1987    // Export Tests (GDPR Article 20)
1988    // =========================================================================
1989
1990    #[test]
1991    fn test_export_format_default() {
1992        let format = ExportFormat::default();
1993        assert_eq!(format, ExportFormat::Json);
1994    }
1995
1996    #[test]
1997    fn test_export_format_serialization() {
1998        // Test JSON format
1999        let format = ExportFormat::Json;
2000        let json = serde_json::to_string(&format).unwrap();
2001        assert_eq!(json, "\"json\"");
2002
2003        let deserialized: ExportFormat = serde_json::from_str(&json).unwrap();
2004        assert_eq!(deserialized, ExportFormat::Json);
2005
2006        // Test CSV format
2007        let format = ExportFormat::Csv;
2008        let json = serde_json::to_string(&format).unwrap();
2009        assert_eq!(json, "\"csv\"");
2010
2011        let deserialized: ExportFormat = serde_json::from_str(&json).unwrap();
2012        assert_eq!(deserialized, ExportFormat::Csv);
2013    }
2014
2015    #[test]
2016    fn test_export_scope_default() {
2017        let scope = ExportScope::default();
2018        match scope {
2019            ExportScope::All => {} // expected
2020            _ => panic!("Expected ExportScope::All as default"),
2021        }
2022    }
2023
2024    #[test]
2025    fn test_export_scope_serialization() {
2026        let scope = ExportScope::SpecificCollections {
2027            collections: vec!["users".to_string(), "orders".to_string()],
2028        };
2029        let json = serde_json::to_string(&scope).unwrap();
2030        assert!(json.contains("specific_collections"));
2031        assert!(json.contains("users"));
2032
2033        let deserialized: ExportScope = serde_json::from_str(&json).unwrap();
2034        match deserialized {
2035            ExportScope::SpecificCollections { collections } => {
2036                assert_eq!(collections.len(), 2);
2037                assert!(collections.contains(&"users".to_string()));
2038            }
2039            _ => panic!("Wrong variant deserialized"),
2040        }
2041    }
2042
2043    #[test]
2044    fn test_export_request_deserialization() {
2045        // Test minimal request
2046        let json = r#"{"subject_id": "user@example.com"}"#;
2047        let request: ExportRequest = serde_json::from_str(json).unwrap();
2048        assert_eq!(request.subject_id, "user@example.com");
2049        assert_eq!(request.format, ExportFormat::Json);
2050        assert!(request.search_fields.is_empty());
2051
2052        // Test full request
2053        let json = r#"{
2054            "subject_id": "user@example.com",
2055            "format": "csv",
2056            "scope": {"type": "specific_collections", "collections": ["users"]},
2057            "date_range": {"start": "2024-01-01T00:00:00Z", "end": "2024-12-31T23:59:59Z"},
2058            "search_fields": ["email", "user_id"]
2059        }"#;
2060        let request: ExportRequest = serde_json::from_str(json).unwrap();
2061        assert_eq!(request.subject_id, "user@example.com");
2062        assert_eq!(request.format, ExportFormat::Csv);
2063        assert_eq!(request.search_fields, vec!["email", "user_id"]);
2064        assert!(request.date_range.is_some());
2065    }
2066
2067    #[test]
2068    fn test_date_range() {
2069        let range = DateRange {
2070            start: Some("2024-01-01T00:00:00Z".to_string()),
2071            end: Some("2024-12-31T23:59:59Z".to_string()),
2072        };
2073
2074        let json = serde_json::to_string(&range).unwrap();
2075        assert!(json.contains("2024-01-01"));
2076        assert!(json.contains("2024-12-31"));
2077    }
2078
2079    #[test]
2080    fn test_escape_csv_field() {
2081        // Normal field
2082        assert_eq!(escape_csv_field("hello"), "hello");
2083
2084        // Field with comma
2085        assert_eq!(escape_csv_field("hello,world"), "\"hello,world\"");
2086
2087        // Field with quotes
2088        assert_eq!(escape_csv_field("hello\"world"), "\"hello\"\"world\"");
2089
2090        // Field with newline
2091        assert_eq!(escape_csv_field("hello\nworld"), "\"hello\nworld\"");
2092    }
2093
2094    #[test]
2095    fn test_json_value_to_csv_string() {
2096        assert_eq!(json_value_to_csv_string(&serde_json::Value::Null), "");
2097        assert_eq!(
2098            json_value_to_csv_string(&serde_json::Value::Bool(true)),
2099            "true"
2100        );
2101        assert_eq!(json_value_to_csv_string(&serde_json::json!(42)), "42");
2102        assert_eq!(
2103            json_value_to_csv_string(&serde_json::json!("hello")),
2104            "hello"
2105        );
2106        assert_eq!(
2107            json_value_to_csv_string(&serde_json::json!([1, 2, 3])),
2108            "[1,2,3]"
2109        );
2110    }
2111
2112    #[test]
2113    fn test_exported_item_structure() {
2114        let item = ExportedItem {
2115            store_type: "kv".to_string(),
2116            location: "kv_store".to_string(),
2117            item_id: "user:123".to_string(),
2118            data: serde_json::json!({"key": "user:123", "value": {"name": "John"}}),
2119            timestamp: Some("2024-01-26T12:00:00Z".to_string()),
2120        };
2121
2122        let json = serde_json::to_string(&item).unwrap();
2123        assert!(json.contains("kv_store"));
2124        assert!(json.contains("user:123"));
2125        assert!(json.contains("John"));
2126    }
2127
2128    #[test]
2129    fn test_export_event_types() {
2130        // Verify the new event types are serializable
2131        let event = DeletionEventType::ExportRequestReceived;
2132        let json = serde_json::to_string(&event).unwrap();
2133        assert_eq!(json, "\"export_request_received\"");
2134
2135        let event = DeletionEventType::ExportCompleted;
2136        let json = serde_json::to_string(&event).unwrap();
2137        assert_eq!(json, "\"export_completed\"");
2138
2139        let event = DeletionEventType::ExportFailed;
2140        let json = serde_json::to_string(&event).unwrap();
2141        assert_eq!(json, "\"export_failed\"");
2142    }
2143}