1#![allow(clippy::default_trait_access)]
16#![allow(clippy::doc_markdown)]
17#![allow(clippy::if_not_else)]
18#![allow(clippy::missing_panics_doc)]
19#![allow(clippy::module_name_repetitions)]
20#![allow(clippy::redundant_closure_for_method_calls)]
21
22use chrono::{self, offset::Utc, DateTime};
23use crev_data::{
24 self,
25 proof::{self, review, trust::TrustLevel, CommonOps, Content},
26 Digest, Id, Level, RegistrySource, Url, Version,
27};
28use default::default;
29use log::debug;
30use std::{
31 collections::{BTreeMap, BTreeSet, HashMap, HashSet},
32 sync,
33};
34
35pub mod trust_set;
36pub use trust_set::TrustSet;
37
38#[derive(thiserror::Error, Debug)]
39pub enum Error {
40 #[error("Unknown proof type '{}'", _0)]
41 UnknownProofType(Box<str>),
42
43 #[error("{}", _0)]
44 Data(#[from] crev_data::Error),
45}
46
47type Result<T, E = Error> = std::result::Result<T, E>;
48
49#[derive(Debug, Clone)]
51pub enum FetchSource {
52 Url(sync::Arc<Url>),
54 LocalUser,
56}
57
58#[derive(Clone, Debug)]
64pub struct Timestamped<T> {
65 pub date: chrono::DateTime<Utc>,
66 value: T,
67}
68
69impl<T> Timestamped<T> {
70 fn update_to_more_recent(&mut self, other: &Self)
72 where
73 T: Clone,
74 {
75 if self.date <= other.date {
79 self.date = other.date;
80 self.value = other.value.clone();
81 }
82 }
83}
84
85impl<T, Tz> From<(&DateTime<Tz>, T)> for Timestamped<T>
86where
87 Tz: chrono::TimeZone,
88{
89 fn from(from: (&DateTime<Tz>, T)) -> Self {
90 Timestamped {
91 date: from.0.with_timezone(&Utc),
92 value: from.1,
93 }
94 }
95}
96
97pub type Signature = String;
98type TimestampedUrl = Timestamped<Url>;
99type TimestampedTrustLevel = Timestamped<TrustLevel>;
100type TimestampedReview = Timestamped<review::Review>;
101type TimestampedSignature = Timestamped<Signature>;
102type TimestampedDigest = Timestamped<proof::Digest>;
103type TimestampedFlags = Timestamped<proof::Flags>;
104
105impl From<proof::Trust> for TimestampedTrustLevel {
106 fn from(trust: proof::Trust) -> Self {
107 TimestampedTrustLevel {
108 date: trust.date_utc(),
109 value: trust.trust,
110 }
111 }
112}
113
114impl<'a, T: proof::WithReview + Content + CommonOps> From<&'a T> for TimestampedReview {
115 fn from(review: &T) -> Self {
116 TimestampedReview {
117 value: review.review().clone(),
118 date: review.date_utc(),
119 }
120 }
121}
122
123#[derive(Hash, Debug, Clone, PartialEq, Eq)]
133pub struct PkgVersionReviewId {
134 from: Id,
135 package_version_id: proof::PackageVersionId,
136}
137
138impl From<review::Package> for PkgVersionReviewId {
139 fn from(review: review::Package) -> Self {
140 PkgVersionReviewId {
141 from: review.from().id.clone(),
142 package_version_id: review.package.id,
143 }
144 }
145}
146
147impl From<&review::Package> for PkgVersionReviewId {
148 fn from(review: &review::Package) -> Self {
149 PkgVersionReviewId {
150 from: review.from().id.clone(),
151 package_version_id: review.package.id.clone(),
152 }
153 }
154}
155
156#[derive(Hash, Debug, Clone, PartialEq, Eq)]
161pub struct PkgReviewId {
162 from: Id,
163 package_id: proof::PackageId,
164}
165
166impl From<review::Package> for PkgReviewId {
167 fn from(review: review::Package) -> Self {
168 PkgReviewId {
169 from: review.from().id.clone(),
170 package_id: review.package.id.id,
171 }
172 }
173}
174
175impl From<&review::Package> for PkgReviewId {
176 fn from(review: &review::Package) -> Self {
177 PkgReviewId {
178 from: review.from().id.clone(),
179 package_id: review.package.id.id.clone(),
180 }
181 }
182}
183
184pub type RegistrySourceOwned = String;
186pub type Name = String;
188
189#[derive(Default)]
193struct AlternativesData {
194 derived_recalculation_counter: usize,
195 for_pkg: HashMap<proof::PackageId, HashMap<Id, HashSet<proof::PackageId>>>,
196 reported_by: HashMap<(proof::PackageId, proof::PackageId), HashMap<Id, Signature>>,
197}
198
199impl AlternativesData {
200 fn new() -> Self {
201 Default::default()
202 }
203
204 fn wipe(&mut self) {
205 *self = Self::new();
206 }
207
208 fn record_from_proof(&mut self, review: &review::Package, signature: &Signature) {
209 for alternative in &review.alternatives {
210 let a = &review.package.id.id;
211 let b = alternative;
212 let id = &review.from().id;
213 self.for_pkg
214 .entry(a.clone())
215 .or_default()
216 .entry(id.clone())
217 .or_default()
218 .insert(b.clone());
219
220 self.for_pkg
221 .entry(b.clone())
222 .or_default()
223 .entry(id.clone())
224 .or_default()
225 .insert(a.clone());
226
227 self.reported_by
228 .entry((a.clone(), b.clone()))
229 .or_default()
230 .insert(id.clone(), signature.clone());
231
232 self.reported_by
233 .entry((b.clone(), a.clone()))
234 .or_default()
235 .insert(id.clone(), signature.clone());
236 }
237 }
238}
239
240pub type TimestampedTrustDetails = Timestamped<TrustDetails>;
241
242#[derive(Debug, Clone)]
243pub struct TrustDetails {
244 level: TrustLevel,
245 override_: HashSet<Id>,
246}
247
248pub struct ProofDB {
257 trust_id_to_id: HashMap<Id, HashMap<Id, TimestampedTrustDetails>>,
259 reverse_trust_id_to_id: HashMap<Id, HashMap<Id, TimestampedTrustLevel>>,
261
262 ids_to_trust_proof_signatures: HashMap<(Id, Id), TimestampedSignature>,
264
265 url_by_id_self_reported: HashMap<Id, (TimestampedUrl, bool)>,
269
270 url_by_id_reported_by_others: HashMap<Id, TimestampedUrl>,
272
273 package_review_by_signature: HashMap<Signature, review::Package>,
275
276 trust_proofs_by_signature: HashMap<Signature, proof::Trust>,
278
279 package_review_signatures_by_package_digest:
281 HashMap<Vec<u8>, HashMap<PkgVersionReviewId, TimestampedSignature>>,
282 package_review_signatures_by_pkg_review_id: HashMap<PkgVersionReviewId, TimestampedSignature>,
283
284 proof_digest_by_pkg_review_id: HashMap<PkgVersionReviewId, TimestampedDigest>,
286
287 package_reviews: BTreeMap<
289 RegistrySourceOwned,
290 BTreeMap<Name, BTreeMap<Version, HashSet<PkgVersionReviewId>>>,
291 >,
292
293 package_flags: HashMap<proof::PackageId, HashMap<Id, TimestampedFlags>>,
294
295 from_id_to_package_reviews: HashMap<Id, HashSet<proof::PackageVersionId>>,
297
298 package_alternatives: HashMap<proof::PackageId, HashMap<Id, TimestampedSignature>>,
303
304 insertion_counter: usize,
310 derived_alternatives: sync::RwLock<AlternativesData>,
311}
312
313impl Default for ProofDB {
314 fn default() -> Self {
315 ProofDB {
316 trust_id_to_id: default(),
317 reverse_trust_id_to_id: default(),
318 ids_to_trust_proof_signatures: default(),
319 trust_proofs_by_signature: default(),
320 url_by_id_self_reported: default(),
321 url_by_id_reported_by_others: default(),
322 package_review_signatures_by_package_digest: default(),
323 package_review_signatures_by_pkg_review_id: default(),
324 proof_digest_by_pkg_review_id: default(),
325 package_review_by_signature: default(),
326 package_reviews: default(),
327 package_alternatives: default(),
328 package_flags: default(),
329 from_id_to_package_reviews: default(),
330
331 insertion_counter: 0,
332 derived_alternatives: sync::RwLock::new(AlternativesData::new()),
333 }
334 }
335}
336
337#[derive(Default, Debug)]
339pub struct IssueDetails {
340 pub severity: Level,
341 pub issues: HashSet<PkgVersionReviewId>,
343 pub advisories: HashSet<PkgVersionReviewId>,
345}
346
347impl ProofDB {
348 #[must_use]
350 pub fn new() -> Self {
351 default()
352 }
353
354 fn get_derived_alternatives(&self) -> sync::RwLockReadGuard<'_, AlternativesData> {
355 {
356 let read = self.derived_alternatives.read().expect("lock to work");
357
358 if read.derived_recalculation_counter == self.insertion_counter {
359 return read;
360 }
361 }
362
363 {
364 let mut write = self.derived_alternatives.write().expect("lock to work");
365
366 write.wipe();
367
368 for alt in self.package_alternatives.values() {
369 for signature in alt.values() {
370 write.record_from_proof(
371 &self.package_review_by_signature[&signature.value],
372 &signature.value,
373 );
374 }
375 }
376
377 write.derived_recalculation_counter = self.insertion_counter;
378 }
379
380 self.derived_alternatives.read().expect("lock to work")
381 }
382
383 pub fn get_pkg_alternatives_by_author(
384 &self,
385 from: &Id,
386 pkg_id: &proof::PackageId,
387 ) -> HashSet<proof::PackageId> {
388 let from = from.clone();
389
390 let alternatives = self.get_derived_alternatives();
391 alternatives
392 .for_pkg
393 .get(pkg_id)
394 .into_iter()
395 .filter_map(move |i| i.get(&from))
396 .flatten()
397 .cloned()
398 .collect()
399 }
400
401 pub fn get_pkg_alternatives(
402 &self,
403 pkg_id: &proof::PackageId,
404 ) -> HashSet<(Id, proof::PackageId)> {
405 let alternatives = self.get_derived_alternatives();
406
407 alternatives
408 .for_pkg
409 .get(pkg_id)
410 .into_iter()
411 .flat_map(move |i| i.iter())
412 .flat_map(move |(id, pkg_ids)| pkg_ids.iter().map(move |v| (id.clone(), v.clone())))
413 .collect()
414 }
415
416 pub fn get_pkg_flags_by_author<'s>(
417 &'s self,
418 from: &Id,
419 pkg_id: &proof::PackageId,
420 ) -> Option<&'s proof::Flags> {
421 let from = from.clone();
422 self.package_flags
423 .get(pkg_id)
424 .and_then(move |i| i.get(&from))
425 .map(move |timestampted| ×tampted.value)
426 }
427
428 pub fn get_pkg_flags(
429 &self,
430 pkg_id: &proof::PackageId,
431 ) -> impl Iterator<Item = (&Id, &proof::Flags)> {
432 self.package_flags
433 .get(pkg_id)
434 .into_iter()
435 .flat_map(move |i| i.iter())
436 .map(|(id, flags)| (id, &flags.value))
437 }
438
439 pub fn get_pkg_reviews_for_source<'a>(
441 &'a self,
442 source: RegistrySource<'_>,
443 ) -> impl Iterator<Item = &'a proof::review::Package> {
444 self.package_reviews
445 .get(source)
446 .into_iter()
447 .flat_map(move |map| map.iter())
448 .flat_map(move |(_, map)| map.iter())
449 .flat_map(|(_, v)| v)
450 .map(move |pkg_review_id| {
451 self.get_pkg_review_by_pkg_review_id(pkg_review_id)
452 .expect("exists")
453 })
454 }
455
456 pub fn get_pkg_reviews_for_name<'a, 'b, 'c: 'a>(
457 &'a self,
458 source: RegistrySource<'b>,
459 name: &'c str,
460 ) -> impl Iterator<Item = &'a proof::review::Package> {
461 self.package_reviews
462 .get(source)
463 .into_iter()
464 .filter_map(move |map| map.get(name))
465 .flat_map(move |map| map.iter())
466 .flat_map(|(_, v)| v)
467 .map(move |pkg_review_id| {
468 self.get_pkg_review_by_pkg_review_id(pkg_review_id)
469 .expect("exists")
470 })
471 }
472
473 pub fn get_pkg_reviews_for_version<'a, 'b, 'c: 'a, 'd: 'a>(
474 &'a self,
475 source: RegistrySource<'b>,
476 name: &'c str,
477 version: &'d Version,
478 ) -> impl Iterator<Item = &'a proof::review::Package> {
479 self.package_reviews
480 .get(source)
481 .into_iter()
482 .filter_map(move |map| map.get(name))
483 .filter_map(move |map| map.get(version))
484 .flatten()
485 .map(move |pkg_review_id| {
486 self.get_pkg_review_by_pkg_review_id(pkg_review_id)
487 .expect("exists")
488 })
489 }
490
491 pub fn get_pkg_reviews_gte_version<'a, 'b, 'c: 'a, 'd: 'a>(
492 &'a self,
493 source: RegistrySource<'b>,
494 name: &'c str,
495 version: &'d Version,
496 ) -> impl Iterator<Item = &'a proof::review::Package> {
497 self.package_reviews
498 .get(source)
499 .into_iter()
500 .filter_map(move |map| map.get(name))
501 .flat_map(move |map| map.range(version..))
502 .flat_map(move |(_, v)| v)
503 .map(move |pkg_review_id| {
504 self.get_pkg_review_by_pkg_review_id(pkg_review_id)
505 .expect("exists")
506 })
507 }
508
509 pub fn get_pkg_reviews_lte_version<'a, 'b, 'c: 'a, 'd: 'a>(
510 &'a self,
511 source: RegistrySource<'b>,
512 name: &'c str,
513 version: &'d Version,
514 ) -> impl Iterator<Item = &'a proof::review::Package> {
515 self.package_reviews
516 .get(source)
517 .into_iter()
518 .filter_map(move |map| map.get(name))
519 .flat_map(move |map| map.range(..=version))
520 .flat_map(|(_, v)| v)
521 .map(move |pkg_review_id| {
522 self.get_pkg_review_by_pkg_review_id(pkg_review_id)
523 .expect("exists")
524 })
525 }
526
527 pub fn get_pkg_review_by_pkg_review_id(
528 &self,
529 uniq: &PkgVersionReviewId,
530 ) -> Option<&proof::review::Package> {
531 let signature = &self
532 .package_review_signatures_by_pkg_review_id
533 .get(uniq)?
534 .value;
535 self.package_review_by_signature.get(signature)
536 }
537
538 pub fn get_proof_digest_by_pkg_review_id(
539 &self,
540 uniq: &PkgVersionReviewId,
541 ) -> Option<&proof::Digest> {
542 Some(&self.proof_digest_by_pkg_review_id.get(uniq)?.value)
543 }
544
545 pub fn get_pkg_review<'a, 'b, 'c: 'a, 'd: 'a>(
546 &'a self,
547 source: RegistrySource<'b>,
548 name: &'c str,
549 version: &'d Version,
550 id: &Id,
551 ) -> Option<&proof::review::Package> {
552 self.get_pkg_reviews_for_version(source, name, version)
553 .find(|pkg_review| pkg_review.from().id == *id)
554 }
555
556 pub fn get_advisories<'a, 'b, 'c: 'a, 'd: 'a>(
557 &'a self,
558 source: RegistrySource<'b>,
559 name: Option<&'c str>,
560 version: Option<&'d Version>,
561 ) -> impl Iterator<Item = &'a proof::review::Package> + 'a {
562 match (name, version) {
563 (Some(name), Some(version)) => {
564 Box::new(self.get_advisories_for_version(source, name, version))
565 as Box<dyn Iterator<Item = _>>
566 }
567
568 (Some(name), None) => Box::new(self.get_advisories_for_package(source, name)),
569 (None, None) => Box::new(self.get_advisories_for_source(source)),
570 (None, Some(_)) => panic!("Wrong usage"),
571 }
572 }
573
574 pub fn get_pkg_reviews_with_issues_for<'a, 'b, 'c: 'a, 'd: 'a>(
575 &'a self,
576 source: RegistrySource<'b>,
577 name: Option<&'c str>,
578 version: Option<&'c Version>,
579 trust_set: &'d TrustSet,
580 trust_level_required: TrustLevel,
581 ) -> impl Iterator<Item = &proof::review::Package> {
582 match (name, version) {
583 (Some(name), Some(version)) => Box::new(self.get_pkg_reviews_with_issues_for_version(
584 source,
585 name,
586 version,
587 trust_set,
588 trust_level_required,
589 )) as Box<dyn Iterator<Item = _>>,
590 (Some(name), None) => Box::new(self.get_pkg_reviews_with_issues_for_name(
591 source,
592 name,
593 trust_set,
594 trust_level_required,
595 )),
596 (None, None) => Box::new(self.get_pkg_reviews_with_issues_for_source(
597 source,
598 trust_set,
599 trust_level_required,
600 )),
601 (None, Some(_)) => panic!("Wrong usage"),
602 }
603 }
604
605 pub fn get_advisories_for_version<'a, 'b, 'c: 'a, 'd: 'a>(
606 &'a self,
607 source: RegistrySource<'b>,
608 name: &'c str,
609 version: &'d Version,
610 ) -> impl Iterator<Item = &proof::review::Package> {
611 self.get_pkg_reviews_gte_version(source, name, version)
612 .filter(move |review| review.is_advisory_for(version))
613 }
614
615 pub fn get_advisories_for_package<'a, 'b, 'c: 'a>(
616 &'a self,
617 source: RegistrySource<'b>,
618 name: &'c str,
619 ) -> impl Iterator<Item = &proof::review::Package> {
620 self.package_reviews
621 .get(source)
622 .into_iter()
623 .filter_map(move |map| map.get(name))
624 .flat_map(move |map| map.iter())
625 .flat_map(|(_, v)| v)
626 .filter_map(move |pkg_review_id| {
627 let review = &self.package_review_by_signature
628 [&self.package_review_signatures_by_pkg_review_id[pkg_review_id].value];
629
630 if !review.advisories.is_empty() {
631 Some(review)
632 } else {
633 None
634 }
635 })
636 }
637
638 pub fn get_advisories_for_source(
639 &self,
640 source: RegistrySource<'_>,
641 ) -> impl Iterator<Item = &proof::review::Package> {
642 self.get_pkg_reviews_for_source(source)
643 .filter(|review| !review.advisories.is_empty())
644 }
645
646 pub fn get_open_issues_for_version(
655 &self,
656 source: RegistrySource<'_>,
657 name: &str,
658 queried_version: &Version,
659 trust_set: &TrustSet,
660 trust_level_required: TrustLevel,
661 ) -> HashMap<String, IssueDetails> {
662 let mut issue_reports_by_id: HashMap<String, IssueDetails> = HashMap::new();
667
668 for (review, issue) in self
671 .get_pkg_reviews_lte_version(source, name, queried_version)
672 .filter(|review| {
673 let effective = trust_set.get_effective_trust_level(&review.from().id);
674 effective >= trust_level_required
675 })
676 .flat_map(move |review| review.issues.iter().map(move |issue| (review, issue)))
677 .filter(|(review, issue)| {
678 issue.is_for_version_when_reported_in_version(
679 queried_version,
680 &review.package.id.version,
681 )
682 })
683 {
684 issue_reports_by_id
685 .entry(issue.id.clone())
686 .or_default()
687 .issues
688 .insert(PkgVersionReviewId::from(review));
689 }
690
691 for (review, advisory) in self
701 .get_pkg_reviews_for_name(source, name)
702 .filter(|review| {
703 let effective = trust_set.get_effective_trust_level(&review.from().id);
704 effective >= trust_level_required
705 })
706 .flat_map(move |review| {
707 review
708 .advisories
709 .iter()
710 .map(move |advisory| (review, advisory))
711 })
712 {
713 if advisory.is_for_version_when_reported_in_version(
715 queried_version,
716 &review.package.id.version,
717 ) {
718 for id in &advisory.ids {
719 issue_reports_by_id
720 .entry(id.clone())
721 .or_default()
722 .issues
723 .insert(PkgVersionReviewId::from(review));
724 }
725 }
726
727 for id in &advisory.ids {
729 if let Some(issue_marker) = issue_reports_by_id.get_mut(id) {
730 let issues = std::mem::take(&mut issue_marker.issues);
731 issue_marker.issues = issues
732 .into_iter()
733 .filter(|pkg_review_id| {
734 let signature = &self
735 .package_review_signatures_by_pkg_review_id
736 .get(pkg_review_id)
737 .expect("review for this signature")
738 .value;
739 let issue_review = self
740 .package_review_by_signature
741 .get(signature)
742 .expect("review for this pkg_review_id");
743 !advisory.is_for_version_when_reported_in_version(
744 &issue_review.package.id.version,
745 &review.package.id.version,
746 )
747 })
748 .collect();
749 }
750 }
751 }
752
753 issue_reports_by_id
754 .into_iter()
755 .filter(|(_id, markers)| !markers.issues.is_empty() || !markers.advisories.is_empty())
756 .collect()
757 }
758
759 pub fn get_pkg_reviews_with_issues_for_version<'a, 'b, 'c: 'a>(
760 &'a self,
761 source: RegistrySource<'b>,
762 name: &'c str,
763 queried_version: &'c Version,
764 trust_set: &'c TrustSet,
765 trust_level_required: TrustLevel,
766 ) -> impl Iterator<Item = &proof::review::Package> {
767 self.get_pkg_reviews_with_issues_for_name(source, name, trust_set, trust_level_required)
768 .filter(move |review| {
769 !review.issues.is_empty()
770 || review.advisories.iter().any(|advi| {
771 advi.is_for_version_when_reported_in_version(
772 queried_version,
773 &review.package.id.version,
774 )
775 })
776 })
777 }
778
779 pub fn get_pkg_reviews_with_issues_for_name<'a, 'b, 'c: 'a>(
780 &'a self,
781 source: RegistrySource<'b>,
782 name: &'c str,
783 trust_set: &'c TrustSet,
784 trust_level_required: TrustLevel,
785 ) -> impl Iterator<Item = &proof::review::Package> {
786 self.get_pkg_reviews_for_name(source, name)
787 .filter(move |review| {
788 let effective = trust_set.get_effective_trust_level(&review.from().id);
789 effective >= trust_level_required
790 })
791 .filter(|review| !review.issues.is_empty() || !review.advisories.is_empty())
792 }
793
794 pub fn get_pkg_reviews_with_issues_for_source<'a, 'b, 'c: 'a>(
795 &'a self,
796 source: RegistrySource<'b>,
797 trust_set: &'c TrustSet,
798 trust_level_required: TrustLevel,
799 ) -> impl Iterator<Item = &proof::review::Package> {
800 self.get_pkg_reviews_for_source(source)
801 .filter(move |review| {
802 let effective = trust_set.get_effective_trust_level(&review.from().id);
803 effective >= trust_level_required
804 })
805 .filter(|review| !review.issues.is_empty() || !review.advisories.is_empty())
806 }
807
808 pub fn unique_package_review_proof_count(&self) -> usize {
809 self.package_review_signatures_by_pkg_review_id.len()
810 }
811
812 pub fn unique_trust_proof_count(&self) -> usize {
813 self.trust_id_to_id
814 .iter()
815 .fold(0, |count, (_id, set)| count + set.len())
816 }
817
818 fn add_code_review(&mut self, review: &review::Code, fetched_from: &FetchSource) {
819 let from = &review.from();
820 self.record_url_from_from_field(&review.date_utc(), from, fetched_from);
821 for _file in &review.files {
822 }
824 }
825
826 fn add_package_review(
827 &mut self,
828 review: review::Package,
829 signature: &str,
830 fetched_from: &FetchSource,
831 proof_digest: proof::Digest,
832 ) {
833 self.insertion_counter += 1;
834
835 let from = review.from();
836 self.record_url_from_from_field(&review.date_utc(), from, fetched_from);
837
838 let pkg_review_id = PkgVersionReviewId::from(&review);
839 let timestamp_signature = TimestampedSignature::from((review.date(), signature.to_owned()));
840 let timestamp_proof_digest = TimestampedDigest::from((review.date(), proof_digest));
841 let timestamp_flags = TimestampedFlags::from((review.date(), review.flags.clone()));
842
843 self.package_review_signatures_by_package_digest
844 .entry(review.package.digest.clone())
845 .or_default()
846 .entry(pkg_review_id.clone())
847 .and_modify(|s| s.update_to_more_recent(×tamp_signature))
848 .or_insert_with(|| timestamp_signature.clone());
849
850 self.package_review_signatures_by_pkg_review_id
851 .entry(pkg_review_id.clone())
852 .and_modify(|s| s.update_to_more_recent(×tamp_signature))
853 .or_insert_with(|| timestamp_signature.clone());
854
855 self.proof_digest_by_pkg_review_id
856 .entry(pkg_review_id.clone())
857 .and_modify(|s| s.update_to_more_recent(×tamp_proof_digest))
858 .or_insert_with(|| timestamp_proof_digest.clone());
859
860 self.from_id_to_package_reviews
861 .entry(review.common.from.id.clone())
862 .or_default()
863 .insert(pkg_review_id.package_version_id.clone());
864
865 self.package_reviews
866 .entry(review.package.id.id.source.clone())
867 .or_default()
868 .entry(review.package.id.id.name.clone())
869 .or_default()
870 .entry(review.package.id.version.clone())
871 .or_default()
872 .insert(pkg_review_id);
873
874 self.package_alternatives
875 .entry(review.package.id.id.clone())
876 .or_default()
877 .entry(review.from().id.clone())
878 .and_modify(|a| a.update_to_more_recent(×tamp_signature))
879 .or_insert_with(|| timestamp_signature);
880
881 self.package_flags
882 .entry(review.package.id.id.clone())
883 .or_default()
884 .entry(review.from().id.clone())
885 .and_modify(|f| f.update_to_more_recent(×tamp_flags))
886 .or_insert_with(|| timestamp_flags);
887
888 self.package_review_by_signature
889 .entry(signature.to_owned())
890 .or_insert(review);
891 }
892
893 pub fn get_package_review_count(
894 &self,
895 source: RegistrySource<'_>,
896 name: Option<&str>,
897 version: Option<&Version>,
898 ) -> usize {
899 self.get_package_reviews_for_package(source, name, version)
900 .count()
901 }
902
903 pub fn get_package_reviews_for_package<'a, 'b, 'c: 'a, 'd: 'a>(
904 &'a self,
905 source: RegistrySource<'b>,
906 name: Option<&'c str>,
907 version: Option<&'d Version>,
908 ) -> impl Iterator<Item = &'a proof::review::Package> + 'a {
909 match (name, version) {
910 (Some(name), Some(version)) => {
911 Box::new(self.get_pkg_reviews_for_version(source, name, version))
912 as Box<dyn Iterator<Item = _>>
913 }
914 (Some(name), None) => Box::new(self.get_pkg_reviews_for_name(source, name)),
915 (None, None) => Box::new(self.get_pkg_reviews_for_source(source)),
916 (None, Some(_)) => panic!("Wrong usage"),
917 }
918 }
919
920 pub fn get_package_reviews_for_package_sorted<'a, 'b, 'c: 'a, 'd: 'a>(
921 &'a self,
922 source: RegistrySource<'b>,
923 name: Option<&'c str>,
924 version: Option<&'d Version>,
925 ) -> Vec<proof::review::Package> {
926 let mut proofs: Vec<_> = self
927 .get_package_reviews_for_package(source, name, version)
928 .cloned()
929 .collect();
930
931 proofs.sort_by_key(|a| a.date_utc());
932
933 proofs
934 }
935
936 fn add_trust_raw(
937 &mut self,
938 from: &Id,
939 to: &Id,
940 date: DateTime<Utc>,
941 trust_proof: &proof::Trust,
942 signature: &str,
943 ) {
944 let trust = TrustDetails {
945 level: trust_proof.trust,
946 override_: trust_proof
947 .override_
948 .iter()
949 .map(|o| o.id.id.clone())
950 .collect(),
951 };
952
953 let tl = TimestampedTrustLevel {
954 value: trust.level,
955 date,
956 };
957 let td = TimestampedTrustDetails { value: trust, date };
958
959 self.trust_proofs_by_signature
960 .insert(signature.to_owned(), trust_proof.clone());
961
962 let signature = TimestampedSignature {
963 value: signature.to_owned(),
964 date,
965 };
966
967 self.ids_to_trust_proof_signatures
968 .entry((from.clone(), to.clone()))
969 .and_modify(|e| e.update_to_more_recent(&signature))
970 .or_insert_with(|| signature);
971
972 self.trust_id_to_id
973 .entry(from.clone())
974 .or_default()
975 .entry(to.clone())
976 .and_modify(|e| e.update_to_more_recent(&td))
977 .or_insert_with(|| td);
978
979 self.reverse_trust_id_to_id
980 .entry(to.clone())
981 .or_default()
982 .entry(from.clone())
983 .and_modify(|e| e.update_to_more_recent(&tl))
984 .or_insert_with(|| tl);
985 }
986
987 fn add_trust(&mut self, trust: &proof::Trust, signature: &str, fetched_from: &FetchSource) {
988 let from = &trust.from();
989 self.record_url_from_from_field(&trust.date_utc(), from, fetched_from);
990 for to in &trust.ids {
991 self.add_trust_raw(&from.id, &to.id, trust.date_utc(), trust, signature);
992 }
993 for to in &trust.ids {
994 self.record_url_from_to_field(&trust.date_utc(), to);
998 }
999 }
1000
1001 pub fn all_known_ids(&self) -> BTreeSet<Id> {
1002 self.url_by_id_self_reported
1003 .keys()
1004 .chain(self.url_by_id_reported_by_others.keys())
1005 .cloned()
1006 .collect()
1007 }
1008
1009 pub fn get_reverse_trust_for<'id, 's: 'id>(
1011 &'s self,
1012 id: &'id Id,
1013 ) -> impl Iterator<Item = (&'id Id, TrustLevel)> + 's {
1014 self.reverse_trust_id_to_id
1015 .get(id)
1016 .into_iter()
1017 .flat_map(|map| map.iter().map(|(id, trust_level)| (id, trust_level.value)))
1018 }
1019
1020 pub fn all_author_ids(&self) -> BTreeMap<Id, usize> {
1022 let mut res = BTreeMap::new();
1023 for (id, set) in &self.trust_id_to_id {
1024 *res.entry(id.clone()).or_default() += set.len();
1025 }
1026
1027 for uniq_rev in self.package_review_signatures_by_pkg_review_id.keys() {
1028 *res.entry(uniq_rev.from.clone()).or_default() += 1;
1029 }
1030
1031 res
1032 }
1033
1034 pub fn get_package_review_by_signature<'a>(
1035 &'a self,
1036 signature: &str,
1037 ) -> Option<&'a review::Package> {
1038 self.package_review_by_signature.get(signature)
1039 }
1040
1041 pub fn get_package_reviews_by_digest<'a>(
1042 &'a self,
1043 digest: &Digest,
1044 ) -> impl Iterator<Item = review::Package> + 'a {
1045 self.package_review_signatures_by_package_digest
1046 .get(digest.as_slice())
1047 .into_iter()
1048 .flat_map(move |unique_reviews| {
1049 unique_reviews
1050 .iter()
1051 .map(move |(_unique_review, signature)| {
1052 self.package_review_by_signature[&signature.value].clone()
1053 })
1054 })
1055 }
1056
1057 fn record_url_from_to_field(&mut self, date: &DateTime<Utc>, to: &crev_data::PublicId) {
1059 if let Some(url) = &to.url {
1060 self.url_by_id_reported_by_others
1061 .entry(to.id.clone())
1062 .or_insert_with(|| TimestampedUrl {
1063 value: url.clone(),
1064 date: *date,
1065 });
1066 }
1067 }
1068
1069 pub fn record_trusted_url_from_own_id(&mut self, own_id: &crev_data::PublicId) {
1070 self.record_url_from_from_field(&Utc::now(), own_id, &FetchSource::LocalUser);
1071 }
1072
1073 fn record_url_from_from_field(
1075 &mut self,
1076 date: &DateTime<Utc>,
1077 from: &crev_data::PublicId,
1078 fetched_from: &FetchSource,
1079 ) {
1080 if let Some(url) = &from.url {
1081 let tu = TimestampedUrl {
1082 value: url.clone(),
1083 date: *date,
1084 };
1085 let fetch_matches = match fetched_from {
1086 FetchSource::LocalUser => true,
1087 FetchSource::Url(fetched_url) if **fetched_url == *url => true,
1088 FetchSource::Url(_other) => false,
1089 };
1090 self.url_by_id_self_reported
1091 .entry(from.id.clone())
1092 .and_modify(|e| {
1093 e.0.update_to_more_recent(&tu);
1094 if fetch_matches {
1095 e.1 = true;
1096 }
1097 })
1098 .or_insert_with(|| (tu, fetch_matches));
1099 }
1100 }
1101
1102 fn add_proof(&mut self, proof: &proof::Proof, fetched_from: FetchSource) -> Result<()> {
1103 proof
1104 .verify()
1105 .expect("All proofs were supposed to be valid here");
1106 match proof.kind() {
1107 proof::CodeReview::KIND => self.add_code_review(&proof.parse_content()?, &fetched_from),
1108 proof::PackageReview::KIND => self.add_package_review(
1109 proof.parse_content()?,
1110 proof.signature(),
1111 &fetched_from,
1112 proof::Digest(*proof.digest()),
1113 ),
1114 proof::Trust::KIND => {
1115 self.add_trust(&proof.parse_content()?, proof.signature(), &fetched_from);
1116 }
1117 other => return Err(Error::UnknownProofType(other.into())),
1118 }
1119
1120 Ok(())
1121 }
1122
1123 pub fn import_from_iter(&mut self, i: impl Iterator<Item = (proof::Proof, FetchSource)>) {
1124 for (proof, fetch_source) in i {
1125 if let Err(e) = self.add_proof(&proof, fetch_source) {
1127 debug!("Ignoring proof: {}", e);
1128 }
1129 }
1130 }
1131
1132 fn get_trust_details_list_of_id(&self, id: &Id) -> impl Iterator<Item = (&TrustDetails, &Id)> {
1133 self.trust_id_to_id
1134 .get(id)
1135 .map(|map| map.iter().map(|(id, trust)| (&trust.value, id)))
1136 .into_iter()
1137 .flatten()
1138 }
1139
1140 pub fn get_trust_proof_between(&self, from: &Id, to: &Id) -> Option<&proof::Trust> {
1142 self.ids_to_trust_proof_signatures
1143 .get(&(from.clone(), to.clone()))
1144 .and_then(|sig| self.trust_proofs_by_signature.get(&sig.value))
1145 }
1146
1147 fn get_package_reviews_by_author<'iter, 's: 'iter, 'id: 'iter>(
1148 &'s self,
1149 id: &'id Id,
1150 ) -> impl Iterator<Item = &'s review::Package> + 'iter {
1151 self.from_id_to_package_reviews
1152 .get(id)
1153 .into_iter()
1154 .flat_map(move |set| {
1155 set.iter()
1156 .map(move |package_version_id| PkgVersionReviewId {
1157 from: id.clone(),
1158 package_version_id: package_version_id.clone(),
1159 })
1160 })
1161 .map(move |pkg_version_review_id| {
1162 &self.package_review_by_signature
1163 [&self.package_review_signatures_by_pkg_review_id[&pkg_version_review_id].value]
1164 })
1165 }
1166
1167 pub fn calculate_trust_set(&self, for_id: &Id, params: &TrustDistanceParams) -> TrustSet {
1169 TrustSet::from(self, for_id, params)
1170 }
1171
1172 pub fn lookup_url(&self, id: &Id) -> UrlOfId<'_> {
1175 self.url_by_id_self_reported
1176 .get(id)
1177 .map(|(url, fetch_matches)| {
1178 if *fetch_matches {
1179 UrlOfId::FromSelfVerified(&url.value)
1180 } else {
1181 UrlOfId::FromSelf(&url.value)
1182 }
1183 })
1184 .or_else(|| {
1185 self.url_by_id_reported_by_others
1186 .get(id)
1187 .map(|url| UrlOfId::FromOthers(&url.value))
1188 })
1189 .unwrap_or(UrlOfId::None)
1190 }
1191}
1192
1193#[derive(Debug, Copy, Clone)]
1195pub enum UrlOfId<'a> {
1196 FromSelfVerified(&'a Url),
1199 FromSelf(&'a Url),
1201 FromOthers(&'a Url),
1203 None,
1205}
1206
1207impl<'a> UrlOfId<'a> {
1208 #[must_use]
1210 pub fn verified(self) -> Option<&'a Url> {
1211 match self {
1212 Self::FromSelfVerified(url) => Some(url),
1213 _ => None,
1214 }
1215 }
1216
1217 #[must_use]
1219 pub fn from_self(self) -> Option<&'a Url> {
1220 match self {
1221 Self::FromSelfVerified(url) | Self::FromSelf(url) => Some(url),
1222 _ => None,
1223 }
1224 }
1225
1226 #[must_use]
1228 pub fn any_unverified(self) -> Option<&'a Url> {
1229 match self {
1230 Self::FromSelfVerified(url) | Self::FromSelf(url) | Self::FromOthers(url) => Some(url),
1231 Self::None => None,
1232 }
1233 }
1234}
1235
1236pub struct TrustDistanceParams {
1237 pub max_distance: u64,
1238 pub high_trust_distance: u64,
1239 pub medium_trust_distance: u64,
1240 pub low_trust_distance: u64,
1241 pub none_trust_distance: u64,
1242 pub distrust_distance: u64,
1243}
1244
1245impl TrustDistanceParams {
1246 #[must_use]
1247 pub fn new_no_wot() -> Self {
1248 Self {
1249 max_distance: 0,
1250 high_trust_distance: 1,
1251 medium_trust_distance: 1,
1252 low_trust_distance: 1,
1253 none_trust_distance: 1,
1254 distrust_distance: 1,
1255 }
1256 }
1257
1258 fn distance_by_level(&self, level: TrustLevel) -> u64 {
1259 use crev_data::proof::trust::TrustLevel::*;
1260 match level {
1261 Distrust => self.distrust_distance,
1262 None => self.none_trust_distance,
1263 Low => self.low_trust_distance,
1264 Medium => self.medium_trust_distance,
1265 High => self.high_trust_distance,
1266 }
1267 }
1268}
1269
1270impl Default for TrustDistanceParams {
1271 fn default() -> Self {
1272 Self {
1273 max_distance: 10,
1274 high_trust_distance: 0,
1275 medium_trust_distance: 1,
1276 low_trust_distance: 5,
1277 none_trust_distance: 11,
1278 distrust_distance: 11,
1279 }
1280 }
1281}
1282
1283#[derive(Debug, Clone, Default)]
1286pub struct OverrideSourcesDetails(HashMap<Id, TrustLevel>);
1287
1288impl OverrideSourcesDetails {
1289 pub fn insert(&mut self, id: Id, level: TrustLevel) {
1290 self.0
1291 .entry(id)
1292 .and_modify(|prev_level| *prev_level = level.max(*prev_level))
1293 .or_insert(level);
1294 }
1295
1296 #[must_use]
1297 pub fn max_level(&self) -> Option<TrustLevel> {
1298 self.0.iter().map(|e| e.1).max().copied()
1299 }
1300}
1301
1302#[test]
1303fn db_is_send_sync() {
1304 fn is<T: Send + Sync>() {}
1305 is::<ProofDB>();
1306}
1307
1308#[cfg(test)]
1309mod tests;