1use std::fmt;
2
3use serde::de::{MapAccess, SeqAccess, Visitor};
4use serde::{Deserialize, Deserializer, Serialize};
5use serde_json::{Map, Number, Value};
6use tea_protocol::{RecordDecodeError, RecordEnvelope, SessionId};
7use thiserror::Error;
8
9use crate::artifact::ArtifactState;
10use crate::{
11 AppendOutcome, AppendTransaction, ApprovalArtifactEntry, GrantJournalEntry, SessionReducer,
12 SessionSnapshot, SessionStore, SessionStoreError, SessionStoreErrorCode, SessionStoreFuture,
13};
14
15pub const CURRENT_ARCHIVE_FORMAT_VERSION: u32 = 1;
17pub const MAX_ARCHIVE_BYTES: usize = 64 * 1024 * 1024;
19pub const MAX_ARCHIVE_ENTRIES: usize = 100_000;
21
22#[derive(Debug, Clone, PartialEq, Serialize)]
24#[serde(rename_all = "camelCase")]
25pub struct SessionArchive {
26 format_version: u32,
27 session_id: SessionId,
28 records: Vec<RecordEnvelope>,
29 approval_artifacts: Vec<ApprovalArtifactEntry>,
30 grant_journal: Vec<GrantJournalEntry>,
31}
32
33impl SessionArchive {
34 pub fn new(
41 session_id: SessionId,
42 records: Vec<RecordEnvelope>,
43 approval_artifacts: Vec<ApprovalArtifactEntry>,
44 grant_journal: Vec<GrantJournalEntry>,
45 ) -> Result<Self, SessionArchiveError> {
46 if records.len() > MAX_ARCHIVE_ENTRIES
47 || approval_artifacts.len() > MAX_ARCHIVE_ENTRIES
48 || grant_journal.len() > MAX_ARCHIVE_ENTRIES
49 {
50 return Err(SessionArchiveError::OutOfBounds);
51 }
52 let archive = Self {
53 format_version: CURRENT_ARCHIVE_FORMAT_VERSION,
54 session_id,
55 records,
56 approval_artifacts,
57 grant_journal,
58 };
59 archive.validate()?;
60 Ok(archive)
61 }
62
63 pub fn from_snapshot(snapshot: &SessionSnapshot) -> Result<Self, SessionArchiveError> {
70 Self::new(
71 snapshot.state().session_id(),
72 snapshot.records().to_vec(),
73 snapshot.approval_artifacts().to_vec(),
74 snapshot.grant_journal().to_vec(),
75 )
76 }
77
78 pub fn decode_json(input: &str) -> Result<Self, SessionArchiveError> {
85 if input.len() > MAX_ARCHIVE_BYTES {
86 return Err(SessionArchiveError::OutOfBounds);
87 }
88 let mut deserializer = serde_json::Deserializer::from_str(input);
89 let value = deserialize_unique_value(&mut deserializer)
90 .map_err(|error| SessionArchiveError::Malformed(error.to_string()))?;
91 deserializer
92 .end()
93 .map_err(|error| SessionArchiveError::Malformed(error.to_string()))?;
94 Self::decode_value(value)
95 }
96
97 pub fn import_into(self, store: &dyn SessionStore) -> SessionStoreFuture<'_, AppendOutcome> {
102 Box::pin(async move {
103 self.validate().map_err(SessionStoreError::from)?;
104 let transaction = AppendTransaction::new(self.session_id, None, self.records)
105 .with_expected_journal_revision(0)
106 .with_approval_artifacts(self.approval_artifacts)
107 .with_grant_entries(self.grant_journal);
108 store.append(transaction).await
109 })
110 }
111
112 #[must_use]
114 pub const fn format_version(&self) -> u32 {
115 self.format_version
116 }
117
118 #[must_use]
120 pub const fn session_id(&self) -> SessionId {
121 self.session_id
122 }
123
124 #[must_use]
126 pub fn records(&self) -> &[RecordEnvelope] {
127 &self.records
128 }
129
130 #[must_use]
132 pub fn approval_artifacts(&self) -> &[ApprovalArtifactEntry] {
133 &self.approval_artifacts
134 }
135
136 #[must_use]
138 pub fn grant_journal(&self) -> &[GrantJournalEntry] {
139 &self.grant_journal
140 }
141
142 fn decode_value(value: Value) -> Result<Self, SessionArchiveError> {
143 let raw: RawSessionArchive = serde_json::from_value(value)
144 .map_err(|error| SessionArchiveError::Malformed(error.to_string()))?;
145 if raw.format_version != CURRENT_ARCHIVE_FORMAT_VERSION {
146 return Err(SessionArchiveError::UnsupportedFormatVersion(
147 raw.format_version,
148 ));
149 }
150 if raw.records.len() > MAX_ARCHIVE_ENTRIES
151 || raw.approval_artifacts.len() > MAX_ARCHIVE_ENTRIES
152 || raw.grant_journal.len() > MAX_ARCHIVE_ENTRIES
153 {
154 return Err(SessionArchiveError::OutOfBounds);
155 }
156 let records = raw
157 .records
158 .into_iter()
159 .map(RecordEnvelope::decode_value)
160 .collect::<Result<Vec<_>, _>>()?;
161 Self::new(
162 raw.session_id,
163 records,
164 raw.approval_artifacts,
165 raw.grant_journal,
166 )
167 }
168
169 fn validate(&self) -> Result<(), SessionArchiveError> {
170 if self.format_version != CURRENT_ARCHIVE_FORMAT_VERSION {
171 return Err(SessionArchiveError::UnsupportedFormatVersion(
172 self.format_version,
173 ));
174 }
175 if self
176 .records
177 .iter()
178 .any(|record| record.session_id() != self.session_id)
179 {
180 return Err(SessionArchiveError::SessionMismatch);
181 }
182 SessionReducer::replay(self.records.clone())?;
183 ArtifactState::default().apply(
184 self.session_id,
185 &self.records,
186 &self.records,
187 &self.approval_artifacts,
188 &self.grant_journal,
189 )?;
190 Ok(())
191 }
192}
193
194#[derive(Deserialize)]
195#[serde(rename_all = "camelCase")]
196struct RawSessionArchive {
197 format_version: u32,
198 session_id: SessionId,
199 records: Vec<Value>,
200 #[serde(default)]
201 approval_artifacts: Vec<ApprovalArtifactEntry>,
202 #[serde(default)]
203 grant_journal: Vec<GrantJournalEntry>,
204}
205
206#[derive(Debug, Error)]
208pub enum SessionArchiveError {
209 #[error("unsupported session archive format version: {0}")]
211 UnsupportedFormatVersion(u32),
212 #[error("session archive exceeds supported bounds")]
214 OutOfBounds,
215 #[error("malformed session archive: {0}")]
217 Malformed(String),
218 #[error("session archive contains another session")]
220 SessionMismatch,
221 #[error("session archive record is invalid: {0}")]
223 Record(#[from] RecordDecodeError),
224 #[error("session archive replay failed: {0}")]
226 Replay(#[from] crate::SessionReplayError),
227 #[error("session archive policy journal failed: {0}")]
229 Artifact(#[from] crate::ArtifactValidationError),
230}
231
232impl SessionArchiveError {
233 #[must_use]
235 pub const fn store_code(&self) -> SessionStoreErrorCode {
236 match self {
237 Self::UnsupportedFormatVersion(_)
238 | Self::Record(
239 RecordDecodeError::UnsupportedVersion { .. }
240 | RecordDecodeError::UnsupportedType { .. },
241 ) => SessionStoreErrorCode::UnsupportedSchemaVersion,
242 Self::Replay(error) => error.store_code(),
243 Self::Artifact(error) => error.store_code(),
244 Self::OutOfBounds | Self::Malformed(_) | Self::SessionMismatch | Self::Record(_) => {
245 SessionStoreErrorCode::InvalidRecord
246 }
247 }
248 }
249}
250
251impl From<SessionArchiveError> for SessionStoreError {
252 fn from(error: SessionArchiveError) -> Self {
253 Self::new(error.store_code(), error.to_string())
254 }
255}
256
257fn deserialize_unique_value<'de, D>(deserializer: D) -> Result<Value, D::Error>
258where
259 D: Deserializer<'de>,
260{
261 deserializer.deserialize_any(UniqueValueVisitor)
262}
263
264struct UniqueValueVisitor;
265
266impl<'de> Visitor<'de> for UniqueValueVisitor {
267 type Value = Value;
268
269 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
270 formatter.write_str("a JSON value without duplicate object keys")
271 }
272
273 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> {
274 Ok(Value::Bool(value))
275 }
276
277 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> {
278 Ok(Value::Number(Number::from(value)))
279 }
280
281 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> {
282 Ok(Value::Number(Number::from(value)))
283 }
284
285 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
286 where
287 E: serde::de::Error,
288 {
289 Number::from_f64(value)
290 .map(Value::Number)
291 .ok_or_else(|| E::custom("non-finite JSON number"))
292 }
293
294 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> {
295 Ok(Value::String(value.to_owned()))
296 }
297
298 fn visit_string<E>(self, value: String) -> Result<Self::Value, E> {
299 Ok(Value::String(value))
300 }
301
302 fn visit_none<E>(self) -> Result<Self::Value, E> {
303 Ok(Value::Null)
304 }
305
306 fn visit_unit<E>(self) -> Result<Self::Value, E> {
307 Ok(Value::Null)
308 }
309
310 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
311 where
312 D: Deserializer<'de>,
313 {
314 deserialize_unique_value(deserializer)
315 }
316
317 fn visit_seq<A>(self, mut sequence: A) -> Result<Self::Value, A::Error>
318 where
319 A: SeqAccess<'de>,
320 {
321 let mut values = Vec::with_capacity(sequence.size_hint().unwrap_or(0));
322 while let Some(value) = sequence.next_element_seed(UniqueValueSeed)? {
323 values.push(value);
324 }
325 Ok(Value::Array(values))
326 }
327
328 fn visit_map<A>(self, mut object: A) -> Result<Self::Value, A::Error>
329 where
330 A: MapAccess<'de>,
331 {
332 let mut values = Map::new();
333 while let Some(key) = object.next_key::<String>()? {
334 if values.contains_key(&key) {
335 return Err(serde::de::Error::custom(format_args!(
336 "duplicate JSON object key: {key}"
337 )));
338 }
339 let value = object.next_value_seed(UniqueValueSeed)?;
340 values.insert(key, value);
341 }
342 Ok(Value::Object(values))
343 }
344}
345
346struct UniqueValueSeed;
347
348impl<'de> serde::de::DeserializeSeed<'de> for UniqueValueSeed {
349 type Value = Value;
350
351 fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
352 where
353 D: Deserializer<'de>,
354 {
355 deserialize_unique_value(deserializer)
356 }
357}