33pub ( crate ) mod rules;
44pub ( crate ) mod utils;
55
6- use std:: { collections:: HashMap , sync:: LazyLock , time:: SystemTime } ;
6+ use std:: {
7+ collections:: HashMap ,
8+ sync:: LazyLock ,
9+ time:: { Duration , SystemTime } ,
10+ } ;
711
12+ use anyhow:: Context ;
813use catalyst_types:: { id_uri:: IdUri , problem_report:: ProblemReport , uuid:: Uuid } ;
914use coset:: { CoseSign , CoseSignature } ;
1015use rules:: {
@@ -115,7 +120,7 @@ fn document_rules_init() -> HashMap<Uuid, Rules> {
115120/// # Errors
116121/// If `provider` returns error, fails fast throwing that error.
117122pub async fn validate < Provider > (
118- doc : & CatalystSignedDocument , future_threshold : u64 , past_threshold : u64 , provider : & Provider ,
123+ doc : & CatalystSignedDocument , provider : & Provider ,
119124) -> anyhow:: Result < bool >
120125where Provider : CatalystSignedDocumentProvider {
121126 let Ok ( doc_type) = doc. doc_type ( ) else {
@@ -126,7 +131,7 @@ where Provider: CatalystSignedDocumentProvider {
126131 return Ok ( false ) ;
127132 } ;
128133
129- if !validate_id_and_ver ( doc, future_threshold , past_threshold ) ? {
134+ if !validate_id_and_ver ( doc, provider ) ? {
130135 return Ok ( false ) ;
131136 }
132137
@@ -144,13 +149,15 @@ where Provider: CatalystSignedDocumentProvider {
144149
145150/// Validates document `id` and `ver` fields on the timestamps:
146151/// 1. document `ver` cannot be smaller than document id field
147- /// 2. document `id` cannot be too far in the future (`future_threshold` arg) from
148- /// `SystemTime::now()` based on the provide threshold
149- /// 3. document `id` cannot be too far behind (`past_threshold` arg) from
150- /// `SystemTime::now()` based on the provide threshold
151- fn validate_id_and_ver (
152- doc : & CatalystSignedDocument , future_threshold : u64 , past_threshold : u64 ,
153- ) -> anyhow:: Result < bool > {
152+ /// 2. If `provider.future_threshold()` not `None`, document `id` cannot be too far in the
153+ /// future (`future_threshold` arg) from `SystemTime::now()` based on the provide
154+ /// threshold
155+ /// 3. If `provider.future_threshold()` not `None`, document `id` cannot be too far behind
156+ /// (`past_threshold` arg) from `SystemTime::now()` based on the provide threshold
157+ fn validate_id_and_ver < Provider > (
158+ doc : & CatalystSignedDocument , provider : & Provider ,
159+ ) -> anyhow:: Result < bool >
160+ where Provider : CatalystSignedDocumentProvider {
154161 let id = doc. doc_id ( ) . ok ( ) ;
155162 let ver = doc. doc_ver ( ) . ok ( ) ;
156163 if id. is_none ( ) {
@@ -178,39 +185,58 @@ fn validate_id_and_ver(
178185 is_valid = false ;
179186 }
180187
181- let ( id_time , _ ) = id
188+ let ( ver_time_secs , ver_time_nanos ) = ver
182189 . uuid ( )
183190 . get_timestamp ( )
184- . ok_or ( anyhow:: anyhow!( "Document id field must be a UUIDv7" ) ) ?
191+ . ok_or ( anyhow:: anyhow!( "Document ver field must be a UUIDv7" ) ) ?
185192 . to_unix ( ) ;
186193
187- let now = SystemTime :: now ( )
188- . duration_since ( SystemTime :: UNIX_EPOCH )
189- . map_err ( |_| {
190- anyhow:: anyhow!(
191- "Cannot validate document id field, SystemTime before UNIX EPOCH!"
192- )
193- } ) ?
194- . as_secs ( ) ;
195-
196- if id_time > now. saturating_add ( future_threshold) {
197- doc. report ( ) . invalid_value (
198- "id" ,
199- & ver. to_string ( ) ,
200- "id < now + future_threshold" ,
201- & format ! ( "Document ID timestamp {id} cannot be too far in future (threshold: {future_threshold}) from now: {now}" ) ,
202- ) ;
203- is_valid = false ;
204- }
205- if id_time < now. saturating_sub ( past_threshold) {
194+ let Some ( ver_time) =
195+ SystemTime :: UNIX_EPOCH . checked_add ( Duration :: new ( ver_time_secs, ver_time_nanos) )
196+ else {
206197 doc. report ( ) . invalid_value (
207- "id " ,
198+ "ver " ,
208199 & ver. to_string ( ) ,
209- "id > now - past_threshold " ,
210- & format ! ( "Document ID timestamp {id} cannot be too far behind (threshold: {past_threshold}) from now: {now}" ) ,
200+ "Must a valid duration since `UNIX_EPOCH` " ,
201+ "Cannot instantiate a valid `SystemTime` value from the provided `ver` field timestamp." ,
211202 ) ;
212- is_valid = false ;
203+ return Ok ( false ) ;
204+ } ;
205+
206+ let now = SystemTime :: now ( ) ;
207+
208+ if let Ok ( version_age) = ver_time. duration_since ( now) {
209+ // `now` is earlier than `ver_time`
210+ if let Some ( future_threshold) = provider. future_threshold ( ) {
211+ if version_age > future_threshold {
212+ doc. report ( ) . invalid_value (
213+ "ver" ,
214+ & ver. to_string ( ) ,
215+ "ver < now + future_threshold" ,
216+ & format ! ( "Document Version timestamp {id} cannot be too far in future (threshold: {future_threshold:?}) from now: {now:?}" ) ,
217+ ) ;
218+ is_valid = false ;
219+ }
220+ }
221+ } else {
222+ // `ver_time` is earlier than `now`
223+ let version_age = now
224+ . duration_since ( ver_time)
225+ . context ( "BUG! `ver_time` must be earlier than `now` at this place" ) ?;
226+
227+ if let Some ( past_threshold) = provider. past_threshold ( ) {
228+ if version_age > past_threshold {
229+ doc. report ( ) . invalid_value (
230+ "ver" ,
231+ & ver. to_string ( ) ,
232+ "ver > now - past_threshold" ,
233+ & format ! ( "Document Version timestamp {id} cannot be too far behind (threshold: {past_threshold:?}) from now: {now:?}" , ) ,
234+ ) ;
235+ is_valid = false ;
236+ }
237+ }
213238 }
239+
214240 Ok ( is_valid)
215241 } ,
216242
@@ -290,24 +316,21 @@ where
290316 Ok ( true )
291317}
292318
293- #[ allow( missing_docs) ]
294- pub mod tests {
295- /// A Test Future Threshold value for the Document's time based id field validation (5
296- /// secs);
297- pub const TEST_FUTURE_THRESHOLD : u64 = 5 ;
298- /// A Test Future Threshold value for the Document's time based id field validation (5
299- /// secs);
300- pub const TEST_PAST_THRESHOLD : u64 = 5 ;
301-
302- #[ cfg( test) ]
303- #[ test]
304- fn document_id_and_ver_test ( ) {
305- use std:: time:: SystemTime ;
319+ #[ cfg( test) ]
320+ mod tests {
321+ use std:: time:: SystemTime ;
306322
307- use uuid:: { Timestamp , Uuid } ;
323+ use uuid:: { Timestamp , Uuid } ;
308324
309- use crate :: { validator:: validate_id_and_ver, Builder , UuidV7 } ;
325+ use crate :: {
326+ providers:: { tests:: TestCatalystSignedDocumentProvider , CatalystSignedDocumentProvider } ,
327+ validator:: { document_rules_init, validate_id_and_ver} ,
328+ Builder , UuidV7 ,
329+ } ;
310330
331+ #[ test]
332+ fn document_id_and_ver_test ( ) {
333+ let provider = TestCatalystSignedDocumentProvider :: default ( ) ;
311334 let now = SystemTime :: now ( )
312335 . duration_since ( SystemTime :: UNIX_EPOCH )
313336 . unwrap ( )
@@ -322,8 +345,7 @@ pub mod tests {
322345 . unwrap ( )
323346 . build ( ) ;
324347
325- let is_valid =
326- validate_id_and_ver ( & doc, TEST_FUTURE_THRESHOLD , TEST_PAST_THRESHOLD ) . unwrap ( ) ;
348+ let is_valid = validate_id_and_ver ( & doc, & provider) . unwrap ( ) ;
327349 assert ! ( is_valid) ;
328350
329351 let ver = Uuid :: new_v7 ( Timestamp :: from_unix_time ( now - 1 , 0 , 0 , 0 ) ) ;
@@ -337,12 +359,11 @@ pub mod tests {
337359 . unwrap ( )
338360 . build ( ) ;
339361
340- let is_valid =
341- validate_id_and_ver ( & doc, TEST_FUTURE_THRESHOLD , TEST_PAST_THRESHOLD ) . unwrap ( ) ;
362+ let is_valid = validate_id_and_ver ( & doc, & provider) . unwrap ( ) ;
342363 assert ! ( !is_valid) ;
343364
344365 let to_far_in_past = Uuid :: new_v7 ( Timestamp :: from_unix_time (
345- now - TEST_PAST_THRESHOLD - 1 ,
366+ now - provider . past_threshold ( ) . unwrap ( ) . as_secs ( ) - 1 ,
346367 0 ,
347368 0 ,
348369 0 ,
@@ -355,12 +376,11 @@ pub mod tests {
355376 . unwrap ( )
356377 . build ( ) ;
357378
358- let is_valid =
359- validate_id_and_ver ( & doc, TEST_FUTURE_THRESHOLD , TEST_PAST_THRESHOLD ) . unwrap ( ) ;
379+ let is_valid = validate_id_and_ver ( & doc, & provider) . unwrap ( ) ;
360380 assert ! ( !is_valid) ;
361381
362382 let to_far_in_future = Uuid :: new_v7 ( Timestamp :: from_unix_time (
363- now + TEST_FUTURE_THRESHOLD + 1 ,
383+ now + provider . future_threshold ( ) . unwrap ( ) . as_secs ( ) + 1 ,
364384 0 ,
365385 0 ,
366386 0 ,
@@ -373,16 +393,12 @@ pub mod tests {
373393 . unwrap ( )
374394 . build ( ) ;
375395
376- let is_valid =
377- validate_id_and_ver ( & doc, TEST_FUTURE_THRESHOLD , TEST_PAST_THRESHOLD ) . unwrap ( ) ;
396+ let is_valid = validate_id_and_ver ( & doc, & provider) . unwrap ( ) ;
378397 assert ! ( !is_valid) ;
379398 }
380399
381- #[ cfg( test) ]
382400 #[ test]
383401 fn document_rules_init_test ( ) {
384- use super :: document_rules_init;
385-
386402 document_rules_init ( ) ;
387403 }
388404}
0 commit comments