1use super::Authorization;
2use crate::blueprints::package::PackageAuthNativeBlueprint;
3use crate::blueprints::resource::AuthZone;
4use crate::errors::*;
5use crate::internal_prelude::*;
6use crate::kernel::call_frame::ReferenceOrigin;
7use crate::kernel::kernel_api::{KernelInternalApi, KernelNodeApi, KernelSubstateApi};
8use crate::object_modules::role_assignment::RoleAssignmentNativePackage;
9use crate::system::actor::Actor;
10use crate::system::module::*;
11use crate::system::node_init::type_info_partition;
12use crate::system::system::SystemService;
13use crate::system::system_callback::*;
14use crate::system::type_info::TypeInfoSubstate;
15use radix_engine_interface::api::{AttachedModuleId, LockFlags, ModuleId, SystemBlueprintApi};
16use radix_engine_interface::blueprints::package::{
17 BlueprintVersion, BlueprintVersionKey, MethodAuthTemplate, RoleSpecification,
18};
19use radix_engine_interface::blueprints::resource::*;
20use radix_engine_interface::blueprints::transaction_processor::TRANSACTION_PROCESSOR_BLUEPRINT;
21use radix_engine_interface::types::*;
22use radix_transactions::model::AuthZoneInit;
23
24#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
25pub enum AuthError {
26 NoFunction(FnIdentifier),
27 NoMethodMapping(FnIdentifier),
28 Unauthorized(Box<Unauthorized>),
29 InnerBlueprintDoesNotExist(String),
30 InvalidOuterObjectMapping,
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
34pub enum FailedAccessRules {
35 RoleList(Vec<(RoleKey, Vec<AccessRule>)>),
36 AccessRule(Vec<AccessRule>),
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
40pub struct Unauthorized {
41 pub failed_access_rules: FailedAccessRules,
42 pub fn_identifier: FnIdentifier,
43}
44
45#[derive(Debug, Clone)]
46pub struct AuthModule {
47 pub v1_transaction_processor_proofs_for_injection: Option<AuthZoneInit>,
51}
52
53pub enum AuthorizationCheckResult {
54 Authorized,
55 Failed(Vec<AccessRule>),
56}
57
58pub enum AuthorityListAuthorizationResult {
59 Authorized,
60 Failed(Vec<(RoleKey, Vec<AccessRule>)>),
61}
62
63pub enum ResolvedPermission {
64 RoleList {
65 role_assignment_of: GlobalAddress,
66 module_id: ModuleId,
67 role_list: RoleList,
68 },
69 AccessRule(AccessRule),
70 AllowAll,
71}
72
73impl AuthModule {
74 pub fn new() -> Self {
75 Self {
76 v1_transaction_processor_proofs_for_injection: None,
77 }
78 }
79
80 pub fn new_with_transaction_processor_auth_zone(auth_zone_init: AuthZoneInit) -> Self {
81 Self {
82 v1_transaction_processor_proofs_for_injection: Some(auth_zone_init),
83 }
84 }
85
86 fn system_v1_resolve_injectable_transaction_processor_proofs<Y: SystemBasedKernelApi>(
92 system: &mut SystemService<Y>,
93 blueprint_id: &BlueprintId,
94 ) -> Result<(BTreeSet<ResourceAddress>, BTreeSet<NonFungibleGlobalId>), RuntimeError> {
95 let is_root_call_frame = system
96 .kernel_get_system_state()
97 .current_call_frame
98 .is_root();
99 let is_root_thread = system.kernel_get_current_stack_id_uncosted() == 0;
100 if is_root_call_frame && is_root_thread {
101 let auth_module = &system.kernel_get_system().modules.auth;
102 if let Some(auth_zone_init) = &auth_module.v1_transaction_processor_proofs_for_injection
103 {
104 let is_transaction_processor_blueprint = blueprint_id
106 .package_address
107 .eq(&TRANSACTION_PROCESSOR_PACKAGE)
108 && blueprint_id
109 .blueprint_name
110 .eq(TRANSACTION_PROCESSOR_BLUEPRINT);
111 if is_transaction_processor_blueprint {
112 return Ok((
113 auth_zone_init.simulate_every_proof_under_resources.clone(),
114 auth_zone_init.initial_non_fungible_id_proofs.clone(),
115 ));
116 }
117 }
118 }
119
120 Ok((BTreeSet::new(), BTreeSet::new()))
121 }
122
123 pub fn on_call_function<Y: SystemBasedKernelApi>(
124 system: &mut SystemService<Y>,
125 blueprint_id: &BlueprintId,
126 ident: &str,
127 ) -> Result<NodeId, RuntimeError> {
128 let auth_zone = {
130 if system
131 .system()
132 .versioned_system_logic
133 .should_inject_transaction_processor_proofs_in_call_function()
134 {
135 let (simulate_all_proofs_under_resources, implicit_non_fungible_proofs) =
136 Self::system_v1_resolve_injectable_transaction_processor_proofs(
137 system,
138 blueprint_id,
139 )?;
140 Self::create_auth_zone(
141 system,
142 None,
143 simulate_all_proofs_under_resources,
144 implicit_non_fungible_proofs,
145 )?
146 } else {
147 Self::create_auth_zone(system, None, Default::default(), Default::default())?
148 }
149 };
150
151 {
153 let permission = PackageAuthNativeBlueprint::resolve_function_permission(
155 blueprint_id.package_address.as_node_id(),
156 &BlueprintVersionKey::new_default(blueprint_id.blueprint_name.as_str()),
157 ident,
158 system.api(),
159 )?;
160
161 let fn_identifier = FnIdentifier {
163 blueprint_id: blueprint_id.clone(),
164 ident: ident.to_string(),
165 };
166 Self::check_permission(&auth_zone, permission, fn_identifier, system)?;
167 }
168
169 Ok(auth_zone)
170 }
171
172 pub fn on_call_function_finish<Y: SystemBasedKernelApi>(
173 api: &mut SystemService<Y>,
174 auth_zone: NodeId,
175 ) -> Result<(), RuntimeError> {
176 Self::teardown_auth_zone(api, auth_zone)
177 }
178
179 pub fn on_call_method<Y: SystemBasedKernelApi>(
180 api: &mut SystemService<Y>,
181 receiver: &NodeId,
182 module_id: ModuleId,
183 direct_access: bool,
184 ident: &str,
185 args: &IndexedScryptoValue,
186 ) -> Result<NodeId, RuntimeError> {
187 let auth_zone = AuthModule::create_auth_zone(
188 api,
189 Some((receiver, direct_access)),
190 btreeset!(),
191 btreeset!(),
192 )?;
193
194 let attached_module_id = match module_id {
196 ModuleId::Main => None,
197 ModuleId::Metadata => Some(AttachedModuleId::Metadata),
198 ModuleId::Royalty => Some(AttachedModuleId::Royalty),
199 ModuleId::RoleAssignment => Some(AttachedModuleId::RoleAssignment),
200 };
201
202 let blueprint_id = api
203 .get_blueprint_info(receiver, attached_module_id)?
204 .blueprint_id;
205
206 let permission =
207 Self::resolve_method_permission(api, &blueprint_id, receiver, &module_id, ident, args)?;
208
209 let fn_identifier = FnIdentifier {
211 blueprint_id: blueprint_id.clone(),
212 ident: ident.to_string(),
213 };
214 Self::check_permission(&auth_zone, permission, fn_identifier, api)?;
215
216 Ok(auth_zone)
217 }
218
219 pub fn on_call_method_finish<Y: SystemBasedKernelApi>(
220 api: &mut SystemService<Y>,
221 auth_zone: NodeId,
222 ) -> Result<(), RuntimeError> {
223 Self::teardown_auth_zone(api, auth_zone)
224 }
225
226 pub fn on_call_fn_mock<Y: SystemBasedKernelApi>(
228 system: &mut SystemService<Y>,
229 receiver: Option<(&NodeId, bool)>,
230 simulate_all_proofs_under_resources: BTreeSet<ResourceAddress>,
231 implicit_non_fungible_proofs: BTreeSet<NonFungibleGlobalId>,
232 ) -> Result<NodeId, RuntimeError> {
233 Self::create_auth_zone(
234 system,
235 receiver,
236 simulate_all_proofs_under_resources,
237 implicit_non_fungible_proofs,
238 )
239 }
240
241 fn copy_global_caller<Y: SystemBasedKernelApi>(
242 system: &mut SystemService<Y>,
243 direct_caller_auth_zone_id: &NodeId,
244 ) -> Result<(Option<(GlobalCaller, Reference)>, Option<SubstateHandle>), RuntimeError> {
245 let direct_caller_auth_zone_handle = system.kernel_open_substate(
246 direct_caller_auth_zone_id,
247 MAIN_BASE_PARTITION,
248 &AuthZoneField::AuthZone.into(),
249 LockFlags::read_only(),
250 SystemLockData::default(),
251 )?;
252
253 let direct_caller_auth_zone = system
254 .kernel_read_substate(direct_caller_auth_zone_handle)?
255 .as_typed::<FieldSubstate<AuthZone>>()
256 .unwrap();
257
258 Ok((
259 direct_caller_auth_zone.into_payload().global_caller,
260 Some(direct_caller_auth_zone_handle),
261 ))
262 }
263
264 pub(crate) fn create_auth_zone<Y: SystemBasedKernelApi>(
265 system: &mut SystemService<Y>,
266 receiver: Option<(&NodeId, bool)>,
267 simulate_all_proofs_under_resources: BTreeSet<ResourceAddress>,
268 implicit_non_fungible_proofs: BTreeSet<NonFungibleGlobalId>,
269 ) -> Result<NodeId, RuntimeError> {
270 let (auth_zone, parent_lock_handle) = {
271 let is_global_context_change = if let Some((receiver, direct_access)) = receiver {
272 let object_info = system.get_object_info(receiver)?;
273 object_info.is_global() || direct_access
274 } else {
275 true
276 };
277
278 let direct_caller = system.current_actor();
279 let direct_caller_package_address = direct_caller.package_address();
280
281 let (global_caller, parent_lock_handle) = match direct_caller {
283 Actor::Root | Actor::BlueprintHook(..) => (None, None),
284 Actor::Method(direct_caller_method_actor) => {
285 let direct_caller_ancestor_visibility_origin = system
286 .kernel_get_node_visibility_uncosted(&direct_caller_method_actor.node_id)
287 .reference_origin(direct_caller_method_actor.node_id)
288 .unwrap();
289 let direct_caller_auth_zone = direct_caller_method_actor.auth_zone;
290
291 match (
304 direct_caller_ancestor_visibility_origin,
305 is_global_context_change,
306 ) {
307 (ReferenceOrigin::Global(global_root_address), true) => {
309 let global_caller_address = global_root_address.into();
310 let global_caller_leaf_auth_zone_reference =
311 Reference(direct_caller_auth_zone);
312 (
313 Some((
314 global_caller_address,
315 global_caller_leaf_auth_zone_reference,
316 )),
317 None,
318 )
319 }
320 (ReferenceOrigin::Global(..), false) => {
322 Self::copy_global_caller(system, &direct_caller_auth_zone)?
323 }
324 (ReferenceOrigin::DirectlyAccessed, _) => (None, None),
326 (ReferenceOrigin::SubstateNonGlobalReference(..), _) => (None, None),
328 (ReferenceOrigin::FrameOwned, _) => {
330 let (caller, lock_handle) =
343 Self::copy_global_caller(system, &direct_caller_auth_zone)?;
344
345 let global_caller = match caller {
348 Some(_) => {
349 let global_caller_address = FRAME_OWNED_GLOBAL_MARKER.into();
350 let global_caller_leaf_auth_zone_reference =
353 Reference(direct_caller_auth_zone);
354 Some((
355 global_caller_address,
356 global_caller_leaf_auth_zone_reference,
357 ))
358 }
359 None => None,
360 };
361
362 (global_caller, lock_handle)
363 }
364 }
365 }
366 Actor::Function(function_actor) => {
367 let direct_caller_auth_zone = function_actor.auth_zone;
368 let global_caller = function_actor.as_global_caller();
369 if is_global_context_change {
370 (
371 Some((global_caller, Reference(direct_caller_auth_zone))),
372 None,
373 )
374 } else {
375 Self::copy_global_caller(system, &direct_caller_auth_zone)?
376 }
377 }
378 };
379
380 let auth_zone_parent = if is_global_context_change {
381 None
382 } else {
383 system
384 .current_actor()
385 .self_auth_zone()
386 .map(|x| Reference(x))
387 };
388
389 let auth_zone = AuthZone::new(
390 vec![],
391 simulate_all_proofs_under_resources,
392 implicit_non_fungible_proofs,
393 direct_caller_package_address,
394 global_caller,
395 auth_zone_parent,
396 );
397
398 (auth_zone, parent_lock_handle)
399 };
400
401 let new_auth_zone = system
403 .api()
404 .kernel_allocate_node_id(EntityType::InternalGenericComponent)?;
405
406 system.api().kernel_create_node(
407 new_auth_zone,
408 btreemap!(
409 MAIN_BASE_PARTITION => btreemap!(
410 AuthZoneField::AuthZone.into() => IndexedScryptoValue::from_typed(&FieldSubstate::new_unlocked_field(auth_zone))
411 ),
412 TYPE_INFO_FIELD_PARTITION => type_info_partition(TypeInfoSubstate::Object(ObjectInfo {
413 blueprint_info: BlueprintInfo {
414 blueprint_id: BlueprintId::new(&RESOURCE_PACKAGE, AUTH_ZONE_BLUEPRINT),
415 blueprint_version: BlueprintVersion::default(),
416 outer_obj_info: OuterObjectInfo::default(),
417 features: indexset!(),
418 generic_substitutions: vec![],
419 },
420 object_type: ObjectType::Owned,
421 }))
422 ),
423 )?;
424 system.api().kernel_pin_node(new_auth_zone)?;
425
426 if let Some(parent_lock_handle) = parent_lock_handle {
427 system.kernel_close_substate(parent_lock_handle)?;
428 }
429
430 Ok(new_auth_zone)
431 }
432
433 pub fn teardown_auth_zone<Y: SystemBasedKernelApi>(
434 api: &mut SystemService<Y>,
435 self_auth_zone: NodeId,
436 ) -> Result<(), RuntimeError> {
437 let handle = api.kernel_open_substate(
439 &self_auth_zone,
440 MAIN_BASE_PARTITION,
441 &AuthZoneField::AuthZone.into(),
442 LockFlags::MUTABLE,
443 SystemLockData::Default,
444 )?;
445 let mut auth_zone = api
446 .kernel_read_substate(handle)?
447 .as_typed::<FieldSubstate<AuthZone>>()
448 .unwrap()
449 .into_payload();
450 let proofs = core::mem::replace(&mut auth_zone.proofs, Vec::new());
451 api.kernel_write_substate(
452 handle,
453 IndexedScryptoValue::from_typed(&FieldSubstate::new_unlocked_field(auth_zone)),
454 )?;
455 api.kernel_close_substate(handle)?;
456
457 for proof in proofs {
459 let object_info = api.get_object_info(proof.0.as_node_id())?;
460 api.call_function(
461 RESOURCE_PACKAGE,
462 &object_info.blueprint_info.blueprint_id.blueprint_name,
463 PROOF_DROP_IDENT,
464 scrypto_encode(&ProofDropInput { proof }).unwrap(),
465 )?;
466 }
467
468 api.kernel_drop_node(&self_auth_zone)?;
470
471 Ok(())
472 }
473
474 fn check_permission<Y: SystemBasedKernelApi>(
475 auth_zone: &NodeId,
476 resolved_permission: ResolvedPermission,
477 fn_identifier: FnIdentifier,
478 api: &mut SystemService<Y>,
479 ) -> Result<(), RuntimeError> {
480 match resolved_permission {
481 ResolvedPermission::AllowAll => return Ok(()),
482 ResolvedPermission::AccessRule(rule) => {
483 let result =
484 Authorization::check_authorization_against_access_rule(api, &auth_zone, &rule)?;
485
486 match result {
487 AuthorizationCheckResult::Authorized => Ok(()),
488 AuthorizationCheckResult::Failed(access_rule_stack) => Err(
489 RuntimeError::SystemModuleError(SystemModuleError::AuthError(
490 AuthError::Unauthorized(Box::new(Unauthorized {
491 failed_access_rules: FailedAccessRules::AccessRule(
492 access_rule_stack,
493 ),
494 fn_identifier,
495 })),
496 )),
497 ),
498 }
499 }
500 ResolvedPermission::RoleList {
501 role_assignment_of,
502 role_list,
503 module_id,
504 } => {
505 let result = Authorization::check_authorization_against_role_list(
506 &auth_zone,
507 &role_assignment_of,
508 module_id,
509 &role_list,
510 api,
511 )?;
512
513 match result {
514 AuthorityListAuthorizationResult::Authorized => Ok(()),
515 AuthorityListAuthorizationResult::Failed(auth_list_fail) => Err(
516 RuntimeError::SystemModuleError(SystemModuleError::AuthError(
517 AuthError::Unauthorized(Box::new(Unauthorized {
518 failed_access_rules: FailedAccessRules::RoleList(auth_list_fail),
519 fn_identifier,
520 })),
521 )),
522 ),
523 }
524 }
525 }
526 }
527
528 fn resolve_method_permission<Y: SystemBasedKernelApi>(
529 system: &mut SystemService<Y>,
530 blueprint_id: &BlueprintId,
531 receiver: &NodeId,
532 module_id: &ModuleId,
533 ident: &str,
534 args: &IndexedScryptoValue,
535 ) -> Result<ResolvedPermission, RuntimeError> {
536 let method_key = MethodKey::new(ident);
537
538 if let ModuleId::RoleAssignment = module_id {
539 let global_address = GlobalAddress::new_or_panic(receiver.0);
541 return RoleAssignmentNativePackage::authorization(
542 &global_address,
543 ident,
544 args,
545 system,
546 );
547 }
548
549 let auth_template = PackageAuthNativeBlueprint::get_bp_auth_template(
550 blueprint_id.package_address.as_node_id(),
551 &BlueprintVersionKey::new_default(blueprint_id.blueprint_name.as_str()),
552 system.api(),
553 )?
554 .method_auth;
555
556 let receiver_object_info = system.get_object_info(&receiver)?;
557
558 let (role_assignment_of, method_permissions) = match auth_template {
559 MethodAuthTemplate::StaticRoleDefinition(static_roles) => {
560 let role_assignment_of = match static_roles.roles {
561 RoleSpecification::Normal(..) => {
562 if !receiver_object_info.is_global() {
564 return Ok(ResolvedPermission::AllowAll);
565 }
566
567 GlobalAddress::new_or_panic(receiver.0)
568 }
569 RoleSpecification::UseOuter => receiver_object_info.get_outer_object(),
570 };
571
572 (role_assignment_of, static_roles.methods)
573 }
574 MethodAuthTemplate::AllowAll => return Ok(ResolvedPermission::AllowAll),
575 };
576
577 match method_permissions.get(&method_key) {
578 Some(MethodAccessibility::Public) => Ok(ResolvedPermission::AllowAll),
579 Some(MethodAccessibility::OwnPackageOnly) => {
580 let package = blueprint_id.package_address;
581 Ok(ResolvedPermission::AccessRule(rule!(require(
582 package_of_direct_caller(package)
583 ))))
584 }
585 Some(MethodAccessibility::OuterObjectOnly) => match module_id {
586 ModuleId::Main => {
587 let outer_object_info = &receiver_object_info.blueprint_info.outer_obj_info;
588 match outer_object_info {
589 OuterObjectInfo::Some { outer_object } => {
590 Ok(ResolvedPermission::AccessRule(rule!(require(
591 global_caller(*outer_object)
592 ))))
593 }
594 OuterObjectInfo::None { .. } => Err(RuntimeError::SystemModuleError(
595 SystemModuleError::AuthError(AuthError::InvalidOuterObjectMapping),
596 )),
597 }
598 }
599 _ => Err(RuntimeError::SystemModuleError(
600 SystemModuleError::AuthError(AuthError::InvalidOuterObjectMapping),
601 )),
602 },
603 Some(MethodAccessibility::RoleProtected(role_list)) => {
604 Ok(ResolvedPermission::RoleList {
605 role_assignment_of,
606 role_list: role_list.clone(),
607 module_id: module_id.clone(),
608 })
609 }
610 None => {
611 let fn_identifier = FnIdentifier {
612 blueprint_id: blueprint_id.clone(),
613 ident: ident.to_string(),
614 };
615 Err(RuntimeError::SystemModuleError(
616 SystemModuleError::AuthError(AuthError::NoMethodMapping(fn_identifier)),
617 ))
618 }
619 }
620 }
621}
622
623impl InitSystemModule for AuthModule {}
624impl ResolvableSystemModule for AuthModule {
625 #[inline]
626 fn resolve_from_system(system: &mut impl HasModules) -> &mut Self {
627 &mut system.modules_mut().auth
628 }
629}
630impl PrivilegedSystemModule for AuthModule {}
631impl<ModuleApi: SystemModuleApiFor<Self>> SystemModule<ModuleApi> for AuthModule {}