radix_engine/system/system_modules/auth/
authorization.rs1use crate::blueprints::resource::AuthZone;
2use crate::errors::RuntimeError;
3use crate::internal_prelude::*;
4use crate::kernel::kernel_api::KernelSubstateApi;
5use crate::object_modules::role_assignment::{
6 RoleAssignmentAccessRuleEntryPayload, RoleAssignmentOwnerFieldPayload,
7};
8use crate::system::system_modules::auth::{
9 AuthorityListAuthorizationResult, AuthorizationCheckResult,
10};
11use crate::system::system_substates::FieldSubstate;
12use crate::system::system_substates::KeyValueEntrySubstate;
13use num_traits::Zero;
14use radix_engine_interface::api::{LockFlags, ModuleId, SystemObjectApi};
15use radix_engine_interface::blueprints::resource::*;
16use radix_native_sdk::resource::{NativeNonFungibleProof, NativeProof};
17use sbor::rust::ops::Fn;
18
19pub struct Authorization;
20
21impl Authorization {
22 fn proof_matches<Y: SystemObjectApi<RuntimeError> + KernelSubstateApi<L>, L: Default>(
23 resource_rule: &ResourceOrNonFungible,
24 proof: &Proof,
25 api: &mut Y,
26 ) -> Result<bool, RuntimeError> {
27 match resource_rule {
28 ResourceOrNonFungible::NonFungible(non_fungible_global_id) => {
29 let proof_resource_address = proof.resource_address(api)?;
30 Ok(
31 proof_resource_address == non_fungible_global_id.resource_address()
32 && proof
33 .non_fungible_local_ids(api)?
34 .contains(non_fungible_global_id.local_id()),
35 )
36 }
37 ResourceOrNonFungible::Resource(resource_address) => {
38 let proof_resource_address = proof.resource_address(api)?;
39 Ok(proof_resource_address == *resource_address)
40 }
41 }
42 }
43
44 fn global_auth_zone_matches<Y: KernelSubstateApi<L>, L: Default>(
45 api: &mut Y,
46 auth_zone_id: &NodeId,
47 check: &impl Fn(
48 &[Proof],
49 &BTreeSet<ResourceAddress>,
50 BTreeSet<NonFungibleGlobalId>,
51 &mut Y,
52 ) -> Result<bool, RuntimeError>,
53 ) -> Result<bool, RuntimeError> {
54 let mut pass = false;
55 let mut current_auth_zone_id = *auth_zone_id;
56 let mut handles = Vec::new();
57 loop {
58 let handle = api.kernel_open_substate(
60 ¤t_auth_zone_id,
61 MAIN_BASE_PARTITION,
62 &AuthZoneField::AuthZone.into(),
63 LockFlags::read_only(),
64 L::default(),
65 )?;
66 let auth_zone = api
67 .kernel_read_substate(handle)?
68 .as_typed::<FieldSubstate<AuthZone>>()
69 .unwrap()
70 .into_payload();
71 handles.push(handle);
72
73 {
74 let mut implicit_non_fungible_proofs = BTreeSet::new();
75 let simulate_all_proofs_under_resources =
76 auth_zone.simulate_all_proofs_under_resources();
77
78 implicit_non_fungible_proofs
79 .extend(auth_zone.implicit_non_fungible_proofs().clone());
80
81 let proofs = auth_zone.proofs();
82
83 if check(
85 proofs,
86 simulate_all_proofs_under_resources,
87 implicit_non_fungible_proofs,
88 api,
89 )? {
90 pass = true;
91 break;
92 }
93 }
94
95 if let Some(id) = auth_zone.parent {
96 current_auth_zone_id = id.into();
97 } else {
98 break;
99 }
100 }
101
102 for handle in handles {
103 api.kernel_close_substate(handle)?;
104 }
105
106 Ok(pass)
107 }
108
109 fn auth_zone_stack_matches<Y: KernelSubstateApi<L>, L: Default>(
110 auth_zone: &NodeId,
111 api: &mut Y,
112 check: impl Fn(
113 &[Proof],
114 &BTreeSet<ResourceAddress>,
115 BTreeSet<NonFungibleGlobalId>,
116 &mut Y,
117 ) -> Result<bool, RuntimeError>,
118 ) -> Result<bool, RuntimeError> {
119 let handle = api.kernel_open_substate(
120 &auth_zone,
121 MAIN_BASE_PARTITION,
122 &AuthZoneField::AuthZone.into(),
123 LockFlags::read_only(),
124 L::default(),
125 )?;
126
127 let rtn = (|| -> Result<bool, RuntimeError> {
131 let auth_zone = api
132 .kernel_read_substate(handle)?
133 .as_typed::<FieldSubstate<AuthZone>>()
134 .unwrap()
135 .into_payload();
136
137 let local_implicit_non_fungible_proofs = auth_zone.local_implicit_non_fungible_proofs();
139 if !local_implicit_non_fungible_proofs.is_empty() {
140 if check(&[], &btreeset!(), local_implicit_non_fungible_proofs, api)? {
141 return Ok(true);
142 }
143 }
144
145 if let Some((_, global_caller_leaf_auth_zone_reference)) = &auth_zone.global_caller {
147 if Self::global_auth_zone_matches(
148 api,
149 &global_caller_leaf_auth_zone_reference.0,
150 &check,
151 )? {
152 return Ok(true);
153 }
154 }
155
156 if let Some(parent) = auth_zone.parent {
159 if Self::global_auth_zone_matches(api, &parent.0, &check)? {
160 return Ok(true);
161 }
162 }
163
164 Ok(false)
165 })()?;
166
167 api.kernel_close_substate(handle)?;
168
169 Ok(rtn)
170 }
171
172 fn auth_zone_stack_has_amount<
173 Y: SystemObjectApi<RuntimeError> + KernelSubstateApi<L>,
174 L: Default,
175 >(
176 auth_zone: &NodeId,
177 resource: &ResourceAddress,
178 amount: Decimal,
179 api: &mut Y,
180 ) -> Result<bool, RuntimeError> {
181 Self::auth_zone_stack_matches(auth_zone, api, |proofs, _, _, api| {
182 for p in proofs {
184 if Self::proof_matches(&ResourceOrNonFungible::Resource(*resource), p, api)?
185 && p.amount(api)? >= amount
186 {
187 return Ok(true);
188 }
189 }
190
191 Ok(false)
192 })
193 }
194
195 fn auth_zone_stack_matches_rule<
196 Y: SystemObjectApi<RuntimeError> + KernelSubstateApi<L>,
197 L: Default,
198 >(
199 auth_zone: &NodeId,
200 resource_rule: &ResourceOrNonFungible,
201 api: &mut Y,
202 ) -> Result<bool, RuntimeError> {
203 Self::auth_zone_stack_matches(
204 auth_zone,
205 api,
206 |proofs, virtual_resources, virtual_non_fungibles, api| {
207 if let ResourceOrNonFungible::NonFungible(non_fungible_global_id) = resource_rule {
208 if virtual_non_fungibles.contains(non_fungible_global_id) {
209 return Ok(true);
210 }
211
212 if virtual_resources.contains(&non_fungible_global_id.resource_address()) {
213 return Ok(true);
214 }
215 }
216
217 for p in proofs {
218 if Self::proof_matches(resource_rule, p, api)? {
219 return Ok(true);
220 }
221 }
222
223 Ok(false)
224 },
225 )
226 }
227
228 pub fn verify_proof_rule<
229 Y: SystemObjectApi<RuntimeError> + KernelSubstateApi<L>,
230 L: Default,
231 >(
232 auth_zone: &NodeId,
233 requirement_rule: &BasicRequirement,
234 api: &mut Y,
235 ) -> Result<bool, RuntimeError> {
236 match requirement_rule {
237 BasicRequirement::Require(resource) => {
238 if Self::auth_zone_stack_matches_rule(auth_zone, resource, api)? {
239 Ok(true)
240 } else {
241 Ok(false)
242 }
243 }
244 BasicRequirement::AmountOf(amount, resource) => {
245 if Self::auth_zone_stack_has_amount(auth_zone, resource, *amount, api)? {
246 Ok(true)
247 } else {
248 Ok(false)
249 }
250 }
251 BasicRequirement::AllOf(resources) => {
252 for resource in resources {
253 if !Self::auth_zone_stack_matches_rule(auth_zone, resource, api)? {
254 return Ok(false);
255 }
256 }
257
258 Ok(true)
259 }
260 BasicRequirement::AnyOf(resources) => {
261 for resource in resources {
262 if Self::auth_zone_stack_matches_rule(auth_zone, resource, api)? {
263 return Ok(true);
264 }
265 }
266
267 Ok(false)
268 }
269 BasicRequirement::CountOf(count, resources) => {
270 if count.is_zero() {
271 return Ok(true);
272 }
273
274 let mut left = count.clone();
275 for resource in resources {
276 if Self::auth_zone_stack_matches_rule(auth_zone, resource, api)? {
277 left -= 1;
278 if left == 0 {
279 return Ok(true);
280 }
281 }
282 }
283 Ok(false)
284 }
285 }
286 }
287
288 pub fn verify_auth_rule<Y: SystemObjectApi<RuntimeError> + KernelSubstateApi<L>, L: Default>(
289 auth_zone: &NodeId,
290 requirement_rule: &CompositeRequirement,
291 api: &mut Y,
292 ) -> Result<AuthorizationCheckResult, RuntimeError> {
293 match requirement_rule {
294 CompositeRequirement::BasicRequirement(rule) => {
295 if Self::verify_proof_rule(auth_zone, rule, api)? {
296 Ok(AuthorizationCheckResult::Authorized)
297 } else {
298 Ok(AuthorizationCheckResult::Failed(vec![]))
299 }
300 }
301 CompositeRequirement::AnyOf(rules) => {
302 for r in rules {
303 let rtn = Self::verify_auth_rule(auth_zone, r, api)?;
304 if matches!(rtn, AuthorizationCheckResult::Authorized) {
305 return Ok(rtn);
306 }
307 }
308 Ok(AuthorizationCheckResult::Failed(vec![]))
309 }
310 CompositeRequirement::AllOf(rules) => {
311 for r in rules {
312 let rtn = Self::verify_auth_rule(auth_zone, r, api)?;
313 if matches!(rtn, AuthorizationCheckResult::Failed(..)) {
314 return Ok(rtn);
315 }
316 }
317
318 return Ok(AuthorizationCheckResult::Authorized);
319 }
320 }
321 }
322
323 pub fn check_authorization_against_role_key_internal<
324 Y: SystemObjectApi<RuntimeError> + KernelSubstateApi<L>,
325 L: Default,
326 >(
327 auth_zone: &NodeId,
328 role_assignment_of: &GlobalAddress,
329 key: &ModuleRoleKey,
330 api: &mut Y,
331 ) -> Result<AuthorizationCheckResult, RuntimeError> {
332 let access_rule = if key.key.key.eq(SELF_ROLE) {
333 rule!(require(global_caller(role_assignment_of.clone())))
334 } else {
335 let handle = api.kernel_open_substate_with_default(
336 role_assignment_of.as_node_id(),
337 ROLE_ASSIGNMENT_BASE_PARTITION
338 .at_offset(ROLE_ASSIGNMENT_ROLE_DEF_PARTITION_OFFSET)
339 .unwrap(),
340 &SubstateKey::Map(scrypto_encode(&key).unwrap()),
341 LockFlags::read_only(),
342 Some(|| {
343 let kv_entry = KeyValueEntrySubstate::<()>::default();
344 IndexedScryptoValue::from_typed(&kv_entry)
345 }),
346 L::default(),
347 )?;
348 let substate: KeyValueEntrySubstate<RoleAssignmentAccessRuleEntryPayload> =
349 api.kernel_read_substate(handle)?.as_typed().unwrap();
350 api.kernel_close_substate(handle)?;
351
352 match substate.into_value() {
353 Some(access_rule) => access_rule.fully_update_and_into_latest_version(),
354 None => {
355 let handle = api.kernel_open_substate(
356 role_assignment_of.as_node_id(),
357 ROLE_ASSIGNMENT_BASE_PARTITION
358 .at_offset(ROLE_ASSIGNMENT_FIELDS_PARTITION_OFFSET)
359 .unwrap(),
360 &SubstateKey::Field(0u8),
361 LockFlags::read_only(),
362 L::default(),
363 )?;
364
365 let owner_role_substate: FieldSubstate<RoleAssignmentOwnerFieldPayload> =
366 api.kernel_read_substate(handle)?.as_typed().unwrap();
367 api.kernel_close_substate(handle)?;
368 owner_role_substate
369 .into_payload()
370 .fully_update_and_into_latest_version()
371 .owner_role_entry
372 .rule
373 }
374 }
375 };
376
377 Self::check_authorization_against_access_rule(api, auth_zone, &access_rule)
378 }
379
380 pub fn check_authorization_against_access_rule<
381 Y: SystemObjectApi<RuntimeError> + KernelSubstateApi<L>,
382 L: Default,
383 >(
384 api: &mut Y,
385 auth_zone: &NodeId,
386 rule: &AccessRule,
387 ) -> Result<AuthorizationCheckResult, RuntimeError> {
388 match rule {
389 AccessRule::Protected(rule_node) => {
390 let mut rtn = Self::verify_auth_rule(auth_zone, rule_node, api)?;
391 match &mut rtn {
392 AuthorizationCheckResult::Authorized => {}
393 AuthorizationCheckResult::Failed(stack) => {
394 stack.push(rule.clone());
395 }
396 }
397 Ok(rtn)
398 }
399 AccessRule::AllowAll => Ok(AuthorizationCheckResult::Authorized),
400 AccessRule::DenyAll => Ok(AuthorizationCheckResult::Failed(vec![rule.clone()])),
401 }
402 }
403
404 pub fn check_authorization_against_role_list<
405 Y: SystemObjectApi<RuntimeError> + KernelSubstateApi<L>,
406 L: Default,
407 >(
408 auth_zone: &NodeId,
409 role_assignment_of: &GlobalAddress,
410 module: ModuleId,
411 role_list: &RoleList,
412 api: &mut Y,
413 ) -> Result<AuthorityListAuthorizationResult, RuntimeError> {
414 let mut failed = Vec::new();
415
416 for key in &role_list.list {
417 let module_role_key = ModuleRoleKey::new(module, key.key.as_str());
418 let result = Self::check_authorization_against_role_key_internal(
419 &auth_zone,
420 role_assignment_of,
421 &module_role_key,
422 api,
423 )?;
424 match result {
425 AuthorizationCheckResult::Authorized => {
426 return Ok(AuthorityListAuthorizationResult::Authorized)
427 }
428 AuthorizationCheckResult::Failed(stack) => {
429 failed.push((key.clone(), stack));
430 }
431 }
432 }
433
434 Ok(AuthorityListAuthorizationResult::Failed(failed))
435 }
436}