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 #[allow(clippy::type_complexity)]
242 fn copy_global_caller<Y: SystemBasedKernelApi>(
243 system: &mut SystemService<Y>,
244 direct_caller_auth_zone_id: &NodeId,
245 ) -> Result<(Option<(GlobalCaller, Reference)>, Option<SubstateHandle>), RuntimeError> {
246 let direct_caller_auth_zone_handle = system.kernel_open_substate(
247 direct_caller_auth_zone_id,
248 MAIN_BASE_PARTITION,
249 &AuthZoneField::AuthZone.into(),
250 LockFlags::read_only(),
251 SystemLockData::default(),
252 )?;
253
254 let direct_caller_auth_zone = system
255 .kernel_read_substate(direct_caller_auth_zone_handle)?
256 .as_typed::<FieldSubstate<AuthZone>>()
257 .unwrap();
258
259 Ok((
260 direct_caller_auth_zone.into_payload().global_caller,
261 Some(direct_caller_auth_zone_handle),
262 ))
263 }
264
265 pub(crate) fn create_auth_zone<Y: SystemBasedKernelApi>(
266 system: &mut SystemService<Y>,
267 receiver: Option<(&NodeId, bool)>,
268 simulate_all_proofs_under_resources: BTreeSet<ResourceAddress>,
269 implicit_non_fungible_proofs: BTreeSet<NonFungibleGlobalId>,
270 ) -> Result<NodeId, RuntimeError> {
271 let (auth_zone, parent_lock_handle) = {
272 let is_global_context_change = if let Some((receiver, direct_access)) = receiver {
273 let object_info = system.get_object_info(receiver)?;
274 object_info.is_global() || direct_access
275 } else {
276 true
277 };
278
279 let direct_caller = system.current_actor();
280 let direct_caller_package_address = direct_caller.package_address();
281
282 let (global_caller, parent_lock_handle) = match direct_caller {
284 Actor::Root | Actor::BlueprintHook(..) => (None, None),
285 Actor::Method(direct_caller_method_actor) => {
286 let direct_caller_ancestor_visibility_origin = system
287 .kernel_get_node_visibility_uncosted(&direct_caller_method_actor.node_id)
288 .reference_origin(direct_caller_method_actor.node_id)
289 .unwrap();
290 let direct_caller_auth_zone = direct_caller_method_actor.auth_zone;
291
292 match (
305 direct_caller_ancestor_visibility_origin,
306 is_global_context_change,
307 ) {
308 (ReferenceOrigin::Global(global_root_address), true) => {
310 let global_caller_address = global_root_address.into();
311 let global_caller_leaf_auth_zone_reference =
312 Reference(direct_caller_auth_zone);
313 (
314 Some((
315 global_caller_address,
316 global_caller_leaf_auth_zone_reference,
317 )),
318 None,
319 )
320 }
321 (ReferenceOrigin::Global(..), false) => {
323 Self::copy_global_caller(system, &direct_caller_auth_zone)?
324 }
325 (ReferenceOrigin::DirectlyAccessed, _) => (None, None),
327 (ReferenceOrigin::SubstateNonGlobalReference(..), _) => (None, None),
329 (ReferenceOrigin::FrameOwned, _) => {
331 let (caller, lock_handle) =
344 Self::copy_global_caller(system, &direct_caller_auth_zone)?;
345
346 let global_caller = match caller {
349 Some(_) => {
350 let global_caller_address = FRAME_OWNED_GLOBAL_MARKER.into();
351 let global_caller_leaf_auth_zone_reference =
354 Reference(direct_caller_auth_zone);
355 Some((
356 global_caller_address,
357 global_caller_leaf_auth_zone_reference,
358 ))
359 }
360 None => None,
361 };
362
363 (global_caller, lock_handle)
364 }
365 }
366 }
367 Actor::Function(function_actor) => {
368 let direct_caller_auth_zone = function_actor.auth_zone;
369 let global_caller = function_actor.as_global_caller();
370 if is_global_context_change {
371 (
372 Some((global_caller, Reference(direct_caller_auth_zone))),
373 None,
374 )
375 } else {
376 Self::copy_global_caller(system, &direct_caller_auth_zone)?
377 }
378 }
379 };
380
381 let auth_zone_parent = if is_global_context_change {
382 None
383 } else {
384 system.current_actor().self_auth_zone().map(Reference)
385 };
386
387 let auth_zone = AuthZone::new(
388 vec![],
389 simulate_all_proofs_under_resources,
390 implicit_non_fungible_proofs,
391 direct_caller_package_address,
392 global_caller,
393 auth_zone_parent,
394 );
395
396 (auth_zone, parent_lock_handle)
397 };
398
399 let new_auth_zone = system
401 .api()
402 .kernel_allocate_node_id(EntityType::InternalGenericComponent)?;
403
404 system.api().kernel_create_node(
405 new_auth_zone,
406 btreemap!(
407 MAIN_BASE_PARTITION => btreemap!(
408 AuthZoneField::AuthZone.into() => IndexedScryptoValue::from_typed(&FieldSubstate::new_unlocked_field(auth_zone))
409 ),
410 TYPE_INFO_FIELD_PARTITION => type_info_partition(TypeInfoSubstate::Object(ObjectInfo {
411 blueprint_info: BlueprintInfo {
412 blueprint_id: BlueprintId::new(&RESOURCE_PACKAGE, AUTH_ZONE_BLUEPRINT),
413 blueprint_version: BlueprintVersion::default(),
414 outer_obj_info: OuterObjectInfo::default(),
415 features: indexset!(),
416 generic_substitutions: vec![],
417 },
418 object_type: ObjectType::Owned,
419 }))
420 ),
421 )?;
422 system.api().kernel_pin_node(new_auth_zone)?;
423
424 if let Some(parent_lock_handle) = parent_lock_handle {
425 system.kernel_close_substate(parent_lock_handle)?;
426 }
427
428 Ok(new_auth_zone)
429 }
430
431 pub fn teardown_auth_zone<Y: SystemBasedKernelApi>(
432 api: &mut SystemService<Y>,
433 self_auth_zone: NodeId,
434 ) -> Result<(), RuntimeError> {
435 let handle = api.kernel_open_substate(
437 &self_auth_zone,
438 MAIN_BASE_PARTITION,
439 &AuthZoneField::AuthZone.into(),
440 LockFlags::MUTABLE,
441 SystemLockData::Default,
442 )?;
443 let mut auth_zone = api
444 .kernel_read_substate(handle)?
445 .as_typed::<FieldSubstate<AuthZone>>()
446 .unwrap()
447 .into_payload();
448 let proofs = core::mem::take(&mut auth_zone.proofs);
449 api.kernel_write_substate(
450 handle,
451 IndexedScryptoValue::from_typed(&FieldSubstate::new_unlocked_field(auth_zone)),
452 )?;
453 api.kernel_close_substate(handle)?;
454
455 for proof in proofs {
457 let object_info = api.get_object_info(proof.0.as_node_id())?;
458 api.call_function(
459 RESOURCE_PACKAGE,
460 &object_info.blueprint_info.blueprint_id.blueprint_name,
461 PROOF_DROP_IDENT,
462 scrypto_encode(&ProofDropInput { proof }).unwrap(),
463 )?;
464 }
465
466 api.kernel_drop_node(&self_auth_zone)?;
468
469 Ok(())
470 }
471
472 fn check_permission<Y: SystemBasedKernelApi>(
473 auth_zone: &NodeId,
474 resolved_permission: ResolvedPermission,
475 fn_identifier: FnIdentifier,
476 api: &mut SystemService<Y>,
477 ) -> Result<(), RuntimeError> {
478 match resolved_permission {
479 ResolvedPermission::AllowAll => Ok(()),
480 ResolvedPermission::AccessRule(rule) => {
481 let result =
482 Authorization::check_authorization_against_access_rule(api, auth_zone, &rule)?;
483
484 match result {
485 AuthorizationCheckResult::Authorized => Ok(()),
486 AuthorizationCheckResult::Failed(access_rule_stack) => Err(
487 RuntimeError::SystemModuleError(SystemModuleError::AuthError(
488 AuthError::Unauthorized(Box::new(Unauthorized {
489 failed_access_rules: FailedAccessRules::AccessRule(
490 access_rule_stack,
491 ),
492 fn_identifier,
493 })),
494 )),
495 ),
496 }
497 }
498 ResolvedPermission::RoleList {
499 role_assignment_of,
500 role_list,
501 module_id,
502 } => {
503 let result = Authorization::check_authorization_against_role_list(
504 auth_zone,
505 &role_assignment_of,
506 module_id,
507 &role_list,
508 api,
509 )?;
510
511 match result {
512 AuthorityListAuthorizationResult::Authorized => Ok(()),
513 AuthorityListAuthorizationResult::Failed(auth_list_fail) => Err(
514 RuntimeError::SystemModuleError(SystemModuleError::AuthError(
515 AuthError::Unauthorized(Box::new(Unauthorized {
516 failed_access_rules: FailedAccessRules::RoleList(auth_list_fail),
517 fn_identifier,
518 })),
519 )),
520 ),
521 }
522 }
523 }
524 }
525
526 fn resolve_method_permission<Y: SystemBasedKernelApi>(
527 system: &mut SystemService<Y>,
528 blueprint_id: &BlueprintId,
529 receiver: &NodeId,
530 module_id: &ModuleId,
531 ident: &str,
532 args: &IndexedScryptoValue,
533 ) -> Result<ResolvedPermission, RuntimeError> {
534 let method_key = MethodKey::new(ident);
535
536 if let ModuleId::RoleAssignment = module_id {
537 let global_address = GlobalAddress::new_or_panic(receiver.0);
539 return RoleAssignmentNativePackage::authorization(
540 &global_address,
541 ident,
542 args,
543 system,
544 );
545 }
546
547 let auth_template = PackageAuthNativeBlueprint::get_bp_auth_template(
548 blueprint_id.package_address.as_node_id(),
549 &BlueprintVersionKey::new_default(blueprint_id.blueprint_name.as_str()),
550 system.api(),
551 )?
552 .method_auth;
553
554 let receiver_object_info = system.get_object_info(receiver)?;
555
556 let (role_assignment_of, method_permissions) = match auth_template {
557 MethodAuthTemplate::StaticRoleDefinition(static_roles) => {
558 let role_assignment_of = match static_roles.roles {
559 RoleSpecification::Normal(..) => {
560 if !receiver_object_info.is_global() {
562 return Ok(ResolvedPermission::AllowAll);
563 }
564
565 GlobalAddress::new_or_panic(receiver.0)
566 }
567 RoleSpecification::UseOuter => receiver_object_info.get_outer_object(),
568 };
569
570 (role_assignment_of, static_roles.methods)
571 }
572 MethodAuthTemplate::AllowAll => return Ok(ResolvedPermission::AllowAll),
573 };
574
575 match method_permissions.get(&method_key) {
576 Some(MethodAccessibility::Public) => Ok(ResolvedPermission::AllowAll),
577 Some(MethodAccessibility::OwnPackageOnly) => {
578 let package = blueprint_id.package_address;
579 Ok(ResolvedPermission::AccessRule(rule!(require(
580 package_of_direct_caller(package)
581 ))))
582 }
583 Some(MethodAccessibility::OuterObjectOnly) => match module_id {
584 ModuleId::Main => {
585 let outer_object_info = &receiver_object_info.blueprint_info.outer_obj_info;
586 match outer_object_info {
587 OuterObjectInfo::Some { outer_object } => {
588 Ok(ResolvedPermission::AccessRule(rule!(require(
589 global_caller(*outer_object)
590 ))))
591 }
592 OuterObjectInfo::None => Err(RuntimeError::SystemModuleError(
593 SystemModuleError::AuthError(AuthError::InvalidOuterObjectMapping),
594 )),
595 }
596 }
597 _ => Err(RuntimeError::SystemModuleError(
598 SystemModuleError::AuthError(AuthError::InvalidOuterObjectMapping),
599 )),
600 },
601 Some(MethodAccessibility::RoleProtected(role_list)) => {
602 Ok(ResolvedPermission::RoleList {
603 role_assignment_of,
604 role_list: role_list.clone(),
605 module_id: *module_id,
606 })
607 }
608 None => {
609 let fn_identifier = FnIdentifier {
610 blueprint_id: blueprint_id.clone(),
611 ident: ident.to_string(),
612 };
613 Err(RuntimeError::SystemModuleError(
614 SystemModuleError::AuthError(AuthError::NoMethodMapping(fn_identifier)),
615 ))
616 }
617 }
618 }
619}
620
621impl Default for AuthModule {
622 fn default() -> Self {
623 Self::new()
624 }
625}
626
627impl InitSystemModule for AuthModule {}
628impl ResolvableSystemModule for AuthModule {
629 #[inline]
630 fn resolve_from_system(system: &mut impl HasModules) -> &mut Self {
631 &mut system.modules_mut().auth
632 }
633}
634impl PrivilegedSystemModule for AuthModule {}
635impl<ModuleApi: SystemModuleApiFor<Self>> SystemModule<ModuleApi> for AuthModule {}