Skip to main content

oxidize_pdf/encryption/
permissions_enforcement.rs

1//! Runtime permissions enforcement for PDF operations
2//!
3//! This module implements runtime validation of PDF permissions with
4//! callbacks and logging according to ISO 32000-1:2008 ยง7.6.3.3.
5
6use crate::encryption::Permissions;
7use crate::error::{PdfError, Result};
8use std::sync::Mutex;
9
10/// Permission operation type
11#[derive(Debug, Clone, Copy, PartialEq)]
12pub enum PermissionOperation {
13    /// Print operation
14    Print,
15    /// Print in high quality
16    PrintHighQuality,
17    /// Modify document contents
18    ModifyContents,
19    /// Copy text and graphics
20    Copy,
21    /// Modify annotations
22    ModifyAnnotations,
23    /// Fill in form fields
24    FillForms,
25    /// Extract text and graphics for accessibility
26    Accessibility,
27    /// Assemble document (insert, rotate, delete pages)
28    Assemble,
29}
30
31impl PermissionOperation {
32    /// Get human-readable name
33    pub fn name(&self) -> &'static str {
34        match self {
35            PermissionOperation::Print => "Print",
36            PermissionOperation::PrintHighQuality => "Print High Quality",
37            PermissionOperation::ModifyContents => "Modify Contents",
38            PermissionOperation::Copy => "Copy",
39            PermissionOperation::ModifyAnnotations => "Modify Annotations",
40            PermissionOperation::FillForms => "Fill Forms",
41            PermissionOperation::Accessibility => "Accessibility",
42            PermissionOperation::Assemble => "Assemble",
43        }
44    }
45}
46
47/// Permission check result
48#[derive(Debug, Clone)]
49pub struct PermissionCheckResult {
50    /// Operation that was checked
51    pub operation: PermissionOperation,
52    /// Whether permission was granted
53    pub allowed: bool,
54    /// Timestamp of check
55    pub timestamp: std::time::SystemTime,
56    /// Additional context
57    pub context: Option<String>,
58}
59
60/// Callback for permission checks
61pub type PermissionCallback = Box<dyn Fn(&PermissionCheckResult) + Send + Sync>;
62
63/// Log level for permission events
64#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
65pub enum LogLevel {
66    /// Debug level - all events
67    Debug,
68    /// Info level - allowed operations
69    Info,
70    /// Warning level - denied operations
71    Warn,
72    /// Error level - security violations
73    Error,
74}
75
76/// Permission event for logging
77#[derive(Debug, Clone)]
78pub struct PermissionEvent {
79    /// Log level
80    pub level: LogLevel,
81    /// Operation
82    pub operation: PermissionOperation,
83    /// Whether allowed
84    pub allowed: bool,
85    /// Timestamp
86    pub timestamp: std::time::SystemTime,
87    /// Message
88    pub message: String,
89}
90
91/// Permissions validator trait
92pub trait PermissionsValidator: Send + Sync {
93    /// Validate a permission operation
94    fn validate(&self, operation: PermissionOperation) -> Result<bool>;
95
96    /// Get current permissions
97    fn permissions(&self) -> Permissions;
98}
99
100/// Runtime permissions enforcer
101pub struct RuntimePermissions {
102    /// Base permissions
103    permissions: Permissions,
104    /// Callbacks for permission checks
105    callbacks: Vec<PermissionCallback>,
106    /// Log level
107    log_level: LogLevel,
108    /// Event log
109    event_log: Mutex<Vec<PermissionEvent>>,
110    /// Whether to enforce permissions (false = allow all)
111    enforce: bool,
112}
113
114impl RuntimePermissions {
115    /// Create new runtime permissions
116    pub fn new(permissions: Permissions) -> Self {
117        Self {
118            permissions,
119            callbacks: Vec::new(),
120            log_level: LogLevel::Info,
121            event_log: Mutex::new(Vec::new()),
122            enforce: true,
123        }
124    }
125
126    /// Add a callback for permission checks
127    pub fn add_callback<F>(&mut self, callback: F)
128    where
129        F: Fn(&PermissionCheckResult) + Send + Sync + 'static,
130    {
131        self.callbacks.push(Box::new(callback));
132    }
133
134    /// Set log level
135    pub fn set_log_level(&mut self, level: LogLevel) {
136        self.log_level = level;
137    }
138
139    /// Set enforcement (false = allow all operations)
140    pub fn set_enforce(&mut self, enforce: bool) {
141        self.enforce = enforce;
142    }
143
144    /// Get event log
145    pub fn get_events(&self) -> Vec<PermissionEvent> {
146        self.event_log
147            .lock()
148            .map(|log| log.clone())
149            .unwrap_or_else(|_| Vec::new())
150    }
151
152    /// Clear event log
153    pub fn clear_events(&self) {
154        if let Ok(mut log) = self.event_log.lock() {
155            log.clear();
156        }
157        // Silently ignore if lock is poisoned
158    }
159
160    /// Check if operation is allowed
161    fn check_permission(&self, operation: PermissionOperation) -> bool {
162        if !self.enforce {
163            return true;
164        }
165
166        match operation {
167            PermissionOperation::Print => self.permissions.can_print(),
168            PermissionOperation::PrintHighQuality => self.permissions.can_print_high_quality(),
169            PermissionOperation::ModifyContents => self.permissions.can_modify_contents(),
170            PermissionOperation::Copy => self.permissions.can_copy(),
171            PermissionOperation::ModifyAnnotations => self.permissions.can_modify_annotations(),
172            PermissionOperation::FillForms => self.permissions.can_fill_forms(),
173            PermissionOperation::Accessibility => self.permissions.can_access_for_accessibility(),
174            PermissionOperation::Assemble => self.permissions.can_assemble(),
175        }
176    }
177
178    /// Log an event
179    fn log_event(&self, operation: PermissionOperation, allowed: bool, message: String) {
180        let level = if allowed {
181            if self.log_level <= LogLevel::Debug {
182                LogLevel::Debug
183            } else {
184                LogLevel::Info
185            }
186        } else {
187            LogLevel::Warn
188        };
189
190        let event = PermissionEvent {
191            level,
192            operation,
193            allowed,
194            timestamp: std::time::SystemTime::now(),
195            message,
196        };
197
198        if level >= self.log_level {
199            if let Ok(mut log) = self.event_log.lock() {
200                log.push(event);
201            }
202            // Silently ignore if lock is poisoned
203        }
204    }
205
206    /// Execute callbacks
207    fn execute_callbacks(&self, result: &PermissionCheckResult) {
208        for callback in &self.callbacks {
209            callback(result);
210        }
211    }
212
213    /// Validate operation with logging and callbacks
214    fn validate_operation(
215        &self,
216        operation: PermissionOperation,
217        context: Option<String>,
218    ) -> Result<()> {
219        let allowed = self.check_permission(operation);
220
221        let result = PermissionCheckResult {
222            operation,
223            allowed,
224            timestamp: std::time::SystemTime::now(),
225            context: context.clone(),
226        };
227
228        // Execute callbacks
229        self.execute_callbacks(&result);
230
231        // Log event
232        let message = if let Some(ctx) = context {
233            format!(
234                "{} operation {} ({})",
235                operation.name(),
236                if allowed { "allowed" } else { "denied" },
237                ctx
238            )
239        } else {
240            format!(
241                "{} operation {}",
242                operation.name(),
243                if allowed { "allowed" } else { "denied" }
244            )
245        };
246        self.log_event(operation, allowed, message);
247
248        if allowed {
249            Ok(())
250        } else {
251            Err(PdfError::PermissionDenied(format!(
252                "Permission denied for {} operation",
253                operation.name()
254            )))
255        }
256    }
257
258    // Public operation methods
259
260    /// Validate print operation
261    pub fn on_print(&self, context: Option<String>) -> Result<()> {
262        self.validate_operation(PermissionOperation::Print, context)
263    }
264
265    /// Validate high-quality print operation
266    pub fn on_print_high_quality(&self, context: Option<String>) -> Result<()> {
267        self.validate_operation(PermissionOperation::PrintHighQuality, context)
268    }
269
270    /// Validate modify operation
271    pub fn on_modify(&self, context: Option<String>) -> Result<()> {
272        self.validate_operation(PermissionOperation::ModifyContents, context)
273    }
274
275    /// Validate copy operation
276    pub fn on_copy(&self, context: Option<String>) -> Result<()> {
277        self.validate_operation(PermissionOperation::Copy, context)
278    }
279
280    /// Validate annotation modification
281    pub fn on_modify_annotations(&self, context: Option<String>) -> Result<()> {
282        self.validate_operation(PermissionOperation::ModifyAnnotations, context)
283    }
284
285    /// Validate form filling
286    pub fn on_form_fill(&self, context: Option<String>) -> Result<()> {
287        self.validate_operation(PermissionOperation::FillForms, context)
288    }
289
290    /// Validate accessibility access
291    pub fn on_accessibility(&self, context: Option<String>) -> Result<()> {
292        self.validate_operation(PermissionOperation::Accessibility, context)
293    }
294
295    /// Validate document assembly
296    pub fn on_assemble(&self, context: Option<String>) -> Result<()> {
297        self.validate_operation(PermissionOperation::Assemble, context)
298    }
299}
300
301impl PermissionsValidator for RuntimePermissions {
302    fn validate(&self, operation: PermissionOperation) -> Result<bool> {
303        Ok(self.check_permission(operation))
304    }
305
306    fn permissions(&self) -> Permissions {
307        self.permissions
308    }
309}
310
311/// Builder for RuntimePermissions
312pub struct RuntimePermissionsBuilder {
313    permissions: Permissions,
314    callbacks: Vec<PermissionCallback>,
315    log_level: LogLevel,
316    enforce: bool,
317}
318
319impl RuntimePermissionsBuilder {
320    /// Create new builder
321    pub fn new(permissions: Permissions) -> Self {
322        Self {
323            permissions,
324            callbacks: Vec::new(),
325            log_level: LogLevel::Info,
326            enforce: true,
327        }
328    }
329
330    /// Add callback
331    pub fn with_callback<F>(mut self, callback: F) -> Self
332    where
333        F: Fn(&PermissionCheckResult) + Send + Sync + 'static,
334    {
335        self.callbacks.push(Box::new(callback));
336        self
337    }
338
339    /// Set log level
340    pub fn with_log_level(mut self, level: LogLevel) -> Self {
341        self.log_level = level;
342        self
343    }
344
345    /// Set enforcement
346    pub fn with_enforcement(mut self, enforce: bool) -> Self {
347        self.enforce = enforce;
348        self
349    }
350
351    /// Build RuntimePermissions
352    pub fn build(self) -> RuntimePermissions {
353        let mut runtime = RuntimePermissions::new(self.permissions);
354        runtime.callbacks = self.callbacks;
355        runtime.log_level = self.log_level;
356        runtime.enforce = self.enforce;
357        runtime
358    }
359}
360
361#[cfg(test)]
362mod tests {
363    use super::*;
364    use std::sync::{Arc, Mutex};
365
366    #[test]
367    fn test_permission_operation_names() {
368        assert_eq!(PermissionOperation::Print.name(), "Print");
369        assert_eq!(
370            PermissionOperation::ModifyContents.name(),
371            "Modify Contents"
372        );
373        assert_eq!(PermissionOperation::Accessibility.name(), "Accessibility");
374    }
375
376    #[test]
377    fn test_runtime_permissions_allow() {
378        let perms = Permissions::all();
379        let runtime = RuntimePermissions::new(perms);
380
381        assert!(runtime.on_print(None).is_ok());
382        assert!(runtime.on_modify(None).is_ok());
383        assert!(runtime.on_copy(None).is_ok());
384    }
385
386    #[test]
387    fn test_runtime_permissions_deny() {
388        let perms = Permissions::new(); // No permissions
389        let runtime = RuntimePermissions::new(perms);
390
391        assert!(runtime.on_print(None).is_err());
392        assert!(runtime.on_modify(None).is_err());
393        assert!(runtime.on_copy(None).is_err());
394    }
395
396    #[test]
397    fn test_runtime_permissions_selective() {
398        let perms = *Permissions::new().set_print(true).set_copy(true);
399        let runtime = RuntimePermissions::new(perms);
400
401        assert!(runtime.on_print(None).is_ok());
402        assert!(runtime.on_copy(None).is_ok());
403        assert!(runtime.on_modify(None).is_err());
404    }
405
406    #[test]
407    fn test_enforcement_disabled() {
408        let perms = Permissions::new(); // No permissions
409        let mut runtime = RuntimePermissions::new(perms);
410        runtime.set_enforce(false);
411
412        // All operations should be allowed
413        assert!(runtime.on_print(None).is_ok());
414        assert!(runtime.on_modify(None).is_ok());
415        assert!(runtime.on_copy(None).is_ok());
416    }
417
418    #[test]
419    fn test_callbacks() {
420        let perms = *Permissions::new().set_print(true);
421        let mut runtime = RuntimePermissions::new(perms);
422
423        let callback_called = Arc::new(Mutex::new(false));
424        let callback_called_clone = callback_called.clone();
425
426        runtime.add_callback(move |result| {
427            assert_eq!(result.operation, PermissionOperation::Print);
428            assert!(result.allowed);
429            *callback_called_clone.lock().unwrap() = true;
430        });
431
432        runtime.on_print(None).unwrap();
433        assert!(*callback_called.lock().unwrap());
434    }
435
436    #[test]
437    fn test_multiple_callbacks() {
438        let perms = *Permissions::new().set_print(true);
439        let mut runtime = RuntimePermissions::new(perms);
440
441        let counter = Arc::new(Mutex::new(0));
442
443        for _ in 0..3 {
444            let counter_clone = counter.clone();
445            runtime.add_callback(move |_| {
446                *counter_clone.lock().unwrap() += 1;
447            });
448        }
449
450        runtime.on_print(None).unwrap();
451        assert_eq!(*counter.lock().unwrap(), 3);
452    }
453
454    #[test]
455    fn test_context_in_callback() {
456        let perms = *Permissions::new().set_copy(true);
457        let mut runtime = RuntimePermissions::new(perms);
458
459        let context_received = Arc::new(Mutex::new(String::new()));
460        let context_clone = context_received.clone();
461
462        runtime.add_callback(move |result| {
463            if let Some(ctx) = &result.context {
464                *context_clone.lock().unwrap() = ctx.clone();
465            }
466        });
467
468        runtime.on_copy(Some("Copying page 5".to_string())).unwrap();
469        assert_eq!(*context_received.lock().unwrap(), "Copying page 5");
470    }
471
472    #[test]
473    fn test_event_logging() {
474        let perms = *Permissions::new().set_print(true);
475        let mut runtime = RuntimePermissions::new(perms);
476        runtime.set_log_level(LogLevel::Debug);
477
478        runtime.on_print(Some("Test print".to_string())).unwrap();
479        let _ = runtime.on_modify(None); // This will fail
480
481        let events = runtime.get_events();
482        assert_eq!(events.len(), 2);
483
484        assert_eq!(events[0].operation, PermissionOperation::Print);
485        assert!(events[0].allowed);
486
487        assert_eq!(events[1].operation, PermissionOperation::ModifyContents);
488        assert!(!events[1].allowed);
489    }
490
491    #[test]
492    fn test_log_levels() {
493        let perms = Permissions::all();
494        let mut runtime = RuntimePermissions::new(perms);
495
496        // Set to Warn - should only log denied operations
497        runtime.set_log_level(LogLevel::Warn);
498
499        runtime.on_print(None).unwrap(); // Allowed - not logged
500
501        let mut runtime2 = RuntimePermissions::new(Permissions::new());
502        runtime2.set_log_level(LogLevel::Warn);
503        let _ = runtime2.on_print(None); // Denied - logged
504
505        assert_eq!(runtime.get_events().len(), 0);
506        assert_eq!(runtime2.get_events().len(), 1);
507    }
508
509    #[test]
510    fn test_clear_events() {
511        let perms = Permissions::all();
512        let runtime = RuntimePermissions::new(perms);
513
514        runtime.on_print(None).unwrap();
515        runtime.on_copy(None).unwrap();
516
517        assert!(runtime.get_events().len() >= 2);
518
519        runtime.clear_events();
520        assert_eq!(runtime.get_events().len(), 0);
521    }
522
523    #[test]
524    fn test_permissions_validator_trait() {
525        let perms = *Permissions::new().set_accessibility(true);
526        let runtime = RuntimePermissions::new(perms);
527
528        // Test trait methods
529        assert!(runtime
530            .validate(PermissionOperation::Accessibility)
531            .unwrap());
532        assert!(!runtime.validate(PermissionOperation::Print).unwrap());
533
534        let retrieved_perms = runtime.permissions();
535        assert!(retrieved_perms.can_access_for_accessibility());
536        assert!(!retrieved_perms.can_print());
537    }
538
539    #[test]
540    fn test_builder() {
541        let counter = Arc::new(Mutex::new(0));
542        let counter_clone = counter.clone();
543
544        let runtime = RuntimePermissionsBuilder::new(Permissions::all())
545            .with_callback(move |_| {
546                *counter_clone.lock().unwrap() += 1;
547            })
548            .with_log_level(LogLevel::Debug)
549            .with_enforcement(true)
550            .build();
551
552        runtime.on_print(None).unwrap();
553        assert_eq!(*counter.lock().unwrap(), 1);
554        assert_eq!(runtime.log_level, LogLevel::Debug);
555    }
556
557    #[test]
558    fn test_all_operations() {
559        let perms = Permissions::all();
560        let runtime = RuntimePermissions::new(perms);
561
562        // Test all operations
563        assert!(runtime.on_print(None).is_ok());
564        assert!(runtime.on_print_high_quality(None).is_ok());
565        assert!(runtime.on_modify(None).is_ok());
566        assert!(runtime.on_copy(None).is_ok());
567        assert!(runtime.on_modify_annotations(None).is_ok());
568        assert!(runtime.on_form_fill(None).is_ok());
569        assert!(runtime.on_accessibility(None).is_ok());
570        assert!(runtime.on_assemble(None).is_ok());
571    }
572
573    #[test]
574    fn test_error_messages() {
575        let perms = Permissions::new();
576        let runtime = RuntimePermissions::new(perms);
577
578        let err = runtime.on_print(None).unwrap_err();
579        match err {
580            PdfError::PermissionDenied(msg) => {
581                assert!(msg.contains("Print"));
582            }
583            _ => panic!("Expected PermissionDenied error"),
584        }
585    }
586}