ic_backup/policy/ic_observation/
mod.rs1use crate::model::{
4 artifacts::ArtifactChecksumRecord,
5 attempt_journal::{AttemptAuthorityRecord, AttemptJournalRecord},
6 ic_lifecycle_reply::{IcLifecycleReply, IcLifecycleReplyError},
7 ic_observation::{
8 IcObservationRequest, IcObservationRequestError, IcObservationResponse,
9 IcObservationResponseInput,
10 },
11 ic_request::{IcManagementMethodRecord, IcManagementRequestRecord},
12 ic_snapshot_reply::{IcSnapshotReply, IcSnapshotReplyError},
13 operation_plan::PlanContextRecord,
14};
15use thiserror::Error;
16
17#[derive(Debug)]
19pub enum IcObservationReplyView<'a> {
20 Inventory(IcSnapshotReply<'a>),
22 Status(IcLifecycleReply<'a>),
24}
25
26#[derive(Debug)]
28pub struct IcObservationResponseView<'a> {
29 response: &'a IcObservationResponse,
30 reply: IcObservationReplyView<'a>,
31}
32impl<'a> IcObservationResponseView<'a> {
33 #[must_use]
35 pub const fn response(&self) -> &'a IcObservationResponse {
36 self.response
37 }
38 #[must_use]
40 pub const fn reply(&self) -> &IcObservationReplyView<'a> {
41 &self.reply
42 }
43}
44
45pub fn validate_response<'a>(
55 request: &IcObservationRequest<'a>,
56 journal: &AttemptJournalRecord,
57 response: &'a IcObservationResponse,
58) -> Result<IcObservationResponseView<'a>, IcObservationAssociationError> {
59 request.validate_journal(journal)?;
60 validate_association(
61 &ObservationAssociation {
62 authority: request.authority(),
63 mutation_attempt: request.mutation_attempt(),
64 observation_attempt: request.observation_attempt(),
65 payload: request.payload(),
66 context: request.plan().context(),
67 },
68 response,
69 )
70}
71
72pub(crate) struct ObservationAssociation<'binding, 'payload> {
73 pub authority: &'binding AttemptAuthorityRecord,
74 pub mutation_attempt: u32,
75 pub observation_attempt: u32,
76 pub payload: &'payload IcManagementRequestRecord,
77 pub context: &'binding PlanContextRecord,
78}
79
80pub(crate) fn validate_association<'a>(
81 binding: &ObservationAssociation<'_, 'a>,
82 response: &'a IcObservationResponse,
83) -> Result<IcObservationResponseView<'a>, IcObservationAssociationError> {
84 validate_claims(
85 &ObservationClaims {
86 authority: binding.authority,
87 mutation_attempt: binding.mutation_attempt,
88 observation_attempt: binding.observation_attempt,
89 request: binding.payload.digest(),
90 context: binding.context,
91 target: binding.payload.target(),
92 },
93 response.input(),
94 )?;
95 let reply = match binding.payload.method() {
96 IcManagementMethodRecord::ListCanisterSnapshots => IcObservationReplyView::Inventory(
97 IcSnapshotReply::decode(binding.payload, &response.input().reply)?,
98 ),
99 _ => IcObservationReplyView::Status(IcLifecycleReply::decode(
101 binding.payload,
102 &response.input().reply,
103 )?),
104 };
105 Ok(IcObservationResponseView { response, reply })
106}
107
108pub(crate) struct ObservationClaims<'a> {
109 pub authority: &'a AttemptAuthorityRecord,
110 pub mutation_attempt: u32,
111 pub observation_attempt: u32,
112 pub request: ArtifactChecksumRecord,
113 pub context: &'a PlanContextRecord,
114 pub target: &'a str,
115}
116
117pub(crate) fn validate_claims(
118 binding: &ObservationClaims<'_>,
119 input: &IcObservationResponseInput,
120) -> Result<(), IcObservationAssociationError> {
121 if input.authority != binding.authority.digest() {
122 return Err(IcObservationAssociationError::AuthorityMismatch);
123 }
124 if input.mutation_attempt != binding.mutation_attempt
125 || input.observation_attempt != binding.observation_attempt
126 {
127 return Err(IcObservationAssociationError::AttemptMismatch);
128 }
129 if input.request != binding.request {
130 return Err(IcObservationAssociationError::RequestMismatch);
131 }
132 if &input.context != binding.context {
133 return Err(IcObservationAssociationError::ContextMismatch);
134 }
135 if input.target != binding.target {
136 return Err(IcObservationAssociationError::TargetMismatch);
137 }
138 Ok(())
139}
140
141#[derive(Debug, Error)]
143pub enum IcObservationAssociationError {
144 #[error(transparent)]
146 Reservation(#[from] IcObservationRequestError),
147 #[error("IC observation response authority mismatch")]
149 AuthorityMismatch,
150 #[error("IC observation response attempt mismatch")]
152 AttemptMismatch,
153 #[error("IC observation response request mismatch")]
155 RequestMismatch,
156 #[error("IC observation response context mismatch")]
158 ContextMismatch,
159 #[error("IC observation response target mismatch")]
161 TargetMismatch,
162 #[error(transparent)]
164 Inventory(#[from] IcSnapshotReplyError),
165 #[error(transparent)]
167 Status(#[from] IcLifecycleReplyError),
168}
169
170mod capture_settlement;
171pub use capture_settlement::{
172 IcCaptureSettlementError, IcCaptureSettlementView, validate_capture_settlement,
173};
174
175mod settlement;
176pub use settlement::{
177 IcLifecycleSettlementError, IcLifecycleSettlementView, validate_lifecycle_settlement,
178};
179
180#[cfg(test)]
181mod tests;