Role-based access control with hierarchy levels and effective dating
| Tier | identity |
| Role | unclassified (baselined) |
| Path | crates/identity/rbac |
| Edition | 2021 |
| Targets | identity_rbac |
| Public items | 71 across 7 modules |
| Tests | 77 |
What it is for
# identity-rbac
Role-Based Access Control with hierarchy levels and effective dating.
Core Concepts
- Roles have hierarchy levels (10-100)
- Users can only manage users with strictly lower levels
- Role assignments support effective dating (
valid_from/valid_to) - Permissions use module.action format
Hierarchy Levels
Level 100: Superuser
Level 80: Administrator
Level 60: Manager
Level 40: Professional
Level 30: Technician
Level 20: Staff (default)
Level 10: Customer
Level 0: No roles
Example
use identity_rbac::{Role, UserRole, SimpleUser, RBACUser, hierarchy};
use uuid::Uuid;
// Create roles
let manager_role = Role::new("Manager", hierarchy::MANAGER).expect("valid");
let staff_role = Role::new("Staff", hierarchy::STAFF).expect("valid");
// Assign role to user
let user_role = UserRole::new(Uuid::new_v4(), manager_role.id)
.with_role(manager_role.clone());
// Create user with role
let manager = SimpleUser::new(Uuid::new_v4()).with_role(user_role);
// Check hierarchy level
assert_eq!(manager.hierarchy_level(), 60);
// Manager can manage staff
let staff_ur = UserRole::new(Uuid::new_v4(), staff_role.id)
.with_role(staff_role);
let staff = SimpleUser::new(Uuid::new_v4()).with_role(staff_ur);
assert!(manager.can_manage_user(&staff));
Capabilities
RBACError
Error types for RBAC.
| Item |
|---|
pub enum RBACError |
verb:constants
Hierarchy level constants for RBAC.
| Item |
|---|
pub const SUPERUSER: i32 |
pub const ADMINISTRATOR: i32 |
pub const MANAGER: i32 |
pub const PROFESSIONAL: i32 |
pub const TECHNICIAN: i32 |
pub const STAFF: i32 |
pub const CUSTOMER: i32 |
pub const NO_ROLES: i32 |
pub const MIN_LEVEL: i32 |
pub const MAX_LEVEL: i32 |
pub const DEFAULT_LEVEL: i32 |
verb:is
Hierarchy level constants for RBAC.
| Item |
|---|
fn is_valid_level(level : i32) -> bool |
verb:level
Hierarchy level constants for RBAC.
| Item |
|---|
fn level_name(level : i32) -> & 'static str |
Permission
Permission model for RBAC.
| Item |
|---|
pub struct Permission |
Permission :: fn new(codename : impl Into <String>) -> Self |
Permission :: fn from_module_action(module : & str, action : & str) -> Self |
Permission :: fn with_description(mut self, description : impl Into <String>) -> Self |
Permission :: fn module(& self) -> Option <& str> |
Permission :: fn action(& self) -> Option <& str> |
Permission :: fn matches(& self, module : & str, action : & str) -> bool |
RolePermission
Permission model for RBAC.
| Item |
|---|
pub struct RolePermission |
RolePermission :: fn new(role_id : Uuid, permission_id : Uuid) -> Self |
permission::actions (other)
_No module-level documentation is present in the source._
| Item |
|---|
pub const VIEW: & str |
pub const EDIT: & str |
pub const CREATE: & str |
pub const DELETE: & str |
pub const MANAGE: & str |
pub const READ_RAW: & str |
Role:Role
Role model for RBAC.
| Item |
|---|
pub struct Role |
Role:can
Role model for RBAC.
| Item |
|---|
Role :: fn can_manage(& self, other : & Role) -> bool |
Role:created
Role model for RBAC.
| Item |
|---|
Role :: fn created_at(& self) -> DateTime <Utc> |
Role:deleted
Role model for RBAC.
| Item |
|---|
Role :: fn deleted_at(& self) -> Option <DateTime <Utc>> |
Role:id
Role model for RBAC.
| Item |
|---|
Role :: fn id(& self) -> Uuid |
Role:inactive
Role model for RBAC.
| Item |
|---|
Role :: fn inactive(mut self) -> Self |
Role:is
Role model for RBAC.
| Item |
|---|
Role :: fn is_valid_level(& self) -> bool |
Role:level
Role model for RBAC.
| Item |
|---|
Role :: fn level_name(& self) -> & 'static str |
Role:new
Role model for RBAC.
| Item |
|---|
Role :: fn new(name : impl Into <String>, hierarchy_level : i32) -> Result <Self, RBACError> |
Role:slugify
Role model for RBAC.
| Item |
|---|
Role :: fn slugify(name : & str) -> String |
Role:updated
Role model for RBAC.
| Item |
|---|
Role :: fn updated_at(& self) -> DateTime <Utc> |
Role:with
Role model for RBAC.
| Item |
|---|
Role :: fn with_default_level(name : impl Into <String>) -> Self |
Role :: fn with_description(mut self, description : impl Into <String>) -> Self |
Role :: fn with_slug(mut self, slug : impl Into <String>) -> Self |
RBACUser
RBAC traits for user types.
| Item |
|---|
pub trait RBACUser |
SimpleUser
RBAC traits for user types.
| Item |
|---|
pub struct SimpleUser |
SimpleUser :: fn new(id : Uuid) -> Self |
SimpleUser :: fn superuser(id : Uuid) -> Self |
SimpleUser :: fn with_role(mut self, user_role : UserRole) -> Self |
SimpleUser :: fn with_permission(mut self, permission : crate::permission::Permission) -> Self |
SimpleUser :: fn user_id(& self) -> Uuid |
SimpleUser :: fn is_superuser(& self) -> bool |
SimpleUser :: fn current_roles(& self) -> & UserRole |
SimpleUser :: fn has_permission(& self, module : & str, action : & str) -> bool |
UserRole:UserRole
User role assignment with effective dating.
| Item |
|---|
pub struct UserRole |
UserRole:as
User role assignment with effective dating.
| Item |
|---|
UserRole :: fn as_primary(mut self) -> Self |
UserRole:assigned
User role assignment with effective dating.
| Item |
|---|
UserRole :: fn assigned_by(mut self, user_id : Uuid) -> Self |
UserRole:created
User role assignment with effective dating.
| Item |
|---|
UserRole :: fn created_at(& self) -> DateTime <Utc> |
UserRole:deleted
User role assignment with effective dating.
| Item |
|---|
UserRole :: fn deleted_at(& self) -> Option <DateTime <Utc>> |
UserRole:hierarchy
User role assignment with effective dating.
| Item |
|---|
UserRole :: fn hierarchy_level(& self) -> Option <i32> |
UserRole:id
User role assignment with effective dating.
| Item |
|---|
UserRole :: fn id(& self) -> Uuid |
UserRole:is
User role assignment with effective dating.
| Item |
|---|
UserRole :: fn is_current(& self) -> bool |
UserRole :: fn is_valid_at(& self, timestamp : DateTime <Utc>) -> bool |
UserRole :: fn is_expired(& self) -> bool |
UserRole :: fn is_future(& self) -> bool |
UserRole:new
User role assignment with effective dating.
| Item |
|---|
UserRole :: fn new(user_id : Uuid, role_id : Uuid) -> Self |
UserRole:updated
User role assignment with effective dating.
| Item |
|---|
UserRole :: fn updated_at(& self) -> DateTime <Utc> |
UserRole:valid
User role assignment with effective dating.
| Item |
|---|
UserRole :: fn valid_for_days(mut self, days : i64) -> Self |
UserRole:validate
User role assignment with effective dating.
| Item |
|---|
UserRole :: fn validate_dating(& self) -> Result <(), RBACError> |
UserRole:with
User role assignment with effective dating.
| Item |
|---|
UserRole :: fn with_valid_from(mut self, from : DateTime <Utc>) -> Self |
UserRole :: fn with_valid_to(mut self, to : DateTime <Utc>) -> Self |
UserRole :: fn with_role(mut self, role : Role) -> Self |
How to use it
From this crate's own rustdoc:
## Example
Module structure
identity_rbac
errorhierarchypermissionpermission::actionsroletraitsuser_role
flowchart TD n_identity_rbac["identity_rbac"] n_identity_rbac --> n_error["error"] n_identity_rbac --> n_hierarchy["hierarchy"] n_identity_rbac --> n_permission["permission"] n_permission --> n_permission__actions["actions"] n_identity_rbac --> n_role["role"] n_identity_rbac --> n_traits["traits"] n_identity_rbac --> n_user_role["user_role"]
Public surface
`error`
| Item | What it is |
|---|---|
pub enum RBACError | Errors that can occur in RBAC operations. |
`hierarchy`
| Item | What it is |
|---|---|
pub const SUPERUSER: i32 | Superuser level - system administrators with full access. |
pub const ADMINISTRATOR: i32 | Administrator level - full system access. |
pub const MANAGER: i32 | Manager level - team leads and supervisors. |
pub const PROFESSIONAL: i32 | Professional level - licensed professionals. |
pub const TECHNICIAN: i32 | Technician level - support staff. |
pub const STAFF: i32 | Staff level - front desk and general staff (default). |
pub const CUSTOMER: i32 | Customer level - end users. |
pub const NO_ROLES: i32 | Level for users with no assigned roles. |
pub const MIN_LEVEL: i32 | Minimum valid hierarchy level. |
pub const MAX_LEVEL: i32 | Maximum valid hierarchy level. |
pub const DEFAULT_LEVEL: i32 | Default hierarchy level for new roles. |
fn is_valid_level(level : i32) -> bool | Check if a hierarchy level is valid (10-100). |
fn level_name(level : i32) -> & 'static str | Get the name for a standard hierarchy level. |
`permission`
| Item | What it is |
|---|---|
pub struct Permission | A permission identified by a module.action codename. |
Permission :: fn new(codename : impl Into <String>) -> Self | Create a new permission. |
Permission :: fn from_module_action(module : & str, action : & str) -> Self | Create a permission from module and action. |
Permission :: fn with_description(mut self, description : impl Into <String>) -> Self | Builder: set description. |
Permission :: fn module(& self) -> Option <& str> | Get the module part of the codename. |
Permission :: fn action(& self) -> Option <& str> | Get the action part of the codename. |
Permission :: fn matches(& self, module : & str, action : & str) -> bool | Check if this permission matches a module and action. |
pub struct RolePermission | Association between a role and a permission. |
RolePermission :: fn new(role_id : Uuid, permission_id : Uuid) -> Self | Create a new role-permission association. |
`permission::actions`
| Item | What it is |
|---|---|
pub const VIEW: & str | View permission action. |
pub const EDIT: & str | Edit permission action. |
pub const CREATE: & str | Create permission action. |
pub const DELETE: & str | Delete permission action. |
pub const MANAGE: & str | Manage permission action (full access). |
pub const READ_RAW: & str | Read-raw permission action — reveals unredacted fields that view collapses or strips |
`role`
| Item | What it is |
|---|---|
pub struct Role | A role with a hierarchy level. |
Role :: fn new(name : impl Into <String>, hierarchy_level : i32) -> Result <Self, RBACError> | Create a new Role with required fields |
Role :: fn with_default_level(name : impl Into <String>) -> Self | Create a role with default hierarchy level (Staff = 20) |
Role :: fn slugify(name : & str) -> String | Generate a URL-friendly slug from a name. |
Role :: fn with_description(mut self, description : impl Into <String>) -> Self | Builder: set description. |
Role :: fn with_slug(mut self, slug : impl Into <String>) -> Self | Builder: set custom slug. |
Role :: fn inactive(mut self) -> Self | Builder: set inactive. |
Role :: fn level_name(& self) -> & 'static str | Get the hierarchy level name. |
Role :: fn can_manage(& self, other : & Role) -> bool | Check if this role can manage another role. |
Role :: fn is_valid_level(& self) -> bool | Check if hierarchy level is valid. |
Role :: fn id(& self) -> Uuid | — |
Role :: fn created_at(& self) -> DateTime <Utc> | — |
Role :: fn updated_at(& self) -> DateTime <Utc> | — |
Role :: fn deleted_at(& self) -> Option <DateTime <Utc>> | — |
`traits`
| Item | What it is |
|---|---|
pub trait RBACUser | Trait for users with RBAC capabilities. |
pub struct SimpleUser | Simple user struct for testing the trait. |
SimpleUser :: fn new(id : Uuid) -> Self | Create a new simple user. |
SimpleUser :: fn superuser(id : Uuid) -> Self | Create a superuser. |
SimpleUser :: fn with_role(mut self, user_role : UserRole) -> Self | Add a role assignment. |
SimpleUser :: fn with_permission(mut self, permission : crate::permission::Permission) -> Self | Grant a permission directly to this user. |
SimpleUser :: fn user_id(& self) -> Uuid | — |
SimpleUser :: fn is_superuser(& self) -> bool | — |
SimpleUser :: fn current_roles(& self) -> & UserRole | — |
SimpleUser :: fn has_permission(& self, module : & str, action : & str) -> bool | — |
`user_role`
| Item | What it is |
|---|---|
pub struct UserRole | Assignment of a role to a user. |
UserRole :: fn new(user_id : Uuid, role_id : Uuid) -> Self | Create a new user role assignment. |
UserRole :: fn assigned_by(mut self, user_id : Uuid) -> Self | Builder: set who assigned this role. |
UserRole :: fn as_primary(mut self) -> Self | Builder: set as primary role. |
UserRole :: fn with_valid_from(mut self, from : DateTime <Utc>) -> Self | Builder: set valid_from date. |
UserRole :: fn with_valid_to(mut self, to : DateTime <Utc>) -> Self | Builder: set valid_to date. |
UserRole :: fn valid_for_days(mut self, days : i64) -> Self | Builder: set validity period in days from now. |
UserRole :: fn with_role(mut self, role : Role) -> Self | Builder: attach the loaded role. |
UserRole :: fn is_current(& self) -> bool | Check if this assignment is currently valid |
UserRole :: fn is_valid_at(& self, timestamp : DateTime <Utc>) -> bool | Check if this assignment was valid at a specific timestamp. |
UserRole :: fn is_expired(& self) -> bool | Check if this assignment has expired. |
UserRole :: fn is_future(& self) -> bool | Check if this assignment is scheduled for the future. |
UserRole :: fn validate_dating(& self) -> Result <(), RBACError> | Validate that effective dating is correct |
UserRole :: fn hierarchy_level(& self) -> Option <i32> | Get the hierarchy level of the assigned role |
UserRole :: fn id(& self) -> Uuid | — |
UserRole :: fn created_at(& self) -> DateTime <Utc> | — |
UserRole :: fn updated_at(& self) -> DateTime <Utc> | — |
UserRole :: fn deleted_at(& self) -> Option <DateTime <Utc>> | — |
Re-exports. Exported here, defined elsewhere.
| Export | Defined in |
|---|---|
RBACError | error::RBACError |
Role | role::Role |
UserRole | user_role::UserRole |
{HasId,SoftDeletable,Timestamped} | foundation_basemodels::{HasId,SoftDeletable,Timestamped} |
{RBACUser,SimpleUser} | traits::{RBACUser,SimpleUser} |
{actions,Permission,RolePermission} | permission::{actions,Permission,RolePermission} |
Boundary
Reaches into foundation.
Shares tier identity with 7 other crates: identity-auth, identity-identifiers, identity-impersonation, identity-parties, identity-party-places, identity-tenant, identity-user-prefs.
_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._
Where it sits
| Tier (ontology) | identity |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/identity/rbac |
| Vocabulary in force (lexicon) | current |
Tier flow. Which tiers this crate's own edges cross.
flowchart LR n_identity["identity"] --> n_foundation["foundation"]
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `foundation-basemodels` | foundation | no | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
chrono | ^0.4 | serde | no | always |
serde | ^1 | derive | no | always |
sqlx | ^0.8 | runtime-tokio, postgres, chrono, uuid, json | no | always |
thiserror | ^2 | — | no | always |
tokio | ^1 | full | no | always |
uuid | ^1 | v4, v7, serde, js | no | always |
Development, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
serde_json | ^1 | — | no | always |
tokio-test | ^0.4 | — | no | always |
Build. None.
Depended on by. 2 workspace crates.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR n_application_rbac["application-rbac"] -->|uses| SELF n_domain_legal_matter["domain-legal-matter"] -->|uses| SELF SELF["identity-rbac"] SELF -->|runtime| n_foundation_basemodels["foundation-basemodels"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.
Targets
| Kind | Name | Source |
|---|---|---|
| lib | identity_rbac | `src/lib.rs` |
Error model
| Error type | Named by |
|---|---|
RBACError | declared, no public signature returns it |
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | none detected |
| async runtime | yes |
| database access | yes |
| network I/O | none detected |
| unsafe code | none detected |
| environment variables | none detected |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
No environment variable is read with a literal name anywhere in this crate. A variable whose key is computed at run time cannot be listed here, and is not claimed to be absent.
Related capabilities
2 workspace crates depend on this one: application-rbac, domain-legal-matter.
Verification
| Kind | Count |
|---|---|
| Unit tests | 77 |
| Integration tests | 0 |
| Examples | 0 |
| Doctests | 1 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
error | 1 | 0 | 0 |
hierarchy | 13 | 0 | 3 |
permission | 2 | 0 | 0 |
permission::actions | 6 | 0 | 1 |
role | 1 | 0 | 2 |
traits | 2 | 0 | 0 |
user_role | 1 | 0 | 0 |
What the tests establish, by name:
test_duplicate_role_name_error—src/error.rstest_insufficient_hierarchy_error—src/error.rstest_invalid_effective_dating_error—src/error.rstest_invalid_hierarchy_level_error—src/error.rstest_permission_denied_error—src/error.rstest_role_not_found_error—src/error.rstest_hierarchy_constants—src/hierarchy.rstest_hierarchy_ordering—src/hierarchy.rstest_is_valid_level—src/hierarchy.rstest_level_name—src/hierarchy.rstest_level_range—src/hierarchy.rstest_effective_dating_workflow—src/lib.rstest_full_rbac_workflow—src/lib.rstest_invalid_dating_error—src/lib.rstest_invalid_hierarchy_error—src/lib.rstest_no_roles_user—src/lib.rstest_permission_format—src/lib.rstest_role_serialization—src/lib.rstest_role_slug_generation—src/lib.rstest_superuser_override—src/lib.rstest_user_role_serialization—src/lib.rstest_user_with_granted_permission_has_it—src/lib.rstest_user_without_granted_permission_lacks_it—src/lib.rsread_raw_composes_into_canonical_codename—src/permission.rstest_action_constants—src/permission.rstest_permission_action—src/permission.rstest_permission_from_module_action—src/permission.rstest_permission_invalid_format—src/permission.rstest_permission_matches—src/permission.rstest_permission_module—src/permission.rs- _… 47 more_
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 59 | 71 |
Public modules with a //! block | 6 | 7 |
pie showData
title Public items with rustdoc
"Documented" : 59
"No rustdoc detected" : 12
Metrics
| Metric | Value |
|---|---|
| Rust source files | 7 |
| Source lines | 1720 |
| Code lines | 1098 |
| Public API items | 71 |
| Public modules | 7 |
| Tests | 77 |
| Examples | 0 |
| Cargo features | 0 |
| Direct runtime dependencies | 7 |
| Workspace reverse dependencies | 2 |
pie showData
title Public API by kind
"constant" : 17
"enum" : 1
"function" : 2
"method" : 45
"struct" : 5
"trait" : 1
pie showData
title Rust source composition
"Code" : 1098
"Blank or comment" : 622
Generation
Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.