diff --git a/near-plugins-derive/src/access_controllable.rs b/near-plugins-derive/src/access_controllable.rs index 2088ebb..18202b3 100644 --- a/near-plugins-derive/src/access_controllable.rs +++ b/near-plugins-derive/src/access_controllable.rs @@ -429,6 +429,11 @@ pub fn access_controllable(attrs: TokenStream, item: TokenStream) -> TokenStream (#storage_prefix).as_bytes() } + #[private] + fn acl_init_super_admin(&mut self, account_id: ::near_sdk::AccountId) -> bool { + self.#acl_field.init_super_admin(&account_id) + } + fn acl_is_super_admin(&self, account_id: ::near_sdk::AccountId) -> bool { self.#acl_field.is_super_admin(&account_id) } diff --git a/near-plugins/src/access_controllable.rs b/near-plugins/src/access_controllable.rs index efc358c..e788b5b 100644 --- a/near-plugins/src/access_controllable.rs +++ b/near-plugins/src/access_controllable.rs @@ -19,6 +19,18 @@ pub trait AccessControllable { /// Returns the storage prefix for collections related to access control. fn acl_storage_prefix() -> &'static [u8]; + /// Adds `account_id` as super-admin __without__ checking any permissions in + /// case there are no super-admins. This function can be used to add a + /// super-admin during contract initialization. Moreover, it may provide a + /// recovery mechanism if (mistakenly) all super-admins have been removed. + /// + /// The return value indicates whether `account_id` was added as + /// super-admin. + /// + /// It is `#[private]` in the implementation provided by this trait, i.e. + /// only the contract itself may call this method. + fn acl_init_super_admin(&mut self, account_id: AccountId) -> bool; + /// Returns whether `account_id` is a super-admin. fn acl_is_super_admin(&self, account_id: AccountId) -> bool; diff --git a/near-plugins/tests/contracts/access_controllable/src/lib.rs b/near-plugins/tests/contracts/access_controllable/src/lib.rs index 394222f..63d29a5 100644 --- a/near-plugins/tests/contracts/access_controllable/src/lib.rs +++ b/near-plugins/tests/contracts/access_controllable/src/lib.rs @@ -23,25 +23,22 @@ pub struct StatusMessage { #[near_bindgen] impl StatusMessage { - // Initially adding (super-)admins is done via internal methods, for - // example: - // - // ``` - // self.__acl.add_super_admin_unchecked(account_id); - // self.__acl.add_admin_unchecked(role, account_id); - // ``` - // - // **Attention**: Contracts should call `__acl.*_unchecked` methods only - // from within methods with attribute `#[init]` or `#[private]`. + // Adding an initial super-admin can be done via trait method + // `AccessControllable::acl_init_super_admin`, which is automatically + // implemented and exported for the contract by `#[access_controllable]`. // // Once an account is (super-)admin, it may add other admins and grant // roles. // - // If needed, It's also possible to grant a role without checks: + // In addition, there are internal `*_unchecked` methods for example: // // ``` + // self.__acl.add_admin_unchecked(role, account_id); // self.__acl.grant_role_unchecked(role, account_id); // ``` + // + // **Attention**: Contracts should call `__acl.*_unchecked` methods only + // from within methods with attribute `#[init]` or `#[private]`. #[payable] pub fn set_status(&mut self, message: String) { @@ -75,11 +72,6 @@ impl StatusMessage { /// Exposing internal methods to facilitate integration testing. #[near_bindgen] impl StatusMessage { - #[private] - pub fn acl_init_super_admin(&mut self, account_id: ::near_sdk::AccountId) -> bool { - self.__acl.init_super_admin(&account_id) - } - #[private] pub fn acl_add_super_admin_unchecked(&mut self, account_id: AccountId) -> bool { self.__acl.add_super_admin_unchecked(&account_id)