Skip to content

Commit 35635bc

Browse files
committed
ACL: don't panic if acl isn't initialized
1 parent 1c504a7 commit 35635bc

File tree

4 files changed

+62
-38
lines changed

4 files changed

+62
-38
lines changed

near-plugins-derive/src/access_controllable.rs

+52-23
Original file line numberDiff line numberDiff line change
@@ -108,17 +108,18 @@ pub fn access_controllable(attrs: TokenStream, item: TokenStream) -> TokenStream
108108
})
109109
}
110110

111-
fn acl_get(&self) -> #acl_type {
112-
self.acl_get_storage().unwrap_or_else(|| ::near_sdk::env::panic_str("ACL: storage isn't initialized"))
111+
fn acl_get_or_init(&self) -> #acl_type {
112+
self.acl_get_storage().unwrap_or_else(|| self.acl_init_storage_unchecked())
113113
}
114114

115-
fn acl_init_storage_unchecked(&self) {
115+
fn acl_init_storage_unchecked(&self) -> #acl_type {
116116
let base_prefix = <#ident as AccessControllable>::acl_storage_prefix();
117117
let acl_storage: #acl_type = Default::default();
118118
near_sdk::env::storage_write(
119119
&__acl_storage_prefix(base_prefix, __AclStorageKey::AclStorage),
120120
&acl_storage.try_to_vec().unwrap(),
121121
);
122+
acl_storage
122123
}
123124
}
124125

@@ -496,6 +497,39 @@ pub fn access_controllable(attrs: TokenStream, item: TokenStream) -> TokenStream
496497
}
497498
}
498499

500+
fn get_default_permissioned_accounts() -> #cratename::access_controllable::PermissionedAccounts {
501+
let roles = <#role_type>::acl_role_variants();
502+
let mut map = ::std::collections::HashMap::new();
503+
for role in roles {
504+
let role: #role_type = ::std::convert::TryFrom::try_from(role)
505+
.unwrap_or_else(|_| ::near_sdk::env::panic_str(#ERR_PARSE_ROLE));
506+
507+
map.insert(
508+
role.into(),
509+
#cratename::access_controllable::PermissionedAccountsPerRole {
510+
admins: Default::default(),
511+
grantees: Default::default(),
512+
}
513+
);
514+
}
515+
516+
#cratename::access_controllable::PermissionedAccounts {
517+
super_admins: Default::default(),
518+
roles: map,
519+
}
520+
}
521+
522+
macro_rules! return_if_none {
523+
($res:expr, $default_value:expr) => {
524+
match $res {
525+
Some(val) => val,
526+
None => {
527+
return $default_value;
528+
}
529+
}
530+
};
531+
}
532+
499533
// Note that `#[near-bindgen]` exposes non-public functions in trait
500534
// implementations. This is [documented] behavior. Therefore some
501535
// functions are made `#[private]` despite _not_ being public.
@@ -507,63 +541,58 @@ pub fn access_controllable(attrs: TokenStream, item: TokenStream) -> TokenStream
507541
(#storage_prefix).as_bytes()
508542
}
509543

510-
fn acl_init_storage(&self) {
511-
::near_sdk::require!(self.acl_get_storage().is_none(), "ACL: storage was already initialized");
512-
self.acl_init_storage_unchecked();
513-
}
514-
515544
#[private]
516545
fn acl_init_super_admin(&mut self, account_id: ::near_sdk::AccountId) -> bool {
517-
self.acl_get().init_super_admin(&account_id)
546+
self.acl_get_or_init().init_super_admin(&account_id)
518547
}
519548

520549
fn acl_role_variants(&self) -> Vec<&'static str> {
521550
<#role_type>::acl_role_variants()
522551
}
523552

524553
fn acl_is_super_admin(&self, account_id: ::near_sdk::AccountId) -> bool {
525-
self.acl_get().is_super_admin(&account_id)
554+
return_if_none!(self.acl_get_storage(), false).is_super_admin(&account_id)
526555
}
527556

528557
fn acl_add_admin(&mut self, role: String, account_id: ::near_sdk::AccountId) -> Option<bool> {
529558
let role: #role_type = ::std::convert::TryFrom::try_from(role.as_str()).unwrap_or_else(|_| ::near_sdk::env::panic_str(#ERR_PARSE_ROLE));
530-
self.acl_get().add_admin(role, &account_id)
559+
self.acl_get_or_init().add_admin(role, &account_id)
531560
}
532561

533562
fn acl_is_admin(&self, role: String, account_id: ::near_sdk::AccountId) -> bool {
534563
let role: #role_type = ::std::convert::TryFrom::try_from(role.as_str()).unwrap_or_else(|_| ::near_sdk::env::panic_str(#ERR_PARSE_ROLE));
535-
self.acl_get().is_admin(role, &account_id)
564+
return_if_none!(self.acl_get_storage(), false).is_admin(role, &account_id)
536565
}
537566

