Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: updates for 0.10.0: 'fct' as a map, move 'ucv' to payload #108

Merged
merged 1 commit into from
Jun 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions ucan/src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::BTreeMap;

use crate::{
capability::{
proof::ProofDelegationSemantics, Action, Capability, CapabilityIpld, CapabilitySemantics,
Expand All @@ -6,15 +8,14 @@ use crate::{
crypto::KeyMaterial,
serde::Base64Encode,
time::now,
ucan::{Ucan, UcanHeader, UcanPayload, UCAN_VERSION},
ucan::{FactsMap, Ucan, UcanHeader, UcanPayload, UCAN_VERSION},
};
use anyhow::{anyhow, Result};
use base64::Engine;
use cid::multihash::Code;
use log::warn;
use rand::Rng;
use serde::{de::DeserializeOwned, Serialize};
use serde_json::Value;

/// A signable is a UCAN that has all the state it needs in order to be signed,
/// but has not yet been signed.
Expand All @@ -33,7 +34,7 @@ where
pub expiration: u64,
pub not_before: Option<u64>,

pub facts: Vec<Value>,
pub facts: FactsMap,
pub proofs: Vec<String>,
pub add_nonce: bool,
}
Expand All @@ -47,7 +48,6 @@ where
UcanHeader {
alg: self.issuer.get_jwt_algorithm_name(),
typ: "JWT".into(),
ucv: UCAN_VERSION.into(),
}
}

Expand All @@ -74,6 +74,7 @@ where
};

Ok(UcanPayload {
ucv: UCAN_VERSION.into(),
aud: self.audience.clone(),
iss: self.issuer.get_did().await?,
exp: self.expiration,
Expand Down Expand Up @@ -121,7 +122,7 @@ where
expiration: Option<u64>,
not_before: Option<u64>,

facts: Vec<Value>,
facts: FactsMap,
proofs: Vec<String>,
add_nonce: bool,
}
Expand Down Expand Up @@ -149,7 +150,7 @@ where
expiration: None,
not_before: None,

facts: Vec::new(),
facts: BTreeMap::new(),
proofs: Vec::new(),
add_nonce: false,
}
Expand Down Expand Up @@ -198,9 +199,11 @@ where
}

