use itertools::Itertools;
use crate::blob::encodings::simplearchive::SimpleArchive;
use crate::blob::encodings::utf8string::UTF8String;
use crate::blob::encodings::UnknownBlob;
use crate::blob::Blob;
use crate::capability::policy::{
capability_handle, resource_collection, resource_policies, resource_policy,
};
use crate::capability::{
CapabilityHandle, CapabilityProof, CapabilityProofError, CapabilityResource,
};
use crate::id::Id;
use crate::inline::encodings::genid::GenId;
use crate::inline::encodings::hash::Handle;
use crate::inline::encodings::UnknownInline;
use crate::inline::{Inline, IntoInline, RawInline};
use crate::metadata::{self, MetaDescribe};
use crate::prelude::{and, entity, exists, find, or, pattern};
use crate::query::TriblePattern;
use crate::repo::{BlobStoreGet, BlobStorePut, SnapshotSource};
use crate::trible::{Fragment, TribleSet};
use super::policy::{
AdmissionPolicy, CollectionPolicy, KIND_ADMISSION_POLICY_OPEN, KIND_ADMISSION_POLICY_QUORUM,
};
use super::records::{
admission_delegate_threshold, admission_invoke_threshold, admission_policy_root,
collection_mapping, collection_name, collection_parent, collection_representation,
collection_source, mapping_algorithm as mapping_algorithm_attribute, mapping_reads_attached,
CollectionHandle, RecordDecodeError, KIND_COLLECTION_DESCRIPTOR, KIND_COLLECTION_MAPPING,
};
use super::{read_capability, write_capability, CollectionEncoding, DeriveMapping, MapMapping};
pub(crate) fn put_closure<S>(
store: &mut S,
descriptor: &Fragment,
) -> Result<CollectionHandle, S::PutError>
where
S: BlobStorePut,
{
let mut blobs = descriptor.blobs().clone();
let mut embedded: Vec<Blob<UnknownBlob>> = blobs
.snapshot()
.expect("MemoryBlobStore::snapshot is infallible")
.into_iter()
.map(|(_, blob)| blob)
.collect();
embedded.sort_unstable_by_key(|blob| blob.get_handle().raw);
for blob in embedded {
store.put::<UnknownBlob, _>(blob)?;
}
store.put::<SimpleArchive, _>(descriptor.facts().clone())
}
pub const PINNED_ROOT_DESCRIPTOR_IDENTITY: RawInline =
hex_literal::hex!("8A63C584C7A645DAA1DA959631E55299181869B7FFC5A567A535D71924367D68");
pub(crate) fn naming<E>(name: &str, policy: CollectionPolicy) -> Fragment
where
E: CollectionEncoding,
{
entity! {
metadata::tag: KIND_COLLECTION_DESCRIPTOR,
collection_name: name.to_owned(),
resource_policy*: policy.fragment(),
collection_representation*: <E as MetaDescribe>::describe(),
}
}
pub fn root_handle_to_read(name: &str, policy: CollectionPolicy) -> CollectionHandle {
crate::blob::IntoBlob::<crate::blob::encodings::simplearchive::SimpleArchive>::to_blob(
naming::<crate::blob::encodings::simplearchive::SimpleArchive>(name, policy)
.facts()
.clone(),
)
.get_handle()
}
pub(crate) fn deriving_with<M>(
source: CollectionHandle,
mapping: &M,
policy: CollectionPolicy,
) -> Fragment
where
M: DeriveMapping,
{
entity! {
metadata::tag: KIND_COLLECTION_DESCRIPTOR,
collection_source: source,
resource_policy*: policy.fragment(),
collection_representation*: <M::Target as MetaDescribe>::describe(),
collection_mapping*: mapping.fragment(),
}
}
pub(crate) fn attaching_with<M>(parent: CollectionHandle, mapping: &M) -> Fragment
where
M: MapMapping,
{
entity! {
metadata::tag: KIND_COLLECTION_DESCRIPTOR,
collection_parent: parent,
collection_representation*: <M::Target as MetaDescribe>::describe(),
collection_mapping*: mapping.fragment(),
}
}
pub fn entity(facts: &TribleSet) -> Result<Id, RecordDecodeError> {
exactly_one(
find!(
(e: Id),
pattern!(facts, [{ ?e @ metadata::tag: KIND_COLLECTION_DESCRIPTOR }])
)
.map(|(e,)| e),
"metadata::tag",
)
}
pub fn representation(facts: &TribleSet) -> Result<Id, RecordDecodeError> {
let descriptor = entity(facts)?;
exactly_one(
find!(
(v: Id?),
pattern!(facts, [{ descriptor @ collection_representation: ?v }])
)
.map(|(v,)| v),
"collection_representation",
)?
.map_err(|_| RecordDecodeError::InvalidId("collection_representation"))
}
pub fn mapping(facts: &TribleSet) -> Result<Option<Id>, RecordDecodeError> {
let descriptor = entity(facts)?;
at_most_one(
find!(
(v: Id?),
pattern!(facts, [{ descriptor @ collection_mapping: ?v }])
)
.map(|(v,)| v),
"collection_mapping",
)?
.map(|value| value.map_err(|_| RecordDecodeError::InvalidId("collection_mapping")))
.transpose()
}
pub fn mapping_algorithm(facts: &TribleSet) -> Result<Option<Id>, RecordDecodeError> {
let Some(mapping) = mapping(facts)? else {
return Ok(None);
};
exactly_one(
find!(
v: Id,
pattern!(facts, [{ mapping @
metadata::tag: KIND_COLLECTION_MAPPING,
mapping_algorithm_attribute: ?v,
}])
),
"mapping_algorithm",
)
.map(Some)
}
pub fn source(facts: &TribleSet) -> Result<Option<CollectionHandle>, RecordDecodeError> {
let descriptor = entity(facts)?;
at_most_one(
find!(
(v: CollectionHandle),
pattern!(facts, [{ descriptor @ collection_source: ?v }])
)
.map(|(v,)| v),
"collection_source",
)
}
pub fn parents(facts: &TribleSet) -> Result<Vec<CollectionHandle>, RecordDecodeError> {
let descriptor = entity(facts)?;
let mut parents: Vec<CollectionHandle> = find!(
(v: CollectionHandle),
pattern!(facts, [{ descriptor @ collection_parent: ?v }])
)
.map(|(v,)| v)
.collect();
parents.sort_unstable_by(|left, right| left.raw.cmp(&right.raw));
parents.dedup();
Ok(parents)
}
pub fn reads_attached(facts: &TribleSet) -> Result<Vec<CollectionHandle>, RecordDecodeError> {
let Some(mapping) = mapping(facts)? else {
return Ok(Vec::new());
};
let mut siblings: Vec<CollectionHandle> = find!(
(v: CollectionHandle),
pattern!(facts, [{ mapping @ mapping_reads_attached: ?v }])
)
.map(|(v,)| v)
.collect();
siblings.sort_unstable_by(|left, right| left.raw.cmp(&right.raw));
siblings.dedup();
Ok(siblings)
}
pub fn name(facts: &TribleSet) -> Result<Option<Inline<Handle<UTF8String>>>, RecordDecodeError> {
let descriptor = entity(facts)?;
at_most_one(
find!(
(v: Inline<Handle<UTF8String>>),
pattern!(facts, [{ descriptor @ collection_name: ?v }])
)
.map(|(v,)| v),
"collection_name",
)
}
pub fn policy(facts: &TribleSet) -> Result<CollectionPolicy, RecordDecodeError> {
let descriptor = entity(facts)?;
let read = exactly_one(
find!(
binding: Id,
pattern!(facts, [
{ descriptor @ resource_policy: ?binding },
{ ?binding @ capability_handle: read_capability() },
])
),
"collection READ capability policy",
)?;
let write = exactly_one(
find!(
binding: Id,
pattern!(facts, [
{ descriptor @ resource_policy: ?binding },
{ ?binding @ capability_handle: write_capability() },
])
),
"collection WRITE capability policy",
)?;
let exports: Vec<_> = find!(
binding: Id,
pattern!(facts, [{ descriptor @ resource_policy: ?binding }])
)
.collect();
let binding_facts: TribleSet = find!(
(binding: Id, attribute: Id, value: Inline<UnknownInline>),
pattern!(facts, [
{ descriptor @ resource_policy: ?binding },
{ ?binding @ ?attribute: ?value },
])
)
.map(|(binding, attribute, value)| crate::trible::Trible::force(&binding, &attribute, &value))
.collect();
Ok(CollectionPolicy::from_bindings(
decode_admission_policy(facts, read)?,
decode_admission_policy(facts, write)?,
Fragment::new(exports, binding_facts),
))
}
pub fn capability_policies<'a>(
facts: &'a TribleSet,
representation: Option<Id>,
) -> impl Iterator<Item = (CapabilityHandle, AdmissionPolicy)> + 'a {
descriptor_entities(facts, representation)
.flat_map(move |descriptor| resource_policies(facts, descriptor, None))
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ProofEvidenceError {
WrongResource,
ResourceDescriptorUnavailable(CollectionHandle),
WrongRoot,
Invalid(CapabilityProofError),
}
pub fn validate_proof_evidence<R: BlobStoreGet>(
reader: &R,
collection: CollectionHandle,
descriptor: &TribleSet,
proof: &CapabilityProof,
) -> Result<(), ProofEvidenceError> {
let resource = CollectionHandle::new(proof.resource().into_bytes());
if resource == collection {
return validate_proof_for_policies(
collection,
capability_policies(descriptor, None).map(|(_, policy)| policy),
proof,
);
}
let facts: TribleSet = reader
.get(resource)
.map_err(|_| ProofEvidenceError::ResourceDescriptorUnavailable(resource))?;
let policies = find!(
route: Id,
pattern!(&facts, [{ ?route @ resource_collection: collection }])
)
.flat_map(|route| resource_policies(&facts, route, None).map(|(_, policy)| policy));
validate_proof_for_policies(resource, policies, proof)
}
pub fn validate_proof_for_policies(
resource: CollectionHandle,
policies: impl IntoIterator<Item = AdmissionPolicy>,
proof: &CapabilityProof,
) -> Result<(), ProofEvidenceError> {
if proof.resource() != CapabilityResource::from(resource) {
return Err(ProofEvidenceError::WrongResource);
}
if !policies
.into_iter()
.any(|policy| policy.has_root(proof.root_key()))
{
return Err(ProofEvidenceError::WrongRoot);
}
proof
.verify_signatures()
.map_err(ProofEvidenceError::Invalid)
}
pub fn admission_policies<'a, R: BlobStoreGet>(
reader: &'a R,
facts: &'a TribleSet,
action: Id,
representation: Option<Id>,
) -> impl Iterator<Item = AdmissionPolicy> + 'a {
capability_policies(facts, representation).filter_map(move |(handle, policy)| {
let definition: TribleSet = reader.get(handle).ok()?;
exists!(
pattern!(&definition, [{ _?definition @ crate::capability::capability_action: action }])
)
.then_some(policy)
})
}
pub(crate) fn admission_policies_with_missing<R: BlobStoreGet>(
reader: &R,
facts: &TribleSet,
action: Id,
representation: Option<Id>,
) -> (Vec<AdmissionPolicy>, Vec<CapabilityHandle>) {
let mut policies = Vec::new();
let mut missing = Vec::new();
for (handle, policy) in capability_policies(facts, representation) {
let Ok(definition) = reader.get::<TribleSet, _>(handle) else {
missing.push(handle);
continue;
};
if exists!(pattern!(&definition, [{ _?definition @ crate::capability::capability_action: action }])) {
policies.push(policy);
}
}
(policies, missing)
}
pub(crate) fn sole_descriptor_entity(facts: &TribleSet) -> Option<Id> {
let mut entities = descriptor_entities(facts, None);
let first = entities.next()?;
entities.next().is_none().then_some(first)
}
fn descriptor_entities<'a>(
facts: &'a TribleSet,
representation: Option<Id>,
) -> Box<dyn Iterator<Item = Id> + 'a> {
match representation {
Some(representation) => Box::new(find!(
descriptor: Id,
pattern!(facts, [{ ?descriptor @
metadata::tag: KIND_COLLECTION_DESCRIPTOR,
collection_representation: representation,
}])
)),
None => Box::new(find!(
descriptor: Id,
pattern!(facts, [{ ?descriptor @
metadata::tag: KIND_COLLECTION_DESCRIPTOR,
}])
)),
}
}
fn decode_admission_policy(
facts: &TribleSet,
policy: Id,
) -> Result<AdmissionPolicy, RecordDecodeError> {
let kind = exactly_one(
find!(
kind: Id,
and!(
pattern!(facts, [{ policy @ metadata::tag: ?kind }]),
or!(
kind.is(KIND_ADMISSION_POLICY_OPEN.to_inline()),
kind.is(KIND_ADMISSION_POLICY_QUORUM.to_inline()),
),
)
),
"admission policy metadata::tag",
)?;
if kind == KIND_ADMISSION_POLICY_OPEN {
return Ok(AdmissionPolicy::Open);
}
let roots = find!(
root: ed25519_dalek::VerifyingKey,
pattern!(facts, [{ policy @ admission_policy_root: ?root }])
);
let invoke = exactly_one(
find!(
invoke: u32,
pattern!(facts, [{ policy @ admission_invoke_threshold: ?invoke }])
),
"admission_invoke_threshold",
)?;
let delegate = at_most_one(
find!(
delegate: u32,
pattern!(facts, [{ policy @ admission_delegate_threshold: ?delegate }])
),
"admission_delegate_threshold",
)?;
AdmissionPolicy::quorum(roots, invoke, delegate)
.map_err(|_| RecordDecodeError::InvalidId("admission policy quorum"))
}
pub fn argument(facts: &TribleSet, attribute: Id) -> Result<Option<RawInline>, RecordDecodeError> {
argument_on(facts, entity(facts)?, attribute, "descriptor argument")
}
pub fn mapping_argument(
facts: &TribleSet,
attribute: Id,
) -> Result<Option<RawInline>, RecordDecodeError> {
let Some(mapping) = mapping(facts)? else {
return Ok(None);
};
argument_on(facts, mapping, attribute, "mapping argument")
}
fn argument_on(
facts: &TribleSet,
subject: Id,
attribute: Id,
field: &'static str,
) -> Result<Option<RawInline>, RecordDecodeError> {
let subject: Inline<GenId> = subject.to_inline();
let attribute: Inline<GenId> = attribute.to_inline();
at_most_one(
find!(
(v: Inline<GenId>),
facts.pattern::<GenId>(subject, attribute, v)
)
.map(|(v,)| v.raw),
field,
)
}
fn exactly_one<T>(
rows: impl Iterator<Item = T>,
field: &'static str,
) -> Result<T, RecordDecodeError> {
rows.exactly_one().map_err(|mut leftover| {
if leftover.next().is_some() {
RecordDecodeError::RepeatedField(field)
} else {
RecordDecodeError::MissingField(field)
}
})
}
fn at_most_one<T>(
mut rows: impl Iterator<Item = T>,
field: &'static str,
) -> Result<Option<T>, RecordDecodeError> {
let Some(first) = rows.next() else {
return Ok(None);
};
if rows.next().is_some() {
return Err(RecordDecodeError::RepeatedField(field));
}
Ok(Some(first))
}
#[cfg(test)]
pub(crate) fn named_for_tests(name: &str, representation: Id) -> Fragment {
let root = ed25519_dalek::SigningKey::from_bytes(&[0xAA; 32]).verifying_key();
entity! {
metadata::tag: KIND_COLLECTION_DESCRIPTOR,
collection_name: name.to_owned(),
resource_policy*: CollectionPolicy::new(AdmissionPolicy::direct(root), AdmissionPolicy::direct(root)).fragment(),
collection_representation: representation,
}
}
#[cfg(test)]
pub(crate) fn identity_for_tests(descriptor: &Fragment) -> CollectionHandle {
crate::blob::IntoBlob::<crate::blob::encodings::simplearchive::SimpleArchive>::to_blob(
descriptor.facts().clone(),
)
.get_handle()
}
#[cfg(test)]
mod policy_tests {
use ed25519_dalek::SigningKey;
use super::*;
use crate::blob::encodings::simplearchive::SimpleArchive;
use crate::collection::policy::{AdmissionPolicy, CollectionPolicy};
use crate::inline::encodings::genid::GenId;
use crate::inline::{Inline, IntoInline};
use crate::metadata;
use crate::trible::Trible;
fn key(byte: u8) -> ed25519_dalek::VerifyingKey {
SigningKey::from_bytes(&[byte; 32]).verifying_key()
}
fn root(name: &str, expected: CollectionPolicy) -> Fragment {
naming::<SimpleArchive>(name, expected)
}
#[test]
fn root_descriptor_identity_is_pinned() {
let authority = key(0x2A);
let policy = CollectionPolicy::new(
AdmissionPolicy::direct(authority),
AdmissionPolicy::direct(authority),
);
let computed = identity_for_tests(&naming::<SimpleArchive>("pinned-root", policy)).raw;
assert_eq!(
computed,
PINNED_ROOT_DESCRIPTOR_IDENTITY,
"the canonical root descriptor now hashes to {} instead of {}; every collection this \
build registers has a new identity and the records of the previous generation are \
unreachable by name. Restore the descriptor shape, or land a migration that carries \
those records forward and repin in the same change.",
hex::encode(computed),
hex::encode(PINNED_ROOT_DESCRIPTOR_IDENTITY),
);
}
#[test]
fn policy_round_trips_independent_quorums() {
let expected = CollectionPolicy::new(
AdmissionPolicy::quorum([key(4), key(5)], 2, Some(2)).unwrap(),
AdmissionPolicy::direct(key(4)),
);
assert_eq!(
policy(root("ledger", expected.clone()).facts()),
Ok(expected)
);
}
#[test]
fn policy_participates_in_collection_identity() {
let closed = root(
"ledger",
CollectionPolicy::new(
AdmissionPolicy::direct(key(5)),
AdmissionPolicy::direct(key(5)),
),
);
let open_read = root(
"ledger",
CollectionPolicy::new(AdmissionPolicy::Open, AdmissionPolicy::direct(key(5))),
);
assert_ne!(identity_for_tests(&closed), identity_for_tests(&open_read));
}
#[test]
fn missing_policy_link_fails_closed() {
let fragment = entity! {
metadata::tag: KIND_COLLECTION_DESCRIPTOR,
resource_policy*: AdmissionPolicy::direct(key(6)).binding(write_capability()),
};
assert_eq!(
policy(fragment.facts()),
Err(RecordDecodeError::MissingField(
"collection READ capability policy"
))
);
}
#[test]
fn unknown_policy_kind_fails_closed() {
let unknown = entity! {
capability_handle: read_capability(),
metadata::tag: crate::id::id_hex!("44444444444444444444444444444444"),
};
let fragment = entity! {
metadata::tag: KIND_COLLECTION_DESCRIPTOR,
resource_policy*: unknown,
resource_policy*: AdmissionPolicy::direct(key(7)).binding(write_capability()),
};
assert_eq!(
policy(fragment.facts()),
Err(RecordDecodeError::MissingField(
"admission policy metadata::tag"
))
);
}
#[test]
fn repeated_policy_link_fails_closed() {
let mut fragment = root(
"ledger",
CollectionPolicy::new(
AdmissionPolicy::direct(key(8)),
AdmissionPolicy::direct(key(8)),
),
);
let descriptor = fragment.root().expect("descriptor root");
let second_binding = AdmissionPolicy::Open.binding(read_capability());
let second: Inline<GenId> = second_binding.root().expect("open policy root").to_inline();
fragment += second_binding;
fragment
.facts_mut()
.insert(&Trible::force(&descriptor, &resource_policy.id(), &second));
assert_eq!(
policy(fragment.facts()),
Err(RecordDecodeError::RepeatedField(
"collection READ capability policy"
))
);
}
#[test]
fn policy_ignores_annotations_and_fields_on_other_entities() {
let read = crate::id::rngid();
let write = crate::id::rngid();
let subject = crate::id::rngid();
let mut fragment = entity! { &subject @
metadata::tag: KIND_COLLECTION_DESCRIPTOR,
resource_policy*: entity! { &read @
capability_handle: read_capability(),
metadata::tag*: [KIND_ADMISSION_POLICY_OPEN, metadata::KIND_BLOB_ENCODING],
admission_policy_root: key(9),
admission_invoke_threshold: 0_u32,
},
resource_policy*: entity! { &write @
capability_handle: write_capability(),
metadata::tag*: [KIND_ADMISSION_POLICY_QUORUM, metadata::KIND_BLOB_ENCODING],
admission_policy_root: key(9),
admission_invoke_threshold: 1_u32,
},
};
fragment += entity! {
resource_policy*: AdmissionPolicy::direct(key(10)).binding(read_capability()),
resource_policy*: AdmissionPolicy::Open.binding(write_capability()),
};
let decoded = policy(fragment.facts()).unwrap();
assert_eq!(decoded.read(), &AdmissionPolicy::Open);
assert_eq!(decoded.write(), &AdmissionPolicy::direct(key(9)));
let root_inline: Inline<crate::inline::encodings::ed25519::ED25519PublicKey> =
key(9).to_inline();
assert!(decoded.fragment().facts().contains(&Trible::force(
&read.id,
&admission_policy_root.id(),
&root_inline
)));
}
#[test]
fn policy_does_not_borrow_a_missing_link_from_another_entity() {
let mut fragment = entity! {
metadata::tag: KIND_COLLECTION_DESCRIPTOR,
resource_policy*: AdmissionPolicy::direct(key(9)).binding(write_capability()),
};
fragment += entity! {
resource_policy*: AdmissionPolicy::Open.binding(read_capability()),
};
assert_eq!(
policy(fragment.facts()),
Err(RecordDecodeError::MissingField(
"collection READ capability policy"
))
);
}
#[test]
fn typed_policy_queries_ignore_undecodable_values() {
let expected =
CollectionPolicy::new(AdmissionPolicy::Open, AdmissionPolicy::direct(key(9)));
let mut fragment = root("typed-policy-values", expected.clone());
let subject = fragment.root().unwrap();
let write = find!(
write: Id,
pattern!(fragment.facts(), [
{ subject @ resource_policy: ?write },
{ ?write @ capability_handle: write_capability() },
])
)
.next()
.unwrap();
fragment.facts_mut().insert(&Trible::force(
&subject,
&resource_policy.id(),
&Inline::<GenId>::new([0xFF; 32]),
));
fragment.facts_mut().insert(&Trible::force(
&write,
&admission_invoke_threshold.id(),
&Inline::<crate::inline::encodings::iu256::U256>::new([0xFF; 32]),
));
let decoded = policy(fragment.facts()).unwrap();
assert_eq!(decoded.read(), expected.read());
assert_eq!(decoded.write(), expected.write());
let unreadable = entity! {
metadata::tag: KIND_COLLECTION_DESCRIPTOR,
resource_policy*: AdmissionPolicy::Open.binding(read_capability()),
resource_policy*: entity! {
capability_handle: write_capability(),
metadata::tag: KIND_ADMISSION_POLICY_QUORUM,
admission_policy_root: key(9),
admission_invoke_threshold:
Inline::<crate::inline::encodings::iu256::U256>::new([0xFF; 32]),
},
};
assert_eq!(
policy(unreadable.facts()),
Err(RecordDecodeError::MissingField(
"admission_invoke_threshold"
))
);
}
#[test]
fn a_derived_root_handle_equals_the_one_registration_returns() {
use crate::collection::CollectionStoreExt;
use crate::repo::memoryrepo::MemoryRepo;
let key = ed25519_dalek::SigningKey::from_bytes(&[0x11; 32]).verifying_key();
let policy = CollectionPolicy::new(
AdmissionPolicy::direct(key),
AdmissionPolicy::direct(key),
);
let mut store = MemoryRepo::default();
let registered = store
.collection("self-config", policy.clone())
.expect("register")
.handle();
assert_eq!(
super::root_handle_to_read("self-config", policy),
registered,
"a derived handle must name the collection registration created"
);
}
#[test]
fn a_derived_root_handle_differs_with_the_key_and_with_the_name() {
let one = ed25519_dalek::SigningKey::from_bytes(&[0x11; 32]).verifying_key();
let two = ed25519_dalek::SigningKey::from_bytes(&[0x22; 32]).verifying_key();
let policy_of = |key| CollectionPolicy::new(
AdmissionPolicy::direct(key),
AdmissionPolicy::direct(key),
);
let a = super::root_handle_to_read("self-config", policy_of(one));
let b = super::root_handle_to_read("self-config", policy_of(two));
let c = super::root_handle_to_read("other-config", policy_of(one));
assert_ne!(a, b, "a different signing key must give a different collection");
assert_ne!(a, c, "a different name must give a different collection");
}
#[test]
fn admission_queries_skip_unsupported_values_without_losing_valid_alternatives() {
let subject = crate::id::rngid();
let policy = crate::id::rngid();
let unknown = crate::id::rngid();
let representation = <SimpleArchive as MetaDescribe>::id();
let mut fragment = entity! { &subject @
metadata::tag: KIND_COLLECTION_DESCRIPTOR,
collection_representation: representation,
resource_policy*: entity! { &policy @
capability_handle: write_capability(),
metadata::tag*: [KIND_ADMISSION_POLICY_QUORUM, unknown.id],
admission_policy_root: key(13),
admission_invoke_threshold*: [0_u32, 1_u32, 2_u32],
admission_delegate_threshold*: [0_u32, 2_u32],
},
};
let invalid = Inline::<GenId>::new([0xFF; 32]);
for attribute in [resource_policy.id(), collection_representation.id()] {
fragment
.facts_mut()
.insert(&Trible::force(&subject.id, &attribute, &invalid));
}
fragment.facts_mut().insert(&Trible::force(
&policy.id,
&admission_invoke_threshold.id(),
&invalid,
));
fragment.facts_mut().insert(&Trible::force(
&policy.id,
&admission_policy_root.id(),
&Inline::<GenId>::new([0; 32]),
));
fragment.put::<SimpleArchive, _>(super::super::policy::write_definition().facts().clone());
let reader = crate::repo::SnapshotSource::snapshot(&mut fragment.blobs().clone()).unwrap();
let policies: Vec<_> = admission_policies(
&reader,
fragment.facts(),
super::super::ACTION_WRITE,
Some(representation),
)
.collect();
assert_eq!(policies, vec![AdmissionPolicy::direct(key(13))]);
}
#[test]
fn admission_queries_preserve_both_recognized_kinds() {
let mut fragment = entity! {
metadata::tag: KIND_COLLECTION_DESCRIPTOR,
collection_representation: <SimpleArchive as MetaDescribe>::id(),
resource_policy*: entity! {
capability_handle: read_capability(),
metadata::tag*: [KIND_ADMISSION_POLICY_OPEN, KIND_ADMISSION_POLICY_QUORUM],
admission_policy_root: key(14),
admission_invoke_threshold: 1_u32,
},
};
fragment.put::<SimpleArchive, _>(super::super::policy::read_definition().facts().clone());
let reader = crate::repo::SnapshotSource::snapshot(&mut fragment.blobs().clone()).unwrap();
let policies: Vec<_> =
admission_policies(&reader, fragment.facts(), super::super::ACTION_READ, None)
.collect();
assert_eq!(policies.len(), 2);
assert!(policies.contains(&AdmissionPolicy::Open));
assert!(policies.contains(&AdmissionPolicy::direct(key(14))));
}
}