use serde::Serialize;
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct AclEntry {
pub tag: AclTag,
pub qualifier: Option<u32>,
pub perms: AclPerms,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[non_exhaustive]
pub enum AclTag {
UserObj,
User,
GroupObj,
Group,
Mask,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct AclPerms {
pub read: bool,
pub write: bool,
pub execute: bool,
}
impl AclPerms {
pub const NONE: Self = Self {
read: false,
write: false,
execute: false,
};
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct PosixAcl(pub(crate) Vec<AclEntry>);
impl PosixAcl {
#[must_use]
pub fn new(entries: Vec<AclEntry>) -> Self {
Self(entries)
}
#[must_use]
pub fn entries(&self) -> &[AclEntry] {
&self.0
}
#[must_use]
pub fn mask(&self) -> Option<&AclPerms> {
self.0
.iter()
.find(|e| e.tag == AclTag::Mask)
.map(|e| &e.perms)
}
#[must_use]
pub fn named_user(&self, uid: u32) -> Option<&AclEntry> {
self.0
.iter()
.find(|e| e.tag == AclTag::User && e.qualifier == Some(uid))
}
#[must_use]
pub fn named_group(&self, gid: u32) -> Option<&AclEntry> {
self.0
.iter()
.find(|e| e.tag == AclTag::Group && e.qualifier == Some(gid))
}
#[must_use]
pub fn user_obj(&self) -> Option<&AclEntry> {
self.0.iter().find(|e| e.tag == AclTag::UserObj)
}
#[must_use]
pub fn group_obj(&self) -> Option<&AclEntry> {
self.0.iter().find(|e| e.tag == AclTag::GroupObj)
}
#[must_use]
pub fn other(&self) -> Option<&AclEntry> {
self.0.iter().find(|e| e.tag == AclTag::Other)
}
#[must_use]
pub fn has_extended_entries(&self) -> bool {
self.0
.iter()
.any(|e| matches!(e.tag, AclTag::User | AclTag::Group))
}
pub fn named_groups(&self) -> impl Iterator<Item = &AclEntry> {
self.0.iter().filter(|e| e.tag == AclTag::Group)
}
#[must_use]
pub fn effective_perms(&self, entry: &AclEntry) -> AclPerms {
let dominated_by_mask =
matches!(entry.tag, AclTag::User | AclTag::GroupObj | AclTag::Group);
match (dominated_by_mask, self.mask()) {
(true, Some(mask)) => AclPerms {
read: entry.perms.read && mask.read,
write: entry.perms.write && mask.write,
execute: entry.perms.execute && mask.execute,
},
_ => entry.perms,
}
}
}
#[cfg(test)]
#[path = "acl_tests.rs"]
mod tests;