538567
fn acl_revoke_admin(&mut self, role: String, account_id: ::near_sdk::AccountId) -> Option<bool> {
539568
let role: #role_type = ::std::convert::TryFrom::try_from(role.as_str()).unwrap_or_else(|_| ::near_sdk::env::panic_str(#ERR_PARSE_ROLE));
540-
self.acl_get().revoke_admin(role, &account_id)
569+
self.acl_get_or_init().revoke_admin(role, &account_id)
541570
}
542571

543572
fn acl_renounce_admin(&mut self, role: String) -> bool {
544573
let role: #role_type = ::std::convert::TryFrom::try_from(role.as_str()).unwrap_or_else(|_| ::near_sdk::env::panic_str(#ERR_PARSE_ROLE));
545-
self.acl_get().renounce_admin(role)
574+
self.acl_get_or_init().renounce_admin(role)
546575
}
547576

548577
fn acl_revoke_role(&mut self, role: String, account_id: ::near_sdk::AccountId) -> Option<bool> {
549578
let role: #role_type = ::std::convert::TryFrom::try_from(role.as_str()).unwrap_or_else(|_| ::near_sdk::env::panic_str(#ERR_PARSE_ROLE));
550-
self.acl_get().revoke_role(role, &account_id)
579+
self.acl_get_or_init().revoke_role(role, &account_id)
551580
}
552581

553582
fn acl_renounce_role(&mut self, role: String) -> bool {
554583
let role: #role_type = ::std::convert::TryFrom::try_from(role.as_str()).unwrap_or_else(|_| ::near_sdk::env::panic_str(#ERR_PARSE_ROLE));
555-
self.acl_get().renounce_role(role)
584+
self.acl_get_or_init().renounce_role(role)
556585
}
557586

558587
fn acl_grant_role(&mut self, role: String, account_id: ::near_sdk::AccountId) -> Option<bool> {
559588
let role: #role_type = ::std::convert::TryFrom::try_from(role.as_str()).unwrap_or_else(|_| ::near_sdk::env::panic_str(#ERR_PARSE_ROLE));
560-
self.acl_get().grant_role(role, &account_id)
589+
self.acl_get_or_init().grant_role(role, &account_id)
561590
}
562591

563592

564593
fn acl_has_role(&self, role: String, account_id: ::near_sdk::AccountId) -> bool {
565594
let role: #role_type = ::std::convert::TryFrom::try_from(role.as_str()).unwrap_or_else(|_| ::near_sdk::env::panic_str(#ERR_PARSE_ROLE));
566-
self.acl_get().has_role(role, &account_id)
595+
return_if_none!(self.acl_get_storage(), false).has_role(role, &account_id)
567596
}
568597

569598
fn acl_has_any_role(&self, roles: Vec<String>, account_id: ::near_sdk::AccountId) -> bool {
@@ -573,33 +602,33 @@ pub fn access_controllable(attrs: TokenStream, item: TokenStream) -> TokenStream
573602
::std::convert::TryFrom::try_from(role.as_str()).unwrap_or_else(|_| ::near_sdk::env::panic_str(#ERR_PARSE_ROLE))
574603
})
575604
.collect();
576-
self.acl_get().has_any_role(roles, &account_id)
605+
return_if_none!(self.acl_get_storage(), false).has_any_role(roles, &account_id)
577606
}
578607

