anda_engine/management.rs
1//! Engine visibility and caller management policies.
2//!
3//! Management policies decide which principals can administer an engine and who
4//! can access exported agents and tools. The default [`BaseManagement`] policy
5//! makes engines private to the controller and explicit managers.
6
7use anda_core::BoxError;
8use async_trait::async_trait;
9use candid::Principal;
10use ic_auth_verifier::ANONYMOUS_PRINCIPAL;
11use std::collections::BTreeSet;
12
13/// Root cache and storage namespace reserved for engine system data.
14pub static SYSTEM_PATH: &str = "_";
15
16/// Authorization policy used by [`Engine`](crate::engine::Engine).
17#[async_trait]
18pub trait Management: Send + Sync {
19 /// Returns whether `caller` is the engine controller.
20 fn is_controller(&self, caller: &Principal) -> bool;
21
22 /// Returns whether `caller` can manage private engine state.
23 fn is_manager(&self, caller: &Principal) -> bool;
24
25 /// Validates access and returns the current engine visibility.
26 fn check_visibility(&self, caller: &Principal) -> Result<Visibility, BoxError>;
27}
28
29/// Basic principal-list management policy for an engine.
30pub struct BaseManagement {
31 /// Principal that controls the engine.
32 pub controller: Principal,
33
34 /// Additional principals with manager privileges.
35 pub managers: BTreeSet<Principal>,
36
37 /// Public access level for non-manager callers.
38 pub visibility: Visibility,
39}
40
41/// Engine visibility for exported agents and tools.
42#[derive(Clone, Copy, PartialEq, Eq)]
43pub enum Visibility {
44 /// Only the controller and managers can access the engine.
45 Private = 0,
46
47 /// Managers can access the engine; non-manager access requires an external
48 /// policy to grant permission before execution.
49 Protected = 1,
50
51 /// Any caller, including anonymous callers, can access exported functions.
52 Public = 2,
53}
54
55#[async_trait]
56impl Management for BaseManagement {
57 /// Returns true if the caller is the controller of the engine.
58 fn is_controller(&self, caller: &Principal) -> bool {
59 caller == &self.controller
60 }
61
62 /// Returns true if the caller is the controller or a manager of the engine.
63 fn is_manager(&self, caller: &Principal) -> bool {
64 caller == &self.controller || self.managers.contains(caller)
65 }
66
67 /// Checks anonymous access and private visibility rules.
68 fn check_visibility(&self, caller: &Principal) -> Result<Visibility, BoxError> {
69 if self.visibility != Visibility::Public && caller == &ANONYMOUS_PRINCIPAL {
70 return Err("anonymous caller not allowed".into());
71 }
72
73 if self.visibility == Visibility::Private && !self.is_manager(caller) {
74 return Err("caller is not allowed".into());
75 }
76
77 Ok(self.visibility)
78 }
79}