1use crate::{
2 access::{AccessError, AccessRuleError, AccessRuleResult},
3 cdk::{api::is_controller as caller_is_controller, types::Principal},
4 config::Config,
5 ops::runtime::env::EnvOps,
6};
7use thiserror::Error as ThisError;
8
9#[derive(Debug, ThisError)]
14pub enum EnvAccessError {
15 #[error("this endpoint is only available on the prime subnet")]
16 NotPrimeSubnet,
17
18 #[error("this endpoint is only available on prime root")]
19 NotPrimeRoot,
20
21 #[error("operation must be called from the root canister")]
22 NotRoot,
23
24 #[error("operation cannot be called from the root canister")]
25 IsRoot,
26}
27
28#[allow(clippy::unused_async)]
33pub async fn self_is_root() -> Result<(), AccessError> {
34 if EnvOps::is_root() {
35 Ok(())
36 } else {
37 Err(EnvAccessError::NotRoot.into())
38 }
39}
40
41#[allow(clippy::unused_async)]
42pub async fn self_is_not_root() -> Result<(), AccessError> {
43 if EnvOps::is_root() {
44 Err(EnvAccessError::IsRoot.into())
45 } else {
46 Ok(())
47 }
48}
49
50#[allow(clippy::unused_async)]
51pub async fn is_prime_root() -> Result<(), AccessError> {
52 if EnvOps::is_prime_root() {
53 Ok(())
54 } else {
55 Err(EnvAccessError::NotPrimeRoot.into())
56 }
57}
58
59#[allow(clippy::unused_async)]
60pub async fn is_prime_subnet() -> Result<(), AccessError> {
61 if EnvOps::is_prime_subnet() {
62 Ok(())
63 } else {
64 Err(EnvAccessError::NotPrimeSubnet.into())
65 }
66}
67
68#[must_use]
75pub fn is_controller(caller: Principal) -> AccessRuleResult {
76 Box::pin(async move {
77 if caller_is_controller(&caller) {
78 Ok(())
79 } else {
80 Err(AccessRuleError::NotController(caller).into())
81 }
82 })
83}
84
85#[must_use]
88pub fn is_whitelisted(caller: Principal) -> AccessRuleResult {
89 Box::pin(async move {
90 let cfg = Config::try_get().ok_or_else(|| {
91 AccessRuleError::DependencyUnavailable("config not initialized".to_string())
92 })?;
93
94 if !cfg.is_whitelisted(&caller) {
95 return Err(AccessRuleError::NotWhitelisted(caller).into());
96 }
97
98 Ok(())
99 })
100}