use alloc::vec;
use miden_protocol::account::component::{
AccountComponentCode,
AccountComponentMetadata,
SchemaType,
StorageSchema,
StorageSlotSchema,
};
use miden_protocol::account::{
AccountComponent,
AccountComponentName,
StorageMap,
StorageSlot,
StorageSlotName,
};
use miden_protocol::utils::sync::LazyLock;
use crate::account::account_component_code;
account_component_code!(RBAC_CODE, "access/rbac.masl");
static ROLE_CONFIG_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
StorageSlotName::new("miden::standards::access::rbac::role_config")
.expect("storage slot name should be valid")
});
static ROLE_MEMBERSHIP_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
StorageSlotName::new("miden::standards::access::rbac::role_membership")
.expect("storage slot name should be valid")
});
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct RoleBasedAccessControl;
impl RoleBasedAccessControl {
pub const NAME: &'static str = "miden::standards::components::access::rbac";
pub const fn name() -> AccountComponentName {
AccountComponentName::from_static_str(Self::NAME)
}
pub fn code() -> &'static AccountComponentCode {
&RBAC_CODE
}
pub fn empty() -> Self {
Self
}
pub fn role_config_slot() -> &'static StorageSlotName {
&ROLE_CONFIG_SLOT_NAME
}
pub fn role_membership_slot() -> &'static StorageSlotName {
&ROLE_MEMBERSHIP_SLOT_NAME
}
pub fn role_config_slot_schema() -> (StorageSlotName, StorageSlotSchema) {
(
Self::role_config_slot().clone(),
StorageSlotSchema::map(
"Per-role RBAC configuration (member count and delegated admin role)",
SchemaType::role_symbol(),
SchemaType::native_word(),
),
)
}
pub fn role_membership_slot_schema() -> (StorageSlotName, StorageSlotSchema) {
(
Self::role_membership_slot().clone(),
StorageSlotSchema::map(
"Role membership flag indexed by role symbol and account ID",
SchemaType::native_word(),
SchemaType::native_word(),
),
)
}
pub fn component_metadata() -> AccountComponentMetadata {
let storage_schema = StorageSchema::new(vec![
Self::role_config_slot_schema(),
Self::role_membership_slot_schema(),
])
.expect("storage schema should be valid");
AccountComponentMetadata::new(Self::NAME)
.with_description("Role-based access control component")
.with_storage_schema(storage_schema)
}
}
impl From<RoleBasedAccessControl> for AccountComponent {
fn from(_rbac: RoleBasedAccessControl) -> Self {
let role_config_slot = StorageSlot::with_map(
RoleBasedAccessControl::role_config_slot().clone(),
StorageMap::with_entries(vec![]).expect("empty role config map should be valid"),
);
let role_membership_slot = StorageSlot::with_map(
RoleBasedAccessControl::role_membership_slot().clone(),
StorageMap::with_entries(vec![]).expect("empty role membership map should be valid"),
);
AccountComponent::new(
RoleBasedAccessControl::code().clone(),
vec![role_config_slot, role_membership_slot],
RoleBasedAccessControl::component_metadata(),
)
.expect("RBAC component should satisfy the requirements of a valid account component")
}
}