From 2172b21e30e102a943a47f9abf2848cb05077602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Thu, 21 Mar 2024 21:41:01 +0100 Subject: [PATCH 1/8] fix: Error early in `Agent::invoke` if there's no valid delegation chain --- src/invocation/agent.rs | 53 ++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/invocation/agent.rs b/src/invocation/agent.rs index 3929e72..6e4305d 100644 --- a/src/invocation/agent.rs +++ b/src/invocation/agent.rs @@ -3,19 +3,21 @@ use super::{ store::Store, Invocation, }; -use crate::ability::arguments::Named; -use crate::ability::command::ToCommand; -use crate::ability::parse::ParseAbility; -use crate::delegation::Delegation; -use crate::invocation::payload::PayloadBuilder; use crate::{ - ability::{self, arguments, parse::ParseAbilityError, ucan::revoke::Revoke}, + ability::{ + self, arguments, + arguments::Named, + command::ToCommand, + parse::{ParseAbility, ParseAbilityError}, + ucan::revoke::Revoke, + }, crypto::{ signature::{self, Envelope}, varsig, Nonce, }, delegation, did::{self, Did}, + invocation::payload::PayloadBuilder, time::Timestamp, }; use enum_as_inner::EnumAsInner; @@ -102,16 +104,15 @@ where vec![] } else { self.delegation_store - .get_chain( + .get_chain_cids( &self.did, &subject.clone(), ability.to_command(), vec![], now, - ) - .map_err(InvokeError::DelegationStoreError)? - .map(|chain| chain.map(|(cid, _)| cid).into()) - .unwrap_or(vec![]) // FIXME + )? + .ok_or(InvokeError::ProofsNotFound)? + .into() }; let payload = Payload { @@ -328,7 +329,10 @@ pub enum ReceiveError< #[derive(Debug, Error)] pub enum InvokeError { #[error("delegation store error: {0}")] - DelegationStoreError(#[source] D), + DelegationStoreError(#[from] D), + + #[error("The current agent does not have the necessary proofs to invoke.")] + ProofsNotFound, #[error("store error: {0}")] SignError(#[source] signature::SignError), @@ -337,23 +341,28 @@ pub enum InvokeError { #[cfg(test)] mod tests { use super::*; - use crate::ability::crud::read::Read; - use crate::crypto::varsig; - use crate::crypto::varsig::encoding; - use crate::crypto::varsig::header; - use crate::invocation::{payload::ValidationError, Agent}; use crate::{ - ability::{arguments::Named, command::Command}, - crypto::signature::Envelope, + ability::{arguments::Named, command::Command, crud::read::Read}, + crypto::{ + signature::Envelope, + varsig, + varsig::{encoding, header}, + }, delegation::store::Store, - invocation::promise::{CantResolve, Resolvable}, + invocation::{ + payload::ValidationError, + promise::{CantResolve, Resolvable}, + Agent, + }, ipld, }; use libipld_core::{cid::Cid, ipld::Ipld}; use pretty_assertions as pretty; use rand::thread_rng; - use std::ops::{Add, Sub}; - use std::time::{Duration, SystemTime}; + use std::{ + ops::{Add, Sub}, + time::{Duration, SystemTime}, + }; use testresult::TestResult; #[derive(Debug, Clone, PartialEq)] From f894e695fa86937ae34021a4e9a8088be61b244a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Thu, 21 Mar 2024 21:46:40 +0100 Subject: [PATCH 2/8] fix: Check for delegations for the specific command, not for "/" --- src/invocation/agent.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/invocation/agent.rs b/src/invocation/agent.rs index 6e4305d..d6a1017 100644 --- a/src/invocation/agent.rs +++ b/src/invocation/agent.rs @@ -104,13 +104,7 @@ where vec![] } else { self.delegation_store - .get_chain_cids( - &self.did, - &subject.clone(), - ability.to_command(), - vec![], - now, - )? + .get_chain_cids(&self.did, &subject, ability.to_command(), vec![], now)? // FIXME policy .ok_or(InvokeError::ProofsNotFound)? .into() }; From 5d26366abaf93a923656e62028eeaa42a4aee7d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Fri, 22 Mar 2024 11:08:20 +0100 Subject: [PATCH 3/8] refactor: Rename `MissingDelegation` -> `DelegationNotFound` --- src/invocation/agent.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/invocation/agent.rs b/src/invocation/agent.rs index d6a1017..3cad747 100644 --- a/src/invocation/agent.rs +++ b/src/invocation/agent.rs @@ -218,7 +218,7 @@ where .map(|(d, cid)| { Ok(&d .as_ref() - .ok_or(ReceiveError::MissingDelegation(*cid))? + .ok_or(ReceiveError::DelegationNotFound(*cid))? .payload) }) .collect::>>()?; @@ -301,8 +301,8 @@ pub enum ReceiveError< > where >::InvocationStoreError: fmt::Debug, { - #[error("missing delegation: {0}")] - MissingDelegation(Cid), + #[error("couldn't find delegation: {0}")] + DelegationNotFound(Cid), #[error("encoding error: {0}")] EncodingError(#[from] libipld_core::error::Error), From 4bd583ab05c94a8d6a210e01c93a9bf75bcd4650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Fri, 22 Mar 2024 11:14:25 +0100 Subject: [PATCH 4/8] refactor: Use `Mutex` instead of `RwLock` --- src/delegation/store/memory.rs | 46 ++++++++++++---------------------- src/invocation/store/memory.rs | 26 ++++++------------- 2 files changed, 24 insertions(+), 48 deletions(-) diff --git a/src/delegation/store/memory.rs b/src/delegation/store/memory.rs index bdc7e6e..10b9401 100644 --- a/src/delegation/store/memory.rs +++ b/src/delegation/store/memory.rs @@ -10,10 +10,10 @@ use libipld_core::codec::Encode; use libipld_core::ipld::Ipld; use libipld_core::{cid::Cid, codec::Codec}; use nonempty::NonEmpty; -use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}; use std::{ collections::{BTreeMap, BTreeSet}, convert::Infallible, + sync::{Arc, Mutex, MutexGuard}, }; use web_time::SystemTime; @@ -79,7 +79,7 @@ pub struct MemoryStore< V: varsig::Header = varsig::header::Preset, C: Codec + TryFrom + Into = varsig::encoding::Preset, > { - inner: Arc>>, + inner: Arc>>, } #[derive(Debug, Clone, PartialEq)] @@ -101,25 +101,15 @@ impl, C: Codec + TryFrom + Into usize { - self.read().ucans.len() + self.lock().ucans.len() } pub fn is_empty(&self) -> bool { - self.read().ucans.is_empty() // FIXME account for revocations? + self.lock().ucans.is_empty() // FIXME account for revocations? } - fn read(&self) -> RwLockReadGuard<'_, MemoryStoreInner> { - match self.inner.read() { - Ok(guard) => guard, - Err(poison) => { - // We ignore lock poisoning for simplicity - poison.into_inner() - } - } - } - - fn write(&self) -> RwLockWriteGuard<'_, MemoryStoreInner> { - match self.inner.write() { + fn lock(&self) -> MutexGuard<'_, MemoryStoreInner> { + match self.inner.lock() { Ok(guard) => guard, Err(poison) => { // We ignore lock poisoning for simplicity @@ -169,7 +159,7 @@ where cid: &Cid, ) -> Result>>, Self::DelegationStoreError> { // cheap Arc clone - Ok(self.read().ucans.get(cid).cloned()) + Ok(self.lock().ucans.get(cid).cloned()) // FIXME } @@ -178,23 +168,22 @@ where cid: Cid, delegation: Delegation, ) -> Result<(), Self::DelegationStoreError> { - let mut write_tx = self.write(); + let mut tx = self.lock(); - write_tx - .index + tx.index .entry(delegation.subject().clone()) .or_default() .entry(delegation.audience().clone()) .or_default() .insert(cid); - write_tx.ucans.insert(cid.clone(), Arc::new(delegation)); + tx.ucans.insert(cid.clone(), Arc::new(delegation)); Ok(()) } fn revoke(&self, cid: Cid) -> Result<(), Self::DelegationStoreError> { - self.write().revocations.insert(cid); + self.lock().revocations.insert(cid); Ok(()) } @@ -209,13 +198,10 @@ where { let blank_set = BTreeSet::new(); let blank_map = BTreeMap::new(); - let read_tx = self.read(); + let tx = self.lock(); - let all_powerlines = read_tx.index.get(&None).unwrap_or(&blank_map); - let all_aud_for_subject = read_tx - .index - .get(&Some(subject.clone())) - .unwrap_or(&blank_map); + let all_powerlines = tx.index.get(&None).unwrap_or(&blank_map); + let all_aud_for_subject = tx.index.get(&Some(subject.clone())).unwrap_or(&blank_map); let powerline_candidates = all_powerlines.get(aud).unwrap_or(&blank_set); let sub_candidates = all_aud_for_subject.get(aud).unwrap_or(&blank_set); @@ -238,11 +224,11 @@ where 'inner: for cid in parent_cid_candidates { // CHECKS - if read_tx.revocations.contains(cid) { + if tx.revocations.contains(cid) { continue; } - if let Some(delegation) = read_tx.ucans.get(cid) { + if let Some(delegation) = tx.ucans.get(cid) { if delegation.check_time(now).is_err() { continue; } diff --git a/src/invocation/store/memory.rs b/src/invocation/store/memory.rs index 2ae9a36..7c7c719 100644 --- a/src/invocation/store/memory.rs +++ b/src/invocation/store/memory.rs @@ -1,7 +1,7 @@ -use crate::{crypto::varsig, did::Did, invocation::Invocation}; use super::Store; +use crate::{crypto::varsig, did::Did, invocation::Invocation}; use libipld_core::{cid::Cid, codec::Codec}; -use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; +use std::sync::{Arc, Mutex, MutexGuard}; use std::{collections::BTreeMap, convert::Infallible}; #[derive(Debug, Clone)] @@ -11,7 +11,7 @@ pub struct MemoryStore< V: varsig::Header = varsig::header::Preset, C: Codec + TryFrom + Into = varsig::encoding::Preset, > { - inner: Arc>>, + inner: Arc>>, } #[derive(Debug, Clone, PartialEq)] @@ -27,18 +27,8 @@ pub struct MemoryStoreInner< impl, Enc: Codec + Into + TryFrom> MemoryStore { - fn read(&self) -> RwLockReadGuard<'_, MemoryStoreInner> { - match self.inner.read() { - Ok(guard) => guard, - Err(poison) => { - // There's no logic errors through lock poisoning in our case - poison.into_inner() - } - } - } - - fn write(&self) -> RwLockWriteGuard<'_, MemoryStoreInner> { - match self.inner.write() { + fn lock(&self) -> MutexGuard<'_, MemoryStoreInner> { + match self.inner.lock() { Ok(guard) => guard, Err(poison) => { // There's no logic errors through lock poisoning in our case @@ -53,7 +43,7 @@ impl, Enc: Codec + Into + TryFrom> { fn default() -> Self { Self { - inner: Arc::new(RwLock::new(MemoryStoreInner { + inner: Arc::new(Mutex::new(MemoryStoreInner { store: BTreeMap::new(), })), } @@ -69,7 +59,7 @@ impl, Enc: Codec + Into + TryFrom> &self, cid: Cid, ) -> Result>>, Self::InvocationStoreError> { - Ok(self.read().store.get(&cid).cloned()) + Ok(self.lock().store.get(&cid).cloned()) } fn put( @@ -77,7 +67,7 @@ impl, Enc: Codec + Into + TryFrom> cid: Cid, invocation: Invocation, ) -> Result<(), Self::InvocationStoreError> { - self.write().store.insert(cid, Arc::new(invocation)); + self.lock().store.insert(cid, Arc::new(invocation)); Ok(()) } } From fd0922f111ade59eb3ffee1a0ca36defc5f2fe80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Fri, 22 Mar 2024 15:06:17 +0100 Subject: [PATCH 5/8] fix: Check for the appropriate command in `Agent::delegate` --- src/delegation/agent.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/delegation/agent.rs b/src/delegation/agent.rs index 23c1b0c..08ee36a 100644 --- a/src/delegation/agent.rs +++ b/src/delegation/agent.rs @@ -1,8 +1,8 @@ use super::{payload::Payload, policy::Predicate, store::Store, Delegation}; -use crate::ability::arguments::Named; -use crate::did; use crate::{ + ability::arguments::Named, crypto::{signature::Envelope, varsig, Nonce}, + did, did::Did, time::Timestamp, }; @@ -94,7 +94,7 @@ where let proofs = &self .store - .get_chain(&self.did, &subject, "/".into(), vec![], now) + .get_chain(&self.did, &subject, command.clone(), vec![], now) .map_err(DelegateError::StoreError)? .ok_or(DelegateError::ProofsNotFound)?; let to_delegate = proofs.first().1.payload(); From 0dfb2a64ce6166b3e3ada0a605a432ee5580ac3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Fri, 22 Mar 2024 15:10:51 +0100 Subject: [PATCH 6/8] refactor: Take `command: &str` instead of by `String` in `Store::get_chain` --- src/delegation/agent.rs | 2 +- src/delegation/store/memory.rs | 9 +++++---- src/delegation/store/traits.rs | 8 ++++---- src/invocation/agent.rs | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/delegation/agent.rs b/src/delegation/agent.rs index 08ee36a..7e9cc9c 100644 --- a/src/delegation/agent.rs +++ b/src/delegation/agent.rs @@ -94,7 +94,7 @@ where let proofs = &self .store - .get_chain(&self.did, &subject, command.clone(), vec![], now) + .get_chain(&self.did, &subject, &command, vec![], now) .map_err(DelegateError::StoreError)? .ok_or(DelegateError::ProofsNotFound)?; let to_delegate = proofs.first().1.payload(); diff --git a/src/delegation/store/memory.rs b/src/delegation/store/memory.rs index 10b9401..91fd610 100644 --- a/src/delegation/store/memory.rs +++ b/src/delegation/store/memory.rs @@ -10,6 +10,7 @@ use libipld_core::codec::Encode; use libipld_core::ipld::Ipld; use libipld_core::{cid::Cid, codec::Codec}; use nonempty::NonEmpty; +use std::borrow::Cow; use std::{ collections::{BTreeMap, BTreeSet}, convert::Infallible, @@ -191,8 +192,8 @@ where &self, aud: &DID, subject: &DID, - command: String, - policy: Vec, + command: &str, + policy: Vec, // FIXME now: SystemTime, ) -> Result>)>>, Self::DelegationStoreError> { @@ -210,9 +211,9 @@ where let mut hypothesis_chain = vec![]; let corrected_target_command = if command.ends_with('/') { - command + Cow::Borrowed(command) } else { - format!("{}/", command) + Cow::Owned(format!("{command}/")) }; 'outer: loop { diff --git a/src/delegation/store/traits.rs b/src/delegation/store/traits.rs index 0ace1c7..c5bc670 100644 --- a/src/delegation/store/traits.rs +++ b/src/delegation/store/traits.rs @@ -50,7 +50,7 @@ where &self, audience: &DID, subject: &DID, - command: String, + command: &str, policy: Vec, now: SystemTime, ) -> Result>)>>, Self::DelegationStoreError>; @@ -59,7 +59,7 @@ where &self, audience: &DID, subject: &DID, - command: String, + command: &str, policy: Vec, now: SystemTime, ) -> Result>, Self::DelegationStoreError> { @@ -71,7 +71,7 @@ where &self, issuer: DID, audience: &DID, - command: String, + command: &str, policy: Vec, now: SystemTime, ) -> Result { @@ -125,7 +125,7 @@ where &self, audience: &DID, subject: &DID, - command: String, + command: &str, policy: Vec, now: SystemTime, ) -> Result>)>>, Self::DelegationStoreError> diff --git a/src/invocation/agent.rs b/src/invocation/agent.rs index 3cad747..e50aefc 100644 --- a/src/invocation/agent.rs +++ b/src/invocation/agent.rs @@ -104,7 +104,7 @@ where vec![] } else { self.delegation_store - .get_chain_cids(&self.did, &subject, ability.to_command(), vec![], now)? // FIXME policy + .get_chain_cids(&self.did, &subject, &ability.to_command(), vec![], now)? // FIXME policy .ok_or(InvokeError::ProofsNotFound)? .into() }; From 8cc6ac51a32a67846e83f8d0d107b6f883258786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Fri, 22 Mar 2024 15:16:40 +0100 Subject: [PATCH 7/8] refactor: Turn `try_fold` into for loop in `Invocation::check` --- src/invocation/payload.rs | 98 ++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/src/invocation/payload.rs b/src/invocation/payload.rs index 6e92ae6..0c45499 100644 --- a/src/invocation/payload.rs +++ b/src/invocation/payload.rs @@ -1,18 +1,19 @@ use super::promise::Resolvable; -use crate::ability::command::Command; -use crate::ability::parse::ParseAbilityError; -use crate::delegation::policy::selector; -use crate::invocation::Named; -use crate::time; use crate::{ - ability::{arguments, command::ToCommand, parse::ParseAbility}, + ability::{ + arguments, + command::{Command, ToCommand}, + parse::{ParseAbility, ParseAbilityError}, + }, capsule::Capsule, crypto::{varsig, Nonce}, delegation::{ self, - policy::{selector::SelectorError, Predicate}, + policy::{selector, selector::SelectorError, Predicate}, }, did::{Did, Verifiable}, + invocation::Named, + time, time::{Expired, Timestamp}, }; use derive_builder::Builder; @@ -22,9 +23,11 @@ use serde::{ ser::SerializeStruct, Deserialize, Serialize, Serializer, }; -use std::collections::BTreeSet; -use std::str::FromStr; -use std::{collections::BTreeMap, fmt}; +use std::{ + collections::{BTreeMap, BTreeSet}, + fmt, + str::FromStr, +}; use thiserror::Error; use web_time::SystemTime; @@ -175,54 +178,54 @@ impl Payload { cmd.push('/'); } - let (final_iss, vias) = proofs.into_iter().try_fold( - (&self.issuer, BTreeSet::new()), - |(iss, mut vias), proof| { - if *iss != proof.audience { - return Err(ValidationError::MisalignedIssAud.into()); - } + let mut current_iss = &self.issuer; + let mut vias = BTreeSet::new(); + for proof in proofs { + if *current_iss != proof.audience { + return Err(ValidationError::MisalignedIssAud.into()); + } - if let Some(proof_subject) = &proof.subject { - if self.subject != *proof_subject { - return Err(ValidationError::InvalidSubject.into()); - } + if let Some(proof_subject) = &proof.subject { + if self.subject != *proof_subject { + return Err(ValidationError::InvalidSubject.into()); } + } - if proof.expiration < now_ts { - return Err(ValidationError::Expired.into()); - } + if proof.expiration < now_ts { + return Err(ValidationError::Expired.into()); + } - if let Some(nbf) = proof.not_before.clone() { - if nbf > now_ts { - return Err(ValidationError::NotYetValid.into()); - } + if let Some(nbf) = proof.not_before.clone() { + if nbf > now_ts { + return Err(ValidationError::NotYetValid.into()); } + } - vias.remove(&iss); - if let Some(via_did) = &proof.via { - vias.insert(via_did); - } + vias.remove(¤t_iss); + if let Some(via_did) = &proof.via { + vias.insert(via_did); + } - if !cmd.starts_with(&proof.command) { - return Err(ValidationError::CommandMismatch(proof.command.clone())); - } + if !cmd.starts_with(&proof.command) { + return Err(ValidationError::CommandMismatch(proof.command.clone())); + } - let ipld_args = Ipld::from(args.clone()); + let ipld_args = Ipld::from(args.clone()); - for predicate in proof.policy.iter() { - if !predicate - .clone() - .run(&ipld_args) - .map_err(ValidationError::SelectorError)? - { - return Err(ValidationError::FailedPolicy(predicate.clone())); - } + for predicate in proof.policy.iter() { + if !predicate + .clone() + .run(&ipld_args) + .map_err(ValidationError::SelectorError)? + { + return Err(ValidationError::FailedPolicy(predicate.clone())); } + } - Ok((&proof.issuer, vias)) - }, - )?; + current_iss = &proof.issuer; + } + let final_iss = current_iss; if self.subject != *final_iss { return Err(ValidationError::DidNotTerminateInSubject); } @@ -731,8 +734,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::ability::msg::Msg; - use crate::ipld; + use crate::{ability::msg::Msg, ipld}; use assert_matches::assert_matches; use pretty_assertions as pretty; use proptest::prelude::*; From d0fab39b4531c491b7129d9d2193c9e2eff283c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Kr=C3=BCger?= Date: Mon, 25 Mar 2024 09:04:13 +0100 Subject: [PATCH 8/8] Revert "refactor: Turn `try_fold` into for loop in `Invocation::check`" This reverts commit c06e6def0553bb7563e33914d7d11316d0e364fb. --- src/invocation/payload.rs | 98 +++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 50 deletions(-) diff --git a/src/invocation/payload.rs b/src/invocation/payload.rs index 0c45499..6e92ae6 100644 --- a/src/invocation/payload.rs +++ b/src/invocation/payload.rs @@ -1,19 +1,18 @@ use super::promise::Resolvable; +use crate::ability::command::Command; +use crate::ability::parse::ParseAbilityError; +use crate::delegation::policy::selector; +use crate::invocation::Named; +use crate::time; use crate::{ - ability::{ - arguments, - command::{Command, ToCommand}, - parse::{ParseAbility, ParseAbilityError}, - }, + ability::{arguments, command::ToCommand, parse::ParseAbility}, capsule::Capsule, crypto::{varsig, Nonce}, delegation::{ self, - policy::{selector, selector::SelectorError, Predicate}, + policy::{selector::SelectorError, Predicate}, }, did::{Did, Verifiable}, - invocation::Named, - time, time::{Expired, Timestamp}, }; use derive_builder::Builder; @@ -23,11 +22,9 @@ use serde::{ ser::SerializeStruct, Deserialize, Serialize, Serializer, }; -use std::{ - collections::{BTreeMap, BTreeSet}, - fmt, - str::FromStr, -}; +use std::collections::BTreeSet; +use std::str::FromStr; +use std::{collections::BTreeMap, fmt}; use thiserror::Error; use web_time::SystemTime; @@ -178,54 +175,54 @@ impl Payload { cmd.push('/'); } - let mut current_iss = &self.issuer; - let mut vias = BTreeSet::new(); - for proof in proofs { - if *current_iss != proof.audience { - return Err(ValidationError::MisalignedIssAud.into()); - } + let (final_iss, vias) = proofs.into_iter().try_fold( + (&self.issuer, BTreeSet::new()), + |(iss, mut vias), proof| { + if *iss != proof.audience { + return Err(ValidationError::MisalignedIssAud.into()); + } - if let Some(proof_subject) = &proof.subject { - if self.subject != *proof_subject { - return Err(ValidationError::InvalidSubject.into()); + if let Some(proof_subject) = &proof.subject { + if self.subject != *proof_subject { + return Err(ValidationError::InvalidSubject.into()); + } } - } - if proof.expiration < now_ts { - return Err(ValidationError::Expired.into()); - } + if proof.expiration < now_ts { + return Err(ValidationError::Expired.into()); + } - if let Some(nbf) = proof.not_before.clone() { - if nbf > now_ts { - return Err(ValidationError::NotYetValid.into()); + if let Some(nbf) = proof.not_before.clone() { + if nbf > now_ts { + return Err(ValidationError::NotYetValid.into()); + } } - } - vias.remove(¤t_iss); - if let Some(via_did) = &proof.via { - vias.insert(via_did); - } + vias.remove(&iss); + if let Some(via_did) = &proof.via { + vias.insert(via_did); + } - if !cmd.starts_with(&proof.command) { - return Err(ValidationError::CommandMismatch(proof.command.clone())); - } + if !cmd.starts_with(&proof.command) { + return Err(ValidationError::CommandMismatch(proof.command.clone())); + } - let ipld_args = Ipld::from(args.clone()); + let ipld_args = Ipld::from(args.clone()); - for predicate in proof.policy.iter() { - if !predicate - .clone() - .run(&ipld_args) - .map_err(ValidationError::SelectorError)? - { - return Err(ValidationError::FailedPolicy(predicate.clone())); + for predicate in proof.policy.iter() { + if !predicate + .clone() + .run(&ipld_args) + .map_err(ValidationError::SelectorError)? + { + return Err(ValidationError::FailedPolicy(predicate.clone())); + } } - } - current_iss = &proof.issuer; - } + Ok((&proof.issuer, vias)) + }, + )?; - let final_iss = current_iss; if self.subject != *final_iss { return Err(ValidationError::DidNotTerminateInSubject); } @@ -734,7 +731,8 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{ability::msg::Msg, ipld}; + use crate::ability::msg::Msg; + use crate::ipld; use assert_matches::assert_matches; use pretty_assertions as pretty; use proptest::prelude::*;