posix-acls: POSIX ACL Modeling
Your team shares a Linux server. Source code, build artifacts, and
documentation all live on the filesystem, and different people need different
levels of access. Traditional Unix permissions (chmod 750) give you three
slots – owner, group, everyone else – but you need to give one contractor
read access and one DBA read-write access without touching the group
membership of every file. That is the problem POSIX ACLs (POSIX.1e) solve.
posix-acls provides Rust types that model the full POSIX.1e ACL system – from
individual rwx permission triples up through complete ACLs with named user
and group entries, default ACLs for directories, mask computation, and the
access-check algorithm. On top of that core model, a higher-level policy
layer lets you declare permissions against a user/group directory and derive
concrete ACLs automatically.
Two layers
The crate is organised into two layers, each building on the previous:
-
Typed POSIX ACL model – the
rwxpermission triple (PosixPerm) with lattice algebra, the six ACL tag types (AclTag), ACL entries (AclEntry), and the complete ACL type (PosixAcl) with fluent construction (AclBuilder),getfacl(1)parsing and generation, Unix mode word conversion, and the POSIX.1e access-check algorithm. -
User/group model and policy – an abstract directory of users and groups (
UserGroupModel) with a policy system (AclPolicy) that assigns permissions to principals and derives validatedPosixAclvalues. The model ensures that every named grant references a real user or group.
An acls-rs bridge connects both layers to the algebraic permission system in acls-rs, so you can mix POSIX file permissions with application-level access control.
Quick start
Add posix-acls to your Cargo.toml:
[dependencies]
posix-acls = "0.1"
With serde support:
[dependencies]
posix-acls = { version = "0.1", features = ["serde"] }
A minimal example that builds an ACL, serialises it to getfacl text, and
checks access:
#![allow(unused)]
fn main() {
use posix_acls::prelude::*;
// Build an ACL for a shared project directory
let acl = AclBuilder::new("alice", "devs")
.owner_perms(PosixPerm::RWX)
.owning_group_perms(PosixPerm::RX)
.other_perms(PosixPerm::NONE)
.add_user("bob", PosixPerm::RW)
.build();
// Serialise to the getfacl(1) text format
let text = generate::<GetfaclGenerator>(&acl).unwrap();
assert!(text.contains("user:bob:rw-"));
// Parse it back
let recovered = parse::<GetfaclParser>(&text).unwrap();
assert_eq!(acl.owner(), recovered.owner());
// Check access -- bob can read, but not execute
let r = acl.check_access("bob", &["staff"] as &[&str], PosixPerm::R);
assert!(r.granted);
let r = acl.check_access("bob", &["staff"] as &[&str], PosixPerm::X);
assert!(!r.granted);
}
Policy-driven ACLs
When your organisation has dozens of users across multiple teams, building ACLs by hand is error-prone. The policy layer lets you declare intent and validate it against a user/group directory:
#![allow(unused)]
fn main() {
use posix_acls::prelude::*;
// Define the organisation's users and groups
let mut model = UserGroupModel::new();
model.add_user(UserEntry::new("alice").with_groups(["devs"]));
model.add_user(UserEntry::new("bob").with_groups(["devs"]));
model.add_user(UserEntry::new("carol").with_groups(["ops"]));
model.add_group(GroupEntry::new("devs"));
model.add_group(GroupEntry::new("ops"));
// Declare a policy for the project directory
let policy = AclPolicy::builder()
.owner("alice")
.owning_group("devs")
.owner_perms(PosixPerm::RWX)
.owning_group_perms(PosixPerm::RX)
.other_perms(PosixPerm::NONE)
.add_user("bob", PosixPerm::RW)
.add_group("ops", PosixPerm::R)
.build()
.unwrap();
// Generate a concrete ACL -- the model validates that "bob" and "ops" exist
let acl = policy.generate(&model).unwrap();
assert!(acl.is_valid().is_ok());
assert!(acl.is_extended());
}
Mode word interoperability
POSIX ACLs and traditional Unix mode words (chmod 755) are two views of the
same permission model. posix-acls converts between them losslessly for
minimal (non-extended) ACLs:
#![allow(unused)]
fn main() {
use posix_acls::prelude::*;
// Build from a mode word
let acl = PosixAcl::from_mode("alice", "devs", 0o755);
assert_eq!(acl.to_mode(), 0o755);
// Extended ACLs reflect the mask in stat()'s group bits (POSIX.1e section 23.1.6)
let extended = AclBuilder::new("alice", "devs")
.owner_perms(PosixPerm::RWX)
.owning_group_perms(PosixPerm::RX)
.other_perms(PosixPerm::R)
.add_user("bob", PosixPerm::RW)
.build();
assert_eq!((extended.to_mode() >> 3) & 0o7, 0o7); // mask = rwx
}
Integration with acls-rs
The bridge module converts between POSIX permission triples and acls-rs
PermissionSet values, so you can combine file-level access control with
application-level policies:
#![allow(unused)]
fn main() {
use posix_acls::prelude::*;
use acls_rs::permission::AtomicPermission;
let acl = AclBuilder::new("alice", "devs")
.owner_perms(PosixPerm::RWX)
.owning_group_perms(PosixPerm::RX)
.other_perms(PosixPerm::NONE)
.build();
// Convert Alice's effective permissions to an acls-rs PermissionSet
let set = acl.to_permission_set("alice", &[] as &[&str]);
assert!(set.contains(&AtomicPermission::new("posix", "read")));
assert!(set.contains(&AtomicPermission::new("posix", "write")));
assert!(set.contains(&AtomicPermission::new("posix", "execute")));
// Or as a GrantDenialPair (POSIX ACLs have no explicit denials)
let gdp = acl.to_grant_denial_pair("alice", &[] as &[&str]);
assert!(gdp.denials.is_empty());
}
What to read next
- Permissions – the
rwxpermission triple, special bits, and the lattice algebra that makes composition well-defined. - Building ACLs – tags, entries, the
PosixAcltype, fluent construction withAclBuilder, and structural validation. - Parsing and Generation – reading and writing the
getfacl(1)text format, round-trip guarantees, and extending with custom formats. - Access Checks – the full POSIX.1e access-check algorithm, the role of the mask, and mode word conversion.
- User/Group Model and Policy – declaring users and groups, building policies, deriving validated ACLs, and handling errors.
- acls-rs Bridge – converting between POSIX permissions and acls-rs types for cross-system access control.
- Temporal Access – end-to-end example: time-bounded contractor team grants → group-based POSIX ACLs → per-user effective access → setfacl commands.