#![allow(clippy::match_same_arms)]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Permission {
Read,
Write,
Delete,
Export,
CreateStream,
AccessAuditLogs,
ManagePermissions,
}
impl Permission {
pub fn is_high_risk(&self) -> bool {
matches!(
self,
Permission::Delete | Permission::Export | Permission::ManagePermissions
)
}
pub fn requires_audit(&self) -> bool {
match self {
Permission::Read => true,
Permission::Write => true,
Permission::Delete => true,
Permission::Export => true, Permission::CreateStream => true,
Permission::AccessAuditLogs => true,
Permission::ManagePermissions => true,
}
}
pub fn applicable_frameworks(&self) -> &'static [&'static str] {
match self {
Permission::Read | Permission::Write => {
&["HIPAA", "GDPR", "SOC2", "PCI_DSS", "ISO27001"]
}
Permission::Delete => {
&["GDPR", "HIPAA", "SOC2", "ISO27001"] }
Permission::Export => {
&["GDPR", "HIPAA"] }
Permission::CreateStream => &["SOC2", "ISO27001"],
Permission::AccessAuditLogs => &["HIPAA", "SOC2", "PCI_DSS", "ISO27001", "FedRAMP"],
Permission::ManagePermissions => &["SOC2", "ISO27001", "FedRAMP"],
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PermissionSet {
permissions: Vec<Permission>,
}
impl PermissionSet {
pub fn new(permissions: Vec<Permission>) -> Self {
Self { permissions }
}
pub fn empty() -> Self {
Self {
permissions: Vec::new(),
}
}
pub fn contains(&self, permission: Permission) -> bool {
self.permissions.contains(&permission)
}
pub fn grant(&mut self, permission: Permission) {
if !self.permissions.contains(&permission) {
self.permissions.push(permission);
}
}
pub fn revoke(&mut self, permission: Permission) {
self.permissions.retain(|p| *p != permission);
}
pub fn iter(&self) -> impl Iterator<Item = &Permission> {
self.permissions.iter()
}
pub fn has_high_risk_permission(&self) -> bool {
self.permissions.iter().any(Permission::is_high_risk)
}
}
impl Default for PermissionSet {
fn default() -> Self {
Self::empty()
}
}
impl From<Vec<Permission>> for PermissionSet {
fn from(permissions: Vec<Permission>) -> Self {
Self::new(permissions)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_permission_high_risk() {
assert!(!Permission::Read.is_high_risk());
assert!(!Permission::Write.is_high_risk());
assert!(Permission::Delete.is_high_risk());
assert!(Permission::Export.is_high_risk());
assert!(!Permission::CreateStream.is_high_risk());
assert!(!Permission::AccessAuditLogs.is_high_risk());
assert!(Permission::ManagePermissions.is_high_risk());
}
#[test]
fn test_permission_audit_required() {
assert!(Permission::Read.requires_audit());
assert!(Permission::Write.requires_audit());
assert!(Permission::Delete.requires_audit());
assert!(Permission::Export.requires_audit());
assert!(Permission::CreateStream.requires_audit());
assert!(Permission::AccessAuditLogs.requires_audit());
assert!(Permission::ManagePermissions.requires_audit());
}
#[test]
fn test_permission_set_operations() {
let mut set = PermissionSet::empty();
assert!(!set.contains(Permission::Read));
set.grant(Permission::Read);
assert!(set.contains(Permission::Read));
set.grant(Permission::Read); assert_eq!(set.permissions.len(), 1);
set.grant(Permission::Write);
assert!(set.contains(Permission::Write));
assert_eq!(set.permissions.len(), 2);
set.revoke(Permission::Read);
assert!(!set.contains(Permission::Read));
assert!(set.contains(Permission::Write));
}
#[test]
fn test_permission_set_high_risk() {
let mut set = PermissionSet::empty();
assert!(!set.has_high_risk_permission());
set.grant(Permission::Read);
assert!(!set.has_high_risk_permission());
set.grant(Permission::Delete);
assert!(set.has_high_risk_permission());
}
#[test]
fn test_permission_set_from_vec() {
let permissions = vec![Permission::Read, Permission::Write];
let set = PermissionSet::from(permissions);
assert!(set.contains(Permission::Read));
assert!(set.contains(Permission::Write));
assert!(!set.contains(Permission::Delete));
}
}