1use std::collections::{BTreeMap, BTreeSet};
15use std::convert::Infallible;
16use std::error::Error;
17use std::fmt;
18use std::future::Future;
19use std::num::NonZeroUsize;
20use std::sync::Arc;
21
22use ed25519_dalek::{SigningKey, VerifyingKey};
23
24use crate::blob::encodings::simplearchive::SimpleArchive;
25use crate::blob::Blob;
26use crate::capability::{
27 capability_quorum_authorized_subjects, capability_quorum_decide, CapabilityHandle,
28 CapabilityProof, CapabilityRequest, CapabilityResource, QuorumOutcome,
29};
30use crate::id::Id;
31use crate::inline::encodings::hash::Handle;
32use crate::inline::{Inline, InlineEncoding};
33use crate::patch::{Blake3Merkle, IdentitySchema, PATCH};
34use crate::repo::async_store::AsyncBlobStoreAcquire;
35use crate::repo::{BlobStoreGet, BlobStorePut, CapabilityProofRead};
36use crate::repo::{CapabilityProofStore, SnapshotSource, Store, StoreRead, StoreSnapshot};
37use crate::trible::{Fragment, TribleSet};
38
39use super::exact_derived::CollectionRealizationError;
40use super::simplearchive_union::PreparedCollectionCommit;
41use super::{
42 descriptor, read_capability, write_capability, Collection, CollectionCommit, CollectionData,
43 CollectionEncoding, CollectionHandle, CollectionSnapshot, CollectionStore, CollectionTypeError,
44 RecordDecodeError, TryFromCover, TryFromCoverError,
45};
46use super::{
47 AdmissionPolicy, AttachedSnapshot, CanonicalAttachment, CanonicalDerivation,
48 CollectionAttachment, CollectionDerivation, CollectionPolicy, DeriveMapping, MapMapping,
49};
50
51#[derive(Debug)]
60pub enum CollectionEvidenceDiscoveryError<ProofsError> {
61 Proofs(ProofsError),
63}
64
65impl<ProofsError> fmt::Display for CollectionEvidenceDiscoveryError<ProofsError>
66where
67 ProofsError: fmt::Display,
68{
69 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
70 match self {
71 Self::Proofs(source) => {
72 write!(
73 formatter,
74 "failed to enumerate resident capability proofs: {source}"
75 )
76 }
77 }
78 }
79}
80
81impl<ProofsError> Error for CollectionEvidenceDiscoveryError<ProofsError>
82where
83 ProofsError: Error + 'static,
84{
85 fn source(&self) -> Option<&(dyn Error + 'static)> {
86 match self {
87 Self::Proofs(source) => Some(source),
88 }
89 }
90}
91
92#[derive(Debug)]
95pub enum CollectionDescriptorError<GetError> {
96 Get {
98 collection: CollectionHandle,
100 source: GetError,
102 },
103 Invalid {
105 collection: CollectionHandle,
107 source: RecordDecodeError,
109 },
110}
111
112impl<GetError> fmt::Display for CollectionDescriptorError<GetError>
113where
114 GetError: fmt::Display,
115{
116 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
117 match self {
118 Self::Get { collection, source } => write!(
119 formatter,
120 "failed to fetch collection descriptor {}: {source}",
121 hex::encode_upper(collection.raw),
122 ),
123 Self::Invalid { collection, source } => write!(
124 formatter,
125 "collection descriptor {} is invalid: {source}",
126 hex::encode_upper(collection.raw),
127 ),
128 }
129 }
130}
131
132impl<GetError> Error for CollectionDescriptorError<GetError>
133where
134 GetError: Error + 'static,
135{
136 fn source(&self) -> Option<&(dyn Error + 'static)> {
137 match self {
138 Self::Get { source, .. } => Some(source),
139 Self::Invalid { source, .. } => Some(source),
140 }
141 }
142}
143
144#[derive(Debug)]
149pub enum CollectionOpenError<GetError> {
150 Descriptor(CollectionDescriptorError<GetError>),
152 WrongType(CollectionTypeError),
154}
155
156impl<GetError> fmt::Display for CollectionOpenError<GetError>
157where
158 GetError: fmt::Display,
159{
160 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
161 match self {
162 Self::Descriptor(source) => source.fmt(formatter),
163 Self::WrongType(source) => write!(formatter, "wrong collection type: {source}"),
164 }
165 }
166}
167
168impl<GetError> Error for CollectionOpenError<GetError>
169where
170 GetError: Error + 'static,
171{
172 fn source(&self) -> Option<&(dyn Error + 'static)> {
173 match self {
174 Self::Descriptor(source) => Some(source),
175 Self::WrongType(source) => Some(source),
176 }
177 }
178}
179
180#[derive(Debug)]
185pub enum CollectionGrantError<SnapshotError, GetError, InsertError> {
186 Snapshot(SnapshotError),
188 Descriptor(CollectionDescriptorError<GetError>),
190 OpenPolicy {
192 capability: CapabilityHandle,
194 collection: CollectionHandle,
196 },
197 RootNotAuthorized {
199 capability: CapabilityHandle,
201 collection: CollectionHandle,
203 root: VerifyingKey,
205 },
206 ProofInsert(InsertError),
208}
209
210impl<SnapshotError, GetError, InsertError> fmt::Display
211 for CollectionGrantError<SnapshotError, GetError, InsertError>
212where
213 SnapshotError: fmt::Display,
214 GetError: fmt::Display,
215 InsertError: fmt::Display,
216{
217 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
218 match self {
219 Self::Snapshot(source) => {
220 write!(formatter, "failed to freeze store snapshot: {source}")
221 }
222 Self::Descriptor(source) => source.fmt(formatter),
223 Self::OpenPolicy {
224 capability,
225 collection,
226 } => write!(
227 formatter,
228 "collection {} has an open {} policy; no proof is required",
229 hex::encode_upper(collection.raw),
230 collection_capability_label(*capability),
231 ),
232 Self::RootNotAuthorized {
233 capability,
234 collection,
235 root,
236 } => write!(
237 formatter,
238 "key {} is not a {} root of collection {}",
239 hex::encode_upper(root.to_bytes()),
240 collection_capability_label(*capability),
241 hex::encode_upper(collection.raw),
242 ),
243 Self::ProofInsert(source) => {
244 write!(formatter, "failed to insert capability proof: {source}")
245 }
246 }
247 }
248}
249
250impl<SnapshotError, GetError, InsertError> Error
251 for CollectionGrantError<SnapshotError, GetError, InsertError>
252where
253 SnapshotError: Error + 'static,
254 GetError: Error + 'static,
255 InsertError: Error + 'static,
256{
257 fn source(&self) -> Option<&(dyn Error + 'static)> {
258 match self {
259 Self::Snapshot(source) => Some(source),
260 Self::Descriptor(source) => Some(source),
261 Self::OpenPolicy { .. } | Self::RootNotAuthorized { .. } => None,
262 Self::ProofInsert(source) => Some(source),
263 }
264 }
265}
266
267pub type CollectionReadGrantError<SnapshotError, GetError, InsertError> =
269 CollectionGrantError<SnapshotError, GetError, InsertError>;
270
271pub type CollectionWriteGrantError<SnapshotError, GetError, InsertError> =
273 CollectionGrantError<SnapshotError, GetError, InsertError>;
274
275fn collection_capability_label(capability: CapabilityHandle) -> String {
276 if capability == read_capability() {
277 "READ".to_owned()
278 } else if capability == write_capability() {
279 "WRITE".to_owned()
280 } else {
281 hex::encode_upper(capability.raw)
282 }
283}
284
285#[derive(Debug)]
292pub enum CollectionAdmissionError<ProofsError, GetError> {
293 Descriptor(CollectionDescriptorError<GetError>),
295 Evidence(CollectionEvidenceDiscoveryError<ProofsError>),
297 Definition(crate::repo::ReadFailure),
299}
300
301#[derive(Clone, Debug, Eq, PartialEq)]
306pub enum CollectionReadAudience {
307 Open,
309 Restricted(Vec<VerifyingKey>),
311}
312
313impl<ProofsError, GetError> fmt::Display for CollectionAdmissionError<ProofsError, GetError>
314where
315 ProofsError: fmt::Display,
316 GetError: fmt::Display,
317{
318 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
319 match self {
320 Self::Descriptor(source) => source.fmt(formatter),
321 Self::Evidence(source) => source.fmt(formatter),
322 Self::Definition(source) => source.fmt(formatter),
323 }
324 }
325}
326
327impl<ProofsError, GetError> Error for CollectionAdmissionError<ProofsError, GetError>
328where
329 ProofsError: Error + 'static,
330 GetError: Error + 'static,
331{
332 fn source(&self) -> Option<&(dyn Error + 'static)> {
333 match self {
334 Self::Descriptor(source) => Some(source),
335 Self::Evidence(source) => Some(source),
336 Self::Definition(source) => Some(source),
337 }
338 }
339}
340
341#[derive(Debug)]
343pub enum CollectionRegistrationError<PutError> {
344 WrongType(CollectionTypeError),
346 DependencyPut(PutError),
348}
349
350impl<PutError> fmt::Display for CollectionRegistrationError<PutError>
351where
352 PutError: fmt::Display,
353{
354 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
355 match self {
356 Self::WrongType(source) => write!(formatter, "wrong collection type: {source}"),
357 Self::DependencyPut(source) => {
358 write!(
359 formatter,
360 "failed to store collection descriptor closure: {source}"
361 )
362 }
363 }
364 }
365}
366
367impl<PutError> Error for CollectionRegistrationError<PutError>
368where
369 PutError: Error + 'static,
370{
371 fn source(&self) -> Option<&(dyn Error + 'static)> {
372 match self {
373 Self::WrongType(source) => Some(source),
374 Self::DependencyPut(source) => Some(source),
375 }
376 }
377}
378
379pub struct Cover<L: CollectionEncoding> {
388 collection: Collection<L>,
389 members: PATCH<32, IdentitySchema, (), Blake3Merkle>,
390}
391
392impl<L: CollectionEncoding> Cover<L> {
393 pub(crate) fn from_members(
394 collection: Collection<L>,
395 members: impl IntoIterator<Item = Inline<Handle<L>>>,
396 ) -> Self {
397 Self {
398 collection,
399 members: PATCH::from_keys(members.into_iter().map(|member| member.raw)),
400 }
401 }
402
403 pub(crate) fn from_data(
404 collection: Collection<L>,
405 members: impl IntoIterator<Item = CollectionData>,
406 ) -> Self {
407 Self {
408 collection,
409 members: PATCH::from_keys(members.into_iter().map(|member| member.raw)),
410 }
411 }
412
413 pub(crate) fn from_patch(
414 collection: Collection<L>,
415 members: PATCH<32, IdentitySchema, (), Blake3Merkle>,
416 ) -> Self {
417 Self {
418 collection,
419 members,
420 }
421 }
422
423 pub const fn collection(&self) -> Collection<L> {
425 self.collection
426 }
427
428 pub fn members(&self) -> impl ExactSizeIterator<Item = Inline<Handle<L>>> + '_ {
430 self.members
431 .iter_ordered()
432 .map(|member| Inline::new(*member))
433 }
434
435 pub fn contains(&self, member: Inline<Handle<L>>) -> bool {
437 self.members.get(&member.raw).is_some()
438 }
439
440 pub(crate) fn data_members(&self) -> impl ExactSizeIterator<Item = CollectionData> + '_ {
441 self.members
442 .iter_ordered()
443 .map(|member| Inline::new(*member))
444 }
445
446 pub fn len(&self) -> usize {
448 self.members.len().min(usize::MAX as u64) as usize
449 }
450
451 pub fn is_empty(&self) -> bool {
453 self.members.is_empty()
454 }
455
456 fn ensure_same_collection(&self, other: &Self) -> Result<(), CoverAlgebraError> {
457 if self.collection == other.collection {
458 Ok(())
459 } else {
460 Err(CoverAlgebraError::DifferentCollection {
461 left: self.collection.handle(),
462 right: other.collection.handle(),
463 })
464 }
465 }
466
467 pub fn union(&self, other: &Self) -> Result<Self, CoverAlgebraError> {
469 self.ensure_same_collection(other)?;
470 let mut members = self.members.clone();
471 members.union(other.members.clone());
472 Ok(Self::from_patch(self.collection, members))
473 }
474
475 pub fn intersection(&self, other: &Self) -> Result<Self, CoverAlgebraError> {
477 self.ensure_same_collection(other)?;
478 Ok(Self::from_patch(
479 self.collection,
480 self.members.intersect(&other.members),
481 ))
482 }
483
484 pub fn difference(&self, other: &Self) -> Result<Self, CoverAlgebraError> {
486 self.ensure_same_collection(other)?;
487 Ok(Self::from_patch(
488 self.collection,
489 self.members.difference(&other.members),
490 ))
491 }
492
493 pub fn is_subset(&self, other: &Self) -> Result<bool, CoverAlgebraError> {
495 self.ensure_same_collection(other)?;
496 Ok(self.members <= other.members)
497 }
498
499 pub fn additions_since(&self, previous: &Self) -> Result<Self, CoverAdvanceError> {
506 if self.collection != previous.collection {
507 return Err(CoverAdvanceError::DifferentCollection {
508 previous: previous.collection.handle(),
509 current: self.collection.handle(),
510 });
511 }
512 let missing = previous.members.difference(&self.members);
513 if let Some(member) = missing.iter_ordered().next() {
514 return Err(CoverAdvanceError::ResetRequired {
515 missing: Inline::new(*member),
516 });
517 }
518 Ok(Self::from_patch(
519 self.collection,
520 self.members.difference(&previous.members),
521 ))
522 }
523}
524
525impl<L: CollectionEncoding> Clone for Cover<L> {
526 fn clone(&self) -> Self {
527 Self {
528 collection: self.collection,
529 members: self.members.clone(),
530 }
531 }
532}
533
534impl<L: CollectionEncoding> fmt::Debug for Cover<L> {
535 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
536 formatter
537 .debug_struct("Cover")
538 .field("collection", &self.collection)
539 .field("members", &self.data_members().collect::<Vec<_>>())
540 .finish()
541 }
542}
543
544impl<L: CollectionEncoding> PartialEq for Cover<L> {
545 fn eq(&self, other: &Self) -> bool {
546 self.collection == other.collection && self.members == other.members
547 }
548}
549
550impl<L: CollectionEncoding> Eq for Cover<L> {}
551
552pub type Support<E = SimpleArchive> = Cover<E>;
564
565#[derive(Clone, Debug, Eq, PartialEq)]
567pub enum CoverAlgebraError {
568 DifferentCollection {
570 left: CollectionHandle,
572 right: CollectionHandle,
574 },
575}
576
577impl fmt::Display for CoverAlgebraError {
578 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
579 match self {
580 Self::DifferentCollection { left, right } => write!(
581 formatter,
582 "cover collection {} differs from {}",
583 hex::encode_upper(left.raw),
584 hex::encode_upper(right.raw),
585 ),
586 }
587 }
588}
589
590impl Error for CoverAlgebraError {}
591
592#[derive(Clone, Debug, Eq, PartialEq)]
594pub enum CoverAdvanceError {
595 DifferentCollection {
597 previous: CollectionHandle,
599 current: CollectionHandle,
601 },
602 ResetRequired {
604 missing: CollectionData,
606 },
607}
608
609impl fmt::Display for CoverAdvanceError {
610 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
611 match self {
612 Self::DifferentCollection { previous, current } => write!(
613 formatter,
614 "cover collection {} differs from {}",
615 hex::encode_upper(current.raw),
616 hex::encode_upper(previous.raw),
617 ),
618 Self::ResetRequired { missing } => write!(
619 formatter,
620 "previous cover member {} is absent from the current observation; additions-only processing requires a reset",
621 hex::encode_upper(missing.raw),
622 ),
623 }
624 }
625}
626
627impl Error for CoverAdvanceError {}
628
629#[derive(Debug)]
631pub enum CollectionCommitError<PutError, InsertError> {
632 DependencyPut(PutError),
634 RecordInsert(InsertError),
636}
637
638impl<PutError, InsertError> fmt::Display for CollectionCommitError<PutError, InsertError>
639where
640 PutError: fmt::Display,
641 InsertError: fmt::Display,
642{
643 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
644 match self {
645 Self::DependencyPut(source) => {
646 write!(formatter, "failed to store collection dependency: {source}")
647 }
648 Self::RecordInsert(source) => {
649 write!(formatter, "failed to insert collection commit: {source}")
650 }
651 }
652 }
653}
654
655impl<PutError, InsertError> Error for CollectionCommitError<PutError, InsertError>
656where
657 PutError: Error + 'static,
658 InsertError: Error + 'static,
659{
660 fn source(&self) -> Option<&(dyn Error + 'static)> {
661 match self {
662 Self::DependencyPut(source) => Some(source),
663 Self::RecordInsert(source) => Some(source),
664 }
665 }
666}
667
668#[derive(Debug)]
670pub enum CollectionReadError<GetError, ViewError> {
671 Realization(CollectionRealizationError),
673 View(TryFromCoverError<GetError, ViewError>),
675}
676
677impl<GetError, ViewError> fmt::Display for CollectionReadError<GetError, ViewError>
678where
679 GetError: fmt::Display,
680 ViewError: fmt::Display,
681{
682 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
683 match self {
684 Self::Realization(source) => source.fmt(formatter),
685 Self::View(source) => source.fmt(formatter),
686 }
687 }
688}
689
690impl<GetError, ViewError> Error for CollectionReadError<GetError, ViewError>
691where
692 GetError: Error + 'static,
693 ViewError: Error + 'static,
694{
695 fn source(&self) -> Option<&(dyn Error + 'static)> {
696 match self {
697 Self::Realization(source) => Some(source),
698 Self::View(source) => Some(source),
699 }
700 }
701}
702
703pub(crate) struct LoadedCollectionDescriptor {
704 pub(crate) fragment: Fragment,
705}
706
707pub(crate) fn load_collection_descriptor<R>(
708 snapshot: &R,
709 collection: CollectionHandle,
710) -> Result<LoadedCollectionDescriptor, CollectionDescriptorError<R::GetError<Infallible>>>
711where
712 R: BlobStoreGet,
713{
714 let descriptor_blob: Blob<SimpleArchive> = snapshot
715 .get(collection)
716 .map_err(|source| CollectionDescriptorError::Get { collection, source })?;
717 let facts =
718 <TribleSet as crate::blob::TryFromBlob<SimpleArchive>>::try_from_blob(descriptor_blob)
719 .map_err(|source| CollectionDescriptorError::Invalid {
720 collection,
721 source: RecordDecodeError::from(source),
722 })?;
723 Ok(LoadedCollectionDescriptor {
724 fragment: Fragment::from(facts),
725 })
726}
727
728pub(crate) enum AdmissionEvidence {
729 Open,
730 Alternatives(Vec<AdmissionEvidence>),
731 Quorum {
732 roots: Vec<VerifyingKey>,
733 invoke_threshold: NonZeroUsize,
734 request: CapabilityRequest,
735 proofs: Arc<[CapabilityProof]>,
736 },
737}
738
739impl AdmissionEvidence {
740 pub(crate) fn authorizes<R: BlobStoreGet>(&self, reader: &R, subject: VerifyingKey) -> bool {
741 matches!(self.decide(reader, subject), QuorumOutcome::Met)
742 }
743
744 pub(crate) fn decide<R: BlobStoreGet>(
748 &self,
749 reader: &R,
750 subject: VerifyingKey,
751 ) -> QuorumOutcome {
752 match self {
753 Self::Open => QuorumOutcome::Met,
754 Self::Alternatives(alternatives) => {
755 let mut undefined = Vec::new();
756 for evidence in alternatives {
757 match evidence.decide(reader, subject) {
758 QuorumOutcome::Met => return QuorumOutcome::Met,
759 QuorumOutcome::Unmet => {}
760 QuorumOutcome::Undefined(handles) => undefined.extend(handles),
761 }
762 }
763 if undefined.is_empty() {
764 QuorumOutcome::Unmet
765 } else {
766 QuorumOutcome::Undefined(undefined)
767 }
768 }
769 Self::Quorum {
770 roots,
771 invoke_threshold,
772 request,
773 proofs,
774 } => capability_quorum_decide(
775 reader,
776 proofs.iter(),
777 roots.iter().copied(),
778 subject,
779 *request,
780 *invoke_threshold,
781 ),
782 }
783 }
784
785 fn authorized_subjects<R: BlobStoreGet>(&self, reader: &R) -> CollectionReadAudience {
786 match self {
787 Self::Open => CollectionReadAudience::Open,
788 Self::Alternatives(alternatives) => {
789 let mut subjects = BTreeMap::new();
790 for alternative in alternatives {
791 match alternative.authorized_subjects(reader) {
792 CollectionReadAudience::Open => return CollectionReadAudience::Open,
793 CollectionReadAudience::Restricted(audience) => {
794 subjects.extend(
795 audience
796 .into_iter()
797 .map(|subject| (subject.to_bytes(), subject)),
798 );
799 }
800 }
801 }
802 CollectionReadAudience::Restricted(subjects.into_values().collect())
803 }
804 Self::Quorum {
805 roots,
806 invoke_threshold,
807 request,
808 proofs,
809 } => CollectionReadAudience::Restricted(capability_quorum_authorized_subjects(
810 reader,
811 proofs.iter(),
812 roots.iter().copied(),
813 *request,
814 *invoke_threshold,
815 )),
816 }
817 }
818}
819
820pub(crate) fn admission_evidence_from_proofs(
821 policy: &AdmissionPolicy,
822 action: Id,
823 collection: CollectionHandle,
824 proofs: Arc<[CapabilityProof]>,
825) -> AdmissionEvidence {
826 let AdmissionPolicy::Quorum(quorum) = policy else {
827 return AdmissionEvidence::Open;
828 };
829 let request = CapabilityRequest::new(CapabilityResource::from(collection), action);
830 AdmissionEvidence::Quorum {
831 roots: quorum.roots().to_vec(),
832 invoke_threshold: NonZeroUsize::new(quorum.invoke_threshold() as usize)
833 .expect("validated collection policy has a nonzero invoke threshold"),
834 request,
835 proofs,
836 }
837}
838
839pub(super) fn discover_admission_evidence<S>(
840 snapshot: &S,
841 policies: impl IntoIterator<Item = AdmissionPolicy>,
842 action: Id,
843 collection: CollectionHandle,
844) -> Result<AdmissionEvidence, CollectionEvidenceDiscoveryError<S::ProofsError>>
845where
846 S: CapabilityProofRead,
847{
848 let policies: Vec<_> = policies.into_iter().collect();
849 if policies
850 .iter()
851 .any(|policy| matches!(policy, AdmissionPolicy::Open))
852 {
853 return Ok(AdmissionEvidence::Open);
854 }
855 if policies.is_empty() {
856 return Ok(AdmissionEvidence::Alternatives(Vec::new()));
857 }
858 let proofs = snapshot
859 .proofs()
860 .map_err(CollectionEvidenceDiscoveryError::Proofs)?
861 .collect::<Result<Vec<_>, _>>()
862 .map_err(CollectionEvidenceDiscoveryError::Proofs)?;
863 let proofs: Arc<[CapabilityProof]> = proofs.into();
864 Ok(AdmissionEvidence::Alternatives(
865 policies
866 .iter()
867 .map(|policy| {
868 admission_evidence_from_proofs(policy, action, collection, Arc::clone(&proofs))
869 })
870 .collect(),
871 ))
872}
873
874pub fn grant_collection_read<S>(
886 store: &mut S,
887 collection: CollectionHandle,
888 root: &SigningKey,
889 recipient: VerifyingKey,
890) -> Result<
891 CapabilityProof,
892 CollectionReadGrantError<
893 <S as SnapshotSource>::SnapshotError,
894 <<S as SnapshotSource>::Snapshot as BlobStoreGet>::GetError<Infallible>,
895 <S as CapabilityProofStore>::InsertError,
896 >,
897>
898where
899 S: SnapshotSource + CapabilityProofStore,
900 <S as SnapshotSource>::Snapshot: BlobStoreGet,
901{
902 grant_collection_capability(store, collection, read_capability(), root, recipient)
903}
904
905pub fn grant_collection_write<S>(
913 store: &mut S,
914 collection: CollectionHandle,
915 root: &SigningKey,
916 recipient: VerifyingKey,
917) -> Result<
918 CapabilityProof,
919 CollectionWriteGrantError<
920 <S as SnapshotSource>::SnapshotError,
921 <<S as SnapshotSource>::Snapshot as BlobStoreGet>::GetError<Infallible>,
922 <S as CapabilityProofStore>::InsertError,
923 >,
924>
925where
926 S: SnapshotSource + CapabilityProofStore,
927 <S as SnapshotSource>::Snapshot: BlobStoreGet,
928{
929 grant_collection_capability(store, collection, write_capability(), root, recipient)
930}
931
932pub fn grant_collection_capability<S>(
940 store: &mut S,
941 collection: CollectionHandle,
942 capability: CapabilityHandle,
943 root: &SigningKey,
944 recipient: VerifyingKey,
945) -> Result<
946 CapabilityProof,
947 CollectionGrantError<
948 <S as SnapshotSource>::SnapshotError,
949 <<S as SnapshotSource>::Snapshot as BlobStoreGet>::GetError<Infallible>,
950 <S as CapabilityProofStore>::InsertError,
951 >,
952>
953where
954 S: SnapshotSource + CapabilityProofStore,
955 <S as SnapshotSource>::Snapshot: BlobStoreGet,
956{
957 let snapshot = store.snapshot().map_err(CollectionGrantError::Snapshot)?;
958 let descriptor = load_collection_descriptor(&snapshot, collection)
959 .map_err(CollectionGrantError::Descriptor)?;
960 let definition = load_collection_descriptor(&snapshot, capability)
961 .map_err(CollectionGrantError::Descriptor)?;
962 let actions = crate::prelude::find!(
963 action: Id,
964 crate::prelude::temp!(
965 (grant),
966 crate::prelude::or!(
967 crate::prelude::pattern!(definition.fragment.facts(), [{ ?grant @ crate::capability::capability_action: ?action }]),
968 crate::prelude::pattern!(definition.fragment.facts(), [{ ?grant @ crate::capability::capability_delegate_action: ?action }]),
969 )
970 )
971 );
972 let root_key = root.verifying_key();
973 let mut has_action = false;
974 let mut needs_grant = false;
975 for action in actions {
976 has_action = true;
977 let mut open = false;
978 let mut root_is_authorized = false;
979 for policy in
980 descriptor::admission_policies(&snapshot, descriptor.fragment.facts(), action, None)
981 {
982 open |= matches!(policy, AdmissionPolicy::Open);
983 root_is_authorized |= policy
984 .roots()
985 .is_some_and(|roots| roots.contains(&root_key));
986 }
987 if open {
988 continue;
989 }
990 if !root_is_authorized {
991 return Err(CollectionGrantError::RootNotAuthorized {
992 capability,
993 collection,
994 root: root_key,
995 });
996 }
997 needs_grant = true;
998 }
999 if !has_action {
1000 return Err(CollectionGrantError::RootNotAuthorized {
1001 capability,
1002 collection,
1003 root: root_key,
1004 });
1005 }
1006 if !needs_grant {
1007 return Err(CollectionGrantError::OpenPolicy {
1008 capability,
1009 collection,
1010 });
1011 }
1012 drop(snapshot);
1013
1014 let proof = CapabilityProof::new(
1015 CapabilityResource::from(collection),
1016 root,
1017 capability,
1018 recipient,
1019 );
1020 store
1021 .insert_proof(proof.clone())
1022 .map_err(CollectionGrantError::ProofInsert)?;
1023 Ok(proof)
1024}
1025
1026pub fn collection_reader_is_admitted_by<S>(
1034 snapshot: &S,
1035 collection: CollectionHandle,
1036 subject: VerifyingKey,
1037 proofs: &[CapabilityProof],
1038) -> Result<bool, CollectionDescriptorError<S::GetError<Infallible>>>
1039where
1040 S: StoreSnapshot + BlobStoreGet,
1041{
1042 let loaded = load_collection_descriptor(snapshot, collection)?;
1043 let admitted =
1044 descriptor::admission_policies(snapshot, loaded.fragment.facts(), super::ACTION_READ, None)
1045 .any(|policy| {
1046 collection_reader_is_admitted_by_policy(
1047 snapshot, collection, &policy, subject, proofs,
1048 )
1049 });
1050 Ok(admitted)
1051}
1052
1053pub fn collection_reader_is_admitted_by_policy<R: BlobStoreGet>(
1060 reader: &R,
1061 collection: CollectionHandle,
1062 policy: &AdmissionPolicy,
1063 subject: VerifyingKey,
1064 proofs: &[CapabilityProof],
1065) -> bool {
1066 let evidence = admission_evidence_from_proofs(
1067 policy,
1068 super::ACTION_READ,
1069 collection,
1070 proofs.to_vec().into(),
1071 );
1072 evidence.authorizes(reader, subject)
1073}
1074
1075pub fn collection_read_audience_by_policy<R: BlobStoreGet>(
1084 reader: &R,
1085 collection: CollectionHandle,
1086 policy: &AdmissionPolicy,
1087 proofs: &[CapabilityProof],
1088) -> CollectionReadAudience {
1089 admission_evidence_from_proofs(
1090 policy,
1091 super::ACTION_READ,
1092 collection,
1093 proofs.to_vec().into(),
1094 )
1095 .authorized_subjects(reader)
1096}
1097
1098pub fn collection_read_audience<S>(
1103 snapshot: &S,
1104 collection: CollectionHandle,
1105) -> Result<CollectionReadAudience, CollectionAdmissionError<S::ProofsError, S::GetError<Infallible>>>
1106where
1107 S: StoreSnapshot + BlobStoreGet + CapabilityProofRead,
1108{
1109 collection_action_audience(snapshot, collection, super::ACTION_READ)
1110}
1111
1112pub fn collection_action_audience<S>(
1117 snapshot: &S,
1118 collection: CollectionHandle,
1119 action: Id,
1120) -> Result<CollectionReadAudience, CollectionAdmissionError<S::ProofsError, S::GetError<Infallible>>>
1121where
1122 S: StoreSnapshot + BlobStoreGet + CapabilityProofRead,
1123{
1124 let loaded = load_collection_descriptor(snapshot, collection)
1125 .map_err(CollectionAdmissionError::Descriptor)?;
1126 let evidence = discover_admission_evidence(
1127 snapshot,
1128 descriptor::admission_policies(snapshot, loaded.fragment.facts(), action, None),
1129 action,
1130 collection,
1131 )
1132 .map_err(CollectionAdmissionError::Evidence)?;
1133 Ok(evidence.authorized_subjects(snapshot))
1134}
1135
1136pub fn collection_writer_is_admitted_by_policy<R: BlobStoreGet>(
1143 reader: &R,
1144 collection: CollectionHandle,
1145 policy: &AdmissionPolicy,
1146 subject: VerifyingKey,
1147 proofs: &[CapabilityProof],
1148) -> bool {
1149 let evidence = admission_evidence_from_proofs(
1150 policy,
1151 super::ACTION_WRITE,
1152 collection,
1153 proofs.to_vec().into(),
1154 );
1155 evidence.authorizes(reader, subject)
1156}
1157
1158impl<L: CollectionEncoding> Collection<L> {
1159 pub fn cover(self, members: impl IntoIterator<Item = Inline<Handle<L>>>) -> Cover<L> {
1166 Cover::from_members(self, members)
1167 }
1168
1169 pub fn open<S>(
1177 snapshot: &S,
1178 handle: CollectionHandle,
1179 ) -> Result<Self, CollectionOpenError<S::GetError<Infallible>>>
1180 where
1181 S: BlobStoreGet,
1182 {
1183 let descriptor = load_collection_descriptor(snapshot, handle)
1184 .map_err(CollectionOpenError::Descriptor)?;
1185 super::encoding::validate_descriptor_type::<L>(&descriptor.fragment)
1186 .map_err(CollectionOpenError::WrongType)?;
1187 Ok(Self::from_handle(handle))
1188 }
1189
1190 pub fn policy<S>(
1196 self,
1197 snapshot: &S,
1198 ) -> Result<CollectionPolicy, CollectionDescriptorError<S::GetError<Infallible>>>
1199 where
1200 S: BlobStoreGet,
1201 {
1202 let loaded = load_collection_descriptor(snapshot, self.handle())?;
1203 descriptor::policy(loaded.fragment.facts()).map_err(|source| {
1204 CollectionDescriptorError::Invalid {
1205 collection: self.handle(),
1206 source,
1207 }
1208 })
1209 }
1210
1211 pub fn writer_is_admitted<S>(
1217 self,
1218 snapshot: &S,
1219 subject: VerifyingKey,
1220 ) -> Result<bool, CollectionAdmissionError<S::ProofsError, S::GetError<Infallible>>>
1221 where
1222 S: StoreSnapshot + BlobStoreGet + CapabilityProofRead,
1223 {
1224 let loaded = load_collection_descriptor(snapshot, self.handle())
1225 .map_err(CollectionAdmissionError::Descriptor)?;
1226 let evidence = discover_admission_evidence(
1227 snapshot,
1228 descriptor::admission_policies(
1229 snapshot,
1230 loaded.fragment.facts(),
1231 super::ACTION_WRITE,
1232 Some(L::id()),
1233 ),
1234 super::ACTION_WRITE,
1235 self.handle(),
1236 )
1237 .map_err(CollectionAdmissionError::Evidence)?;
1238 Ok(evidence.authorizes(snapshot, subject))
1239 }
1240
1241 pub fn writer_is_admitted_acquiring<S>(
1245 self,
1246 snapshot: &S,
1247 subject: VerifyingKey,
1248 ) -> Result<bool, CollectionAdmissionError<S::ProofsError, S::GetError<Infallible>>>
1249 where
1250 S: StoreSnapshot + BlobStoreGet + CapabilityProofRead,
1251 {
1252 self.admitted_acquiring(snapshot, subject, super::ACTION_WRITE)
1253 }
1254
1255 pub fn reader_is_admitted_acquiring<S>(
1257 self,
1258 snapshot: &S,
1259 subject: VerifyingKey,
1260 ) -> Result<bool, CollectionAdmissionError<S::ProofsError, S::GetError<Infallible>>>
1261 where
1262 S: StoreSnapshot + BlobStoreGet + CapabilityProofRead,
1263 {
1264 self.admitted_acquiring(snapshot, subject, super::ACTION_READ)
1265 }
1266
1267 fn admitted_acquiring<S>(
1268 self,
1269 snapshot: &S,
1270 subject: VerifyingKey,
1271 action: Id,
1272 ) -> Result<bool, CollectionAdmissionError<S::ProofsError, S::GetError<Infallible>>>
1273 where
1274 S: StoreSnapshot + BlobStoreGet + CapabilityProofRead,
1275 {
1276 let loaded = load_collection_descriptor(snapshot, self.handle())
1277 .map_err(CollectionAdmissionError::Descriptor)?;
1278 let attempt = crate::repo::read_attempt::ReadAttempt::new(snapshot);
1279 let evidence = discover_admission_evidence(
1280 snapshot,
1281 descriptor::admission_policies(
1282 &attempt,
1283 loaded.fragment.facts(),
1284 action,
1285 Some(L::id()),
1286 ),
1287 action,
1288 self.handle(),
1289 )
1290 .map_err(CollectionAdmissionError::Evidence)?;
1291 let admitted = evidence.authorizes(&attempt, subject);
1292 attempt
1293 .finish()
1294 .map_err(CollectionAdmissionError::Definition)?;
1295 Ok(admitted)
1296 }
1297
1298 pub fn reader_is_admitted<S>(
1304 self,
1305 snapshot: &S,
1306 subject: VerifyingKey,
1307 ) -> Result<bool, CollectionAdmissionError<S::ProofsError, S::GetError<Infallible>>>
1308 where
1309 S: StoreSnapshot + BlobStoreGet + CapabilityProofRead,
1310 {
1311 let loaded = load_collection_descriptor(snapshot, self.handle())
1312 .map_err(CollectionAdmissionError::Descriptor)?;
1313 let evidence = discover_admission_evidence(
1314 snapshot,
1315 descriptor::admission_policies(
1316 snapshot,
1317 loaded.fragment.facts(),
1318 super::ACTION_READ,
1319 Some(L::id()),
1320 ),
1321 super::ACTION_READ,
1322 self.handle(),
1323 )
1324 .map_err(CollectionAdmissionError::Evidence)?;
1325 Ok(evidence.authorizes(snapshot, subject))
1326 }
1327
1328 pub fn reader_is_admitted_by<S>(
1336 self,
1337 snapshot: &S,
1338 subject: VerifyingKey,
1339 proofs: &[CapabilityProof],
1340 ) -> Result<bool, CollectionDescriptorError<S::GetError<Infallible>>>
1341 where
1342 S: StoreSnapshot + BlobStoreGet,
1343 {
1344 let loaded = load_collection_descriptor(snapshot, self.handle())?;
1345 let admitted = descriptor::admission_policies(
1346 snapshot,
1347 loaded.fragment.facts(),
1348 super::ACTION_READ,
1349 Some(L::id()),
1350 )
1351 .any(|policy| {
1352 collection_reader_is_admitted_by_policy(
1353 snapshot,
1354 self.handle(),
1355 &policy,
1356 subject,
1357 proofs,
1358 )
1359 });
1360 Ok(admitted)
1361 }
1362
1363 pub fn admitted<S>(self, snapshot: &S) -> Result<Cover<L>, S::RecordsError>
1371 where
1372 S: StoreRead,
1373 {
1374 let coverage = snapshot.coverage(&BTreeSet::from([self.handle()]))?;
1375 let (support, _) = coverage.frontier_support(self.handle());
1376 Ok(Cover::from_patch(self, support))
1377 }
1378
1379 pub fn read<V, S>(
1384 self,
1385 snapshot: &S,
1386 ) -> Result<V, CollectionReadError<S::GetError<Infallible>, V::Error>>
1387 where
1388 S: StoreRead,
1389 V: TryFromCover<L>,
1390 {
1391 snapshot
1392 .collection(self)
1393 .map_err(CollectionReadError::Realization)?
1394 .view()
1395 .map_err(CollectionReadError::View)
1396 }
1397
1398 pub fn read_acquiring<V, S>(
1402 self,
1403 snapshot: &S,
1404 ) -> Result<V, CollectionReadError<S::GetError<Infallible>, V::Error>>
1405 where
1406 S: StoreRead,
1407 V: TryFromCover<L>,
1408 {
1409 snapshot
1410 .collection_acquiring(self)
1411 .map_err(CollectionReadError::Realization)?
1412 .view()
1413 .map_err(CollectionReadError::View)
1414 }
1415}
1416
1417pub trait CollectionSnapshotExt: StoreRead + Sized {
1424 fn collection<E>(
1432 &self,
1433 target: Collection<E>,
1434 ) -> Result<CollectionSnapshot<Self, E>, CollectionRealizationError>
1435 where
1436 E: CollectionEncoding,
1437 Handle<E>: InlineEncoding,
1438 {
1439 super::observation::attach(self, target)
1440 }
1441
1442 fn collection_acquiring<E>(
1447 &self,
1448 target: Collection<E>,
1449 ) -> Result<CollectionSnapshot<Self, E>, CollectionRealizationError>
1450 where
1451 E: CollectionEncoding,
1452 Handle<E>: InlineEncoding,
1453 {
1454 super::observation::attach_acquiring(self, target)
1455 }
1456
1457 fn attached<E>(
1469 &self,
1470 attached: Collection<E>,
1471 ) -> Result<AttachedSnapshot<Self, E>, CollectionRealizationError>
1472 where
1473 E: CollectionEncoding,
1474 Handle<E>: InlineEncoding,
1475 {
1476 super::observation::attach_attached(self, attached)
1477 }
1478
1479 fn attached_acquiring<E>(
1485 &self,
1486 attached: Collection<E>,
1487 ) -> Result<AttachedSnapshot<Self, E>, CollectionRealizationError>
1488 where
1489 E: CollectionEncoding,
1490 Handle<E>: InlineEncoding,
1491 {
1492 super::observation::attach_attached_acquiring(self, attached)
1493 }
1494}
1495
1496impl<R> CollectionSnapshotExt for R where R: StoreRead {}
1497
1498pub trait CollectionRealization: CollectionEncoding {
1514 fn ensure<'a, S>(
1518 store: &'a mut S,
1519 target: Collection<Self>,
1520 signing_key: &'a SigningKey,
1521 ) -> impl Future<Output = Result<S::Snapshot, CollectionRealizationError>> + Send + 'a
1522 where
1523 S: Store + AsyncBlobStoreAcquire + Send;
1524
1525 fn maintain<'a, S>(
1530 store: &'a mut S,
1531 target: Collection<Self>,
1532 signing_key: &'a SigningKey,
1533 ) -> impl Future<Output = Result<S::Snapshot, CollectionRealizationError>> + Send + 'a
1534 where
1535 S: Store + AsyncBlobStoreAcquire + Send;
1536}
1537
1538impl<T: CollectionDerivation> CollectionRealization for T {
1539 async fn ensure<'a, S>(
1540 store: &'a mut S,
1541 target: Collection<Self>,
1542 signing_key: &'a SigningKey,
1543 ) -> Result<S::Snapshot, CollectionRealizationError>
1544 where
1545 S: Store + AsyncBlobStoreAcquire + Send,
1546 {
1547 store
1548 .ensure_with::<CanonicalDerivation<T>>(target, signing_key)
1549 .await
1550 }
1551
1552 async fn maintain<'a, S>(
1553 store: &'a mut S,
1554 target: Collection<Self>,
1555 signing_key: &'a SigningKey,
1556 ) -> Result<S::Snapshot, CollectionRealizationError>
1557 where
1558 S: Store + AsyncBlobStoreAcquire + Send,
1559 {
1560 store
1561 .maintain_with::<CanonicalDerivation<T>>(target, signing_key)
1562 .await
1563 }
1564}
1565
1566impl CollectionRealization for SimpleArchive {
1567 async fn ensure<'a, S>(
1568 store: &'a mut S,
1569 target: Collection<Self>,
1570 signing_key: &'a SigningKey,
1571 ) -> Result<S::Snapshot, CollectionRealizationError>
1572 where
1573 S: Store + AsyncBlobStoreAcquire + Send,
1574 {
1575 realize_root(store, target, signing_key, false).await
1576 }
1577
1578 async fn maintain<'a, S>(
1579 store: &'a mut S,
1580 target: Collection<Self>,
1581 signing_key: &'a SigningKey,
1582 ) -> Result<S::Snapshot, CollectionRealizationError>
1583 where
1584 S: Store + AsyncBlobStoreAcquire + Send,
1585 {
1586 realize_root(store, target, signing_key, true).await
1587 }
1588}
1589
1590async fn realize_root<S>(
1591 store: &mut S,
1592 target: Collection<SimpleArchive>,
1593 signing_key: &SigningKey,
1594 compact: bool,
1595) -> Result<S::Snapshot, CollectionRealizationError>
1596where
1597 S: Store + AsyncBlobStoreAcquire,
1598{
1599 super::exact_derived::acquire_authority(store, target).await?;
1600 if compact {
1601 super::exact_derived::maintain_root_acquiring(store, target, signing_key).await?;
1604 } else {
1605 super::exact_derived::ensure_root(store, target).await?;
1606 }
1607 store.snapshot().map_err(|error| {
1608 CollectionRealizationError::storage("freeze realized root snapshot", error)
1609 })
1610}
1611
1612pub trait CollectionStoreExt: BlobStorePut + CollectionStore + Sized {
1618 fn register_collection<L>(
1628 &mut self,
1629 descriptor: Fragment,
1630 ) -> Result<Collection<L>, CollectionRegistrationError<<Self as BlobStorePut>::PutError>>
1631 where
1632 L: CollectionEncoding,
1633 {
1634 super::encoding::validate_descriptor_type::<L>(&descriptor)
1635 .map_err(CollectionRegistrationError::WrongType)?;
1636 let handle = descriptor::put_closure(self, &descriptor)
1637 .map_err(CollectionRegistrationError::DependencyPut)?;
1638 Ok(Collection::from_handle(handle))
1639 }
1640
1641 fn collection(
1643 &mut self,
1644 name: &str,
1645 policy: CollectionPolicy,
1646 ) -> Result<
1647 Collection<SimpleArchive>,
1648 CollectionRegistrationError<<Self as BlobStorePut>::PutError>,
1649 > {
1650 let descriptor = descriptor::naming::<SimpleArchive>(name, policy);
1651 let handle = descriptor::put_closure(self, &descriptor)
1652 .map_err(CollectionRegistrationError::DependencyPut)?;
1653 Ok(Collection::from_handle(handle))
1654 }
1655
1656 fn derive<T>(
1661 &mut self,
1662 source: Collection<T::Source>,
1663 argument: T::Argument,
1664 policy: CollectionPolicy,
1665 ) -> Result<Collection<T>, CollectionRegistrationError<<Self as BlobStorePut>::PutError>>
1666 where
1667 T: CollectionDerivation,
1668 {
1669 self.derive_with::<CanonicalDerivation<T>>(
1670 source,
1671 CanonicalDerivation::new(argument),
1672 policy,
1673 )
1674 }
1675
1676 fn derive_with<M>(
1682 &mut self,
1683 source: Collection<M::Source>,
1684 mapping: M,
1685 policy: CollectionPolicy,
1686 ) -> Result<Collection<M::Target>, CollectionRegistrationError<<Self as BlobStorePut>::PutError>>
1687 where
1688 M: DeriveMapping,
1689 {
1690 let descriptor = descriptor::deriving_with(source.handle(), &mapping, policy);
1691 M::Target::validate_descriptor(&descriptor).map_err(|source| {
1694 CollectionRegistrationError::WrongType(CollectionTypeError::InvalidDescriptor(source))
1695 })?;
1696 let handle = descriptor::put_closure(self, &descriptor)
1697 .map_err(CollectionRegistrationError::DependencyPut)?;
1698 Ok(Collection::from_handle(handle))
1699 }
1700
1701 fn attach<T>(
1707 &mut self,
1708 parent: Collection<SimpleArchive>,
1709 argument: T::Argument,
1710 ) -> Result<Collection<T>, CollectionRegistrationError<<Self as BlobStorePut>::PutError>>
1711 where
1712 T: CollectionAttachment,
1713 {
1714 self.attach_with::<CanonicalAttachment<T>>(parent, CanonicalAttachment::new(argument))
1715 }
1716
1717 fn attach_with<M>(
1720 &mut self,
1721 parent: Collection<SimpleArchive>,
1722 mapping: M,
1723 ) -> Result<Collection<M::Target>, CollectionRegistrationError<<Self as BlobStorePut>::PutError>>
1724 where
1725 M: MapMapping,
1726 {
1727 let descriptor = descriptor::attaching_with(parent.handle(), &mapping);
1728 M::Target::validate_descriptor(&descriptor).map_err(|source| {
1729 CollectionRegistrationError::WrongType(CollectionTypeError::InvalidDescriptor(source))
1730 })?;
1731 let handle = descriptor::put_closure(self, &descriptor)
1732 .map_err(CollectionRegistrationError::DependencyPut)?;
1733 Ok(Collection::from_handle(handle))
1734 }
1735
1736 fn ensure_attached<'a, T>(
1746 &'a mut self,
1747 attached: Collection<T>,
1748 signing_key: &'a SigningKey,
1749 ) -> impl Future<Output = Result<Self::Snapshot, CollectionRealizationError>> + Send + 'a
1750 where
1751 T: CollectionAttachment,
1752 Self: Store + AsyncBlobStoreAcquire + Send,
1753 Handle<T>: InlineEncoding,
1754 {
1755 self.ensure_attached_with::<CanonicalAttachment<T>>(attached, signing_key)
1756 }
1757
1758 fn ensure_attached_with<'a, M>(
1760 &'a mut self,
1761 attached: Collection<M::Target>,
1762 signing_key: &'a SigningKey,
1763 ) -> impl Future<Output = Result<Self::Snapshot, CollectionRealizationError>> + Send + 'a
1764 where
1765 M: MapMapping,
1766 Self: Store + AsyncBlobStoreAcquire + Send,
1767 Handle<M::Target>: InlineEncoding,
1768 {
1769 async move {
1770 super::exact_derived::acquire_attached_lineage(self, attached).await?;
1771 super::exact_derived::attach_acquiring_with::<Self, M>(self, attached, signing_key)
1772 .await?;
1773 self.snapshot().map_err(|error| {
1774 CollectionRealizationError::storage("freeze post-attach snapshot", error)
1775 })
1776 }
1777 }
1778
1779 fn maintain_attached<'a, T>(
1785 &'a mut self,
1786 attached: Collection<T>,
1787 signing_key: &'a SigningKey,
1788 ) -> impl Future<Output = Result<Self::Snapshot, CollectionRealizationError>> + Send + 'a
1789 where
1790 T: CollectionAttachment,
1791 Self: Store + AsyncBlobStoreAcquire + Send,
1792 Handle<T>: InlineEncoding,
1793 {
1794 self.maintain_attached_with::<CanonicalAttachment<T>>(attached, signing_key)
1795 }
1796
1797 fn maintain_attached_with<'a, M>(
1799 &'a mut self,
1800 attached: Collection<M::Target>,
1801 signing_key: &'a SigningKey,
1802 ) -> impl Future<Output = Result<Self::Snapshot, CollectionRealizationError>> + Send + 'a
1803 where
1804 M: MapMapping,
1805 Self: Store + AsyncBlobStoreAcquire + Send,
1806 Handle<M::Target>: InlineEncoding,
1807 {
1808 async move {
1809 super::exact_derived::acquire_attached_lineage(self, attached).await?;
1810 let snapshot = self.snapshot().map_err(|error| {
1811 CollectionRealizationError::storage("observe attached parent", error)
1812 })?;
1813 let (parent, _, _) = super::maintenance::attached_lineage(&snapshot, attached)?;
1814 drop(snapshot);
1815 realize_root(self, parent, signing_key, true).await?;
1816 super::exact_derived::attach_acquiring_with::<Self, M>(self, attached, signing_key)
1817 .await?;
1818 self.snapshot().map_err(|error| {
1819 CollectionRealizationError::storage("freeze post-attach snapshot", error)
1820 })
1821 }
1822 }
1823
1824 fn ensure<'a, T>(
1842 &'a mut self,
1843 target: Collection<T>,
1844 signing_key: &'a SigningKey,
1845 ) -> impl Future<Output = Result<Self::Snapshot, CollectionRealizationError>> + Send + 'a
1846 where
1847 T: CollectionRealization,
1848 Self: Store + AsyncBlobStoreAcquire + Send,
1849 Handle<T>: InlineEncoding,
1850 {
1851 T::ensure(self, target, signing_key)
1852 }
1853
1854 fn ensure_with<'a, M>(
1857 &'a mut self,
1858 target: Collection<M::Target>,
1859 signing_key: &'a SigningKey,
1860 ) -> impl Future<Output = Result<Self::Snapshot, CollectionRealizationError>> + Send + 'a
1861 where
1862 M: DeriveMapping,
1863 Self: Store + AsyncBlobStoreAcquire + Send,
1864 Handle<M::Target>: InlineEncoding,
1865 {
1866 async move {
1867 super::exact_derived::acquire_authority(self, target).await?;
1868 super::exact_derived::ensure_acquiring_with::<Self, M>(self, target, signing_key)
1869 .await?;
1870 self.snapshot().map_err(|error| {
1871 CollectionRealizationError::storage("freeze post-ensure snapshot", error)
1872 })
1873 }
1874 }
1875
1876 fn maintain<'a, T>(
1920 &'a mut self,
1921 target: Collection<T>,
1922 signing_key: &'a SigningKey,
1923 ) -> impl Future<Output = Result<Self::Snapshot, CollectionRealizationError>> + Send + 'a
1924 where
1925 T: CollectionRealization,
1926 Self: Store + AsyncBlobStoreAcquire + Send,
1927 Handle<T>: InlineEncoding,
1928 {
1929 T::maintain(self, target, signing_key)
1930 }
1931
1932 fn maintain_with<'a, M>(
1935 &'a mut self,
1936 target: Collection<M::Target>,
1937 signing_key: &'a SigningKey,
1938 ) -> impl Future<Output = Result<Self::Snapshot, CollectionRealizationError>> + Send + 'a
1939 where
1940 M: DeriveMapping,
1941 Self: Store + AsyncBlobStoreAcquire + Send,
1942 Handle<M::Target>: InlineEncoding,
1943 {
1944 async move {
1945 super::exact_derived::acquire_authority(self, target).await?;
1946 super::exact_derived::maintain_acquiring_with::<Self, M>(self, target, signing_key)
1947 .await?;
1948 self.snapshot().map_err(|error| {
1949 CollectionRealizationError::storage("freeze post-maintenance snapshot", error)
1950 })
1951 }
1952 }
1953
1954 fn rederive<'a, T>(
1957 &'a mut self,
1958 target: Collection<T>,
1959 foundations: &Cover<T::Source>,
1960 signing_key: &'a SigningKey,
1961 ) -> impl Future<Output = Result<Self::Snapshot, CollectionRealizationError>> + Send + 'a
1962 where
1963 T: CollectionDerivation,
1964 Self: Store + AsyncBlobStoreAcquire + Send,
1965 Handle<T>: InlineEncoding,
1966 {
1967 self.rederive_with::<CanonicalDerivation<T>>(target, foundations, signing_key)
1968 }
1969
1970 fn rederive_with<'a, M>(
1986 &'a mut self,
1987 target: Collection<M::Target>,
1988 foundations: &Cover<M::Source>,
1989 signing_key: &'a SigningKey,
1990 ) -> impl Future<Output = Result<Self::Snapshot, CollectionRealizationError>> + Send + 'a
1991 where
1992 M: DeriveMapping,
1993 Self: Store + AsyncBlobStoreAcquire + Send,
1994 Handle<M::Target>: InlineEncoding,
1995 {
1996 let source = foundations.collection().handle();
1997 let members: BTreeSet<CollectionData> = foundations.data_members().collect();
1998 async move {
1999 super::exact_derived::acquire_authority(self, target).await?;
2000 super::exact_derived::rederive_acquiring_with::<Self, M>(
2001 self,
2002 target,
2003 source,
2004 &members,
2005 signing_key,
2006 )
2007 .await?;
2008 self.snapshot().map_err(|error| {
2009 CollectionRealizationError::storage("freeze post-re-derivation snapshot", error)
2010 })
2011 }
2012 }
2013
2014 fn commit(
2022 &mut self,
2023 collection: Collection<SimpleArchive>,
2024 signing_key: &SigningKey,
2025 fragment: Fragment,
2026 ) -> Result<
2027 CollectionCommit,
2028 CollectionCommitError<
2029 <Self as BlobStorePut>::PutError,
2030 <Self as CollectionStore>::InsertError,
2031 >,
2032 > {
2033 PreparedCollectionCommit::from_fragment(fragment)
2034 .stage_for(self, collection, signing_key)?
2035 .finalize()
2036 }
2037}
2038
2039impl<S> CollectionStoreExt for S where S: BlobStorePut + CollectionStore {}
2040
2041#[cfg(test)]
2042mod grant_tests {
2043 use super::*;
2044 use crate::capability::{capability_action, capability_delegate_action};
2045 use crate::collection::{ACTION_READ, ACTION_WRITE};
2046 use crate::prelude::entity;
2047 use crate::repo::memoryrepo::MemoryRepo;
2048
2049 #[test]
2050 fn compound_grants_require_a_root_for_every_invocation_and_delegation_action() {
2051 let read_root = SigningKey::from_bytes(&[71; 32]);
2052 let write_root = SigningKey::from_bytes(&[72; 32]);
2053 let recipient = SigningKey::from_bytes(&[73; 32]).verifying_key();
2054 let mut store = MemoryRepo::default();
2055 let collection = store
2056 .collection(
2057 "split action roots",
2058 CollectionPolicy::new(
2059 AdmissionPolicy::direct(read_root.verifying_key()),
2060 AdmissionPolicy::direct(write_root.verifying_key()),
2061 ),
2062 )
2063 .unwrap();
2064 for definition in [
2065 entity! { capability_action*: [ACTION_READ, ACTION_WRITE] },
2066 entity! {
2067 capability_action: ACTION_READ,
2068 capability_delegate_action: ACTION_WRITE,
2069 },
2070 entity! { capability_delegate_action*: [ACTION_READ, ACTION_WRITE] },
2071 ] {
2072 let capability = store
2073 .put::<SimpleArchive, _>(definition.facts().clone())
2074 .unwrap();
2075 for root in [&read_root, &write_root] {
2076 assert!(matches!(
2077 grant_collection_capability(
2078 &mut store,
2079 collection.handle(),
2080 capability,
2081 root,
2082 recipient,
2083 ),
2084 Err(CollectionGrantError::RootNotAuthorized { .. })
2085 ));
2086 }
2087 }
2088 assert_eq!(store.snapshot().unwrap().proofs().unwrap().count(), 0);
2089 }
2090
2091 #[test]
2092 fn an_unknown_action_or_empty_definition_cannot_borrow_a_known_action_root() {
2093 let root = SigningKey::from_bytes(&[74; 32]);
2094 let recipient = SigningKey::from_bytes(&[75; 32]).verifying_key();
2095 let mut store = MemoryRepo::default();
2096 let collection = store
2097 .collection(
2098 "unknown grant actions",
2099 CollectionPolicy::new(
2100 AdmissionPolicy::direct(root.verifying_key()),
2101 AdmissionPolicy::direct(root.verifying_key()),
2102 ),
2103 )
2104 .unwrap();
2105 let unknown = crate::id::genid().id;
2106 for definition in [
2107 entity! { capability_action*: [ACTION_READ, unknown] },
2108 entity! {
2109 capability_action: ACTION_READ,
2110 capability_delegate_action: unknown,
2111 },
2112 Fragment::empty(),
2113 ] {
2114 let capability = store
2115 .put::<SimpleArchive, _>(definition.facts().clone())
2116 .unwrap();
2117 assert!(matches!(
2118 grant_collection_capability(
2119 &mut store,
2120 collection.handle(),
2121 capability,
2122 &root,
2123 recipient,
2124 ),
2125 Err(CollectionGrantError::RootNotAuthorized { .. })
2126 ));
2127 }
2128 assert_eq!(store.snapshot().unwrap().proofs().unwrap().count(), 0);
2129 }
2130
2131 #[test]
2132 fn compound_grants_preserve_independent_quorum_shares() {
2133 let first = SigningKey::from_bytes(&[76; 32]);
2134 let second = SigningKey::from_bytes(&[77; 32]);
2135 let recipient = SigningKey::from_bytes(&[78; 32]).verifying_key();
2136 let mut store = MemoryRepo::default();
2137 let quorum =
2138 AdmissionPolicy::quorum([first.verifying_key(), second.verifying_key()], 2, None)
2139 .unwrap();
2140 let collection = store
2141 .collection(
2142 "compound quorum shares",
2143 CollectionPolicy::new(quorum.clone(), quorum),
2144 )
2145 .unwrap();
2146 let capability = store
2147 .put::<SimpleArchive, _>(
2148 entity! {
2149 capability_action*: [ACTION_READ, ACTION_WRITE],
2150 capability_delegate_action: ACTION_READ,
2151 }
2152 .facts()
2153 .clone(),
2154 )
2155 .unwrap();
2156 let first_proof = grant_collection_capability(
2157 &mut store,
2158 collection.handle(),
2159 capability,
2160 &first,
2161 recipient,
2162 )
2163 .unwrap();
2164 let one_share = store.snapshot().unwrap();
2165 assert!(!collection
2166 .reader_is_admitted(&one_share, recipient)
2167 .unwrap());
2168 assert!(!collection
2169 .writer_is_admitted(&one_share, recipient)
2170 .unwrap());
2171 grant_collection_capability(
2172 &mut store,
2173 collection.handle(),
2174 capability,
2175 &second,
2176 recipient,
2177 )
2178 .unwrap();
2179 let repeated = grant_collection_capability(
2180 &mut store,
2181 collection.handle(),
2182 capability,
2183 &first,
2184 recipient,
2185 )
2186 .unwrap();
2187 assert_eq!(repeated, first_proof);
2188 let both_shares = store.snapshot().unwrap();
2189 assert_eq!(both_shares.proofs().unwrap().count(), 2);
2190 assert!(collection
2191 .reader_is_admitted(&both_shares, recipient)
2192 .unwrap());
2193 assert!(collection
2194 .writer_is_admitted(&both_shares, recipient)
2195 .unwrap());
2196 }
2197
2198 #[test]
2199 fn delegate_only_read_grant_can_confer_read_without_invoking_it() {
2200 let root = SigningKey::from_bytes(&[79; 32]);
2201 let delegate = SigningKey::from_bytes(&[80; 32]);
2202 let reader = SigningKey::from_bytes(&[81; 32]).verifying_key();
2203 let mut store = MemoryRepo::default();
2204 let collection = store
2205 .collection(
2206 "delegate without invocation",
2207 CollectionPolicy::new(
2208 AdmissionPolicy::direct(root.verifying_key()),
2209 AdmissionPolicy::direct(root.verifying_key()),
2210 ),
2211 )
2212 .unwrap();
2213 let capability = store
2214 .put::<SimpleArchive, _>(
2215 entity! { capability_delegate_action: ACTION_READ }
2216 .facts()
2217 .clone(),
2218 )
2219 .unwrap();
2220 let proof = grant_collection_capability(
2221 &mut store,
2222 collection.handle(),
2223 capability,
2224 &root,
2225 delegate.verifying_key(),
2226 )
2227 .unwrap();
2228 let child = proof
2229 .delegate(&delegate, read_capability(), reader)
2230 .unwrap();
2231 store.insert_proof(child).unwrap();
2232 let snapshot = store.snapshot().unwrap();
2233 assert!(!collection
2234 .reader_is_admitted(&snapshot, delegate.verifying_key())
2235 .unwrap());
2236 assert!(!collection
2237 .writer_is_admitted(&snapshot, delegate.verifying_key())
2238 .unwrap());
2239 assert!(collection.reader_is_admitted(&snapshot, reader).unwrap());
2240 assert!(!collection.writer_is_admitted(&snapshot, reader).unwrap());
2241 }
2242
2243 #[test]
2244 fn open_actions_can_accompany_a_restricted_grant_but_cannot_authorize_it() {
2245 let root = SigningKey::from_bytes(&[82; 32]);
2246 let outsider = SigningKey::from_bytes(&[83; 32]);
2247 let recipient = SigningKey::from_bytes(&[84; 32]).verifying_key();
2248 let mut store = MemoryRepo::default();
2249 let collection = store
2250 .collection(
2251 "public reads private writes",
2252 CollectionPolicy::new(
2253 AdmissionPolicy::Open,
2254 AdmissionPolicy::direct(root.verifying_key()),
2255 ),
2256 )
2257 .unwrap();
2258 let capability = store
2259 .put::<SimpleArchive, _>(
2260 entity! {
2261 capability_action*: [ACTION_READ, ACTION_WRITE],
2262 capability_delegate_action: ACTION_READ,
2263 }
2264 .facts()
2265 .clone(),
2266 )
2267 .unwrap();
2268 assert!(matches!(
2269 grant_collection_capability(
2270 &mut store,
2271 collection.handle(),
2272 capability,
2273 &outsider,
2274 recipient,
2275 ),
2276 Err(CollectionGrantError::RootNotAuthorized { .. })
2277 ));
2278 grant_collection_capability(
2279 &mut store,
2280 collection.handle(),
2281 capability,
2282 &root,
2283 recipient,
2284 )
2285 .unwrap();
2286 let snapshot = store.snapshot().unwrap();
2287 assert!(collection.reader_is_admitted(&snapshot, recipient).unwrap());
2288 assert!(collection.writer_is_admitted(&snapshot, recipient).unwrap());
2289 assert!(matches!(
2290 grant_collection_read(&mut store, collection.handle(), &root, recipient),
2291 Err(CollectionGrantError::OpenPolicy { .. })
2292 ));
2293 let open = store
2294 .collection(
2295 "both actions open",
2296 CollectionPolicy::new(AdmissionPolicy::Open, AdmissionPolicy::Open),
2297 )
2298 .unwrap();
2299 assert!(matches!(
2300 grant_collection_capability(&mut store, open.handle(), capability, &root, recipient),
2301 Err(CollectionGrantError::OpenPolicy { .. })
2302 ));
2303 assert_eq!(store.snapshot().unwrap().proofs().unwrap().count(), 1);
2304 }
2305}