use crate::{
ids::{EndpointCall, EndpointCallKind},
protocol::CANIC_FLEET_ACTIVATION_STATUS,
};
use thiserror::Error as ThisError;
#[derive(Debug, Eq, PartialEq, ThisError)]
pub enum FleetActivationEndpointPolicyError {
#[error("endpoint {endpoint} ({kind:?}) is fenced while the managed Canister is Prepared")]
Fenced {
endpoint: &'static str,
kind: EndpointCallKind,
},
}
pub fn require_prepared_nonroot_endpoint(
call: EndpointCall,
) -> Result<(), FleetActivationEndpointPolicyError> {
require_prepared_status_query(call)
}
pub fn require_prepared_root_endpoint(
call: EndpointCall,
) -> Result<(), FleetActivationEndpointPolicyError> {
require_prepared_status_query(call)
}
fn require_prepared_status_query(
call: EndpointCall,
) -> Result<(), FleetActivationEndpointPolicyError> {
if call.kind == EndpointCallKind::Query && call.endpoint.name == CANIC_FLEET_ACTIVATION_STATUS {
return Ok(());
}
Err(FleetActivationEndpointPolicyError::Fenced {
endpoint: call.endpoint.name,
kind: call.kind,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ids::EndpointId;
fn call(name: &'static str, kind: EndpointCallKind) -> EndpointCall {
EndpointCall {
endpoint: EndpointId::new(name),
kind,
}
}
#[test]
fn prepared_root_admits_only_implemented_recovery_inspection() {
assert_eq!(
require_prepared_root_endpoint(call(
CANIC_FLEET_ACTIVATION_STATUS,
EndpointCallKind::Query,
)),
Ok(())
);
}
#[test]
fn prepared_root_rejects_ordinary_and_wrong_kind_calls() {
for (endpoint, kind) in [
("application_update", EndpointCallKind::Update),
("canic_sync_state", EndpointCallKind::Update),
("canic_upsert_root_issuer_policy", EndpointCallKind::Update),
(CANIC_FLEET_ACTIVATION_STATUS, EndpointCallKind::Update),
(
CANIC_FLEET_ACTIVATION_STATUS,
EndpointCallKind::QueryComposite,
),
] {
assert_eq!(
require_prepared_root_endpoint(call(endpoint, kind)),
Err(FleetActivationEndpointPolicyError::Fenced { endpoint, kind })
);
}
}
#[test]
fn prepared_nonroot_uses_its_own_exact_recovery_allowlist() {
assert_eq!(
require_prepared_nonroot_endpoint(call(
CANIC_FLEET_ACTIVATION_STATUS,
EndpointCallKind::Query,
)),
Ok(())
);
for (endpoint, kind) in [
("application_update", EndpointCallKind::Update),
("canic_sync_state", EndpointCallKind::Update),
(
"canic_active_delegation_proof_status",
EndpointCallKind::Query,
),
(CANIC_FLEET_ACTIVATION_STATUS, EndpointCallKind::Update),
] {
assert_eq!(
require_prepared_nonroot_endpoint(call(endpoint, kind)),
Err(FleetActivationEndpointPolicyError::Fenced { endpoint, kind })
);
}
}
}