acls-rs Bridge
Your platform manages both POSIX file permissions and application-level
access control. A deployment script checks whether a user can write to a
directory (POSIX ACL) and also whether they hold the deploy:execute
permission in your RBAC system. The bridge module connects these two worlds
by converting between PosixPerm and acls-rs PermissionSet types.
Namespace convention
The bridge uses a PermissionMapping with namespace "posix":
PosixPerm bit | AtomicPermission |
|---|---|
Read (r, bit 4) | posix:read |
Write (w, bit 2) | posix:write |
Execute (x, bit 1) | posix:execute |
The mapping is also available as posix_acls::bridge::posix_mapping()
for use with the PermissionMapping API (e.g. for building custom
cross-model bridges).
PosixPerm to PermissionSet
PosixPerm::to_permission_set() converts a permission triple into an
acls-rs PermissionSet:
#![allow(unused)]
fn main() {
use posix_acls::PosixPerm;
use acls_rs::permission::AtomicPermission;
let set = PosixPerm::RW.to_permission_set();
assert_eq!(set.len(), 2);
assert!(set.contains(&AtomicPermission::new("posix", "read")));
assert!(set.contains(&AtomicPermission::new("posix", "write")));
assert!(!set.contains(&AtomicPermission::new("posix", "execute")));
}
PermissionSet to PosixPerm
PosixPerm::from_permission_set(set) converts back. It recognises only
AtomicPermission values with namespace "posix" and actions "read",
"write", or "execute". All other permissions in the set are ignored:
#![allow(unused)]
fn main() {
use posix_acls::PosixPerm;
use acls_rs::permission::{AtomicPermission, PermissionSet};
let set = PermissionSet::from([
AtomicPermission::new("posix", "read"),
AtomicPermission::new("posix", "write"),
AtomicPermission::new("app", "deploy"), // ignored
]);
assert_eq!(PosixPerm::from_permission_set(&set), PosixPerm::RW);
}
The conversion round-trips cleanly for all eight permission values:
#![allow(unused)]
fn main() {
use posix_acls::PosixPerm;
for bits in 0u8..8 {
let p = PosixPerm::new(bits);
let set = p.to_permission_set();
let p2 = PosixPerm::from_permission_set(&set);
assert_eq!(p, p2);
}
}
ACL to PermissionSet
PosixAcl::to_permission_set(user, groups) runs the POSIX.1e access-check
algorithm and converts the effective permissions into a PermissionSet:
#![allow(unused)]
fn main() {
use posix_acls::{AclBuilder, PosixPerm};
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();
// Alice is the owner -- gets rwx
let set = acl.to_permission_set("alice", &[] as &[&str]);
assert_eq!(set.len(), 3);
assert!(set.contains(&AtomicPermission::new("posix", "read")));
assert!(set.contains(&AtomicPermission::new("posix", "write")));
assert!(set.contains(&AtomicPermission::new("posix", "execute")));
// Eve matches "other" -- gets nothing
let set = acl.to_permission_set("eve", &[] as &[&str]);
assert_eq!(set.len(), 0);
}
ACL to GrantDenialPair
PosixAcl::to_grant_denial_pair(user, groups) returns the effective
permissions as a GrantDenialPair. POSIX ACLs have no explicit denial
mechanism – absence of a permission is implicit denial, so the denials
field is always empty:
#![allow(unused)]
fn main() {
use posix_acls::{AclBuilder, PosixPerm};
let acl = AclBuilder::new("alice", "devs")
.owner_perms(PosixPerm::RWX)
.owning_group_perms(PosixPerm::RX)
.other_perms(PosixPerm::NONE)
.build();
let gdp = acl.to_grant_denial_pair("alice", &[] as &[&str]);
assert!(gdp.denials.is_empty());
assert_eq!(gdp.grants.len(), 3);
let effective = gdp.effective_permissions();
assert_eq!(effective.len(), 3);
}
This is useful when you need to compose POSIX file permissions with
application-level GrantDenialPair values from other access control
systems, since GrantDenialPair supports algebraic combination.
Combining with application-level permissions
The bridge enables cross-system access checks. You can merge POSIX file
permissions with application-level grants into a single PermissionSet:
#![allow(unused)]
fn main() {
use posix_acls::{AclBuilder, PosixPerm};
use acls_rs::prelude::*;
let acl = AclBuilder::new("alice", "devs")
.owner_perms(PosixPerm::RWX)
.owning_group_perms(PosixPerm::RX)
.other_perms(PosixPerm::NONE)
.build();
// Get POSIX file permissions for alice
let file_perms = acl.to_permission_set("alice", &[] as &[&str]);
// Application-level permissions
let app_perms = PermissionSet::from([
AtomicPermission::new("deploy", "execute"),
AtomicPermission::new("config", "read"),
]);
// Combine both into a single permission set
let combined = file_perms.combine(app_perms);
assert!(combined.contains(&AtomicPermission::new("posix", "write")));
assert!(combined.contains(&AtomicPermission::new("deploy", "execute")));
}
The "posix" namespace keeps file permissions distinct from application
permissions, so they compose without collision.
Working with temporal permissions
Through the posix:read/write/execute namespace convention, POSIX
permission bits integrate naturally with acls-rs temporal types. Each rwx
bit becomes an AtomicPermission that carries its own validity window:
#![allow(unused)]
fn main() {
use posix_acls::PosixPerm;
use acls_rs::permission::AtomicPermission;
use acls_rs::permission::temporal::{TemporalPermission, TemporalPermissionSet};
// Grant read+write for a 24-hour window (timestamps in milliseconds)
let start = 1_700_000_000_000u64;
let end = 1_700_086_400_000u64;
let mut temporal = TemporalPermissionSet::new();
temporal.add(TemporalPermission::new(
AtomicPermission::new("posix", "read"), Some(start), Some(end)));
temporal.add(TemporalPermission::new(
AtomicPermission::new("posix", "write"), Some(start), Some(end)));
// During the window: both bits are active
let mid = 1_700_040_000_000u64;
let active = temporal.effective_at(mid);
assert_eq!(PosixPerm::from_permission_set(&active), PosixPerm::RW);
// After the window: nothing is active
let after = 1_700_100_000_000u64;
let expired = temporal.effective_at(after);
assert_eq!(PosixPerm::from_permission_set(&expired), PosixPerm::NONE);
}
For a complete end-to-end walkthrough — contractor teams with overlapping
time windows, group-based ACLs on shared mounts, per-user effective access,
and generated setfacl commands — see
Temporal Access.