Skip to content

Commit

Permalink
feat: Allow nullable expiry, per 0.9.0 spec. Fixes #23
Browse files Browse the repository at this point in the history
  • Loading branch information
jsantell committed May 24, 2023
1 parent 806c646 commit ade9916
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 24 deletions.
25 changes: 11 additions & 14 deletions ucan/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ where

pub capabilities: Vec<CapabilityIpld>,

pub expiration: u64,
pub expiration: Option<u64>,
pub not_before: Option<u64>,

pub facts: Vec<Value>,
Expand Down Expand Up @@ -259,19 +259,16 @@ where
pub fn build(self) -> Result<Signable<'a, K>> {
match &self.issuer {
Some(issuer) => match &self.audience {
Some(audience) => match self.implied_expiration() {
Some(expiration) => Ok(Signable {
issuer,
audience: audience.clone(),
not_before: self.not_before,
expiration,
facts: self.facts.clone(),
capabilities: self.capabilities.clone(),
proofs: self.proofs.clone(),
add_nonce: self.add_nonce,
}),
None => Err(anyhow!("Ambiguous lifetime")),
},
Some(audience) => Ok(Signable {
issuer,
audience: audience.clone(),
not_before: self.not_before,
expiration: self.implied_expiration(),
facts: self.facts.clone(),
capabilities: self.capabilities.clone(),
proofs: self.proofs.clone(),
add_nonce: self.add_nonce,
}),
None => Err(anyhow!("Missing audience")),
},
None => Err(anyhow!("Missing issuer")),
Expand Down
2 changes: 1 addition & 1 deletion ucan/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const PROOF_DELEGATION_SEMANTICS: ProofDelegationSemantics = ProofDelegationSema
pub struct CapabilityInfo<S: Scope, A: Action> {
pub originators: BTreeSet<String>,
pub not_before: Option<u64>,
pub expires_at: u64,
pub expires_at: Option<u64>,
pub capability: Capability<S, A>,
}

Expand Down
2 changes: 1 addition & 1 deletion ucan/src/ipld/ucan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub struct UcanIpld {

pub att: Vec<CapabilityIpld>,
pub prf: Vec<Cid>,
pub exp: u64,
pub exp: Option<u64>,
pub fct: Vec<Value>,

pub nnc: Option<String>,
Expand Down
5 changes: 3 additions & 2 deletions ucan/src/tests/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ async fn it_builds_with_a_simple_example() {

assert_eq!(ucan.issuer(), identities.alice_did);
assert_eq!(ucan.audience(), identities.bob_did);
assert_eq!(ucan.expires_at(), &expiration);
assert!(ucan.expires_at().is_some());
assert_eq!(ucan.expires_at().unwrap(), expiration);
assert!(ucan.not_before().is_some());
assert_eq!(ucan.not_before().unwrap(), not_before);
assert_eq!(ucan.facts(), &vec![fact_1, fact_2]);
Expand All @@ -85,7 +86,7 @@ async fn it_builds_with_lifetime_in_seconds() {
.await
.unwrap();

assert!(*ucan.expires_at() > (now() + 290));
assert!(ucan.expires_at().unwrap() > (now() + 290));
}

#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
Expand Down
35 changes: 35 additions & 0 deletions ucan/src/tests/ucan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,38 @@ mod validate {
);
}
}

mod spec_0_9_0 {
use crate::{builder::UcanBuilder, tests::fixtures::Identities};
use anyhow::Result;

#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};

#[cfg(target_arch = "wasm32")]
wasm_bindgen_test_configure!(run_in_browser);

#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
async fn it_allows_nullable_expiry() -> Result<()> {
let identities = Identities::new().await;
let ucan = UcanBuilder::default()
.issued_by(&identities.alice_key)
.for_audience(identities.bob_did.as_str())
.build()?
.sign()
.await?;
let other_ucan = UcanBuilder::default()
.issued_by(&identities.alice_key)
.for_audience(identities.bob_did.as_str())
.with_lifetime(2000)
.build()?
.sign()
.await?;

assert_eq!(*ucan.expires_at(), None);
assert!(ucan.lifetime_ends_after(&other_ucan));
assert!(!other_ucan.lifetime_ends_after(&ucan));
Ok(())
}
}
18 changes: 12 additions & 6 deletions ucan/src/ucan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub struct UcanHeader {
pub struct UcanPayload {
pub iss: String,
pub aud: String,
pub exp: u64,
pub exp: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub nbf: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -97,9 +97,11 @@ impl Ucan {

/// Returns true if the UCAN has past its expiration date
pub fn is_expired(&self, now_time: Option<u64>) -> bool {
let now_time = now_time.unwrap_or_else(now);

self.payload.exp < now_time
if let Some(exp) = self.payload.exp {
exp < now_time.unwrap_or_else(now)
} else {
false
}
}

/// Raw bytes of signed data for this UCAN
Expand Down Expand Up @@ -133,7 +135,11 @@ impl Ucan {

/// Returns true if this UCAN expires no earlier than the other
pub fn lifetime_ends_after(&self, other: &Ucan) -> bool {
self.payload.exp >= other.payload.exp
match (self.payload.exp, other.payload.exp) {
(Some(exp), Some(other_exp)) => exp >= other_exp,
(Some(_), None) => false,
(None, _) => true,
}
}

/// Returns true if this UCAN's lifetime fully encompasses the other
Expand All @@ -157,7 +163,7 @@ impl Ucan {
&self.payload.prf
}

pub fn expires_at(&self) -> &u64 {
pub fn expires_at(&self) -> &Option<u64> {
&self.payload.exp
}

Expand Down

0 comments on commit ade9916

Please sign in to comment.