use super::Privilege;
#[derive(Debug, Clone)]
pub struct AclTarget {
pub cluster: Option<u32>,
pub endpoint: Option<u16>,
}
#[derive(Debug, Clone)]
pub struct AccessControlEntry {
pub fabric_index: u8,
pub privilege: Privilege,
pub auth_mode: u8,
pub subjects: Vec<u64>,
pub targets: Vec<AclTarget>,
}
pub struct AccessControlList {
entries: Vec<AccessControlEntry>,
}
impl AccessControlList {
pub fn new() -> Self {
Self {
entries: Vec::new(),
}
}
pub fn add_entry(&mut self, entry: AccessControlEntry) {
self.entries.push(entry);
}
pub fn check_access(
&self,
fabric_index: u8,
subject: u64,
endpoint: u16,
cluster: u32,
required: Privilege,
) -> bool {
for entry in &self.entries {
if entry.fabric_index != fabric_index {
continue;
}
if entry.privilege < required {
continue;
}
if !entry.subjects.is_empty() && !entry.subjects.contains(&subject) {
continue;
}
if !entry.targets.is_empty() {
let target_match = entry.targets.iter().any(|t| {
let ep_ok = t.endpoint.is_none_or(|e| e == endpoint);
let cl_ok = t.cluster.is_none_or(|c| c == cluster);
ep_ok && cl_ok
});
if !target_match {
continue;
}
}
return true;
}
false
}
pub fn grant_commissioning_access(&mut self) {
self.entries.push(AccessControlEntry {
fabric_index: 0,
privilege: Privilege::Administer,
auth_mode: 1, subjects: vec![],
targets: vec![],
});
}
}
impl Default for AccessControlList {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn acl_commissioning_access_grants_administer() {
let mut acl = AccessControlList::new();
acl.grant_commissioning_access();
assert!(acl.check_access(0, 0xDEAD_BEEF_0000_0001, 0, 0x0028, Privilege::Administer));
assert!(acl.check_access(0, 0, 1, 0x003E, Privilege::View));
assert!(!acl.check_access(1, 0, 0, 0x0028, Privilege::View));
}
#[test]
fn acl_specific_subject_check() {
let mut acl = AccessControlList::new();
let node_a: u64 = 0x0000_0000_0000_0001;
let node_b: u64 = 0x0000_0000_0000_0002;
acl.add_entry(AccessControlEntry {
fabric_index: 1,
privilege: Privilege::Operate,
auth_mode: 2, subjects: vec![node_a],
targets: vec![AclTarget {
cluster: Some(0x0006),
endpoint: Some(1),
}],
});
assert!(acl.check_access(1, node_a, 1, 0x0006, Privilege::View));
assert!(acl.check_access(1, node_a, 1, 0x0006, Privilege::Operate));
assert!(!acl.check_access(1, node_a, 1, 0x0006, Privilege::Manage));
assert!(!acl.check_access(1, node_b, 1, 0x0006, Privilege::View));
assert!(!acl.check_access(1, node_a, 2, 0x0006, Privilege::View));
assert!(!acl.check_access(1, node_a, 1, 0x0008, Privilege::View));
}
}