From 8bb87f508a22a5984008ce7e65f6b458579f98a4 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 15 Mar 2023 12:08:53 +0100 Subject: [PATCH 1/2] feat(Acl): add `acl_add_super_admin` --- .../src/access_controllable.rs | 11 +++++++ near-plugins/src/access_controllable.rs | 33 ++++++++++++++++--- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/near-plugins-derive/src/access_controllable.rs b/near-plugins-derive/src/access_controllable.rs index 3569815..cd6c788 100644 --- a/near-plugins-derive/src/access_controllable.rs +++ b/near-plugins-derive/src/access_controllable.rs @@ -151,6 +151,13 @@ pub fn access_controllable(attrs: TokenStream, item: TokenStream) -> TokenStream res } + fn add_super_admin(&mut self, account_id: &::near_sdk::AccountId) -> Option { + if !self.is_super_admin(&::near_sdk::env::predecessor_account_id()) { + return None; + } + Some(self.add_super_admin_unchecked(account_id)) + } + /// Makes `account_id` a super-admin __without__ checking any permissions. /// It returns whether `account_id` is a new super-admin. /// @@ -563,6 +570,10 @@ pub fn access_controllable(attrs: TokenStream, item: TokenStream) -> TokenStream self.acl_get_or_init().init_super_admin(&account_id) } + fn acl_add_super_admin(&mut self, account_id: ::near_sdk::AccountId) -> Option { + self.acl_get_or_init().add_super_admin(&account_id) + } + fn acl_role_variants(&self) -> Vec<&'static str> { <#role_type>::acl_role_variants() } diff --git a/near-plugins/src/access_controllable.rs b/near-plugins/src/access_controllable.rs index d03918e..170c898 100644 --- a/near-plugins/src/access_controllable.rs +++ b/near-plugins/src/access_controllable.rs @@ -58,6 +58,9 @@ pub trait AccessControllable { /// It is `#[private]` in the implementation provided by this trait, i.e. /// only the contract itself may call this method. /// + /// Despite the restrictions of this method, it is possible to add multiple + /// super-admins using [`acl_add_super_admin`]. + /// /// If a super-admin is added, the following event will be emitted: /// /// ```json @@ -71,13 +74,33 @@ pub trait AccessControllable { /// } /// } /// ``` - /// - /// Despite the restrictions of this method, there might be multiple - /// super-admins. Adding more than one admin requires the use of internal - /// methods. The default implementation of `AccessControllable` provided by - /// this trait offers `add_super_admin_unchecked.` fn acl_init_super_admin(&mut self, account_id: AccountId) -> bool; + /// Adds `account_id` as super-admin provided that the predecessor has sufficient permissions, + /// i.e. is a super-admin as defined by [`acl_is_super_admin`]. To add the first super-admin, + /// [`acl_init_super_admin`] can be used. + /// + /// In case of sufficient permissions, the returned `Some(bool)` indicates whether `account_id` + /// is a new super-admin. Without permissions, `None` is returned and internal state is not + /// modified. + /// + /// Note that there may be multiple (or zero) super-admins. + /// + /// If a super-admin is added, the following event will be emitted: + /// + /// ```json + /// { + /// "standard":"AccessControllable", + /// "version":"1.0.0", + /// "event":"super_admin_added", + /// "data":{ + /// "account":"", + /// "by":"" + /// } + /// } + /// ``` + fn acl_add_super_admin(&mut self, account_id: AccountId) -> Option; + /// Returns whether `account_id` is a super-admin. A super-admin has admin /// permissions for every role. However, a super-admin is not considered /// grantee of any role. From 0856a76cf7528affcf97f763cfd4423385b3cf47 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 15 Mar 2023 12:47:52 +0100 Subject: [PATCH 2/2] Add test --- .../tests/access_controllable.rs | 41 +++++++++++++++++++ .../common/access_controllable_contract.rs | 18 ++++++++ 2 files changed, 59 insertions(+) diff --git a/near-plugins-derive/tests/access_controllable.rs b/near-plugins-derive/tests/access_controllable.rs index 87de2cf..aba6b3e 100644 --- a/near-plugins-derive/tests/access_controllable.rs +++ b/near-plugins-derive/tests/access_controllable.rs @@ -323,6 +323,47 @@ async fn test_acl_init_super_admin() -> anyhow::Result<()> { Ok(()) } +#[tokio::test] +async fn test_acl_add_super_admin() -> anyhow::Result<()> { + let setup = Setup::new().await?; + let to_be_super_admin = setup.worker.dev_create_account().await?; + + // Create accounts that add a super-admin. + let caller_unauth = setup.worker.dev_create_account().await?; + let caller_auth = setup.new_super_admin_account().await?; + + // Adding is a no-op if the caller is not a super-admin. + let res = setup + .contract + .acl_add_super_admin(&caller_unauth, to_be_super_admin.id()) + .await?; + assert_eq!(res, None); + setup + .contract + .assert_acl_is_super_admin(false, setup.contract_account(), to_be_super_admin.id()) + .await; + + // Adding succeeds if the caller is a super-admin. + let res = setup + .contract + .acl_add_super_admin(&caller_auth, to_be_super_admin.id()) + .await?; + assert_eq!(res, Some(true)); + setup + .contract + .assert_acl_is_super_admin(true, setup.contract_account(), to_be_super_admin.id()) + .await; + + // Adding an account which is already super-admin returns `Some(false)`. + let res = setup + .contract + .acl_add_super_admin(&caller_auth, to_be_super_admin.id()) + .await?; + assert_eq!(res, Some(false)); + + Ok(()) +} + #[tokio::test] async fn test_acl_add_super_admin_unchecked() -> anyhow::Result<()> { let Setup { diff --git a/near-plugins-derive/tests/common/access_controllable_contract.rs b/near-plugins-derive/tests/common/access_controllable_contract.rs index 670f4f7..4e52f26 100644 --- a/near-plugins-derive/tests/common/access_controllable_contract.rs +++ b/near-plugins-derive/tests/common/access_controllable_contract.rs @@ -67,6 +67,24 @@ impl AccessControllableContract { .await } + pub async fn acl_add_super_admin( + &self, + caller: &Account, + account_id: &AccountId, + ) -> anyhow::Result> { + let res = caller + .call(self.contract.id(), "acl_add_super_admin") + .args_json(json!({ + "account_id": account_id, + })) + .max_gas() + .transact() + .await? + .into_result()? + .json::>()?; + Ok(res) + } + pub async fn acl_add_super_admin_unchecked( &self, caller: &Account,