use crate::error::PdfError;
use crate::forms::signature_field::{SignatureAlgorithm, SignatureField, SignerInfo};
use crate::objects::{Dictionary, Object};
use chrono::Utc;
use std::collections::{HashMap, HashSet};
pub struct SignatureHandler {
signature_fields: HashMap<String, SignatureField>,
locked_fields: HashSet<String>,
settings: SignatureSettings,
}
#[derive(Debug, Clone)]
pub struct SignatureSettings {
pub incremental_save: bool,
pub require_all_signatures: bool,
pub lock_all_after_first_signature: bool,
pub default_algorithm: SignatureAlgorithm,
pub enable_timestamps: bool,
}
impl Default for SignatureSettings {
fn default() -> Self {
Self {
incremental_save: true,
require_all_signatures: false,
lock_all_after_first_signature: false,
default_algorithm: SignatureAlgorithm::RsaSha256,
enable_timestamps: true,
}
}
}
#[derive(Debug, Clone)]
pub struct ValidationResult {
pub field_name: String,
pub is_valid: bool,
pub signer: Option<SignerInfo>,
pub validated_at: chrono::DateTime<Utc>,
pub errors: Vec<String>,
pub warnings: Vec<String>,
}
#[allow(clippy::derivable_impls)]
impl Default for SignatureHandler {
fn default() -> Self {
Self {
signature_fields: HashMap::new(),
locked_fields: HashSet::new(),
settings: SignatureSettings::default(),
}
}
}
impl SignatureHandler {
pub fn new() -> Self {
Self::default()
}
pub fn with_settings(settings: SignatureSettings) -> Self {
Self {
signature_fields: HashMap::new(),
locked_fields: HashSet::new(),
settings,
}
}
pub fn add_signature_field(&mut self, field: SignatureField) -> Result<(), PdfError> {
if self.signature_fields.contains_key(&field.name) {
return Err(PdfError::DuplicateField(format!(
"Signature field '{}' already exists",
field.name
)));
}
self.signature_fields.insert(field.name.clone(), field);
Ok(())
}
pub fn get_field(&self, name: &str) -> Option<&SignatureField> {
self.signature_fields.get(name)
}
pub fn get_field_mut(&mut self, name: &str) -> Option<&mut SignatureField> {
self.signature_fields.get_mut(name)
}
pub fn sign_field(
&mut self,
field_name: &str,
signer: SignerInfo,
reason: Option<String>,
) -> Result<(), PdfError> {
if self.locked_fields.contains(field_name) {
return Err(PdfError::InvalidOperation(format!(
"Field '{}' is locked and cannot be signed",
field_name
)));
}
let field = self
.signature_fields
.get_mut(field_name)
.ok_or_else(|| PdfError::FieldNotFound(field_name.to_string()))?;
field.sign(signer, reason)?;
let fields_to_lock = field.lock_fields.clone();
for field_to_lock in fields_to_lock {
self.locked_fields.insert(field_to_lock);
}
if self.settings.lock_all_after_first_signature {
for field_name in self.signature_fields.keys() {
self.locked_fields.insert(field_name.clone());
}
}
Ok(())
}
pub fn validate_all(&self) -> Vec<ValidationResult> {
let mut results = Vec::new();
for (name, field) in &self.signature_fields {
results.push(self.validate_field(name, field));
}
results
}
fn validate_field(&self, name: &str, field: &SignatureField) -> ValidationResult {
let mut result = ValidationResult {
field_name: name.to_string(),
is_valid: false,
signer: field.signer.clone(),
validated_at: Utc::now(),
errors: Vec::new(),
warnings: Vec::new(),
};
if !field.is_signed() {
if field.required {
result
.errors
.push("Required signature field is not signed".to_string());
} else {
result.warnings.push("Field is not signed".to_string());
}
return result;
}
match field.verify() {
Ok(valid) => {
result.is_valid = valid;
if !valid {
result
.errors
.push("Signature verification failed".to_string());
}
}
Err(e) => {
result.errors.push(format!("Validation error: {}", e));
}
}
if let Some(ref sig_value) = field.signature_value {
if sig_value.certificates.is_empty() {
result
.warnings
.push("No certificates found in signature".to_string());
}
}
result
}
pub fn is_field_locked(&self, field_name: &str) -> bool {
self.locked_fields.contains(field_name)
}
pub fn get_unsigned_required_fields(&self) -> Vec<String> {
self.signature_fields
.iter()
.filter(|(_, field)| field.required && !field.is_signed())
.map(|(name, _)| name.clone())
.collect()
}
pub fn all_required_signed(&self) -> bool {
self.get_unsigned_required_fields().is_empty()
}
pub fn get_signing_order(&self) -> Vec<String> {
let mut order = Vec::new();
let mut added = HashSet::new();
for (name, field) in &self.signature_fields {
if field.lock_fields.is_empty() && !field.is_signed() {
order.push(name.clone());
added.insert(name.clone());
}
}
for (name, field) in &self.signature_fields {
if !added.contains(name) && !field.is_signed() {
order.push(name.clone());
}
}
order
}
pub fn export_to_dict(&self) -> Dictionary {
let mut dict = Dictionary::new();
let mut fields = Vec::new();
for field in self.signature_fields.values() {
fields.push(Object::Dictionary(field.to_dict()));
}
dict.set("Fields", Object::Array(fields));
dict.set("SigFlags", Object::Integer(3));
dict
}
pub fn generate_summary(&self) -> SignatureSummary {
let total = self.signature_fields.len();
let signed = self
.signature_fields
.values()
.filter(|f| f.is_signed())
.count();
let required = self
.signature_fields
.values()
.filter(|f| f.required)
.count();
let required_signed = self
.signature_fields
.values()
.filter(|f| f.required && f.is_signed())
.count();
SignatureSummary {
total_fields: total,
signed_fields: signed,
unsigned_fields: total - signed,
required_fields: required,
required_signed,
required_unsigned: required - required_signed,
all_required_complete: required == required_signed,
locked_fields: self.locked_fields.len(),
}
}
}
#[derive(Debug, Clone)]
pub struct SignatureSummary {
pub total_fields: usize,
pub signed_fields: usize,
pub unsigned_fields: usize,
pub required_fields: usize,
pub required_signed: usize,
pub required_unsigned: usize,
pub all_required_complete: bool,
pub locked_fields: usize,
}
impl SignatureSummary {
pub fn completion_percentage(&self) -> f64 {
if self.total_fields == 0 {
100.0
} else {
(self.signed_fields as f64 / self.total_fields as f64) * 100.0
}
}
pub fn is_ready(&self) -> bool {
self.all_required_complete
}
pub fn to_report(&self) -> String {
format!(
"Signature Summary:\n\
- Total fields: {}\n\
- Signed: {} ({:.1}%)\n\
- Unsigned: {}\n\
- Required fields: {} ({} signed, {} unsigned)\n\
- Status: {}\n\
- Locked fields: {}",
self.total_fields,
self.signed_fields,
self.completion_percentage(),
self.unsigned_fields,
self.required_fields,
self.required_signed,
self.required_unsigned,
if self.is_ready() {
"Ready"
} else {
"Incomplete"
},
self.locked_fields
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_signature_handler_creation() {
let handler = SignatureHandler::new();
assert_eq!(handler.signature_fields.len(), 0);
assert_eq!(handler.locked_fields.len(), 0);
}
#[test]
fn test_add_signature_field() {
let mut handler = SignatureHandler::new();
let field = SignatureField::new("sig1");
assert!(handler.add_signature_field(field.clone()).is_ok());
assert_eq!(handler.signature_fields.len(), 1);
assert!(handler.add_signature_field(field).is_err());
}
#[test]
fn test_sign_field() {
let mut handler = SignatureHandler::new();
let field =
SignatureField::new("sig1").lock_fields_after_signing(vec!["field1".to_string()]);
handler.add_signature_field(field).unwrap();
let signer = SignerInfo::new("John Doe");
assert!(handler
.sign_field("sig1", signer, Some("Approved".to_string()))
.is_ok());
assert!(handler.is_field_locked("field1"));
}
#[test]
fn test_validation() {
let mut handler = SignatureHandler::new();
let field = SignatureField::new("sig1").required();
handler.add_signature_field(field).unwrap();
let results = handler.validate_all();
assert_eq!(results.len(), 1);
assert!(!results[0].is_valid);
assert!(!results[0].errors.is_empty());
}
#[test]
fn test_signing_order() {
let mut handler = SignatureHandler::new();
let field1 =
SignatureField::new("sig1").lock_fields_after_signing(vec!["sig2".to_string()]);
let field2 = SignatureField::new("sig2");
handler.add_signature_field(field1).unwrap();
handler.add_signature_field(field2).unwrap();
let order = handler.get_signing_order();
assert_eq!(order[0], "sig2"); assert_eq!(order[1], "sig1"); }
#[test]
fn test_summary() {
let mut handler = SignatureHandler::new();
handler
.add_signature_field(SignatureField::new("sig1").required())
.unwrap();
handler
.add_signature_field(SignatureField::new("sig2"))
.unwrap();
let summary = handler.generate_summary();
assert_eq!(summary.total_fields, 2);
assert_eq!(summary.required_fields, 1);
assert_eq!(summary.signed_fields, 0);
assert!(!summary.is_ready());
handler
.sign_field("sig1", SignerInfo::new("Signer"), None)
.unwrap();
let summary = handler.generate_summary();
assert_eq!(summary.signed_fields, 1);
assert!(summary.is_ready());
}
}