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