ic_backup/policy/control_authority/
mod.rs1use crate::model::{
4 artifacts::ArtifactChecksumRecord,
5 control_authority::{ControlObservation, ControlObservationRequest, ControllerSet},
6};
7use thiserror::Error;
8
9#[derive(Clone, Debug)]
11pub struct ControlAuthorityView<'a> {
12 request: ArtifactChecksumRecord,
13 observation: &'a ControlObservation,
14}
15impl ControlAuthorityView<'_> {
16 #[must_use]
18 pub const fn request(&self) -> &ArtifactChecksumRecord {
19 &self.request
20 }
21 #[must_use]
23 pub fn target(&self) -> &str {
24 self.observation.target()
25 }
26 #[must_use]
28 pub const fn controllers(&self) -> &ControllerSet {
29 self.observation.controllers()
30 }
31 #[must_use]
33 pub const fn evidence(&self) -> &ArtifactChecksumRecord {
34 self.observation.evidence()
35 }
36 #[must_use]
38 pub const fn remote_observations(&self) -> u32 {
39 self.observation.remote_observations()
40 }
41}
42pub fn validate<'a>(
52 request: &ControlObservationRequest<'_>,
53 observation: &'a ControlObservation,
54) -> Result<ControlAuthorityView<'a>, ControlAuthorityError> {
55 let digest = request.digest();
56 if *observation.request() != digest {
57 return Err(ControlAuthorityError::RequestMismatch);
58 }
59 let binding = request.binding();
60 if let Some(field) = super::observed_context::first_mismatch(binding, observation.context()) {
61 return Err(ControlAuthorityError::ContextMismatch(field));
62 }
63 if observation.target() != binding.target() {
64 return Err(ControlAuthorityError::TargetMismatch);
65 }
66 if observation.remote_observations() > request.max_remote_observations() {
67 return Err(ControlAuthorityError::ObservationLimitExceeded {
68 limit: request.max_remote_observations(),
69 reported: observation.remote_observations(),
70 });
71 }
72 if !observation.controllers().contains_caller(binding) {
73 return Err(ControlAuthorityError::CallerNotController);
74 }
75 Ok(ControlAuthorityView {
76 request: digest,
77 observation,
78 })
79}
80#[derive(Debug, Eq, Error, PartialEq)]
82pub enum ControlAuthorityError {
83 #[error("control observation request mismatch")]
85 RequestMismatch,
86 #[error("control observed {0} mismatch")]
88 ContextMismatch(&'static str),
89 #[error("control observed target mismatch")]
91 TargetMismatch,
92 #[error("selected caller is not an observed controller")]
94 CallerNotController,
95 #[error("control reports {reported} observations above ceiling {limit}")]
97 ObservationLimitExceeded {
98 limit: u32,
100 reported: u32,
102 },
103}
104
105#[cfg(test)]
106mod tests;