use super::{HasPermissions, PermissionEffect};
use crate::permission::{GrantDenialPair, PermissionDelta, PermissionSet, Timestamp};
#[derive(Debug)]
pub struct PermissionPreview<T: HasPermissions> {
original: T,
predicted: T,
}
impl<T: HasPermissions + Clone> PermissionPreview<T> {
pub fn new(original: T) -> Self {
Self {
predicted: original.clone(),
original,
}
}
pub fn apply_delta(mut self, delta: &PermissionDelta) -> Self {
let current_perms = self.predicted.permissions().clone();
let new_grants = delta.apply_to(current_perms.grants);
*self.predicted.permissions_mut() = GrantDenialPair::new(new_grants, current_perms.denials);
self
}
pub fn apply_grant_denial(mut self, gd: &GrantDenialPair) -> Self {
use crate::algebra::Semigroup;
let current = self.predicted.permissions().clone();
*self.predicted.permissions_mut() = current.combine(gd.clone());
self
}
pub fn grant(mut self, perm: crate::permission::AtomicPermission) -> Self {
self.predicted.permissions_mut().grants.extend([perm]);
self
}
pub fn deny(mut self, perm: crate::permission::AtomicPermission) -> Self {
self.predicted.permissions_mut().denials.extend([perm]);
self
}
pub fn has_changes(&self) -> bool {
self.original.permissions() != self.predicted.permissions()
}
pub fn changes(&self) -> PermissionDelta {
let original_grants = &self.original.permissions().grants;
let predicted_grants = &self.predicted.permissions().grants;
let added = predicted_grants.difference(original_grants);
let removed = original_grants.difference(predicted_grants);
PermissionDelta::new(added, removed)
}
pub fn effect(&self) -> PermissionEffect {
PermissionEffect::from_transition(self.original.permissions(), self.predicted.permissions())
}
pub fn effective_permissions(&self) -> PermissionSet {
self.predicted.effective_permissions()
}
pub fn effective_permissions_at(&self, time: Timestamp) -> PermissionSet {
self.predicted.effective_permissions_at(time)
}
pub fn original(&self) -> &T {
&self.original
}
pub fn predicted(&self) -> &T {
&self.predicted
}
pub fn commit(self) -> T {
self.predicted
}
pub fn discard(self) -> T {
self.original
}
pub fn preview_at(&self, time: Timestamp) -> PermissionSet {
self.predicted.effective_permissions_at(time)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::permission::AtomicPermission;
use crate::Subject;
#[test]
fn test_preview_delta() {
let subject = Subject::builder()
.id("alice")
.grant(AtomicPermission::new("file", "read"))
.build()
.unwrap();
let delta = PermissionDelta::builder()
.grant_str("file", "write")
.build();
let preview = PermissionPreview::new(subject).apply_delta(&delta);
assert!(preview.has_changes());
let changes = preview.changes();
assert_eq!(changes.add.len(), 1);
}
#[test]
fn test_preview_commit() {
let subject = Subject::builder()
.id("bob")
.grant(AtomicPermission::new("file", "read"))
.build()
.unwrap();
let preview = PermissionPreview::new(subject).grant(AtomicPermission::new("file", "write"));
assert!(preview.has_changes());
let updated = preview.commit();
assert!(updated.has_permission(&AtomicPermission::new("file", "write")));
}
#[test]
fn test_preview_discard() {
let subject = Subject::builder()
.id("charlie")
.grant(AtomicPermission::new("file", "read"))
.build()
.unwrap();
let preview = PermissionPreview::new(subject).grant(AtomicPermission::new("file", "write"));
let original = preview.discard();
assert!(!original.has_permission(&AtomicPermission::new("file", "write")));
}
#[test]
fn test_preview_effect() {
let subject = Subject::builder()
.id("diana")
.grant(AtomicPermission::new("file", "read"))
.build()
.unwrap();
let preview = PermissionPreview::new(subject)
.grant(AtomicPermission::new("file", "write"))
.deny(AtomicPermission::new("file", "delete"));
let effect = preview.effect();
assert_eq!(effect.grants_added.len(), 1);
assert_eq!(effect.denials_added.len(), 1);
}
}