use super::{Aci, BindRule};
use crate::entry::LdapEntry;
use crate::operation::{LdapOperation, LdapPermission};
use acls_rs::prelude::*;
#[derive(Debug, Default)]
pub struct AciPolicy {
acis: Vec<Aci>,
}
impl AciPolicy {
pub fn new() -> Self {
Self::default()
}
pub fn add_aci(&mut self, aci: Aci) {
self.acis.push(aci);
}
pub fn acis(&self) -> &[Aci] {
&self.acis
}
pub fn compile(&self) -> crate::cache::CachedAciPolicy {
crate::cache::CachedAciPolicy::new(&self.acis)
}
pub fn compile_with_cache_size(&self, cache_size: usize) -> crate::cache::CachedAciPolicy {
crate::cache::CachedAciPolicy::with_cache_size(&self.acis, cache_size)
}
pub fn authorize(
&self,
operation: &LdapOperation,
target_entry: &LdapEntry,
user_dn: Option<&str>,
user_entry: Option<&LdapEntry>,
) -> AuthorizationResult {
let mut grants = PermissionSet::new();
let mut denials = PermissionSet::new();
for aci in &self.acis {
let target_matches = aci.applies_to_target(operation, target_entry);
if target_matches {
let bind_matches = aci.bind_rule.matches(user_dn, user_entry, &target_entry.dn);
if bind_matches {
let perm = LdapPermission::new(operation.operation_type, &operation.target_dn)
.to_atomic();
if aci.grant {
grants.extend([perm]);
} else {
denials.extend([perm]);
}
} else if !aci.grant {
if let BindRule::Not(ref inner) = aci.bind_rule {
match **inner {
BindRule::GroupDn(_) | BindRule::UserDn(_) => {
let perm = LdapPermission::new(
operation.operation_type,
&operation.target_dn,
)
.to_atomic();
grants.extend([perm]);
}
_ => {}
}
}
}
}
}
let gd_pair = GrantDenialPair::new(grants, denials);
let effective = gd_pair.effective_permissions();
let authorized = effective.contains(&operation.to_permission().to_atomic());
AuthorizationResult {
authorized,
grants: gd_pair.grants,
denials: gd_pair.denials,
effective,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct AuthorizationResult {
pub authorized: bool,
pub grants: PermissionSet,
pub denials: PermissionSet,
pub effective: PermissionSet,
}
impl AuthorizationResult {
pub fn cached(authorized: bool) -> Self {
Self {
authorized,
grants: PermissionSet::new(),
denials: PermissionSet::new(),
effective: PermissionSet::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::super::{AciBuilder, BindRule, Scope};
use super::*;
use crate::operation::OperationType;
#[test]
fn test_self_read_aci() {
let aci = AciBuilder::new("self-read")
.target_dn("ou=people,dc=example,dc=com")
.target_scope(Scope::OneLevel)
.permission(OperationType::Read)
.bind_rule(BindRule::SelfUser)
.build();
let entry = LdapEntry::new("uid=alice,ou=people,dc=example,dc=com");
let operation =
LdapOperation::new(OperationType::Read, "uid=alice,ou=people,dc=example,dc=com");
assert!(aci.applies_to(
&operation,
&entry,
Some("uid=alice,ou=people,dc=example,dc=com"),
None
));
assert!(!aci.applies_to(
&operation,
&entry,
Some("uid=bob,ou=people,dc=example,dc=com"),
None
));
}
#[test]
fn test_aci_policy() {
let mut policy = AciPolicy::new();
policy.add_aci(
AciBuilder::new("read-people")
.target_dn("ou=people,dc=example,dc=com")
.target_scope(Scope::OneLevel)
.permission(OperationType::Read)
.bind_rule(BindRule::Authenticated)
.build(),
);
let entry = LdapEntry::new("uid=alice,ou=people,dc=example,dc=com");
let operation =
LdapOperation::new(OperationType::Read, "uid=alice,ou=people,dc=example,dc=com");
let result = policy.authorize(
&operation,
&entry,
Some("uid=bob,ou=people,dc=example,dc=com"),
None,
);
assert!(result.authorized);
}
}