diff --git a/client/tests/integration/permissions.rs b/client/tests/integration/permissions.rs index de6b435d9d2..f099f4f6628 100644 --- a/client/tests/integration/permissions.rs +++ b/client/tests/integration/permissions.rs @@ -399,7 +399,7 @@ fn associated_permission_tokens_removed_on_unregister() { &json!({ "domain_id": kingdom_id }), ); let allow_bob_to_set_kv_in_domain = - Grant::permission_token(bob_to_set_kv_in_domain_token.clone(), bob_id.clone()); + Grant::permission(bob_to_set_kv_in_domain_token.clone(), bob_id.clone()); iroha_client .submit_all_blocking([ diff --git a/configs/peer/executor.wasm b/configs/peer/executor.wasm index a7abc19985d..f53330f1e72 100644 Binary files a/configs/peer/executor.wasm and b/configs/peer/executor.wasm differ diff --git a/smart_contract/executor/src/default.rs b/smart_contract/executor/src/default.rs index 2c6e32c2932..50eed1b823e 100644 --- a/smart_contract/executor/src/default.rs +++ b/smart_contract/executor/src/default.rs @@ -173,7 +173,11 @@ pub mod peer { } pub mod domain { - use permission::{account::is_account_owner, domain::is_domain_owner}; + use iroha_smart_contract::data_model::{domain::DomainId, permission::PermissionToken}; + use permission::{ + account::is_account_owner, accounts_permission_tokens, domain::is_domain_owner, + }; + use tokens::AnyPermissionToken; use super::*; @@ -192,21 +196,28 @@ pub mod domain { ) { let domain_id = isi.object_id(); - if is_genesis(executor) { - execute!(executor, isi); - } - match is_domain_owner(domain_id, authority) { - Err(err) => deny!(executor, err), - Ok(true) => execute!(executor, isi), - Ok(false) => {} - } - let can_unregister_domain_token = tokens::domain::CanUnregisterDomain { - domain_id: domain_id.clone(), - }; - if can_unregister_domain_token.is_owned_by(authority) { + if is_genesis(executor) + || match is_domain_owner(domain_id, authority) { + Err(err) => deny!(executor, err), + Ok(is_domain_owner) => is_domain_owner, + } + || { + let can_unregister_domain_token = tokens::domain::CanUnregisterDomain { + domain_id: domain_id.clone(), + }; + can_unregister_domain_token.is_owned_by(authority) + } + { + for (owner_id, permission) in accounts_permission_tokens() { + if is_token_domain_associated(&permission, domain_id) { + let isi = Revoke::permission(permission, owner_id.clone()); + if let Err(_err) = isi.execute() { + deny!(executor, "Can't revoke associated permission token"); + } + } + } execute!(executor, isi); } - deny!(executor, "Can't unregister domain"); } @@ -284,10 +295,123 @@ pub mod domain { deny!(executor, "Can't remove key value in domain metadata"); } + + #[allow(clippy::too_many_lines)] + fn is_token_domain_associated(permission: &PermissionToken, domain_id: &DomainId) -> bool { + let Ok(permission) = AnyPermissionToken::try_from(permission.clone()) else { + return false; + }; + match permission { + AnyPermissionToken::CanUnregisterDomain(permission) => { + &permission.domain_id == domain_id + } + AnyPermissionToken::CanSetKeyValueInDomain(permission) => { + &permission.domain_id == domain_id + } + AnyPermissionToken::CanRemoveKeyValueInDomain(permission) => { + &permission.domain_id == domain_id + } + AnyPermissionToken::CanRegisterAccountInDomain(permission) => { + &permission.domain_id == domain_id + } + AnyPermissionToken::CanRegisterAssetDefinitionInDomain(permission) => { + &permission.domain_id == domain_id + } + AnyPermissionToken::CanUnregisterAssetDefinition(permission) => { + permission.asset_definition_id.domain_id() == domain_id + } + AnyPermissionToken::CanSetKeyValueInAssetDefinition(permission) => { + permission.asset_definition_id.domain_id() == domain_id + } + AnyPermissionToken::CanRemoveKeyValueInAssetDefinition(permission) => { + permission.asset_definition_id.domain_id() == domain_id + } + AnyPermissionToken::CanRegisterAssetWithDefinition(permission) => { + permission.asset_definition_id.domain_id() == domain_id + } + AnyPermissionToken::CanUnregisterAssetWithDefinition(permission) => { + permission.asset_definition_id.domain_id() == domain_id + } + AnyPermissionToken::CanBurnAssetWithDefinition(permission) => { + permission.asset_definition_id.domain_id() == domain_id + } + AnyPermissionToken::CanMintAssetWithDefinition(permission) => { + permission.asset_definition_id.domain_id() == domain_id + } + AnyPermissionToken::CanTransferAssetWithDefinition(permission) => { + permission.asset_definition_id.domain_id() == domain_id + } + AnyPermissionToken::CanBurnUserAsset(permission) => { + permission.asset_id.definition_id().domain_id() == domain_id + || permission.asset_id.account_id().domain_id() == domain_id + } + AnyPermissionToken::CanTransferUserAsset(permission) => { + permission.asset_id.definition_id().domain_id() == domain_id + || permission.asset_id.account_id().domain_id() == domain_id + } + AnyPermissionToken::CanUnregisterUserAsset(permission) => { + permission.asset_id.definition_id().domain_id() == domain_id + || permission.asset_id.account_id().domain_id() == domain_id + } + AnyPermissionToken::CanSetKeyValueInUserAsset(permission) => { + permission.asset_id.definition_id().domain_id() == domain_id + || permission.asset_id.account_id().domain_id() == domain_id + } + AnyPermissionToken::CanRemoveKeyValueInUserAsset(permission) => { + permission.asset_id.definition_id().domain_id() == domain_id + || permission.asset_id.account_id().domain_id() == domain_id + } + AnyPermissionToken::CanMintUserAsset(permission) => { + permission.asset_id.definition_id().domain_id() == domain_id + || permission.asset_id.account_id().domain_id() == domain_id + } + AnyPermissionToken::CanUnregisterAccount(permission) => { + permission.account_id.domain_id() == domain_id + } + AnyPermissionToken::CanMintUserPublicKeys(permission) => { + permission.account_id.domain_id() == domain_id + } + AnyPermissionToken::CanBurnUserPublicKeys(permission) => { + permission.account_id.domain_id() == domain_id + } + AnyPermissionToken::CanMintUserSignatureCheckConditions(permission) => { + permission.account_id.domain_id() == domain_id + } + AnyPermissionToken::CanSetKeyValueInUserAccount(permission) => { + permission.account_id.domain_id() == domain_id + } + AnyPermissionToken::CanRemoveKeyValueInUserAccount(permission) => { + permission.account_id.domain_id() == domain_id + } + AnyPermissionToken::CanUnregisterUserTrigger(permission) => { + permission.trigger_id.domain_id().as_ref() == Some(domain_id) + } + AnyPermissionToken::CanExecuteUserTrigger(permission) => { + permission.trigger_id.domain_id().as_ref() == Some(domain_id) + } + AnyPermissionToken::CanBurnUserTrigger(permission) => { + permission.trigger_id.domain_id().as_ref() == Some(domain_id) + } + AnyPermissionToken::CanMintUserTrigger(permission) => { + permission.trigger_id.domain_id().as_ref() == Some(domain_id) + } + AnyPermissionToken::CanUnregisterAnyPeer(_) + | AnyPermissionToken::CanGrantPermissionToCreateParameters(_) + | AnyPermissionToken::CanRevokePermissionToCreateParameters(_) + | AnyPermissionToken::CanCreateParameters(_) + | AnyPermissionToken::CanGrantPermissionToSetParameters(_) + | AnyPermissionToken::CanRevokePermissionToSetParameters(_) + | AnyPermissionToken::CanSetParameters(_) + | AnyPermissionToken::CanUnregisterAnyRole(_) + | AnyPermissionToken::CanUpgradeExecutor(_) => false, + } + } } pub mod account { - use permission::account::is_account_owner; + use iroha_smart_contract::data_model::permission::PermissionToken; + use permission::{account::is_account_owner, accounts_permission_tokens}; + use tokens::AnyPermissionToken; use super::*; @@ -324,21 +448,28 @@ pub mod account { ) { let account_id = isi.object_id(); - if is_genesis(executor) { - execute!(executor, isi); - } - match is_account_owner(account_id, authority) { - Err(err) => deny!(executor, err), - Ok(true) => execute!(executor, isi), - Ok(false) => {} - } - let can_unregister_user_account = tokens::account::CanUnregisterAccount { - account_id: account_id.clone(), - }; - if can_unregister_user_account.is_owned_by(authority) { + if is_genesis(executor) + || match is_account_owner(account_id, authority) { + Err(err) => deny!(executor, err), + Ok(is_account_owner) => is_account_owner, + } + || { + let can_unregister_user_account = tokens::account::CanUnregisterAccount { + account_id: account_id.clone(), + }; + can_unregister_user_account.is_owned_by(authority) + } + { + for (owner_id, permission) in accounts_permission_tokens() { + if is_token_account_associated(&permission, account_id) { + let isi = Revoke::permission(permission, owner_id.clone()); + if let Err(_err) = isi.execute() { + deny!(executor, "Can't revoke associated permission token"); + } + } + } execute!(executor, isi); } - deny!(executor, "Can't unregister another account"); } @@ -478,10 +609,85 @@ pub mod account { "Can't remove value from the metadata of another account" ); } + + fn is_token_account_associated(permission: &PermissionToken, account_id: &AccountId) -> bool { + let Ok(permission) = AnyPermissionToken::try_from(permission.clone()) else { + return false; + }; + match permission { + AnyPermissionToken::CanUnregisterAccount(permission) => { + &permission.account_id == account_id + } + AnyPermissionToken::CanMintUserPublicKeys(permission) => { + &permission.account_id == account_id + } + AnyPermissionToken::CanBurnUserPublicKeys(permission) => { + &permission.account_id == account_id + } + AnyPermissionToken::CanMintUserSignatureCheckConditions(permission) => { + &permission.account_id == account_id + } + AnyPermissionToken::CanSetKeyValueInUserAccount(permission) => { + &permission.account_id == account_id + } + AnyPermissionToken::CanRemoveKeyValueInUserAccount(permission) => { + &permission.account_id == account_id + } + AnyPermissionToken::CanBurnUserAsset(permission) => { + permission.asset_id.account_id() == account_id + } + AnyPermissionToken::CanTransferUserAsset(permission) => { + permission.asset_id.account_id() == account_id + } + AnyPermissionToken::CanUnregisterUserAsset(permission) => { + permission.asset_id.account_id() == account_id + } + AnyPermissionToken::CanSetKeyValueInUserAsset(permission) => { + permission.asset_id.account_id() == account_id + } + AnyPermissionToken::CanRemoveKeyValueInUserAsset(permission) => { + permission.asset_id.account_id() == account_id + } + AnyPermissionToken::CanMintUserAsset(permission) => { + permission.asset_id.account_id() == account_id + } + AnyPermissionToken::CanUnregisterUserTrigger(_) + | AnyPermissionToken::CanExecuteUserTrigger(_) + | AnyPermissionToken::CanBurnUserTrigger(_) + | AnyPermissionToken::CanMintUserTrigger(_) + | AnyPermissionToken::CanUnregisterAnyPeer(_) + | AnyPermissionToken::CanUnregisterDomain(_) + | AnyPermissionToken::CanSetKeyValueInDomain(_) + | AnyPermissionToken::CanRemoveKeyValueInDomain(_) + | AnyPermissionToken::CanRegisterAccountInDomain(_) + | AnyPermissionToken::CanRegisterAssetDefinitionInDomain(_) + | AnyPermissionToken::CanUnregisterAssetDefinition(_) + | AnyPermissionToken::CanSetKeyValueInAssetDefinition(_) + | AnyPermissionToken::CanRemoveKeyValueInAssetDefinition(_) + | AnyPermissionToken::CanRegisterAssetWithDefinition(_) + | AnyPermissionToken::CanUnregisterAssetWithDefinition(_) + | AnyPermissionToken::CanBurnAssetWithDefinition(_) + | AnyPermissionToken::CanMintAssetWithDefinition(_) + | AnyPermissionToken::CanTransferAssetWithDefinition(_) + | AnyPermissionToken::CanGrantPermissionToCreateParameters(_) + | AnyPermissionToken::CanRevokePermissionToCreateParameters(_) + | AnyPermissionToken::CanCreateParameters(_) + | AnyPermissionToken::CanGrantPermissionToSetParameters(_) + | AnyPermissionToken::CanRevokePermissionToSetParameters(_) + | AnyPermissionToken::CanSetParameters(_) + | AnyPermissionToken::CanUnregisterAnyRole(_) + | AnyPermissionToken::CanUpgradeExecutor(_) => false, + } + } } pub mod asset_definition { - use permission::{account::is_account_owner, asset_definition::is_asset_definition_owner}; + use iroha_smart_contract::data_model::{asset::AssetDefinitionId, permission::PermissionToken}; + use permission::{ + account::is_account_owner, accounts_permission_tokens, + asset_definition::is_asset_definition_owner, + }; + use tokens::AnyPermissionToken; use super::*; @@ -519,22 +725,29 @@ pub mod asset_definition { ) { let asset_definition_id = isi.object_id(); - if is_genesis(executor) { - execute!(executor, isi); - } - match is_asset_definition_owner(asset_definition_id, authority) { - Err(err) => deny!(executor, err), - Ok(true) => execute!(executor, isi), - Ok(false) => {} - } - let can_unregister_asset_definition_token = - tokens::asset_definition::CanUnregisterAssetDefinition { - asset_definition_id: asset_definition_id.clone(), - }; - if can_unregister_asset_definition_token.is_owned_by(authority) { + if is_genesis(executor) + || match is_asset_definition_owner(asset_definition_id, authority) { + Err(err) => deny!(executor, err), + Ok(is_asset_definition_owner) => is_asset_definition_owner, + } + || { + let can_unregister_asset_definition_token = + tokens::asset_definition::CanUnregisterAssetDefinition { + asset_definition_id: asset_definition_id.clone(), + }; + can_unregister_asset_definition_token.is_owned_by(authority) + } + { + for (owner_id, permission) in accounts_permission_tokens() { + if is_token_asset_definition_associated(&permission, asset_definition_id) { + let isi = Revoke::permission(permission, owner_id.clone()); + if let Err(_err) = isi.execute() { + deny!(executor, "Can't revoke associated permission token"); + } + } + } execute!(executor, isi); } - deny!( executor, "Can't unregister asset definition in a domain owned by another account" @@ -626,6 +839,83 @@ pub mod asset_definition { "Can't remove value from the asset definition metadata created by another account" ); } + + fn is_token_asset_definition_associated( + permission: &PermissionToken, + asset_definition_id: &AssetDefinitionId, + ) -> bool { + let Ok(permission) = AnyPermissionToken::try_from(permission.clone()) else { + return false; + }; + match permission { + AnyPermissionToken::CanUnregisterAssetDefinition(permission) => { + &permission.asset_definition_id == asset_definition_id + } + AnyPermissionToken::CanSetKeyValueInAssetDefinition(permission) => { + &permission.asset_definition_id == asset_definition_id + } + AnyPermissionToken::CanRemoveKeyValueInAssetDefinition(permission) => { + &permission.asset_definition_id == asset_definition_id + } + AnyPermissionToken::CanRegisterAssetWithDefinition(permission) => { + &permission.asset_definition_id == asset_definition_id + } + AnyPermissionToken::CanUnregisterAssetWithDefinition(permission) => { + &permission.asset_definition_id == asset_definition_id + } + AnyPermissionToken::CanBurnAssetWithDefinition(permission) => { + &permission.asset_definition_id == asset_definition_id + } + AnyPermissionToken::CanMintAssetWithDefinition(permission) => { + &permission.asset_definition_id == asset_definition_id + } + AnyPermissionToken::CanTransferAssetWithDefinition(permission) => { + &permission.asset_definition_id == asset_definition_id + } + AnyPermissionToken::CanBurnUserAsset(permission) => { + permission.asset_id.definition_id() == asset_definition_id + } + AnyPermissionToken::CanTransferUserAsset(permission) => { + permission.asset_id.definition_id() == asset_definition_id + } + AnyPermissionToken::CanUnregisterUserAsset(permission) => { + permission.asset_id.definition_id() == asset_definition_id + } + AnyPermissionToken::CanSetKeyValueInUserAsset(permission) => { + permission.asset_id.definition_id() == asset_definition_id + } + AnyPermissionToken::CanRemoveKeyValueInUserAsset(permission) => { + permission.asset_id.definition_id() == asset_definition_id + } + AnyPermissionToken::CanMintUserAsset(permission) => { + permission.asset_id.definition_id() == asset_definition_id + } + AnyPermissionToken::CanUnregisterAccount(_) + | AnyPermissionToken::CanMintUserPublicKeys(_) + | AnyPermissionToken::CanBurnUserPublicKeys(_) + | AnyPermissionToken::CanMintUserSignatureCheckConditions(_) + | AnyPermissionToken::CanSetKeyValueInUserAccount(_) + | AnyPermissionToken::CanRemoveKeyValueInUserAccount(_) + | AnyPermissionToken::CanUnregisterUserTrigger(_) + | AnyPermissionToken::CanExecuteUserTrigger(_) + | AnyPermissionToken::CanBurnUserTrigger(_) + | AnyPermissionToken::CanMintUserTrigger(_) + | AnyPermissionToken::CanUnregisterAnyPeer(_) + | AnyPermissionToken::CanUnregisterDomain(_) + | AnyPermissionToken::CanSetKeyValueInDomain(_) + | AnyPermissionToken::CanRemoveKeyValueInDomain(_) + | AnyPermissionToken::CanRegisterAccountInDomain(_) + | AnyPermissionToken::CanRegisterAssetDefinitionInDomain(_) + | AnyPermissionToken::CanGrantPermissionToCreateParameters(_) + | AnyPermissionToken::CanRevokePermissionToCreateParameters(_) + | AnyPermissionToken::CanCreateParameters(_) + | AnyPermissionToken::CanGrantPermissionToSetParameters(_) + | AnyPermissionToken::CanRevokePermissionToSetParameters(_) + | AnyPermissionToken::CanSetParameters(_) + | AnyPermissionToken::CanUnregisterAnyRole(_) + | AnyPermissionToken::CanUpgradeExecutor(_) => false, + } + } } pub mod asset { @@ -1102,7 +1392,9 @@ pub mod role { } pub mod trigger { - use permission::trigger::is_trigger_owner; + use iroha_smart_contract::data_model::{permission::PermissionToken, trigger::TriggerId}; + use permission::{accounts_permission_tokens, trigger::is_trigger_owner}; + use tokens::AnyPermissionToken; use super::*; @@ -1121,21 +1413,28 @@ pub mod trigger { ) { let trigger_id = isi.object_id(); - if is_genesis(executor) { - execute!(executor, isi); - } - match is_trigger_owner(trigger_id, authority) { - Err(err) => deny!(executor, err), - Ok(true) => execute!(executor, isi), - Ok(false) => {} - } - let can_unregister_user_trigger_token = tokens::trigger::CanUnregisterUserTrigger { - trigger_id: trigger_id.clone(), - }; - if can_unregister_user_trigger_token.is_owned_by(authority) { + if is_genesis(executor) + || match is_trigger_owner(trigger_id, authority) { + Err(err) => deny!(executor, err), + Ok(is_trigger_owner) => is_trigger_owner, + } + || { + let can_unregister_user_trigger_token = tokens::trigger::CanUnregisterUserTrigger { + trigger_id: trigger_id.clone(), + }; + can_unregister_user_trigger_token.is_owned_by(authority) + } + { + for (owner_id, permission) in accounts_permission_tokens() { + if is_token_trigger_associated(&permission, trigger_id) { + let isi = Revoke::permission(permission, owner_id.clone()); + if let Err(_err) = isi.execute() { + deny!(executor, "Can't revoke associated permission token"); + } + } + } execute!(executor, isi); } - deny!( executor, "Can't unregister trigger owned by another account" @@ -1222,6 +1521,60 @@ pub mod trigger { deny!(executor, "Can't execute trigger owned by another account"); } + + fn is_token_trigger_associated(permission: &PermissionToken, trigger_id: &TriggerId) -> bool { + let Ok(permission) = AnyPermissionToken::try_from(permission.clone()) else { + return false; + }; + match permission { + AnyPermissionToken::CanUnregisterUserTrigger(permission) => { + &permission.trigger_id == trigger_id + } + AnyPermissionToken::CanExecuteUserTrigger(permission) => { + &permission.trigger_id == trigger_id + } + AnyPermissionToken::CanBurnUserTrigger(permission) => { + &permission.trigger_id == trigger_id + } + AnyPermissionToken::CanMintUserTrigger(permission) => { + &permission.trigger_id == trigger_id + } + AnyPermissionToken::CanUnregisterAnyPeer(_) + | AnyPermissionToken::CanUnregisterDomain(_) + | AnyPermissionToken::CanSetKeyValueInDomain(_) + | AnyPermissionToken::CanRemoveKeyValueInDomain(_) + | AnyPermissionToken::CanRegisterAccountInDomain(_) + | AnyPermissionToken::CanRegisterAssetDefinitionInDomain(_) + | AnyPermissionToken::CanUnregisterAccount(_) + | AnyPermissionToken::CanMintUserPublicKeys(_) + | AnyPermissionToken::CanBurnUserPublicKeys(_) + | AnyPermissionToken::CanMintUserSignatureCheckConditions(_) + | AnyPermissionToken::CanSetKeyValueInUserAccount(_) + | AnyPermissionToken::CanRemoveKeyValueInUserAccount(_) + | AnyPermissionToken::CanUnregisterAssetDefinition(_) + | AnyPermissionToken::CanSetKeyValueInAssetDefinition(_) + | AnyPermissionToken::CanRemoveKeyValueInAssetDefinition(_) + | AnyPermissionToken::CanRegisterAssetWithDefinition(_) + | AnyPermissionToken::CanUnregisterAssetWithDefinition(_) + | AnyPermissionToken::CanUnregisterUserAsset(_) + | AnyPermissionToken::CanBurnAssetWithDefinition(_) + | AnyPermissionToken::CanBurnUserAsset(_) + | AnyPermissionToken::CanMintAssetWithDefinition(_) + | AnyPermissionToken::CanTransferAssetWithDefinition(_) + | AnyPermissionToken::CanTransferUserAsset(_) + | AnyPermissionToken::CanSetKeyValueInUserAsset(_) + | AnyPermissionToken::CanRemoveKeyValueInUserAsset(_) + | AnyPermissionToken::CanMintUserAsset(_) + | AnyPermissionToken::CanGrantPermissionToCreateParameters(_) + | AnyPermissionToken::CanRevokePermissionToCreateParameters(_) + | AnyPermissionToken::CanCreateParameters(_) + | AnyPermissionToken::CanGrantPermissionToSetParameters(_) + | AnyPermissionToken::CanRevokePermissionToSetParameters(_) + | AnyPermissionToken::CanSetParameters(_) + | AnyPermissionToken::CanUnregisterAnyRole(_) + | AnyPermissionToken::CanUpgradeExecutor(_) => false, + } + } } pub mod permission_token { diff --git a/smart_contract/executor/src/default/tokens.rs b/smart_contract/executor/src/default/tokens.rs index 3deb532f5eb..7f7a7f67f88 100644 --- a/smart_contract/executor/src/default/tokens.rs +++ b/smart_contract/executor/src/default/tokens.rs @@ -49,6 +49,28 @@ macro_rules! declare_tokens { }; } + /// Enum with every default token + #[allow(clippy::enum_variant_names)] + pub(crate) enum AnyPermissionToken { + $( + $token_ty($($token_path::)+$token_ty), + )* + } + + impl TryFrom<::iroha_smart_contract::data_model::permission::PermissionToken> for AnyPermissionToken { + type Error = $crate::permission::PermissionTokenConversionError; + + fn try_from(token: ::iroha_smart_contract::data_model::permission::PermissionToken) -> Result { + match token.definition_id().as_ref() { $( + stringify!($token_ty) => { + let token = <$($token_path::)+$token_ty>::try_from(token)?; + Ok(Self::$token_ty(token)) + } )+ + _ => Err(Self::Error::Id(token.definition_id().clone())) + } + } + } + pub(crate) use map_token; pub(crate) use map_token_type; }; diff --git a/smart_contract/executor/src/permission.rs b/smart_contract/executor/src/permission.rs index e08040fe76a..d733d0c57c9 100644 --- a/smart_contract/executor/src/permission.rs +++ b/smart_contract/executor/src/permission.rs @@ -3,7 +3,7 @@ use alloc::borrow::ToOwned as _; use iroha_schema::IntoSchema; -use iroha_smart_contract::QueryOutputCursor; +use iroha_smart_contract::{data_model::permission::PermissionToken, QueryOutputCursor}; use iroha_smart_contract_utils::debug::DebugExpectExt as _; use serde::{de::DeserializeOwned, Serialize}; @@ -338,3 +338,20 @@ impl From<&T> for OnlyGenesis { Self } } + +/// Iterator over all accounts and theirs permission tokens +pub(crate) fn accounts_permission_tokens() -> impl Iterator { + FindAllAccounts + .execute() + .dbg_expect("failed to query all accounts") + .into_iter() + .map(|account| account.dbg_expect("failed to retrieve account")) + .flat_map(|account| { + FindPermissionTokensByAccountId::new(account.id().clone()) + .execute() + .dbg_expect("failed to query permssion token for account") + .into_iter() + .map(|token| token.dbg_expect("failed to retrieve permission token")) + .map(move |token| (account.id().clone(), token)) + }) +}