Skip to main content

oxidize_pdf/forms/
signature_handler.rs

1//! Signature handler for managing signature fields and signing process
2//!
3//! This module provides the integration between signature fields and the PDF document,
4//! handling the signing process, field locking, and signature validation.
5
6use crate::error::PdfError;
7use crate::forms::signature_field::{SignatureAlgorithm, SignatureField, SignerInfo};
8use crate::objects::{Dictionary, Object};
9use chrono::Utc;
10use std::collections::{HashMap, HashSet};
11
12/// Handler for managing signature fields in a document
13pub struct SignatureHandler {
14    /// All signature fields in the document
15    signature_fields: HashMap<String, SignatureField>,
16    /// Fields that are locked by signatures
17    locked_fields: HashSet<String>,
18    /// Document-wide signature settings
19    settings: SignatureSettings,
20}
21
22/// Settings for signature handling
23#[derive(Debug, Clone)]
24pub struct SignatureSettings {
25    /// Allow incremental saves for signatures
26    pub incremental_save: bool,
27    /// Require all signature fields to be signed
28    pub require_all_signatures: bool,
29    /// Lock all fields after any signature
30    pub lock_all_after_first_signature: bool,
31    /// Default signature algorithm
32    pub default_algorithm: SignatureAlgorithm,
33    /// Enable signature timestamps
34    pub enable_timestamps: bool,
35}
36
37impl Default for SignatureSettings {
38    fn default() -> Self {
39        Self {
40            incremental_save: true,
41            require_all_signatures: false,
42            lock_all_after_first_signature: false,
43            default_algorithm: SignatureAlgorithm::RsaSha256,
44            enable_timestamps: true,
45        }
46    }
47}
48
49/// Signature validation result
50#[derive(Debug, Clone)]
51pub struct ValidationResult {
52    /// Field name
53    pub field_name: String,
54    /// Whether signature is valid
55    pub is_valid: bool,
56    /// Signer information
57    pub signer: Option<SignerInfo>,
58    /// Validation timestamp
59    pub validated_at: chrono::DateTime<Utc>,
60    /// Validation errors if any
61    pub errors: Vec<String>,
62    /// Warning messages
63    pub warnings: Vec<String>,
64}
65
66#[allow(clippy::derivable_impls)]
67impl Default for SignatureHandler {
68    fn default() -> Self {
69        Self {
70            signature_fields: HashMap::new(),
71            locked_fields: HashSet::new(),
72            settings: SignatureSettings::default(),
73        }
74    }
75}
76
77impl SignatureHandler {
78    /// Create a new signature handler
79    pub fn new() -> Self {
80        Self::default()
81    }
82
83    /// Create with custom settings
84    pub fn with_settings(settings: SignatureSettings) -> Self {
85        Self {
86            signature_fields: HashMap::new(),
87            locked_fields: HashSet::new(),
88            settings,
89        }
90    }
91
92    /// Add a signature field
93    pub fn add_signature_field(&mut self, field: SignatureField) -> Result<(), PdfError> {
94        if self.signature_fields.contains_key(&field.name) {
95            return Err(PdfError::DuplicateField(format!(
96                "Signature field '{}' already exists",
97                field.name
98            )));
99        }
100
101        self.signature_fields.insert(field.name.clone(), field);
102        Ok(())
103    }
104
105    /// Get a signature field by name
106    pub fn get_field(&self, name: &str) -> Option<&SignatureField> {
107        self.signature_fields.get(name)
108    }
109
110    /// Get mutable signature field by name
111    pub fn get_field_mut(&mut self, name: &str) -> Option<&mut SignatureField> {
112        self.signature_fields.get_mut(name)
113    }
114
115    /// Sign a field
116    pub fn sign_field(
117        &mut self,
118        field_name: &str,
119        signer: SignerInfo,
120        reason: Option<String>,
121    ) -> Result<(), PdfError> {
122        // Check if field is locked
123        if self.locked_fields.contains(field_name) {
124            return Err(PdfError::InvalidOperation(format!(
125                "Field '{}' is locked and cannot be signed",
126                field_name
127            )));
128        }
129
130        // Get the field and sign it
131        let field = self
132            .signature_fields
133            .get_mut(field_name)
134            .ok_or_else(|| PdfError::FieldNotFound(field_name.to_string()))?;
135
136        field.sign(signer, reason)?;
137
138        // Lock fields as specified
139        let fields_to_lock = field.lock_fields.clone();
140        for field_to_lock in fields_to_lock {
141            self.locked_fields.insert(field_to_lock);
142        }
143
144        // Lock all fields if setting is enabled
145        if self.settings.lock_all_after_first_signature {
146            for field_name in self.signature_fields.keys() {
147                self.locked_fields.insert(field_name.clone());
148            }
149        }
150
151        Ok(())
152    }
153
154    /// Validate all signatures in the document
155    pub fn validate_all(&self) -> Vec<ValidationResult> {
156        let mut results = Vec::new();
157
158        for (name, field) in &self.signature_fields {
159            results.push(self.validate_field(name, field));
160        }
161
162        results
163    }
164
165    /// Validate a single signature field
166    fn validate_field(&self, name: &str, field: &SignatureField) -> ValidationResult {
167        let mut result = ValidationResult {
168            field_name: name.to_string(),
169            is_valid: false,
170            signer: field.signer.clone(),
171            validated_at: Utc::now(),
172            errors: Vec::new(),
173            warnings: Vec::new(),
174        };
175
176        if !field.is_signed() {
177            if field.required {
178                result
179                    .errors
180                    .push("Required signature field is not signed".to_string());
181            } else {
182                result.warnings.push("Field is not signed".to_string());
183            }
184            return result;
185        }
186
187        // Perform validation (placeholder - always succeeds for now)
188        match field.verify() {
189            Ok(valid) => {
190                result.is_valid = valid;
191                if !valid {
192                    result
193                        .errors
194                        .push("Signature verification failed".to_string());
195                }
196            }
197            Err(e) => {
198                result.errors.push(format!("Validation error: {}", e));
199            }
200        }
201
202        // Check certificate validity (placeholder)
203        if let Some(ref sig_value) = field.signature_value {
204            // In a real implementation, check certificate chain
205            if sig_value.certificates.is_empty() {
206                result
207                    .warnings
208                    .push("No certificates found in signature".to_string());
209            }
210        }
211
212        result
213    }
214
215    /// Check if a field is locked
216    pub fn is_field_locked(&self, field_name: &str) -> bool {
217        self.locked_fields.contains(field_name)
218    }
219
220    /// Get all unsigned required fields
221    pub fn get_unsigned_required_fields(&self) -> Vec<String> {
222        self.signature_fields
223            .iter()
224            .filter(|(_, field)| field.required && !field.is_signed())
225            .map(|(name, _)| name.clone())
226            .collect()
227    }
228
229    /// Check if all required signatures are present
230    pub fn all_required_signed(&self) -> bool {
231        self.get_unsigned_required_fields().is_empty()
232    }
233
234    /// Get signing order based on dependencies
235    pub fn get_signing_order(&self) -> Vec<String> {
236        // Simple implementation - fields with no lock dependencies first
237        let mut order = Vec::new();
238        let mut added = HashSet::new();
239
240        // First add fields that don't lock any others
241        for (name, field) in &self.signature_fields {
242            if field.lock_fields.is_empty() && !field.is_signed() {
243                order.push(name.clone());
244                added.insert(name.clone());
245            }
246        }
247
248        // Then add remaining fields
249        for (name, field) in &self.signature_fields {
250            if !added.contains(name) && !field.is_signed() {
251                order.push(name.clone());
252            }
253        }
254
255        order
256    }
257
258    /// Export signature fields to PDF dictionary format
259    pub fn export_to_dict(&self) -> Dictionary {
260        let mut dict = Dictionary::new();
261        let mut fields = Vec::new();
262
263        for field in self.signature_fields.values() {
264            fields.push(Object::Dictionary(field.to_dict()));
265        }
266
267        dict.set("Fields", Object::Array(fields));
268        dict.set("SigFlags", Object::Integer(3)); // Signatures exist and are append-only
269
270        dict
271    }
272
273    /// Create a signature summary report
274    pub fn generate_summary(&self) -> SignatureSummary {
275        let total = self.signature_fields.len();
276        let signed = self
277            .signature_fields
278            .values()
279            .filter(|f| f.is_signed())
280            .count();
281        let required = self
282            .signature_fields
283            .values()
284            .filter(|f| f.required)
285            .count();
286        let required_signed = self
287            .signature_fields
288            .values()
289            .filter(|f| f.required && f.is_signed())
290            .count();
291
292        SignatureSummary {
293            total_fields: total,
294            signed_fields: signed,
295            unsigned_fields: total - signed,
296            required_fields: required,
297            required_signed,
298            required_unsigned: required - required_signed,
299            all_required_complete: required == required_signed,
300            locked_fields: self.locked_fields.len(),
301        }
302    }
303}
304
305/// Summary of signature status in document
306#[derive(Debug, Clone)]
307pub struct SignatureSummary {
308    /// Total number of signature fields
309    pub total_fields: usize,
310    /// Number of signed fields
311    pub signed_fields: usize,
312    /// Number of unsigned fields
313    pub unsigned_fields: usize,
314    /// Number of required fields
315    pub required_fields: usize,
316    /// Number of required fields that are signed
317    pub required_signed: usize,
318    /// Number of required fields that are unsigned
319    pub required_unsigned: usize,
320    /// Whether all required fields are signed
321    pub all_required_complete: bool,
322    /// Number of locked fields
323    pub locked_fields: usize,
324}
325
326impl SignatureSummary {
327    /// Get completion percentage
328    pub fn completion_percentage(&self) -> f64 {
329        if self.total_fields == 0 {
330            100.0
331        } else {
332            (self.signed_fields as f64 / self.total_fields as f64) * 100.0
333        }
334    }
335
336    /// Check if document is ready (all required signed)
337    pub fn is_ready(&self) -> bool {
338        self.all_required_complete
339    }
340
341    /// Generate a text report
342    pub fn to_report(&self) -> String {
343        format!(
344            "Signature Summary:\n\
345             - Total fields: {}\n\
346             - Signed: {} ({:.1}%)\n\
347             - Unsigned: {}\n\
348             - Required fields: {} ({} signed, {} unsigned)\n\
349             - Status: {}\n\
350             - Locked fields: {}",
351            self.total_fields,
352            self.signed_fields,
353            self.completion_percentage(),
354            self.unsigned_fields,
355            self.required_fields,
356            self.required_signed,
357            self.required_unsigned,
358            if self.is_ready() {
359                "Ready"
360            } else {
361                "Incomplete"
362            },
363            self.locked_fields
364        )
365    }
366}
367
368#[cfg(test)]
369mod tests {
370    use super::*;
371
372    #[test]
373    fn test_signature_handler_creation() {
374        let handler = SignatureHandler::new();
375        assert_eq!(handler.signature_fields.len(), 0);
376        assert_eq!(handler.locked_fields.len(), 0);
377    }
378
379    #[test]
380    fn test_add_signature_field() {
381        let mut handler = SignatureHandler::new();
382        let field = SignatureField::new("sig1");
383
384        assert!(handler.add_signature_field(field.clone()).is_ok());
385        assert_eq!(handler.signature_fields.len(), 1);
386
387        // Cannot add duplicate
388        assert!(handler.add_signature_field(field).is_err());
389    }
390
391    #[test]
392    fn test_sign_field() {
393        let mut handler = SignatureHandler::new();
394        let field =
395            SignatureField::new("sig1").lock_fields_after_signing(vec!["field1".to_string()]);
396
397        handler.add_signature_field(field).unwrap();
398
399        let signer = SignerInfo::new("John Doe");
400        assert!(handler
401            .sign_field("sig1", signer, Some("Approved".to_string()))
402            .is_ok());
403
404        // Check that field1 is now locked
405        assert!(handler.is_field_locked("field1"));
406    }
407
408    #[test]
409    fn test_validation() {
410        let mut handler = SignatureHandler::new();
411        let field = SignatureField::new("sig1").required();
412
413        handler.add_signature_field(field).unwrap();
414
415        let results = handler.validate_all();
416        assert_eq!(results.len(), 1);
417        assert!(!results[0].is_valid);
418        assert!(!results[0].errors.is_empty());
419    }
420
421    #[test]
422    fn test_signing_order() {
423        let mut handler = SignatureHandler::new();
424
425        // Field that locks others should come last
426        let field1 =
427            SignatureField::new("sig1").lock_fields_after_signing(vec!["sig2".to_string()]);
428        let field2 = SignatureField::new("sig2");
429
430        handler.add_signature_field(field1).unwrap();
431        handler.add_signature_field(field2).unwrap();
432
433        let order = handler.get_signing_order();
434        assert_eq!(order[0], "sig2"); // Should be signed first
435        assert_eq!(order[1], "sig1"); // Should be signed second
436    }
437
438    #[test]
439    fn test_summary() {
440        let mut handler = SignatureHandler::new();
441
442        handler
443            .add_signature_field(SignatureField::new("sig1").required())
444            .unwrap();
445        handler
446            .add_signature_field(SignatureField::new("sig2"))
447            .unwrap();
448
449        let summary = handler.generate_summary();
450        assert_eq!(summary.total_fields, 2);
451        assert_eq!(summary.required_fields, 1);
452        assert_eq!(summary.signed_fields, 0);
453        assert!(!summary.is_ready());
454
455        // Sign the required field
456        handler
457            .sign_field("sig1", SignerInfo::new("Signer"), None)
458            .unwrap();
459
460        let summary = handler.generate_summary();
461        assert_eq!(summary.signed_fields, 1);
462        assert!(summary.is_ready());
463    }
464}