diff options
4 files changed, 114 insertions, 36 deletions
diff --git a/crates/sellershut/src/entity/user.rs b/crates/sellershut/src/entity/user.rs index 1abf50f..b420682 100644 --- a/crates/sellershut/src/entity/user.rs +++ b/crates/sellershut/src/entity/user.rs @@ -213,6 +213,12 @@ pub struct Person { image: Option<Url>, } +impl Person { + pub fn public_id(&self) -> &str { + &self.public_key.id + } +} + #[async_trait] impl Object for User { #[doc = " App data type passed to handlers. Must be identical to"] diff --git a/crates/sellershut/src/server/activities/follow.rs b/crates/sellershut/src/server/activities/follow.rs index f0df0d9..e39b81e 100644 --- a/crates/sellershut/src/server/activities/follow.rs +++ b/crates/sellershut/src/server/activities/follow.rs @@ -118,10 +118,7 @@ mod tests { "http://localhost:{}/activity/follow/1", config.application.port ); - let actor = format!( - "http://localhost/users/{}", - hut_config.instance_name - ); + let actor = format!("http://localhost/users/{}", hut_config.instance_name); let app = server::router(state); diff --git a/crates/sellershut/src/server/middleware/sign_request.rs b/crates/sellershut/src/server/middleware/sign_request.rs index 889984f..4eb3bd3 100644 --- a/crates/sellershut/src/server/middleware/sign_request.rs +++ b/crates/sellershut/src/server/middleware/sign_request.rs @@ -1,20 +1,21 @@ mod signature; -use activitypub_federation::config::FederationConfig; +use activitypub_federation::{config::FederationConfig, traits::Object}; use axum::{ body::Body, extract::Request, - http::HeaderValue, + http::{HeaderValue, StatusCode}, response::Response, }; use futures_util::future::BoxFuture; -use std::{ - task::{Context, Poll}, -}; +use std::task::{Context, Poll}; use tower::{Layer, Service}; +use tracing::trace; -use crate::{server::middleware::sign_request::signature::Signature, state::AppHandle}; - +use crate::{ + server::{middleware::sign_request::signature::Signature, routes::users::get_user::read_user}, + state::AppHandle, +}; #[derive(Clone)] pub struct SignRequestLayer { @@ -62,30 +63,105 @@ where fn call(&mut self, request: Request) -> Self::Future { let mut inner = self.inner.clone(); + let uri = request.uri().clone(); let (parts, body) = request.into_parts(); + let state = self.state.to_request_data(); + let domain = self.state.domain().to_owned(); Box::pin(async move { - let bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap(); - - let signature = Signature::create( - "" - .as_bytes(), - bytes, - ) - .unwrap(); + let ok_500 = || { + let mut response = axum::response::Response::default(); + *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + Ok(response) + }; + let bytes = match axum::body::to_bytes(body, usize::MAX).await { + Ok(b) => b, + Err(e) => { + trace!("could not decode response body: {e:?}"); + return ok_500(); + } + }; + + // Get user + let user = match read_user("sellershut", &state).await { + Ok(Some(u)) => u, + Ok(None) => { + trace!("user not found"); + return ok_500(); + } + Err(e) => { + trace!("failed to read user: {e:?}"); + return ok_500(); + } + }; + + let pk = user.private_key.clone().unwrap_or_default(); + + let json_user = match user.into_json(&state).await { + Ok(j) => j, + Err(e) => { + trace!("failed to serialise user: {e:?}"); + return ok_500(); + } + }; + + // Sign the body + let signature = match Signature::new(pk.as_bytes(), bytes, uri, &domain) { + Ok(sig) => sig, + Err(e) => { + trace!("signature creation failed: {e:?}"); + return ok_500(); + } + }; let mut new_request = Request::from_parts(parts, Body::from(signature.body)); let head = new_request.headers_mut(); - let header = format!( - "keyId=\"http://localhost/users/sellershut#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest\",signature=\"{}\"", - signature.signature, - ); - println!("{header}"); - head.insert("Host", HeaderValue::from_str(&signature.host).unwrap()); - head.insert("Date", HeaderValue::from_str(&signature.date).unwrap()); - head.insert("Digest", HeaderValue::from_str(&signature.digest).unwrap()); - head.insert("Signature", HeaderValue::from_str(&header).unwrap()); + + let header = [ + format!("keyId=\"{}\"", json_user.public_id()), + "algorithm=rsa-sha256".to_string(), + "headers=\"(request-target) host date digest\"".to_string(), + format!("signature=\"{}\"", signature.signature), + ] + .join(","); + + let host = match HeaderValue::from_str(&signature.host) { + Ok(value) => value, + Err(e) => { + trace!(name = "host",value = ?signature.host, "header creation failed: {e:?}"); + return ok_500(); + } + }; + + let date = match HeaderValue::from_str(&signature.date) { + Ok(value) => value, + Err(e) => { + trace!(name = "date",value = ?signature.date, "header creation failed: {e:?}"); + return ok_500(); + } + }; + + let digest = match HeaderValue::from_str(&signature.digest) { + Ok(value) => value, + Err(e) => { + trace!(name = "digest",value = ?signature.digest, "header creation failed: {e:?}"); + return ok_500(); + } + }; + + let signature = match HeaderValue::from_str(&header) { + Ok(value) => value, + Err(e) => { + trace!(name = "signature", "header creation failed: {e:?}"); + return ok_500(); + } + }; + + head.insert("Host", host); + head.insert("Date", date); + head.insert("Digest", digest); + head.insert("Signature", signature); inner.call(new_request).await }) diff --git a/crates/sellershut/src/server/middleware/sign_request/signature.rs b/crates/sellershut/src/server/middleware/sign_request/signature.rs index 18e62c0..06a1037 100644 --- a/crates/sellershut/src/server/middleware/sign_request/signature.rs +++ b/crates/sellershut/src/server/middleware/sign_request/signature.rs @@ -1,4 +1,4 @@ -use axum::body::Bytes; +use axum::{body::Bytes, http::Uri}; use base64::{Engine, engine::general_purpose}; use openssl::{pkey::PKey, rsa::Rsa, sign::Signer}; use sha2::{Digest, Sha256}; @@ -13,11 +13,11 @@ pub struct Signature { pub date: String, pub digest: String, pub signature: String, - pub body: Bytes + pub body: Bytes, } impl Signature { - pub fn create(key: &[u8], body: Bytes) -> Result<Self, AppError> { + pub fn new(key: &[u8], body: Bytes, uri: Uri, domain: &str) -> Result<Self, AppError> { let mut hasher = Sha256::new(); hasher.update(&body); @@ -26,12 +26,11 @@ impl Signature { let digest_hash = general_purpose::STANDARD.encode(result); let digest = format!("SHA-256={digest_hash}"); - let inbox_path = "/users/sellershut/inbox"; let now = OffsetDateTime::now_utc().format(&Rfc2822)?; let signing_string = [ - format!("(request-target): post {inbox_path}"), - "host: localhost:2210".to_owned(), + format!("(request-target): post {uri}"), + format!("host: {domain}"), format!("date: {now}"), format!("digest: {digest}"), ] @@ -48,8 +47,8 @@ impl Signature { let signature = signer.sign_to_vec()?; let result = general_purpose::STANDARD.encode(signature); - Ok(Self{ - host: "localhost:2210".to_string(), + Ok(Self { + host: domain.to_owned(), date: now, digest, signature: result, |