fetch: add authorized_fetch()

This commit is contained in:
Astro 2023-05-14 22:44:26 +02:00
parent c0601f8eef
commit e8b2b807b2
4 changed files with 63 additions and 15 deletions

20
src/digest.rs Normal file
View file

@ -0,0 +1,20 @@
use http_digest_headers::{DigestHeader, DigestMethod};
pub fn generate_header(body: &[u8]) -> Result<String, ()> {
let mut digest_header = DigestHeader::new()
.with_method(DigestMethod::SHA256, body)
.map(|h| format!("{}", h))
.map_err(|_| ())?;
// mastodon expects uppercase algo name
if digest_header.starts_with("sha-") {
digest_header.replace_range(..4, "SHA-");
}
// mastodon uses base64::alphabet::STANDARD, not base64::alphabet::URL_SAFE
digest_header.replace_range(
7..,
&digest_header[7..].replace('-', "+").replace('_', "/")
);
Ok(digest_header)
}

View file

@ -1,4 +1,8 @@
use http::StatusCode;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use sigh::{PrivateKey, SigningConfig, alg::RsaSha256};
use crate::digest;
use crate::send::SendError;
pub async fn fetch<T>(client: &reqwest::Client, url: &str) -> Result<T, reqwest::Error> pub async fn fetch<T>(client: &reqwest::Client, url: &str) -> Result<T, reqwest::Error>
where where
@ -11,3 +15,38 @@ where
.json() .json()
.await .await
} }
pub async fn authorized_fetch<T>(
client: &reqwest::Client,
uri: &str,
key_id: &str,
private_key: &PrivateKey,
) -> Result<T, SendError>
where
T: DeserializeOwned,
{
let url = reqwest::Url::parse(uri)
.map_err(|_| SendError::InvalidUri)?;
let host = format!("{}", url.host().ok_or(SendError::InvalidUri)?);
let digest_header = digest::generate_header(&[])
.expect("digest::generate_header");
let mut req = http::Request::builder()
.uri(uri)
.header("host", &host)
.header("content-type", "application/activity+json")
.header("date", chrono::Utc::now().to_rfc2822()
.replace("+0000", "GMT"))
.header("accept", "application/activity+json")
.header("digest", digest_header)
.body(vec![])?;
SigningConfig::new(RsaSha256, private_key, key_id)
.sign(&mut req)?;
let req: reqwest::Request = req.try_into()?;
let res = client.execute(req)
.await?;
if res.status() >= StatusCode::OK && res.status() < StatusCode::MULTIPLE_CHOICES {
Ok(res.json().await?)
} else {
Err(SendError::Response(format!("{}", res.text().await?)))
}
}

View file

@ -17,8 +17,8 @@ use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
mod config; mod config;
mod actor; mod actor;
mod db; mod db;
mod digest;
mod fetch; mod fetch;
pub use fetch::fetch;
mod send; mod send;
mod stream; mod stream;
mod relay; mod relay;

View file

@ -47,22 +47,11 @@ pub async fn send_raw(
private_key: &PrivateKey, private_key: &PrivateKey,
body: Arc<Vec<u8>>, body: Arc<Vec<u8>>,
) -> Result<(), SendError> { ) -> Result<(), SendError> {
let mut digest_header = DigestHeader::new()
.with_method(DigestMethod::SHA256, &body)
.map(|h| format!("{}", h))
.map_err(|_| SendError::Digest)?;
if digest_header.starts_with("sha-") {
digest_header.replace_range(..4, "SHA-");
}
// mastodon uses base64::alphabet::STANDARD, not base64::alphabet::URL_SAFE
digest_header.replace_range(
7..,
&digest_header[7..].replace('-', "+").replace('_', "/")
);
let url = reqwest::Url::parse(uri) let url = reqwest::Url::parse(uri)
.map_err(|_| SendError::InvalidUri)?; .map_err(|_| Error::InvalidUri)?;
let host = format!("{}", url.host().ok_or(SendError::InvalidUri)?); let host = format!("{}", url.host().ok_or(SendError::InvalidUri)?);
let digest_header = digest::generate_header(&body)
.map_err(|()| SendError::Digest)?;
let mut req = http::Request::builder() let mut req = http::Request::builder()
.method("POST") .method("POST")
.uri(uri) .uri(uri)