579608
fn acl_get_super_admins(&self, skip: u64, limit: u64) -> Vec<::near_sdk::AccountId> {
580609
let permission = <#bitflags_type>::from_bits(
581610
<#role_type>::acl_super_admin_permission()
582611
)
583612
.unwrap_or_else(|| ::near_sdk::env::panic_str(#ERR_PARSE_BITFLAG));
584-
self.acl_get().get_bearers(permission, skip, limit)
613+
return_if_none!(self.acl_get_storage(), vec![]).get_bearers(permission, skip, limit)
585614
}
586615

587616
fn acl_get_admins(&self, role: String, skip: u64, limit: u64) -> Vec<::near_sdk::AccountId> {
588617
let role: #role_type = ::std::convert::TryFrom::try_from(role.as_str()).unwrap_or_else(|_| ::near_sdk::env::panic_str(#ERR_PARSE_ROLE));
589618
let permission = <#bitflags_type>::from_bits(role.acl_admin_permission())
590619
.unwrap_or_else(|| ::near_sdk::env::panic_str(#ERR_PARSE_BITFLAG));
591-
self.acl_get().get_bearers(permission, skip, limit)
620+
return_if_none!(self.acl_get_storage(), vec![]).get_bearers(permission, skip, limit)
592621
}
593622

594623
fn acl_get_grantees(&self, role: String, skip: u64, limit: u64) -> Vec<::near_sdk::AccountId> {
595624
let role: #role_type = ::std::convert::TryFrom::try_from(role.as_str()).unwrap_or_else(|_| ::near_sdk::env::panic_str(#ERR_PARSE_ROLE));
596625
let permission = <#bitflags_type>::from_bits(role.acl_permission())
597626
.unwrap_or_else(|| ::near_sdk::env::panic_str(#ERR_PARSE_BITFLAG));
598-
self.acl_get().get_bearers(permission, skip, limit)
627+
return_if_none!(self.acl_get_storage(), vec![]).get_bearers(permission, skip, limit)
599628
}
600629

601630
fn acl_get_permissioned_accounts(&self) -> #cratename::access_controllable::PermissionedAccounts {
602-
self.acl_get().get_permissioned_accounts()
631+
return_if_none!(self.acl_get_storage(), get_default_permissioned_accounts()).get_permissioned_accounts()
603632
}
604633
}
605634
};

near-plugins-derive/tests/contracts/access_controllable/src/lib.rs

+10-11
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ impl Counter {
3838
#[init]
3939
pub fn new(admins: HashMap<String, AccountId>, grantees: HashMap<String, AccountId>) -> Self {
4040
let mut contract = Self { counter: 0 };
41-
contract.acl_init_storage();
4241

4342
if admins.len() > 0 || grantees.len() > 0 {
4443
// First we make the contract itself super admin to allow it adding admin and grantees.
@@ -65,11 +64,11 @@ impl Counter {
6564
// granting roles, for example:
6665
//
6766
// ```
68-
// contract.acl_get().add_admin_unchecked(role, account_id);
69-
// contract.acl_get().grant_role_unchecked(role, account_id);
67+
// contract.acl_get_or_init().add_admin_unchecked(role, account_id);
68+
// contract.acl_get_or_init().grant_role_unchecked(role, account_id);
7069
// ```
7170
//
72-
// **Attention**: for security reasons, `acl_get().*_unchecked` methods should only be called
71+
// **Attention**: for security reasons, `acl_get_or_init().*_unchecked` methods should only be called
7372
// from within methods with attribute `#[init]` or `#[private]`.
7473
}
7574

@@ -133,7 +132,7 @@ impl Counter {
133132
self.acl_is_super_admin(env::predecessor_account_id()),
134133
"Only super admins are allowed to add other super admins."
135134
);
136-
self.acl_get().add_super_admin_unchecked(&account_id)
135+
self.acl_get_or_init().add_super_admin_unchecked(&account_id)
137136
}
138137
}
139138

@@ -142,32 +141,32 @@ impl Counter {
142141
impl Counter {
143142
#[private]
144143
pub fn acl_add_super_admin_unchecked(&mut self, account_id: AccountId) -> bool {
145-
self.acl_get().add_super_admin_unchecked(&account_id)
144+
self.acl_get_or_init().add_super_admin_unchecked(&account_id)
146145
}
147146

148147
#[private]
149148
pub fn acl_revoke_super_admin_unchecked(&mut self, account_id: AccountId) -> bool {
150-
self.acl_get().revoke_super_admin_unchecked(&account_id)
149+
self.acl_get_or_init().revoke_super_admin_unchecked(&account_id)
151150
}
152151

153152
#[private]
154153
pub fn acl_revoke_role_unchecked(&mut self, role: Role, account_id: AccountId) -> bool {
155-
self.acl_get()
154+
self.acl_get_or_init()
156155
.revoke_role_unchecked(role.into(), &account_id)
157156
}
158157

159158
#[private]
160159
pub fn acl_add_admin_unchecked(&mut self, role: Role, account_id: AccountId) -> bool {
161-
self.acl_get().add_admin_unchecked(role, &account_id)
160+
self.acl_get_or_init().add_admin_unchecked(role, &account_id)
162161
}
163162

164163
#[private]
165164
pub fn acl_revoke_admin_unchecked(&mut self, role: Role, account_id: AccountId) -> bool {
166-
self.acl_get().revoke_admin_unchecked(role, &account_id)
165+
self.acl_get_or_init().revoke_admin_unchecked(role, &account_id)
167166
}
168167

169168
#[private]
170169
pub fn acl_grant_role_unchecked(&mut self, role: Role, account_id: AccountId) -> bool {
171-
self.acl_get().grant_role_unchecked(role, &account_id)
170+
self.acl_get_or_init().grant_role_unchecked(role, &account_id)
172171
}
173172
}

near-plugins-derive/tests/contracts/pausable/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ impl Counter {
4141
let mut contract = Self {
4242
counter: 0,
4343
};
44-
contract.acl_init_storage();
4544

4645
// Make the contract itself super admin. This allows us to grant any role in the
4746
// constructor.

near-plugins/src/access_controllable.rs

-3
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ pub trait AccessControllable {
2929
/// ```
3030
fn acl_storage_prefix() -> &'static [u8];
3131

32-
/// Initialize the access control storage.
33-
fn acl_init_storage(&self);
34-
3532
/// Returns the names of all variants of the enum that represents roles.
3633
///
3734
/// In the default implementation provided by this crate, this enum is defined by contract

0 commit comments

Comments
 (0)