# miden::standards::access::authority
#
# Single source of truth for the account-wide authority. Components that gate state-mutating
# procedures (TokenPolicyManager `set_*_policy`, fungible token metadata `set_*` procedures,
# future NFT metadata setters, ...) all consult this slot via `assert_authorized`.

use miden::protocol::active_account
use miden::standards::access::ownable2step
use miden::standards::access::rbac

# CONSTANTS
# =================================================================================================

pub const AUTHORITY_SLOT = word("miden::standards::access::authority")

const AUTH_CONTROLLED = 0
const OWNER_CONTROLLED = 1
const RBAC_CONTROLLED = 2

# ERRORS
# =================================================================================================

const ERR_UNSUPPORTED_AUTHORITY = "authority is not supported"

# PUBLIC PROCEDURES
# =================================================================================================

#! Asserts the caller is authorized to invoke an authority-gated procedure.
#!
#! - AuthControlled (0)  → no-op (the account's auth component already gated the call).
#! - OwnerControlled (1) → calls `ownable2step::assert_sender_is_owner`.
#! - RbacControlled (2)  → loads the role symbol from the authority slot and calls
#!   `rbac::assert_sender_has_role`. Requires the
#!   [`RoleBasedAccessControl`][crate::account::access::RoleBasedAccessControl] component to be
#!   installed on the account; otherwise linking the account fails.
#!
#! Inputs:  []
#! Outputs: []
#!
#! Panics if:
#! - the authority is OwnerControlled and the sender is not the registered owner.
#! - the authority is RbacControlled and the sender does not hold the configured role.
#! - the authority is an unknown value.
#!
#! Invocation: exec
pub proc assert_authorized
    # Note: invariant — role_symbol felt is 0 unless authority == RBAC_CONTROLLED.
    # Enforced by `From<Authority> for Word` in Rust.
    push.AUTHORITY_SLOT[0..2] exec.active_account::get_item
    # => [authority, role_symbol, 0, 0]

    dup eq.AUTH_CONTROLLED
    if.true
        # AuthControlled — auth component already gated the call.
        dropw
        # => []
    else
        dup eq.OWNER_CONTROLLED
        if.true
            exec.ownable2step::assert_sender_is_owner
            # => [authority, role_symbol, 0, 0]
            dropw
            # => []
        else
            eq.RBAC_CONTROLLED
            if.true
                # => [role_symbol, 0, 0]
                exec.rbac::assert_sender_has_role
                # => [0, 0]
                drop drop
                # => []
            else
                # Unknown authority — panic. Stack state irrelevant.
                push.0 assert.err=ERR_UNSUPPORTED_AUTHORITY
            end
        end
    end
end
