Skip to content
This repository has been archived by the owner on Sep 21, 2024. It is now read-only.

Commit

Permalink
Implement orb sync subcommand (#107)
Browse files Browse the repository at this point in the history
* Implement `orb sync` subcommand

* Add TODO issues

* Remove vestigial debugging
  • Loading branch information
cdata committed Oct 24, 2022
1 parent 09b4cda commit 81e9618
Show file tree
Hide file tree
Showing 53 changed files with 1,597 additions and 545 deletions.
11 changes: 1 addition & 10 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[workspace]
members = [
"noosphere-cbor",
"noosphere-storage",
"noosphere-collections",
"noosphere",
Expand Down
1 change: 1 addition & 0 deletions rust/noosphere-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ anyhow = "^1"
cid = "~0.8"
url = "^2"
serde = "^1"
serde_urlencoded = "~0.7"
tracing = "~0.1"
noosphere = { path = "../noosphere" }
noosphere-storage = { path = "../noosphere-storage" }
Expand Down
122 changes: 91 additions & 31 deletions rust/noosphere-api/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use crate::{
use anyhow::Result;
use cid::Cid;
use libipld_cbor::DagCborCodec;
use noosphere::authority::{SphereAction, SphereReference};

use noosphere::authority::{Authorization, SphereAction, SphereReference};
use noosphere_storage::encoding::{block_deserialize, block_serialize};
use reqwest::{header::HeaderMap, Body};
use ucan::{
Expand All @@ -29,7 +30,7 @@ where
pub sphere_identity: String,
pub api_base: Url,
pub credential: &'a K,
pub authorization: Ucan,
pub authorization: Authorization,
pub store: S,
client: reqwest::Client,
}
Expand All @@ -43,23 +44,44 @@ where
sphere_identity: &str,
api_base: &Url,
credential: &'a K,
authorization: Ucan,
authorization: &Authorization,
did_parser: &mut DidParser,
store: S,
) -> Result<Client<'a, K, S>> {
debug!("Initializing Noosphere API client");
debug!("Client represents sphere {}", sphere_identity);
debug!("Client targetting API at {}", api_base);

let client = reqwest::Client::new();

let mut url = api_base.clone();
url.set_path(&Route::Did.to_string());

let gateway_identity = client.get(url).send().await?.text().await?;

let mut url = api_base.clone();
url.set_path(&Route::Identify.to_string());

let client = reqwest::Client::new();
let (jwt, ucan_headers) = Self::make_bearer_token(
&gateway_identity,
credential,
authorization,
&Capability {
with: With::Resource {
kind: Resource::Scoped(SphereReference {
did: sphere_identity.to_string(),
}),
},
can: SphereAction::Fetch,
},
&store,
)
.await?;

let identify_response: IdentifyResponse = client
.get(url)
.bearer_auth(authorization.encode()?)
.bearer_auth(jwt)
.headers(ucan_headers)
.send()
.await?
.json()
Expand All @@ -77,49 +99,71 @@ where
sphere_identity: sphere_identity.into(),
api_base: api_base.clone(),
credential,
authorization,
authorization: authorization.clone(),
store,
client,
})
}

async fn make_bearer_token(
&self,
gateway_identity: &str,
credential: &'a K,
authorization: &Authorization,
capability: &Capability<SphereReference, SphereAction>,
store: &S,
) -> Result<(String, HeaderMap)> {
let ucan = UcanBuilder::default()
.issued_by(self.credential)
.for_audience(&self.session.sphere_identity)
let mut signable = UcanBuilder::default()
.issued_by(credential)
.for_audience(gateway_identity)
.with_lifetime(120)
.claiming_capability(capability)
.witnessed_by(&self.authorization)
.with_nonce()
.build()?
.sign()
.await?;
.build()?;

// TODO: We should integrate a helper for this kind of stuff into rs-ucan
let mut proofs_to_search: Vec<String> = ucan.proofs().clone();
let mut ucan_headers = HeaderMap::new();

debug!("Making bearer token... {:?}", proofs_to_search);
while let Some(cid_string) = proofs_to_search.pop() {
let cid = Cid::from_str(cid_string.as_str())?;
let jwt = self.store.require_token(&cid).await?;
let ucan = Ucan::try_from_token_string(&jwt)?;
let authorization_cid = Cid::try_from(authorization)?;

match authorization.resolve_ucan(store).await {
Ok(ucan) => {
// TODO(ucan-wg/rs-ucan#37): We should integrate a helper for this kind of stuff into rs-ucan
let mut proofs_to_search: Vec<String> = ucan.proofs().clone();

debug!("Making bearer token... {:?}", proofs_to_search);
while let Some(cid_string) = proofs_to_search.pop() {
let cid = Cid::from_str(cid_string.as_str())?;
let jwt = store.require_token(&cid).await?;
let ucan = Ucan::try_from_token_string(&jwt)?;

debug!("Adding UCAN header for {}", cid);

proofs_to_search.extend(ucan.proofs().clone().into_iter());
ucan_headers.append("ucan", format!("{} {}", cid, jwt).parse()?);
}

ucan_headers.append(
"ucan",
format!("{} {}", authorization_cid, ucan.encode()?).parse()?,
);
}
_ => {
warn!("Unable to resolve authorization to a UCAN; it will be used as a blind proof")
}
};

debug!("Adding UCAN header for {}", cid);
// TODO(ucan-wg/rs-ucan#32): This is kind of a hack until we can add proofs by CID
signable
.proofs
.push(Cid::try_from(authorization)?.to_string());

proofs_to_search.extend(ucan.proofs().clone().into_iter());
ucan_headers.append("ucan", format!("{} {}", cid, jwt).parse()?);
}
let jwt = signable.sign().await?.encode()?;

// TODO: It is inefficient to send the same UCANs with every request,
// we should probably establish a conventional flow for syncing UCANs
// this way only once when pairing a gateway. For now, this is about the
// same efficiency as what we had before when UCANs were all inlined to
// a single token.
Ok((ucan.encode()?, ucan_headers))
Ok((jwt, ucan_headers))
}

pub async fn fetch(&self, params: &FetchParameters) -> Result<FetchResponse> {
Expand All @@ -134,7 +178,14 @@ where
can: SphereAction::Fetch,
};

let (token, ucan_headers) = self.make_bearer_token(&capability).await?;
let (token, ucan_headers) = Self::make_bearer_token(
&self.session.gateway_identity,
self.credential,
&self.authorization,
&capability,
&self.store,
)
.await?;

let bytes = self
.client
Expand Down Expand Up @@ -166,11 +217,18 @@ where
can: SphereAction::Push,
};

let (token, ucan_headers) = self.make_bearer_token(&capability).await?;
let (token, ucan_headers) = Self::make_bearer_token(
&self.session.gateway_identity,
self.credential,
&self.authorization,
&capability,
&self.store,
)
.await?;

let (_, push_body_bytes) = block_serialize::<DagCborCodec, _>(push_body)?;

Ok(self
let bytes = self
.client
.put(url)
.bearer_auth(token)
Expand All @@ -179,7 +237,9 @@ where
.body(Body::from(push_body_bytes))
.send()
.await?
.json()
.await?)
.bytes()
.await?;

block_deserialize::<DagCborCodec, _>(bytes.as_ref())
}
}
Loading

0 comments on commit 81e9618

Please sign in to comment.