Refactor and verify http signature on personnal inbox

Verify signature on personnal inbox
Reduce code duplication
Put Headers in plume-models
This commit is contained in:
Trinity Pointard 2018-10-03 20:48:25 +02:00
parent 0a5d435249
commit 62c94ed463
5 changed files with 42 additions and 29 deletions

View File

@ -128,6 +128,15 @@ pub enum SignatureValidity {
Absent, Absent,
} }
impl SignatureValidity {
pub fn is_secure(&self) -> bool {
match self {
SignatureValidity::Valid => true,
_ => false,
}
}
}
pub fn verify_http_headers<S: sign::Signer+::std::fmt::Debug>(sender: &S, all_headers: HeaderMap, data: String) -> SignatureValidity{ pub fn verify_http_headers<S: sign::Signer+::std::fmt::Debug>(sender: &S, all_headers: HeaderMap, data: String) -> SignatureValidity{
if let Some(sig_header) = all_headers.get_one("Signature") { if let Some(sig_header) = all_headers.get_one("Signature") {
let mut _key_id = None; let mut _key_id = None;

View File

@ -0,0 +1,17 @@
use rocket::request::{self, FromRequest, Request};
use rocket::{http::HeaderMap, Outcome};
pub struct Headers<'r>(pub HeaderMap<'r>);
impl<'a, 'r> FromRequest<'a, 'r> for Headers<'r> {
type Error = ();
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, ()> {
let mut headers = HeaderMap::new();
for header in request.headers().clone().into_iter() {
headers.add(header);
}
Outcome::Success(Headers(headers))
}
}

View File

@ -110,6 +110,7 @@ pub mod blogs;
pub mod comments; pub mod comments;
pub mod db_conn; pub mod db_conn;
pub mod follows; pub mod follows;
pub mod headers;
pub mod instance; pub mod instance;
pub mod likes; pub mod likes;
pub mod medias; pub mod medias;

View File

@ -1,16 +1,15 @@
use gettextrs::gettext; use gettextrs::gettext;
use rocket::{http::HeaderMap, Outcome, use rocket::{request::LenientForm, response::Redirect};
request::{self, FromRequest, LenientForm, Request},
response::Redirect};
use rocket_contrib::{Json, Template}; use rocket_contrib::{Json, Template};
use serde_json; use serde_json;
use validator::{Validate}; use validator::{Validate};
use plume_common::activity_pub::{verify_http_headers, SignatureValidity}; use plume_common::activity_pub::verify_http_headers;
use plume_models::{ use plume_models::{
admin::Admin, admin::Admin,
comments::Comment, comments::Comment,
db_conn::DbConn, db_conn::DbConn,
headers::Headers,
posts::Post, posts::Post,
users::User, users::User,
safe_string::SafeString, safe_string::SafeString,
@ -191,22 +190,6 @@ fn ban(_admin: Admin, conn: DbConn, id: i32) -> Redirect {
Redirect::to(uri!(admin_users)) Redirect::to(uri!(admin_users))
} }
struct Headers<'r> {
headers: HeaderMap<'r>,
}
impl<'a, 'r> FromRequest<'a, 'r> for Headers<'r> {
type Error = ();
fn from_request(request: &'a Request<'r>) ->request::Outcome<Self, ()> {
let mut headers = HeaderMap::new();
for header in request.headers().clone().into_iter() {
headers.add(header);
}
Outcome::Success(Headers{headers})
}
}
#[post("/inbox", data = "<data>")] #[post("/inbox", data = "<data>")]
fn shared_inbox(conn: DbConn, data: String, headers: Headers) -> String { fn shared_inbox(conn: DbConn, data: String, headers: Headers) -> String {
let act: serde_json::Value = serde_json::from_str(&data[..]).unwrap(); let act: serde_json::Value = serde_json::from_str(&data[..]).unwrap();
@ -215,14 +198,9 @@ fn shared_inbox(conn: DbConn, data: String, headers: Headers) -> String {
let actor_id = activity["actor"].as_str() let actor_id = activity["actor"].as_str()
.unwrap_or_else(|| activity["actor"]["id"].as_str().expect("No actor ID for incoming activity, blocks by panicking")); .unwrap_or_else(|| activity["actor"]["id"].as_str().expect("No actor ID for incoming activity, blocks by panicking"));
let sig = match verify_http_headers(&User::from_url(&conn, actor_id.to_owned()).unwrap(), headers.headers, data) { let sig = verify_http_headers(&User::from_url(&conn, actor_id.to_owned()).unwrap(), headers.0, data).is_secure();
SignatureValidity::Valid => true,
_ => {
// TODO verify json signature
false
}
};
if !sig { if !sig {
// TODO check for valid json-ld signature
return "invalid signature".to_owned(); return "invalid signature".to_owned();
} }

View File

@ -16,13 +16,14 @@ use workerpool::thunk::*;
use plume_common::activity_pub::{ use plume_common::activity_pub::{
ActivityStream, broadcast, Id, IntoId, ApRequest, ActivityStream, broadcast, Id, IntoId, ApRequest,
inbox::{FromActivity, Notify, Deletable} verify_http_headers, inbox::{FromActivity, Notify, Deletable}
}; };
use plume_common::utils; use plume_common::utils;
use plume_models::{ use plume_models::{
blogs::Blog, blogs::Blog,
db_conn::DbConn, db_conn::DbConn,
follows, follows,
headers::Headers,
instance::Instance, instance::Instance,
posts::Post, posts::Post,
reshares::Reshare, reshares::Reshare,
@ -295,13 +296,20 @@ fn outbox(name: String, conn: DbConn) -> ActivityStream<OrderedCollection> {
} }
#[post("/@/<name>/inbox", data = "<data>")] #[post("/@/<name>/inbox", data = "<data>")]
fn inbox(name: String, conn: DbConn, data: String) -> String { fn inbox(name: String, conn: DbConn, data: String, headers: Headers) -> String {
let user = User::find_local(&*conn, name).unwrap(); let user = User::find_local(&*conn, name).unwrap();
let act: serde_json::Value = serde_json::from_str(&data[..]).unwrap(); let act: serde_json::Value = serde_json::from_str(&data[..]).unwrap();
let activity = act.clone(); let activity = act.clone();
let actor_id = activity["actor"].as_str() let actor_id = activity["actor"].as_str()
.unwrap_or_else(|| activity["actor"]["id"].as_str().expect("User: No actor ID for incoming activity, blocks by panicking")); .unwrap_or_else(|| activity["actor"]["id"].as_str().expect("User: No actor ID for incoming activity, blocks by panicking"));
let sig = verify_http_headers(&User::from_url(&conn, actor_id.to_owned()).unwrap(), headers.0, data).is_secure();
if !sig {
// TODO check for json-ld signature
return "invalid signature".to_owned();
}
if Instance::is_blocked(&*conn, actor_id.to_string()) { if Instance::is_blocked(&*conn, actor_id.to_string()) {
return String::new(); return String::new();
} }