pallet_permission0/ext/
curator_impl.rs1use crate::{
2 generate_permission_id, pallet, update_permission_indices, Config, CuratorPermissions,
3 CuratorScope, Error, Event, Pallet, PermissionContract, PermissionDuration, PermissionScope,
4 Permissions, PermissionsByGrantee, RevocationTerms,
5};
6
7use pallet_permission0_api::{
8 CuratorPermissions as ApiCuratorPermissions, Permission0CuratorApi,
9 PermissionDuration as ApiPermissionDuration, PermissionId,
10 RevocationTerms as ApiRevocationTerms,
11};
12use polkadot_sdk::frame_system::{ensure_root, ensure_signed_or_root};
13use polkadot_sdk::sp_core::Get;
14use polkadot_sdk::sp_runtime::traits::{AccountIdConversion, Saturating};
15use polkadot_sdk::{
16 frame_support::ensure,
17 frame_system,
18 polkadot_sdk_frame::prelude::{BlockNumberFor, OriginFor},
19 sp_runtime::{DispatchError, DispatchResult},
20};
21
22impl<T: Config> Permission0CuratorApi<T::AccountId, OriginFor<T>, BlockNumberFor<T>>
23 for pallet::Pallet<T>
24{
25 fn grant_curator_permission(
26 grantor: OriginFor<T>,
27 grantee: T::AccountId,
28 flags: ApiCuratorPermissions,
29 cooldown: Option<BlockNumberFor<T>>,
30 duration: ApiPermissionDuration<BlockNumberFor<T>>,
31 revocation: ApiRevocationTerms<T::AccountId, BlockNumberFor<T>>,
32 ) -> Result<PermissionId, DispatchError> {
33 let duration = super::translate_duration::<T>(duration)?;
34 let revocation = super::translate_revocation_terms::<T>(revocation)?;
35
36 let flags = CuratorPermissions::from_bits_truncate(flags.bits());
37 grant_curator_permission_impl(grantor, grantee, flags, cooldown, duration, revocation)
38 }
39
40 fn ensure_curator_permission(
41 grantee: OriginFor<T>,
42 flags: ApiCuratorPermissions,
43 ) -> Result<T::AccountId, DispatchError> {
44 let Some(grantee) = ensure_signed_or_root(grantee)? else {
45 return Ok(T::PalletId::get().into_account_truncating());
46 };
47
48 let permissions = PermissionsByGrantee::<T>::get(&grantee);
49 let Some((_, contract)) = permissions.into_iter().find_map(|permission_id| {
50 let contract = Permissions::<T>::get(permission_id)?;
51
52 if matches!(&contract.scope, PermissionScope::Curator(_)) {
53 Some((permission_id, contract))
54 } else {
55 None
56 }
57 }) else {
58 return Err(Error::<T>::PermissionNotFound.into());
59 };
60
61 let PermissionScope::Curator(scope) = &contract.scope else {
62 return Err(Error::<T>::PermissionNotFound.into());
63 };
64
65 let flags = CuratorPermissions::from_bits_truncate(flags.bits());
66 if !scope.has_permission(flags) {
67 return Err(Error::<T>::PermissionNotFound.into());
68 }
69
70 if let Some(cooldown) = scope.cooldown {
71 let now = <frame_system::Pallet<T>>::block_number();
72
73 if contract
74 .last_execution
75 .is_some_and(|last_execution| last_execution.saturating_add(cooldown) > now)
76 {
77 return Err(Error::<T>::PermissionInCooldown.into());
78 }
79 }
80
81 Ok(grantee)
82 }
83
84 fn get_curator_permission(grantee: &T::AccountId) -> Option<PermissionId> {
85 PermissionsByGrantee::<T>::get(grantee)
86 .into_iter()
87 .find_map(|permission_id| {
88 let contract = Permissions::<T>::get(permission_id)?;
89
90 if matches!(&contract.scope, PermissionScope::Curator(_)) {
91 Some(permission_id)
92 } else {
93 None
94 }
95 })
96 }
97}
98
99pub fn grant_curator_permission_impl<T: Config>(
100 grantor: OriginFor<T>,
101 grantee: T::AccountId,
102 mut flags: CuratorPermissions,
103 cooldown: Option<BlockNumberFor<T>>,
104 duration: PermissionDuration<T>,
105 revocation: RevocationTerms<T>,
106) -> Result<PermissionId, DispatchError> {
107 ensure_root(grantor)?;
108
109 flags.remove(CuratorPermissions::ROOT);
111
112 ensure!(!flags.is_empty(), Error::<T>::InvalidCuratorPermissions);
113
114 let grantor = <T as Config>::PalletId::get().into_account_truncating();
115
116 for perm in PermissionsByGrantee::<T>::get(&grantee) {
122 let Some(contract) = Permissions::<T>::get(perm) else {
123 continue;
124 };
125
126 if matches!(&contract.scope, PermissionScope::Curator(_)) {
127 return Err(Error::<T>::DuplicatePermission.into());
128 }
129 }
130
131 let scope = PermissionScope::Curator(CuratorScope { flags, cooldown });
132 let permission_id = generate_permission_id::<T>(&grantor, &grantee, &scope)?;
133
134 let contract = PermissionContract {
135 grantor,
136 grantee,
137 scope,
138 duration,
139 revocation,
140 enforcement: crate::EnforcementAuthority::None,
141 last_execution: None,
142 execution_count: 0,
143 parent: None,
145 created_at: <frame_system::Pallet<T>>::block_number(),
146 };
147
148 Permissions::<T>::insert(permission_id, &contract);
149 update_permission_indices::<T>(&contract.grantor, &contract.grantee, permission_id)?;
150
151 <Pallet<T>>::deposit_event(Event::PermissionGranted {
152 grantor: contract.grantor,
153 grantee: contract.grantee,
154 permission_id,
155 });
156
157 Ok(permission_id)
158}
159
160pub fn execute_permission_impl<T: Config>(permission_id: &PermissionId) -> DispatchResult {
161 Permissions::<T>::mutate(permission_id, |maybe_contract| {
162 if let Some(c) = maybe_contract {
163 c.last_execution = Some(<frame_system::Pallet<T>>::block_number());
164 c.execution_count = c.execution_count.saturating_add(1);
165 }
166 });
167
168 Ok(())
169}