Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 bitAtomicPermission
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.