diff --git a/permissions_validators/src/private_blockchain/mod.rs b/permissions_validators/src/private_blockchain/mod.rs index 252f67691a5..2e4b0f4a5ca 100644 --- a/permissions_validators/src/private_blockchain/mod.rs +++ b/permissions_validators/src/private_blockchain/mod.rs @@ -38,3 +38,633 @@ impl IsGrantAllowed for ProhibitGrant { Err("Granting at runtime is prohibited.".to_owned()) } } + +#[cfg(test)] +mod tests { + #![allow(clippy::restriction)] + + use std::str::FromStr as _; + + use super::*; + + struct TestEnv { + // Alice's Id that owns Gold and Bronze assets. + alice_id: AccountId, + // Bob's Id that owns Silver asset. + bob_id: AccountId, + // Carol's Id that owns Bronze asset. + carol_id: AccountId, + // Gold asset's Id. + gold_asset_id: AssetId, + // Gold asset definition's Id. + gold_asset_definition_id: AssetDefinitionId, + // Silver asset's Id. + silver_asset_id: AssetId, + // Silver asset definition's Id. + silver_asset_definition_id: AssetDefinitionId, + // Bronze asset's Id. + bronze_asset_id: AssetId, + // Bronze asset definition's Id. + bronze_asset_definition_id: AssetDefinitionId, + // Wonderland is a domain that contains Alice and Bob + wonderland: (DomainId, Domain), + // Denoland is a domain that contains Carol + denoland: (DomainId, Domain), + // World state view contains wonderland and denoland domains. + wsv: WorldStateView, + } + + impl TestEnv { + fn new() -> Self { + let alice_id = AccountId::from_str("alice@wonderland").expect("Valid"); + let mut alice = Account::new(alice_id.clone(), []).build(); + + let gold_asset_definition_id = + AssetDefinitionId::from_str("gold#wonderland").expect("Valid"); + let gold_asset_id = AssetId::new(gold_asset_definition_id.clone(), alice_id.clone()); + let gold_asset = Asset::new(gold_asset_id.clone(), AssetValue::Quantity(100)); + + alice.add_asset(gold_asset); + + let bob_id = AccountId::from_str("bob@wonderland").expect("Valid"); + let mut bob = Account::new(bob_id.clone(), []).build(); + + let silver_asset_definition_id = + AssetDefinitionId::from_str("silver#wonderland").expect("Valid"); + let silver_asset_id = AssetId::new(silver_asset_definition_id.clone(), bob_id.clone()); + let silver_asset = Asset::new(silver_asset_id.clone(), AssetValue::Quantity(200)); + + bob.add_asset(silver_asset); + + let carol_id = AccountId::from_str("carol@denoland").expect("Valid"); + let mut carol = Account::new(carol_id.clone(), []).build(); + + let bronze_asset_definition_id = + AssetDefinitionId::from_str("bronze#denoland").expect("Valid"); + let bronze_asset_id = + AssetId::new(bronze_asset_definition_id.clone(), carol_id.clone()); + let bronze_asset = Asset::new(bronze_asset_id.clone(), AssetValue::Quantity(300)); + + carol.add_asset(bronze_asset.clone()); + + alice.add_asset(bronze_asset); + + let wonderland_id = DomainId::from_str("wonderland").expect("Valid"); + let mut wonderland = Domain::new(wonderland_id.clone()).build(); + + wonderland.add_account(alice); + + wonderland.add_account(bob); + + let denoland_id = DomainId::from_str("denoland").expect("Valid"); + let mut denoland = Domain::new(denoland_id.clone()).build(); + + denoland.add_account(carol); + + let wsv = WorldStateView::::new(World::with( + [wonderland.clone(), denoland.clone()], + Vec::new(), + )); + + Self { + alice_id, + bob_id, + carol_id, + gold_asset_id, + gold_asset_definition_id, + silver_asset_id, + silver_asset_definition_id, + bronze_asset_id, + bronze_asset_definition_id, + wonderland: (wonderland_id, wonderland), + denoland: (denoland_id, denoland), + wsv, + } + } + } + + mod queries { + use super::*; + + #[test] + fn find_all_accounts() { + let TestEnv { + alice_id, + bob_id, + carol_id, + wsv, + .. + } = TestEnv::new(); + + let op = QueryBox::FindAllAccounts(FindAllAccounts::new()); + + { + let only_accounts_domain: IsQueryAllowedBoxed = + query::OnlyAccountsDomain.into(); + + assert!(only_accounts_domain.check(&alice_id, &op, &wsv).is_err()); + assert!(only_accounts_domain.check(&bob_id, &op, &wsv).is_err()); + assert!(only_accounts_domain.check(&carol_id, &op, &wsv).is_err()); + } + + { + let only_accounts_data: IsQueryAllowedBoxed = query::OnlyAccountsData.into(); + + assert!(only_accounts_data.check(&alice_id, &op, &wsv).is_err()); + assert!(only_accounts_data.check(&bob_id, &op, &wsv).is_err()); + assert!(only_accounts_data.check(&carol_id, &op, &wsv).is_err()); + } + } + + #[test] + fn find_account_by_id() { + let TestEnv { + alice_id, + bob_id, + carol_id, + wsv, + .. + } = TestEnv::new(); + + let op = QueryBox::FindAccountById(FindAccountById::new(alice_id.clone())); + + { + let only_accounts_domain: IsQueryAllowedBoxed = + query::OnlyAccountsDomain.into(); + + assert!(only_accounts_domain.check(&alice_id, &op, &wsv).is_ok()); + assert!(only_accounts_domain.check(&bob_id, &op, &wsv).is_ok()); + assert!(only_accounts_domain.check(&carol_id, &op, &wsv).is_err()); + } + + { + let only_accounts_data: IsQueryAllowedBoxed = query::OnlyAccountsData.into(); + + assert!(only_accounts_data.check(&alice_id, &op, &wsv).is_ok()); + assert!(only_accounts_data.check(&bob_id, &op, &wsv).is_err()); + assert!(only_accounts_data.check(&carol_id, &op, &wsv).is_err()); + } + } + + #[test] + fn find_account_key_value_by_id_and_key() { + let TestEnv { + alice_id, + bob_id, + carol_id, + wsv, + .. + } = TestEnv::new(); + + let op = QueryBox::FindAccountKeyValueByIdAndKey(FindAccountKeyValueByIdAndKey::new( + alice_id.clone(), + "name".to_owned(), + )); + + { + let only_accounts_domain: IsQueryAllowedBoxed = + query::OnlyAccountsDomain.into(); + + assert!(only_accounts_domain.check(&alice_id, &op, &wsv).is_ok()); + assert!(only_accounts_domain.check(&bob_id, &op, &wsv).is_ok()); + assert!(only_accounts_domain.check(&carol_id, &op, &wsv).is_err()); + } + + { + let only_accounts_data: IsQueryAllowedBoxed = query::OnlyAccountsData.into(); + + assert!(only_accounts_data.check(&alice_id, &op, &wsv).is_ok()); + assert!(only_accounts_data.check(&bob_id, &op, &wsv).is_err()); + assert!(only_accounts_data.check(&carol_id, &op, &wsv).is_err()); + } + } + + #[test] + fn find_account_by_name() { + let TestEnv { + alice_id, + bob_id, + carol_id, + wsv, + .. + } = TestEnv::new(); + + let op = QueryBox::FindAccountsByName(FindAccountsByName::new(alice_id.clone())); + + { + let only_accounts_domain: IsQueryAllowedBoxed = + query::OnlyAccountsDomain.into(); + + assert!(only_accounts_domain.check(&alice_id, &op, &wsv).is_err()); + assert!(only_accounts_domain.check(&bob_id, &op, &wsv).is_err()); + assert!(only_accounts_domain.check(&carol_id, &op, &wsv).is_err()); + } + + { + let only_accounts_data: IsQueryAllowedBoxed = query::OnlyAccountsData.into(); + + assert!(only_accounts_data.check(&alice_id, &op, &wsv).is_err()); + assert!(only_accounts_data.check(&bob_id, &op, &wsv).is_err()); + assert!(only_accounts_data.check(&carol_id, &op, &wsv).is_err()); + } + } + + #[test] + fn find_accounts_by_domain_id() { + let TestEnv { + alice_id, + bob_id, + carol_id, + wonderland: (wonderland_id, _), + denoland: (second_domain_id, _), + wsv, + .. + } = TestEnv::new(); + + let find_by_first_domain = + QueryBox::FindAccountsByDomainId(FindAccountsByDomainId::new(wonderland_id)); + + { + let only_accounts_domain: IsQueryAllowedBoxed = + query::OnlyAccountsDomain.into(); + + assert!(only_accounts_domain + .check(&alice_id, &find_by_first_domain, &wsv) + .is_ok()); + assert!(only_accounts_domain + .check(&bob_id, &find_by_first_domain, &wsv) + .is_ok()); + assert!(only_accounts_domain + .check(&carol_id, &find_by_first_domain, &wsv) + .is_err()); + } + + { + let only_accounts_data: IsQueryAllowedBoxed = query::OnlyAccountsData.into(); + + assert!(only_accounts_data + .check(&alice_id, &find_by_first_domain, &wsv) + .is_err()); + assert!(only_accounts_data + .check(&bob_id, &find_by_first_domain, &wsv) + .is_err()); + assert!(only_accounts_data + .check(&carol_id, &find_by_first_domain, &wsv) + .is_err()); + } + + let find_by_second_domain = + QueryBox::FindAccountsByDomainId(FindAccountsByDomainId::new(second_domain_id)); + + { + let only_accounts_domain: IsQueryAllowedBoxed = + query::OnlyAccountsDomain.into(); + + assert!(only_accounts_domain + .check(&alice_id, &find_by_second_domain, &wsv) + .is_err()); + assert!(only_accounts_domain + .check(&bob_id, &find_by_second_domain, &wsv) + .is_err()); + assert!(only_accounts_domain + .check(&carol_id, &find_by_second_domain, &wsv) + .is_ok()); + } + + { + let only_accounts_data: IsQueryAllowedBoxed = query::OnlyAccountsData.into(); + + assert!(only_accounts_data + .check(&alice_id, &find_by_second_domain, &wsv) + .is_err()); + assert!(only_accounts_data + .check(&bob_id, &find_by_second_domain, &wsv) + .is_err()); + assert!(only_accounts_data + .check(&carol_id, &find_by_second_domain, &wsv) + .is_err()); + } + } + + #[test] + fn find_accounts_with_asset() { + let TestEnv { + alice_id, + bob_id, + carol_id, + wsv, + .. + } = TestEnv::new(); + + let op = QueryBox::FindAccountsWithAsset(FindAccountsWithAsset::new("xor".to_owned())); + + { + let only_accounts_domain: IsQueryAllowedBoxed = + query::OnlyAccountsDomain.into(); + + assert!(only_accounts_domain.check(&alice_id, &op, &wsv).is_err()); + assert!(only_accounts_domain.check(&bob_id, &op, &wsv).is_err()); + assert!(only_accounts_domain.check(&carol_id, &op, &wsv).is_err()); + } + + { + let only_accounts_data: IsQueryAllowedBoxed = query::OnlyAccountsData.into(); + + assert!(only_accounts_data.check(&alice_id, &op, &wsv).is_err()); + assert!(only_accounts_data.check(&bob_id, &op, &wsv).is_err()); + assert!(only_accounts_data.check(&carol_id, &op, &wsv).is_err()); + } + } + + #[test] + fn find_all_assets() { + let TestEnv { alice_id, wsv, .. } = TestEnv::new(); + + let op = QueryBox::FindAllAssets(FindAllAssets::new()); + + { + let only_accounts_domain: IsQueryAllowedBoxed = + query::OnlyAccountsDomain.into(); + + assert!(only_accounts_domain.check(&alice_id, &op, &wsv).is_err()); + } + + { + let only_accounts_data: IsQueryAllowedBoxed = query::OnlyAccountsData.into(); + + assert!(only_accounts_data.check(&alice_id, &op, &wsv).is_err()); + } + } + + #[test] + fn find_all_assets_definitions() { + let TestEnv { alice_id, wsv, .. } = TestEnv::new(); + + let op = QueryBox::FindAllAssetsDefinitions(FindAllAssetsDefinitions::new()); + + { + let only_accounts_domain: IsQueryAllowedBoxed = + query::OnlyAccountsDomain.into(); + + assert!(only_accounts_domain.check(&alice_id, &op, &wsv).is_err()); + } + + { + let only_accounts_data: IsQueryAllowedBoxed = query::OnlyAccountsData.into(); + + assert!(only_accounts_data.check(&alice_id, &op, &wsv).is_err()); + } + } + + #[test] + fn find_asset_by_id() { + let TestEnv { + alice_id, + carol_id, + wsv, + gold_asset_id, + silver_asset_id, + bronze_asset_id, + .. + } = TestEnv::new(); + + let find_gold = QueryBox::FindAssetById(FindAssetById::new(gold_asset_id)); + let find_silver = QueryBox::FindAssetById(FindAssetById::new(silver_asset_id)); + let find_bronze = QueryBox::FindAssetById(FindAssetById::new(bronze_asset_id)); + + { + let only_accounts_domain: IsQueryAllowedBoxed = + query::OnlyAccountsDomain.into(); + + assert!(only_accounts_domain + .check(&alice_id, &find_gold, &wsv) + .is_ok()); + assert!(only_accounts_domain + .check(&alice_id, &find_silver, &wsv) + .is_ok()); + assert!(only_accounts_domain + .check(&carol_id, &find_bronze, &wsv) + .is_ok()); + assert!(only_accounts_domain + .check(&alice_id, &find_bronze, &wsv) + .is_err()); + } + + { + let only_accounts_data: IsQueryAllowedBoxed = query::OnlyAccountsData.into(); + + assert!(only_accounts_data + .check(&alice_id, &find_gold, &wsv) + .is_ok()); + assert!(only_accounts_data + .check(&alice_id, &find_silver, &wsv) + .is_err()); + assert!(only_accounts_data + .check(&carol_id, &find_bronze, &wsv) + .is_ok()); + assert!(only_accounts_data + .check(&alice_id, &find_bronze, &wsv) + .is_err()); + } + } + + #[test] + fn find_asset_definition_by_id() { + let TestEnv { + alice_id, + carol_id, + wsv, + gold_asset_definition_id, + silver_asset_definition_id, + bronze_asset_definition_id, + .. + } = TestEnv::new(); + + let find_gold = QueryBox::FindAssetDefinitionById(FindAssetDefinitionById::new( + gold_asset_definition_id, + )); + let find_silver = QueryBox::FindAssetDefinitionById(FindAssetDefinitionById::new( + silver_asset_definition_id, + )); + let find_bronze = QueryBox::FindAssetDefinitionById(FindAssetDefinitionById::new( + bronze_asset_definition_id, + )); + + { + let only_accounts_domain: IsQueryAllowedBoxed = + query::OnlyAccountsDomain.into(); + + assert!(only_accounts_domain + .check(&alice_id, &find_gold, &wsv) + .is_ok()); + assert!(only_accounts_domain + .check(&alice_id, &find_silver, &wsv) + .is_ok()); + assert!(only_accounts_domain + .check(&carol_id, &find_bronze, &wsv) + .is_ok()); + assert!(only_accounts_domain + .check(&alice_id, &find_bronze, &wsv) + .is_err()); + } + + { + let only_accounts_data: IsQueryAllowedBoxed = query::OnlyAccountsData.into(); + + assert!(only_accounts_data + .check(&alice_id, &find_gold, &wsv) + .is_err()); + assert!(only_accounts_data + .check(&alice_id, &find_silver, &wsv) + .is_err()); + assert!(only_accounts_data + .check(&carol_id, &find_bronze, &wsv) + .is_err()); + assert!(only_accounts_data + .check(&alice_id, &find_bronze, &wsv) + .is_err()); + } + } + + #[test] + fn find_assets_by_name() { + let TestEnv { + alice_id, + carol_id, + wsv, + .. + } = TestEnv::new(); + + let find_gold = QueryBox::FindAssetsByName(FindAssetsByName::new( + Name::from_str("gold").expect("Valid"), + )); + let find_silver = QueryBox::FindAssetsByName(FindAssetsByName::new( + Name::from_str("silver").expect("Valid"), + )); + let find_bronze = QueryBox::FindAssetsByName(FindAssetsByName::new( + Name::from_str("bronze").expect("Valid"), + )); + + { + let only_accounts_domain: IsQueryAllowedBoxed = + query::OnlyAccountsDomain.into(); + + assert!(only_accounts_domain + .check(&alice_id, &find_gold, &wsv) + .is_err()); + assert!(only_accounts_domain + .check(&alice_id, &find_silver, &wsv) + .is_err()); + assert!(only_accounts_domain + .check(&carol_id, &find_bronze, &wsv) + .is_err()); + assert!(only_accounts_domain + .check(&alice_id, &find_bronze, &wsv) + .is_err()); + } + + { + let only_accounts_data: IsQueryAllowedBoxed = query::OnlyAccountsData.into(); + + assert!(only_accounts_data + .check(&alice_id, &find_gold, &wsv) + .is_err()); + assert!(only_accounts_data + .check(&alice_id, &find_silver, &wsv) + .is_err()); + assert!(only_accounts_data + .check(&carol_id, &find_bronze, &wsv) + .is_err()); + assert!(only_accounts_data + .check(&alice_id, &find_bronze, &wsv) + .is_err()); + } + } + } + + mod revoke_and_grant { + use super::*; + + #[test] + fn add_register_domains_permission_denies_registering_domain() { + let alice_id = AccountId::from_str("alice@test0").expect("Valid"); + + let instruction = Instruction::Register(RegisterBox::new(Domain::new( + "new_domain".parse().expect("Valid"), + ))); + + let wsv = WorldStateView::::new(World::new()); + + assert!(register::ProhibitRegisterDomains + .check(&alice_id, &instruction, &wsv) + .is_err()); + } + + #[test] + fn add_register_domains_permission_allows_registering_account() { + let alice_id = AccountId::from_str("alice@test0").expect("Valid"); + + let instruction = Instruction::Register(RegisterBox::new(Account::new( + "bob@test".parse().expect("Valid"), + [], + ))); + + let wsv = WorldStateView::::new(World::new()); + + assert!(register::ProhibitRegisterDomains + .check(&alice_id, &instruction, &wsv) + .is_ok()); + } + + #[test] + fn add_register_domains_permission_allows_registering_domain_with_right_token() { + let alice_id = AccountId::from_str("alice@test0").expect("Valid"); + + let mut alice = Account::new(alice_id.clone(), []).build(); + alice.add_permission(PermissionToken::new( + register::CAN_REGISTER_DOMAINS_TOKEN.clone(), + )); + + let domain_id = DomainId::from_str("test0").expect("Valid"); + let mut domain = Domain::new(domain_id).build(); + domain.add_account(alice.clone()); + + let wsv = WorldStateView::::new(World::with([domain], vec![])); + + let validator: IsInstructionAllowedBoxed = + register::GrantedAllowedRegisterDomains.into(); + + let op = Instruction::Register(RegisterBox::new(Domain::new( + "newdomain".parse().expect("Valid"), + ))); + + assert!(validator.check(&alice_id, &op, &wsv).is_ok()); + } + + #[test] + fn add_register_domains_permission_denies_registering_domain_with_right_token_with_wrong_token( + ) { + let alice_id = AccountId::from_str("alice@test0").expect("Valid"); + + let mut alice = Account::new(alice_id.clone(), []).build(); + alice.add_permission(PermissionToken::new( + Name::from_str("incorrecttoken").expect("Valid"), + )); + + let domain_id = DomainId::from_str("test0").expect("Valid"); + let mut domain = Domain::new(domain_id).build(); + domain.add_account(alice.clone()); + + let wsv = WorldStateView::::new(World::with([domain], vec![])); + + let validator: IsInstructionAllowedBoxed = + register::GrantedAllowedRegisterDomains.into(); + + let op = Instruction::Register(RegisterBox::new(Domain::new( + "newdomain".parse().expect("Valid"), + ))); + + assert!(validator.check(&alice_id, &op, &wsv).is_err()); + } + } +} diff --git a/permissions_validators/src/private_blockchain/register.rs b/permissions_validators/src/private_blockchain/register.rs index c52118d19b4..005afd1ac98 100644 --- a/permissions_validators/src/private_blockchain/register.rs +++ b/permissions_validators/src/private_blockchain/register.rs @@ -20,14 +20,19 @@ impl IsAllowed for ProhibitRegisterDomains { &self, _authority: &AccountId, instruction: &Instruction, - _wsv: &WorldStateView, + wsv: &WorldStateView, ) -> Result<(), DenialReason> { - let _register_box = if let Instruction::Register(register) = instruction { + let register = if let Instruction::Register(register) = instruction { register } else { return Ok(()); }; - Err("Domain registration is prohibited.".to_owned()) + + if let Ok(RegistrableBox::Domain(_)) = register.object.evaluate(wsv, &Context::new()) { + Err("Domain registration is prohibited.".to_owned()) + } else { + Ok(()) + } } }