diff --git a/Cargo.toml b/Cargo.toml index b58c136..e4e3997 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ libipld = { version = "0.16.0", default-features = false, features = [ ] } libipld-json = { version = "0.16.0", default-features = false, optional = true } serde = "1.0.152" +serde_json = "1.0.117" serde_derive = "1.0.152" serde_ipld_dagcbor = "0.3.0" thiserror = "1.0.38" diff --git a/src/codec.rs b/src/codec.rs index c86bcbe..7564908 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -75,7 +75,19 @@ impl TryFrom for JsonWebSignature { type Error = Error; fn try_from(value: Encoded) -> Result { - let link = Cid::try_from(value.payload.as_ref().ok_or(Error::NotJws)?.as_slice())?; + let payload = value.payload.as_ref().ok_or(Error::NotJws)?; + + //do we need to parse all the strings as ipfs:// CIDs here and error or can we wait until we encode? + let (link, pld) = match serde_json::from_slice::(payload.as_ref()) { + Ok(json) => { + let res = match crate::JsonPld(json).try_into().map_err(|_| Error::NotJws)? { + Ipld::Map(map) => map, + _ => return Err(Error::NotJws), + }; + (None, Some(res)) + } + Err(_) => (Some(Cid::try_from(payload.as_ref())?), None), + }; Ok(Self { payload: value .payload @@ -88,6 +100,7 @@ impl TryFrom for JsonWebSignature { .map(Signature::from) .collect(), link, + pld, }) } } diff --git a/src/lib.rs b/src/lib.rs index 6cb0ec5..5439d04 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,13 +71,10 @@ mod error; use std::{collections::BTreeMap, io::BufReader}; +use libipld::codec::{Codec, Decode, Encode}; use libipld::error::UnsupportedCodec; -use libipld::Cid; -use libipld::Ipld; -use libipld::{ - codec::{Codec, Decode, Encode}, - ipld, -}; + +use libipld::{Cid, Ipld}; #[cfg(feature = "dag-json")] use libipld_json::DagJsonCodec; @@ -158,21 +155,87 @@ impl Encode for Jose { pub struct JsonWebSignature { /// The payload base64 url encoded. pub payload: String, - /// The set of signatures. pub signatures: Vec, - /// CID link from the payload. - pub link: Cid, + pub link: Option, + /// The json payload with ipfs:// prefixed strings replaced with CIDs. + pub pld: Option>, +} + +#[derive(Clone, Debug, PartialEq)] +struct JsonPld(serde_json::Value); + +impl TryFrom for Ipld { + type Error = anyhow::Error; + + fn try_from(value: JsonPld) -> Result { + match value.0 { + serde_json::Value::Null => Ok(Ipld::Null), + serde_json::Value::Bool(b) => Ok(Ipld::Bool(b)), + serde_json::Value::Number(n) => { + if let Some(i) = n.as_i64() { + Ok(Ipld::Integer(i as i128)) + } else if let Some(f) = n.as_f64() { + Ok(Ipld::Float(f)) + } else { + unreachable!("Number out of range") + } + } + serde_json::Value::String(s) => { + if let Some(str) = s.strip_prefix("ipfs://") { + let cid = Cid::try_from(str)?; + Ok(Ipld::Link(cid)) + } else { + Ok(Ipld::String(s)) + } + } + serde_json::Value::Array(arr) => { + let list = arr + .into_iter() + .map(|v| JsonPld(v).try_into()) + .collect::, _>>()?; + Ok(Ipld::List(list)) + } + serde_json::Value::Object(obj) => { + let map = obj + .into_iter() + .map(|(k, v)| JsonPld(v).try_into().map(|v| (k, v))) + .collect::, _>>()?; + Ok(Ipld::Map(map)) + } + } + } } -impl<'a> From<&'a JsonWebSignature> for Ipld { - fn from(value: &'a JsonWebSignature) -> Self { - ipld!({ - "payload": value.payload.to_owned(), - "signatures": value.signatures.iter().map(Ipld::from).collect::>(), - "link": value.link, - }) +impl<'a> TryFrom<&'a JsonWebSignature> for Ipld { + type Error = anyhow::Error; + + fn try_from(value: &'a JsonWebSignature) -> Result { + let mut fields = BTreeMap::new(); + + fields.insert("payload".to_string(), value.payload.to_owned().into()); + fields.insert( + "signatures".to_string(), + Ipld::List( + value + .signatures + .iter() + .map(Ipld::from) + .collect::>(), + ), + ); + + if let Some(link) = value.link { + fields.insert("link".to_string(), link.into()); + } + + if let Some(pld) = value.pld.to_owned() { + let data: Ipld = pld.try_into()?; + fields.insert("pld".to_string(), data); + } + + Ok(Ipld::Map(fields)) } } @@ -194,7 +257,7 @@ impl Decode for JsonWebSignature { #[cfg(feature = "dag-json")] impl Encode for JsonWebSignature { fn encode(&self, c: DagJsonCodec, w: &mut W) -> anyhow::Result<()> { - let data: Ipld = self.into(); + let data: Ipld = self.try_into()?; data.encode(c, w) } } @@ -403,7 +466,7 @@ mod tests { } = fixture_jws(); let (payload_b64, protected_b64, signature_b64) = fixture_jws_base64(&payload, &protected, &signature); - let link = Cid::try_from(base64_url::decode(&payload_b64).unwrap()).unwrap(); + let link = Some(Cid::try_from(base64_url::decode(&payload_b64).unwrap()).unwrap()); assert_roundtrip( DagJoseCodec, &JsonWebSignature { @@ -417,6 +480,7 @@ mod tests { signature: signature_b64, }], link, + pld: None, }, &ipld!({ "payload": payload,