/// Add a fact or proof of knowledge to this UCAN.
pub fn with_fact<T: Serialize + DeserializeOwned>(mut self, fact: T) -> Self {
pub fn with_fact<T: Serialize + DeserializeOwned>(mut self, key: &str, fact: T) -> Self {
match serde_json::to_value(fact) {
Ok(value) => self.facts.push(value),
Ok(value) => {
self.facts.insert(key.to_owned(), value);
}
Err(error) => warn!("Could not add fact to UCAN: {}", error),
}
self
Expand Down
40 changes: 24 additions & 16 deletions ucan/src/ipld/ucan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ use crate::{
crypto::JwtSignatureAlgorithm,
ipld::{Principle, Signature},
serde::Base64Encode,
ucan::{Ucan, UcanHeader, UcanPayload, UCAN_VERSION},
ucan::{FactsMap, Ucan, UcanHeader, UcanPayload, UCAN_VERSION},
};
use cid::Cid;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::{convert::TryFrom, str::FromStr};

#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -21,7 +20,7 @@ pub struct UcanIpld {
pub att: Vec<CapabilityIpld>,
pub prf: Option<Vec<Cid>>,
pub exp: u64,
pub fct: Option<Vec<Value>>,
pub fct: Option<FactsMap>,

pub nnc: Option<String>,
pub nbf: Option<u64>,
Expand Down Expand Up @@ -72,10 +71,10 @@ impl TryFrom<&UcanIpld> for Ucan {
let header = UcanHeader {
alg: algorithm.to_string(),
typ: "JWT".into(),
ucv: UCAN_VERSION.into(),
};

let payload = UcanPayload {
ucv: UCAN_VERSION.into(),
iss: value.iss.to_string(),
aud: value.aud.to_string(),
exp: value.exp,
Expand Down Expand Up @@ -131,10 +130,13 @@ mod tests {
let other_builder = scaffold_ucan_builder(&identities).await.unwrap();

let canon_jwt = canon_builder
.with_fact(json!({
"baz": true,
"foo": "bar"
}))
.with_fact(
"abc/challenge",
json!({
"baz": true,
"foo": "bar"
}),
)
.build()
.unwrap()
.sign()
Expand All @@ -144,10 +146,13 @@ mod tests {
.unwrap();

let other_jwt = other_builder
.with_fact(json!({
"foo": "bar",
"baz": true
}))
.with_fact(
"abc/challenge",
json!({
"foo": "bar",
"baz": true
}),
)
.build()
.unwrap()
.sign()
Expand All @@ -166,10 +171,13 @@ mod tests {
let builder = scaffold_ucan_builder(&identities).await.unwrap();

let jwt = builder
.with_fact(json!({
"baz": true,
"foo": "bar"
}))
.with_fact(
"abc/challenge",
json!({
"baz": true,
"foo": "bar"
}),
)
.with_nonce()
.build()
.unwrap()
Expand Down
14 changes: 11 additions & 3 deletions ucan/src/tests/builder.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::BTreeMap;

use crate::{
builder::UcanBuilder,
capability::{CapabilityIpld, CapabilitySemantics},
Expand Down Expand Up @@ -52,8 +54,8 @@ async fn it_builds_with_a_simple_example() {
.for_audience(identities.bob_did.as_str())
.with_expiration(expiration)
.not_before(not_before)
.with_fact(fact_1.clone())
.with_fact(fact_2.clone())
.with_fact("abc/challenge", fact_1.clone())
.with_fact("def/challenge", fact_2.clone())
.claiming_capability(&cap_1)
.claiming_capability(&cap_2)
.with_nonce()
Expand All @@ -67,7 +69,13 @@ async fn it_builds_with_a_simple_example() {
assert_eq!(ucan.expires_at(), &expiration);
assert!(ucan.not_before().is_some());
assert_eq!(ucan.not_before().unwrap(), not_before);
assert_eq!(ucan.facts(), &Some(vec![fact_1, fact_2]));
assert_eq!(
ucan.facts(),
&Some(BTreeMap::from([
(String::from("abc/challenge"), fact_1),
(String::from("def/challenge"), fact_2),
]))
);

let expected_attenuations =
Vec::from([CapabilityIpld::from(&cap_1), CapabilityIpld::from(&cap_2)]);
Expand Down
9 changes: 7 additions & 2 deletions ucan/src/tests/ucan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod validate {
ucan::Ucan,
};

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

Expand Down Expand Up @@ -81,6 +82,7 @@ mod validate {
.for_audience(identities.bob_did.as_str())
.not_before(now() / 1000)
.with_lifetime(30)
.with_fact("abc/challenge", json!({ "foo": "bar" }))
.build()
.unwrap()
.sign()
Expand All @@ -94,15 +96,18 @@ mod validate {
serde_json::json!({
"header": {
"alg": "EdDSA",
"typ": "JWT",
"ucv": crate::ucan::UCAN_VERSION
"typ": "JWT"
},
"payload": {
"ucv": crate::ucan::UCAN_VERSION,
"iss": ucan.issuer(),
"aud": ucan.audience(),
"exp": ucan.expires_at(),
"nbf": ucan.not_before(),
"att": [],
"fct": {
"abc/challenge": { "foo": "bar" }
}
},
"signed_data": ucan.signed_data(),
"signature": ucan.signature()
Expand Down
14 changes: 8 additions & 6 deletions ucan/src/ucan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,21 @@ use cid::{
use libipld_core::{codec::Codec, raw::RawCodec};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::{convert::TryFrom, str::FromStr};
use std::{collections::BTreeMap, convert::TryFrom, str::FromStr};

pub const UCAN_VERSION: &str = "0.9.0-canary";
pub const UCAN_VERSION: &str = "0.10.0-canary";

pub type FactsMap = BTreeMap<String, Value>;

#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct UcanHeader {
pub alg: String,
pub typ: String,
pub ucv: String,
}

#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct UcanPayload {
pub ucv: String,
pub iss: String,
pub aud: String,
pub exp: u64,
Expand All @@ -35,7 +37,7 @@ pub struct UcanPayload {
pub nnc: Option<String>,
pub att: Vec<CapabilityIpld>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fct: Option<Vec<Value>>,
pub fct: Option<FactsMap>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prf: Option<Vec<String>>,
}
Expand Down Expand Up @@ -175,12 +177,12 @@ impl Ucan {
&self.payload.att
}

pub fn facts(&self) -> &Option<Vec<Value>> {
pub fn facts(&self) -> &Option<FactsMap> {
&self.payload.fct
}

pub fn version(&self) -> &str {
&self.header.ucv
&self.payload.ucv
}

pub fn to_cid(&self, hasher: Code) -> Result<Cid> {
Expand Down