-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
File Location(s)
No response
Proposal
RBAC allocation-free storage (proposal)
This proposal keeps the current behavior (named roles, role admins, membership lists, default admin as master key) but removes dynamic allocation by using fixed-size arrays.
Goals
- Named roles with per-role admin role.
- Role membership lists.
- Deterministic, allocation-free storage.
- Keep the current “super admin passes any check” behavior.
Core types
Role ID size is configurable at compile time:
pub type RoleId<const R: usize> = [u8; R];
pub const fn default_admin_role<const R: usize>() -> RoleId<R> {
[0u8; R]
}Storage layout (no allocation)
#[derive(Clone, Copy)]
pub struct RoleEntry<const R: usize, const M: usize> {
pub role_id: RoleId<R>,
pub admin_role_id: RoleId<R>,
pub member_count: u32,
pub members: [Option<ActorId>; M],
}
#[derive(Clone, Copy)]
pub struct AccessControlStorage<const R: usize, const N: usize, const M: usize> {
/// Fixed capacity for roles.
pub role_count: u32,
pub roles: [Option<RoleEntry<R, M>>; N],
}Invariants
role_countis the number ofSome(RoleEntry)inroles.- Each
RoleEntryhas uniquerole_id. member_countmatches the number ofSome(ActorId)inmembers.- The default admin role (
default_admin_role::<R>()) exists and has at least one member (deployer) at initialization. - A caller with the default admin role satisfies any
require_role.
Operations (behavior)
has_role(role_id, actor_id)checks membership or the default admin role for master-key semantics.get_role_admin(role_id)returns the role’sadmin_role_idif it exists, otherwisedefault_admin_role::<R>().grant_role(role_id, actor_id)requires caller hasget_role_admin(role_id). Adds role entry if missing.revoke_role(role_id, actor_id)requires caller hasget_role_admin(role_id).renounce_role(role_id, actor_id)requires caller isactor_id.set_role_admin(role_id, new_admin)requires caller has current admin ofrole_id.
Capacity handling
- If
rolesis full when creating a new role, returnError::CapacityExceeded. - If
membersis full when granting to a new actor, returnError::CapacityExceeded. - Re-granting an existing membership returns
falseand does not change counts.
Helper functions (sketch)
fn find_role_index<const R: usize, const N: usize, const M: usize>(
storage: &AccessControlStorage<R, N, M>,
role_id: RoleId<R>,
) -> Option<usize>;
fn ensure_role_entry<const R: usize, const N: usize, const M: usize>(
storage: &mut AccessControlStorage<R, N, M>,
role_id: RoleId<R>,
) -> Result<usize, Error>;
fn insert_member<const M: usize>(
members: &mut [Option<ActorId>; M],
actor_id: ActorId,
) -> Result<bool, Error>;
fn remove_member<const M: usize>(
members: &mut [Option<ActorId>; M],
actor_id: ActorId,
) -> bool;Notes
- Using fixed arrays keeps deterministic iteration and removes
BTreeMap/BTreeSetallocation. - Role and member enumeration can be built by iterating the arrays and skipping
Noneslots. - Pagination can be implemented on top of enumeration by counting matches and slicing.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels