cougr_core/accounts/
policy.rs1use soroban_sdk::{Address, BytesN, Env};
2
3use super::device_storage::DeviceStorage;
4use super::error::AccountError;
5use super::intent::SignedIntent;
6use super::recovery_storage::RecoveryStorage;
7use super::storage::SessionStorage;
8
9pub trait Policy<C> {
11 fn evaluate(&self, env: &Env, context: &C) -> Result<(), AccountError>;
12}
13
14pub struct IntentContext<'a> {
15 pub account: &'a Address,
16 pub intent: &'a SignedIntent,
17}
18
19pub struct SessionContext<'a> {
20 pub account: &'a Address,
21 pub intent: &'a SignedIntent,
22}
23
24pub struct DeviceContext<'a> {
25 pub account: &'a Address,
26 pub key_id: &'a BytesN<32>,
27}
28
29pub struct RecoveryContext<'a> {
30 pub account: &'a Address,
31 pub guardian: &'a Address,
32}
33
34pub struct IntentExpiryPolicy;
36
37impl Policy<IntentContext<'_>> for IntentExpiryPolicy {
38 fn evaluate(&self, env: &Env, context: &IntentContext<'_>) -> Result<(), AccountError> {
39 if env.ledger().timestamp() >= context.intent.expires_at {
40 return Err(AccountError::IntentExpired);
41 }
42 Ok(())
43 }
44}
45
46pub struct SessionPolicy;
48
49impl Policy<SessionContext<'_>> for SessionPolicy {
50 fn evaluate(&self, env: &Env, context: &SessionContext<'_>) -> Result<(), AccountError> {
51 let session =
52 SessionStorage::load(env, context.account, &context.intent.signer.session_key_id)
53 .ok_or(AccountError::SessionRevoked)?;
54
55 if env.ledger().timestamp() >= session.scope.expires_at {
56 return Err(AccountError::SessionExpired);
57 }
58 if session.operations_used >= session.scope.max_operations {
59 return Err(AccountError::SessionBudgetExceeded);
60 }
61 if session.next_nonce != context.intent.nonce {
62 return Err(AccountError::NonceMismatch);
63 }
64 if !SessionStorage::is_action_allowed(&session.scope, &context.intent.action.system_name) {
65 return Err(AccountError::ActionNotAllowed);
66 }
67 Ok(())
68 }
69}
70
71pub struct ActiveDevicePolicy;
73
74impl Policy<DeviceContext<'_>> for ActiveDevicePolicy {
75 fn evaluate(&self, env: &Env, context: &DeviceContext<'_>) -> Result<(), AccountError> {
76 let devices = DeviceStorage::load_devices(env, context.account);
77 for i in 0..devices.len() {
78 if let Some(device) = devices.get(i) {
79 if &device.key_id == context.key_id && device.is_active {
80 return Ok(());
81 }
82 }
83 }
84 Err(AccountError::DeviceNotFound)
85 }
86}
87
88pub struct GuardianPolicy;
90
91impl Policy<RecoveryContext<'_>> for GuardianPolicy {
92 fn evaluate(&self, env: &Env, context: &RecoveryContext<'_>) -> Result<(), AccountError> {
93 let guardians = RecoveryStorage::load_guardians(env, context.account);
94 for i in 0..guardians.len() {
95 if let Some(guardian) = guardians.get(i) {
96 if &guardian.address == context.guardian {
97 return Ok(());
98 }
99 }
100 }
101 Err(AccountError::Unauthorized)
102 }
103}