Support blind key rotation (#399)
* Allow receiving objects with new unknown key * Rotate key after sending Delete activity * Do the right check
This commit is contained in:
committed by
Igor Galić
parent
aa72334dc6
commit
c4a4ea5b6c
@@ -7,6 +7,8 @@ use rocket_i18n::I18n;
|
||||
use validator::Validate;
|
||||
use template_utils::Ructe;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use plume_common::{utils, activity_pub::{broadcast, ApRequest,
|
||||
ActivityStream, inbox::Deletable}};
|
||||
use plume_models::{
|
||||
@@ -104,7 +106,9 @@ pub fn delete(blog: String, slug: String, id: i32, user: User, conn: DbConn, wor
|
||||
if comment.author_id == user.id {
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let delete_activity = comment.delete(&*conn)?;
|
||||
worker.execute(move || broadcast(&user, delete_activity, dest));
|
||||
let user_c = user.clone();
|
||||
worker.execute(move || broadcast(&user_c, delete_activity, dest));
|
||||
worker.execute_after(Duration::from_secs(10*60), move || {user.rotate_keypair(&conn).expect("Failed to rotate keypair");});
|
||||
}
|
||||
}
|
||||
Ok(Redirect::to(uri!(super::posts::details: blog = blog, slug = slug, responding_to = _)))
|
||||
|
||||
+14
-3
@@ -10,6 +10,7 @@ use plume_models::{
|
||||
admin::Admin,
|
||||
comments::Comment,
|
||||
db_conn::DbConn,
|
||||
Error,
|
||||
headers::Headers,
|
||||
posts::Post,
|
||||
users::User,
|
||||
@@ -180,16 +181,26 @@ pub fn ban(_admin: Admin, conn: DbConn, id: i32, searcher: Searcher) -> Result<R
|
||||
#[post("/inbox", data = "<data>")]
|
||||
pub fn shared_inbox(conn: DbConn, data: SignedJson<serde_json::Value>, headers: Headers, searcher: Searcher) -> Result<String, status::BadRequest<&'static str>> {
|
||||
let act = data.1.into_inner();
|
||||
let sig = data.0;
|
||||
|
||||
let activity = act.clone();
|
||||
let actor_id = activity["actor"].as_str()
|
||||
.or_else(|| activity["actor"]["id"].as_str()).ok_or(status::BadRequest(Some("Missing actor id for activity")))?;
|
||||
|
||||
let actor = User::from_url(&conn, actor_id).expect("instance::shared_inbox: user error");
|
||||
if !verify_http_headers(&actor, &headers.0, &data.0).is_secure() &&
|
||||
if !verify_http_headers(&actor, &headers.0, &sig).is_secure() &&
|
||||
!act.clone().verify(&actor) {
|
||||
println!("Rejected invalid activity supposedly from {}, with headers {:?}", actor.username, headers.0);
|
||||
return Err(status::BadRequest(Some("Invalid signature")));
|
||||
// maybe we just know an old key?
|
||||
actor.refetch(&conn).and_then(|_| User::get(&conn, actor.id))
|
||||
.and_then(|u| if verify_http_headers(&u, &headers.0, &sig).is_secure() ||
|
||||
act.clone().verify(&u) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::Signature)
|
||||
})
|
||||
.map_err(|_| {
|
||||
println!("Rejected invalid activity supposedly from {}, with headers {:?}", actor.username, headers.0);
|
||||
status::BadRequest(Some("Invalid signature"))})?;
|
||||
}
|
||||
|
||||
if Instance::is_blocked(&*conn, actor_id).map_err(|_| status::BadRequest(Some("Can't tell if instance is blocked")))? {
|
||||
|
||||
+7
-2
@@ -3,7 +3,10 @@ use heck::{CamelCase, KebabCase};
|
||||
use rocket::request::LenientForm;
|
||||
use rocket::response::{Redirect, Flash};
|
||||
use rocket_i18n::I18n;
|
||||
use std::{collections::{HashMap, HashSet}, borrow::Cow};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
borrow::Cow, time::Duration,
|
||||
};
|
||||
use validator::{Validate, ValidationError, ValidationErrors};
|
||||
|
||||
use plume_common::activity_pub::{broadcast, ActivityStream, ApRequest, inbox::Deletable};
|
||||
@@ -397,7 +400,9 @@ pub fn delete(blog_name: String, slug: String, conn: DbConn, user: User, worker:
|
||||
} else {
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let delete_activity = post.delete(&(&conn, &searcher))?;
|
||||
worker.execute(move || broadcast(&user, delete_activity, dest));
|
||||
let user_c = user.clone();
|
||||
worker.execute(move || broadcast(&user_c, delete_activity, dest));
|
||||
worker.execute_after(Duration::from_secs(10*60), move || {user.rotate_keypair(&conn).expect("Failed to rotate keypair");});
|
||||
|
||||
Ok(Redirect::to(uri!(super::blogs::details: name = blog_name, page = _)))
|
||||
}
|
||||
|
||||
+14
-6
@@ -370,6 +370,7 @@ pub fn inbox(
|
||||
) -> Result<String, Option<status::BadRequest<&'static str>>> {
|
||||
let user = User::find_local(&*conn, &name).map_err(|_| None)?;
|
||||
let act = data.1.into_inner();
|
||||
let sig = data.0;
|
||||
|
||||
let activity = act.clone();
|
||||
let actor_id = activity["actor"]
|
||||
@@ -380,14 +381,21 @@ pub fn inbox(
|
||||
))))?;
|
||||
|
||||
let actor = User::from_url(&conn, actor_id).expect("user::inbox: user error");
|
||||
if !verify_http_headers(&actor, &headers.0, &data.0).is_secure()
|
||||
if !verify_http_headers(&actor, &headers.0, &sig).is_secure()
|
||||
&& !act.clone().verify(&actor)
|
||||
{
|
||||
println!(
|
||||
"Rejected invalid activity supposedly from {}, with headers {:?}",
|
||||
actor.username, headers.0
|
||||
);
|
||||
return Err(Some(status::BadRequest(Some("Invalid signature"))));
|
||||
// maybe we just know an old key?
|
||||
actor.refetch(&conn).and_then(|_| User::get(&conn, actor.id))
|
||||
.and_then(|actor| if verify_http_headers(&actor, &headers.0, &sig).is_secure()
|
||||
|| act.clone().verify(&actor)
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::Signature)
|
||||
})
|
||||
.map_err(|_| {
|
||||
println!("Rejected invalid activity supposedly from {}, with headers {:?}", actor.username, headers.0);
|
||||
status::BadRequest(Some("Invalid signature"))})?;
|
||||
}
|
||||
|
||||
if Instance::is_blocked(&*conn, actor_id).map_err(|_| None)? {
|
||||
|
||||
Reference in New Issue
Block a user