ic_backup/policy/membership/
mod.rs1use crate::model::{
4 artifacts::ArtifactChecksumRecord,
5 inventory::InventoryRecord,
6 membership::{MembershipObservation, MembershipObservationRequest},
7};
8use thiserror::Error;
9
10#[derive(Clone, Debug)]
12pub struct MembershipView<'a> {
13 request: ArtifactChecksumRecord,
14 inventory: &'a InventoryRecord,
15 selected_targets: &'a [String],
16 revision: Option<&'a ArtifactChecksumRecord>,
17 evidence: &'a ArtifactChecksumRecord,
18 remote_observations: u32,
19}
20
21impl MembershipView<'_> {
22 #[must_use]
24 pub const fn request(&self) -> &ArtifactChecksumRecord {
25 &self.request
26 }
27 #[must_use]
29 pub const fn inventory(&self) -> &InventoryRecord {
30 self.inventory
31 }
32 #[must_use]
34 pub const fn selected_targets(&self) -> &[String] {
35 self.selected_targets
36 }
37 #[must_use]
39 pub const fn revision(&self) -> Option<&ArtifactChecksumRecord> {
40 self.revision
41 }
42 #[must_use]
44 pub const fn evidence(&self) -> &ArtifactChecksumRecord {
45 self.evidence
46 }
47 #[must_use]
49 pub const fn remote_observations(&self) -> u32 {
50 self.remote_observations
51 }
52}
53
54pub fn validate<'a>(
62 request: &'a MembershipObservationRequest<'_>,
63 observation: &'a MembershipObservation,
64) -> Result<MembershipView<'a>, MembershipError> {
65 let digest = request.digest();
66 let binding = request.binding();
67 super::observed_context::admit_request_context(
68 &digest,
69 &observation.request,
70 binding,
71 &observation.context,
72 MembershipError::RequestMismatch,
73 MembershipError::ContextMismatch,
74 )?;
75 if observation.inventory != *request.inventory() {
76 return Err(MembershipError::InventoryMismatch);
77 }
78 super::observed_context::admit_limit(
79 request.max_remote_observations(),
80 observation.remote_observations,
81 |limit, reported| MembershipError::ObservationLimitExceeded { limit, reported },
82 )?;
83 Ok(MembershipView {
84 request: digest,
85 inventory: &observation.inventory,
86 selected_targets: request.selected_targets(),
87 revision: observation.revision.as_ref(),
88 evidence: &observation.evidence,
89 remote_observations: observation.remote_observations,
90 })
91}
92
93#[derive(Debug, Eq, Error, PartialEq)]
95pub enum MembershipError {
96 #[error("membership request identity mismatch")]
98 RequestMismatch,
99 #[error("membership observed {0} mismatch")]
101 ContextMismatch(&'static str),
102 #[error("membership inventory mismatch")]
104 InventoryMismatch,
105 #[error("membership reports {reported} observations above ceiling {limit}")]
107 ObservationLimitExceeded {
108 limit: u32,
110 reported: u32,
112 },
113}
114
115#[cfg(test)]
116mod tests;