@@ -6,14 +6,13 @@ pub(crate) mod rules;
66use std:: {
77 collections:: HashMap ,
88 sync:: { Arc , LazyLock } ,
9- time:: { Duration , SystemTime } ,
109} ;
1110
1211use anyhow:: Context ;
1312use catalyst_types:: { catalyst_id:: role_index:: RoleId , problem_report:: ProblemReport } ;
1413use rules:: {
15- ContentEncodingRule , ContentRule , ContentSchema , ContentTypeRule , ParametersRule , RefRule ,
16- ReplyRule , Rules , SectionRule , SignatureKidRule ,
14+ ContentEncodingRule , ContentRule , ContentSchema , ContentTypeRule , IdRule , ParametersRule ,
15+ RefRule , ReplyRule , Rules , SectionRule , SignatureKidRule , VerRule ,
1716} ;
1817
1918use crate :: {
@@ -41,6 +40,8 @@ fn proposal_rule() -> Rules {
4140 CATEGORY_PARAMETERS . clone( ) ,
4241 ] ;
4342 Rules {
43+ id : IdRule ,
44+ ver : VerRule ,
4445 content_type : ContentTypeRule {
4546 exp : ContentType :: Json ,
4647 } ,
@@ -75,6 +76,8 @@ fn proposal_comment_rule() -> Rules {
7576 CATEGORY_PARAMETERS . clone( ) ,
7677 ] ;
7778 Rules {
79+ id : IdRule ,
80+ ver : VerRule ,
7881 content_type : ContentTypeRule {
7982 exp : ContentType :: Json ,
8083 } ,
@@ -126,6 +129,8 @@ fn proposal_submission_action_rule() -> Rules {
126129 . expect ( "Must be a valid json scheme file" ) ;
127130
128131 Rules {
132+ id : IdRule ,
133+ ver : VerRule ,
129134 content_type : ContentTypeRule {
130135 exp : ContentType :: Json ,
131136 } ,
@@ -190,10 +195,6 @@ where
190195 return Ok ( false ) ;
191196 } ;
192197
193- if !validate_id_and_ver ( doc, provider) ? {
194- return Ok ( false ) ;
195- }
196-
197198 let Some ( rules) = DOCUMENT_RULES . get ( doc_type) else {
198199 doc. report ( ) . invalid_value (
199200 "`type`" ,
@@ -206,106 +207,6 @@ where
206207 rules. check ( doc, provider) . await
207208}
208209
209- /// Validates document `id` and `ver` fields on the timestamps:
210- /// 1. document `ver` cannot be smaller than document id field
211- /// 2. If `provider.future_threshold()` not `None`, document `id` cannot be too far in the
212- /// future (`future_threshold` arg) from `SystemTime::now()` based on the provide
213- /// threshold
214- /// 3. If `provider.future_threshold()` not `None`, document `id` cannot be too far behind
215- /// (`past_threshold` arg) from `SystemTime::now()` based on the provide threshold
216- fn validate_id_and_ver < Provider > (
217- doc : & CatalystSignedDocument ,
218- provider : & Provider ,
219- ) -> anyhow:: Result < bool >
220- where
221- Provider : CatalystSignedDocumentProvider ,
222- {
223- let id = doc. doc_id ( ) . ok ( ) ;
224- let ver = doc. doc_ver ( ) . ok ( ) ;
225- if id. is_none ( ) {
226- doc. report ( ) . missing_field (
227- "id" ,
228- "Can't get a document id during the validation process" ,
229- ) ;
230- }
231- if ver. is_none ( ) {
232- doc. report ( ) . missing_field (
233- "ver" ,
234- "Can't get a document ver during the validation process" ,
235- ) ;
236- }
237- match ( id, ver) {
238- ( Some ( id) , Some ( ver) ) => {
239- let mut is_valid = true ;
240- if ver < id {
241- doc. report ( ) . invalid_value (
242- "ver" ,
243- & ver. to_string ( ) ,
244- "ver < id" ,
245- & format ! ( "Document Version {ver} cannot be smaller than Document ID {id}" ) ,
246- ) ;
247- is_valid = false ;
248- }
249-
250- let ( ver_time_secs, ver_time_nanos) = ver
251- . uuid ( )
252- . get_timestamp ( )
253- . ok_or ( anyhow:: anyhow!( "Document ver field must be a UUIDv7" ) ) ?
254- . to_unix ( ) ;
255-
256- let Some ( ver_time) =
257- SystemTime :: UNIX_EPOCH . checked_add ( Duration :: new ( ver_time_secs, ver_time_nanos) )
258- else {
259- doc. report ( ) . invalid_value (
260- "ver" ,
261- & ver. to_string ( ) ,
262- "Must a valid duration since `UNIX_EPOCH`" ,
263- "Cannot instantiate a valid `SystemTime` value from the provided `ver` field timestamp." ,
264- ) ;
265- return Ok ( false ) ;
266- } ;
267-
268- let now = SystemTime :: now ( ) ;
269-
270- if let Ok ( version_age) = ver_time. duration_since ( now) {
271- // `now` is earlier than `ver_time`
272- if let Some ( future_threshold) = provider. future_threshold ( ) {
273- if version_age > future_threshold {
274- doc. report ( ) . invalid_value (
275- "ver" ,
276- & ver. to_string ( ) ,
277- "ver < now + future_threshold" ,
278- & format ! ( "Document Version timestamp {id} cannot be too far in future (threshold: {future_threshold:?}) from now: {now:?}" ) ,
279- ) ;
280- is_valid = false ;
281- }
282- }
283- } else {
284- // `ver_time` is earlier than `now`
285- let version_age = now
286- . duration_since ( ver_time)
287- . context ( "BUG! `ver_time` must be earlier than `now` at this place" ) ?;
288-
289- if let Some ( past_threshold) = provider. past_threshold ( ) {
290- if version_age > past_threshold {
291- doc. report ( ) . invalid_value (
292- "ver" ,
293- & ver. to_string ( ) ,
294- "ver > now - past_threshold" ,
295- & format ! ( "Document Version timestamp {id} cannot be too far behind (threshold: {past_threshold:?}) from now: {now:?}" , ) ,
296- ) ;
297- is_valid = false ;
298- }
299- }
300- }
301-
302- Ok ( is_valid)
303- } ,
304-
305- _ => Ok ( false ) ,
306- }
307- }
308-
309210/// Verify document signatures.
310211/// Return true if all signatures are valid, otherwise return false.
311212///
@@ -384,82 +285,7 @@ where
384285
385286#[ cfg( test) ]
386287mod tests {
387- use std:: time:: SystemTime ;
388-
389- use uuid:: { Timestamp , Uuid } ;
390-
391- use crate :: {
392- builder:: tests:: Builder ,
393- metadata:: SupportedField ,
394- providers:: { tests:: TestCatalystSignedDocumentProvider , CatalystSignedDocumentProvider } ,
395- validator:: { document_rules_init, validate_id_and_ver} ,
396- UuidV7 ,
397- } ;
398-
399- #[ test]
400- fn document_id_and_ver_test ( ) {
401- let provider = TestCatalystSignedDocumentProvider :: default ( ) ;
402- let now = SystemTime :: now ( )
403- . duration_since ( SystemTime :: UNIX_EPOCH )
404- . unwrap ( )
405- . as_secs ( ) ;
406-
407- let uuid_v7 = UuidV7 :: new ( ) ;
408- let doc = Builder :: new ( )
409- . with_metadata_field ( SupportedField :: Id ( uuid_v7) )
410- . with_metadata_field ( SupportedField :: Ver ( uuid_v7) )
411- . build ( ) ;
412-
413- let is_valid = validate_id_and_ver ( & doc, & provider) . unwrap ( ) ;
414- assert ! ( is_valid) ;
415-
416- let ver = Uuid :: new_v7 ( Timestamp :: from_unix_time ( now - 1 , 0 , 0 , 0 ) )
417- . try_into ( )
418- . unwrap ( ) ;
419- let id = Uuid :: new_v7 ( Timestamp :: from_unix_time ( now + 1 , 0 , 0 , 0 ) )
420- . try_into ( )
421- . unwrap ( ) ;
422- assert ! ( ver < id) ;
423- let doc = Builder :: new ( )
424- . with_metadata_field ( SupportedField :: Id ( id) )
425- . with_metadata_field ( SupportedField :: Ver ( ver) )
426- . build ( ) ;
427-
428- let is_valid = validate_id_and_ver ( & doc, & provider) . unwrap ( ) ;
429- assert ! ( !is_valid) ;
430-
431- let to_far_in_past = Uuid :: new_v7 ( Timestamp :: from_unix_time (
432- now - provider. past_threshold ( ) . unwrap ( ) . as_secs ( ) - 1 ,
433- 0 ,
434- 0 ,
435- 0 ,
436- ) )
437- . try_into ( )
438- . unwrap ( ) ;
439- let doc = Builder :: new ( )
440- . with_metadata_field ( SupportedField :: Id ( to_far_in_past) )
441- . with_metadata_field ( SupportedField :: Ver ( to_far_in_past) )
442- . build ( ) ;
443-
444- let is_valid = validate_id_and_ver ( & doc, & provider) . unwrap ( ) ;
445- assert ! ( !is_valid) ;
446-
447- let to_far_in_future = Uuid :: new_v7 ( Timestamp :: from_unix_time (
448- now + provider. future_threshold ( ) . unwrap ( ) . as_secs ( ) + 1 ,
449- 0 ,
450- 0 ,
451- 0 ,
452- ) )
453- . try_into ( )
454- . unwrap ( ) ;
455- let doc = Builder :: new ( )
456- . with_metadata_field ( SupportedField :: Id ( to_far_in_future) )
457- . with_metadata_field ( SupportedField :: Ver ( to_far_in_future) )
458- . build ( ) ;
459-
460- let is_valid = validate_id_and_ver ( & doc, & provider) . unwrap ( ) ;
461- assert ! ( !is_valid) ;
462- }
288+ use crate :: validator:: document_rules_init;
463289
464290 #[ test]
465291 fn document_rules_init_test ( ) {
0 commit comments