Federate user deletion (#551)

* Federate user deletion

- When someone deletes their account
- When a local user is banned

Fixes #509

* cargo fmt
This commit is contained in:
Baptiste Gelez 2019-04-28 18:01:41 +01:00 committed by GitHub
parent 85aa0883c8
commit 787eb7f399
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 157 additions and 72 deletions

26
Cargo.lock generated
View File

@ -1777,7 +1777,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "plume" name = "plume"
version = "0.2.0" version = "0.3.0"
dependencies = [ dependencies = [
"activitypub 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "activitypub 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"askama_escape 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "askama_escape 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1797,9 +1797,9 @@ dependencies = [
"lettre_email 0.9.0 (git+https://github.com/lettre/lettre?rev=c988b1760ad8179d9e7f3fb8594d2b86cf2a0a49)", "lettre_email 0.9.0 (git+https://github.com/lettre/lettre?rev=c988b1760ad8179d9e7f3fb8594d2b86cf2a0a49)",
"multipart 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)", "multipart 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"plume-api 0.2.0", "plume-api 0.3.0",
"plume-common 0.2.0", "plume-common 0.3.0",
"plume-models 0.2.0", "plume-models 0.3.0",
"rocket 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rocket 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rocket_contrib 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rocket_contrib 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rocket_csrf 0.1.0 (git+https://github.com/fdb-hiroshima/rocket_csrf?rev=4a72ea2ec716cb0b26188fb00bccf2ef7d1e031c)", "rocket_csrf 0.1.0 (git+https://github.com/fdb-hiroshima/rocket_csrf?rev=4a72ea2ec716cb0b26188fb00bccf2ef7d1e031c)",
@ -1819,7 +1819,7 @@ dependencies = [
[[package]] [[package]]
name = "plume-api" name = "plume-api"
version = "0.2.0" version = "0.3.0"
dependencies = [ dependencies = [
"canapi 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "canapi 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1828,18 +1828,18 @@ dependencies = [
[[package]] [[package]]
name = "plume-cli" name = "plume-cli"
version = "0.2.0" version = "0.3.0"
dependencies = [ dependencies = [
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"diesel 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "diesel 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dotenv 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "dotenv 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"plume-models 0.2.0", "plume-models 0.3.0",
"rpassword 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rpassword 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "plume-common" name = "plume-common"
version = "0.2.0" version = "0.3.0"
dependencies = [ dependencies = [
"activitypub 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "activitypub 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"activitystreams-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "activitystreams-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1861,7 +1861,7 @@ dependencies = [
[[package]] [[package]]
name = "plume-front" name = "plume-front"
version = "0.1.0" version = "0.3.0"
dependencies = [ dependencies = [
"gettext 0.3.0 (git+https://github.com/Plume-org/gettext/?rev=294c54d74c699fbc66502b480a37cc66c1daa7f3)", "gettext 0.3.0 (git+https://github.com/Plume-org/gettext/?rev=294c54d74c699fbc66502b480a37cc66c1daa7f3)",
"gettext-macros 0.4.0 (git+https://github.com/Plume-org/gettext-macros/?rev=a7c605f7edd6bfbfbfe7778026bfefd88d82db10)", "gettext-macros 0.4.0 (git+https://github.com/Plume-org/gettext-macros/?rev=a7c605f7edd6bfbfbfe7778026bfefd88d82db10)",
@ -1872,7 +1872,7 @@ dependencies = [
[[package]] [[package]]
name = "plume-models" name = "plume-models"
version = "0.2.0" version = "0.3.0"
dependencies = [ dependencies = [
"activitypub 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "activitypub 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"ammonia 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "ammonia 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1885,10 +1885,10 @@ dependencies = [
"guid-create 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "guid-create 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl 0.10.19 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.10.19 (registry+https://github.com/rust-lang/crates.io-index)",
"plume-api 0.2.0", "plume-api 0.3.0",
"plume-common 0.2.0", "plume-common 0.3.0",
"reqwest 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
"rocket 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rocket 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rocket_i18n 0.4.0 (git+https://github.com/Plume-org/rocket_i18n?rev=e922afa7c366038b3433278c03b1456b346074f2)", "rocket_i18n 0.4.0 (git+https://github.com/Plume-org/rocket_i18n?rev=e922afa7c366038b3433278c03b1456b346074f2)",

View File

@ -53,6 +53,7 @@ pub fn inbox(ctx: &PlumeRocket, act: serde_json::Value) -> Result<InboxResult, E
.with::<User, Create, Post>() .with::<User, Create, Post>()
.with::<User, Delete, Comment>() .with::<User, Delete, Comment>()
.with::<User, Delete, Post>() .with::<User, Delete, Post>()
.with::<User, Delete, User>()
.with::<User, Follow, User>() .with::<User, Follow, User>()
.with::<User, Like, Post>() .with::<User, Like, Post>()
.with::<User, Undo, Reshare>() .with::<User, Undo, Reshare>()
@ -286,6 +287,34 @@ pub(crate) mod tests {
}); });
} }
#[test]
fn delete_user() {
let r = rockets();
let conn = &*r.conn;
conn.test_transaction::<_, (), _>(|| {
let (_, users, _) = fill_database(&r);
let fail_act = json!({
"id": "https://plu.me/@/Admin#delete",
"actor": users[1].ap_url, // Not the same account
"object": users[0].ap_url,
"type": "Delete",
});
assert!(super::inbox(&r, fail_act).is_err());
let ok_act = json!({
"id": "https://plu.me/@/Admin#delete",
"actor": users[0].ap_url,
"object": users[0].ap_url,
"type": "Delete",
});
assert!(super::inbox(&r, ok_act).is_ok());
assert!(crate::users::User::get(conn, users[0].id).is_err());
Ok(())
});
}
#[test] #[test]
fn follow() { fn follow() {
let r = rockets(); let r = rockets();

View File

@ -1,5 +1,9 @@
use activitypub::{ use activitypub::{
actor::Person, collection::OrderedCollection, object::Image, Activity, CustomObject, Endpoint, activity::Delete,
actor::Person,
collection::OrderedCollection,
object::{Image, Tombstone},
Activity, CustomObject, Endpoint,
}; };
use bcrypt; use bcrypt;
use chrono::{NaiveDateTime, Utc}; use chrono::{NaiveDateTime, Utc};
@ -12,9 +16,9 @@ use openssl::{
}; };
use plume_common::activity_pub::{ use plume_common::activity_pub::{
ap_accept_header, ap_accept_header,
inbox::{AsActor, FromId}, inbox::{AsActor, AsObject, FromId},
sign::{gen_keypair, Signer}, sign::{gen_keypair, Signer},
ActivityStream, ApSignature, Id, IntoId, PublicKey, ActivityStream, ApSignature, Id, IntoId, PublicKey, PUBLIC_VISIBILITY,
}; };
use plume_common::utils; use plume_common::utils;
use reqwest::{ use reqwest::{
@ -632,6 +636,29 @@ impl User {
Ok(CustomPerson::new(actor, ap_signature)) Ok(CustomPerson::new(actor, ap_signature))
} }
pub fn delete_activity(&self, conn: &Connection) -> Result<Delete> {
let mut del = Delete::default();
let mut tombstone = Tombstone::default();
tombstone.object_props.set_id_string(self.ap_url.clone())?;
del.delete_props
.set_actor_link(Id::new(self.ap_url.clone()))?;
del.delete_props.set_object_object(tombstone)?;
del.object_props
.set_id_string(format!("{}#delete", self.ap_url))?;
del.object_props
.set_to_link_vec(vec![Id::new(PUBLIC_VISIBILITY)])?;
del.object_props.set_cc_link_vec(
self.get_followers(conn)?
.into_iter()
.map(|f| Id::new(f.ap_url))
.collect(),
)?;
Ok(del)
}
pub fn avatar_url(&self, conn: &Connection) -> String { pub fn avatar_url(&self, conn: &Connection) -> String {
self.avatar_id self.avatar_id
.and_then(|id| Media::get(conn, id).and_then(|m| m.url(conn)).ok()) .and_then(|id| Media::get(conn, id).and_then(|m| m.url(conn)).ok())
@ -832,6 +859,19 @@ impl AsActor<&PlumeRocket> for User {
} }
} }
impl AsObject<User, Delete, &PlumeRocket> for User {
type Error = Error;
type Output = ();
fn activity(self, c: &PlumeRocket, actor: User, _id: &str) -> Result<()> {
if self.id == actor.id {
self.delete(&c.conn, &c.searcher).map(|_| ())
} else {
Err(Error::Unauthorized)
}
}
}
impl Signer for User { impl Signer for User {
type Error = Error; type Error = Error;

View File

@ -268,7 +268,7 @@ msgstr "اتبع"
msgid "Log in to follow" msgid "Log in to follow"
msgstr "قم بتسجيل الدخول قصد مشاركته" msgstr "قم بتسجيل الدخول قصد مشاركته"
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -265,7 +265,7 @@ msgstr ""
msgid "Log in to follow" msgid "Log in to follow"
msgstr "" msgstr ""
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -265,7 +265,7 @@ msgstr ""
msgid "Log in to follow" msgid "Log in to follow"
msgstr "" msgstr ""
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -264,10 +264,12 @@ msgstr "Beží na Plume {0}"
msgid "Follow {}" msgid "Follow {}"
msgstr "Následovat {}" msgstr "Následovat {}"
#, fuzzy
msgid "Log in to follow" msgid "Log in to follow"
msgstr "Pro následování se přihlášte" msgstr "Pro následování se přihlášte"
msgid "Enter your full username to follow" #, fuzzy
msgid "Enter your full username handle to follow"
msgstr "Pro následovaní zadejte své úplné uživatelské jméno" msgstr "Pro následovaní zadejte své úplné uživatelské jméno"
msgid "Edit your account" msgid "Edit your account"

View File

@ -267,7 +267,7 @@ msgstr "Folgen"
msgid "Log in to follow" msgid "Log in to follow"
msgstr "Um zu boosten, musst du eingeloggt sein" msgstr "Um zu boosten, musst du eingeloggt sein"
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -265,7 +265,7 @@ msgstr ""
msgid "Log in to follow" msgid "Log in to follow"
msgstr "" msgstr ""
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -265,7 +265,7 @@ msgstr ""
msgid "Log in to follow" msgid "Log in to follow"
msgstr "" msgstr ""
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -267,7 +267,7 @@ msgstr "Seguir"
msgid "Log in to follow" msgid "Log in to follow"
msgstr "Dejar de seguir" msgstr "Dejar de seguir"
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -268,7 +268,7 @@ msgstr "Sabonner"
msgid "Log in to follow" msgid "Log in to follow"
msgstr "Connectez-vous pour partager" msgstr "Connectez-vous pour partager"
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -268,7 +268,7 @@ msgstr "Seguir"
msgid "Log in to follow" msgid "Log in to follow"
msgstr "Conéctese para promover" msgstr "Conéctese para promover"
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -267,7 +267,7 @@ msgstr ""
msgid "Log in to follow" msgid "Log in to follow"
msgstr "" msgstr ""
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -266,7 +266,7 @@ msgstr ""
msgid "Log in to follow" msgid "Log in to follow"
msgstr "" msgstr ""
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -267,7 +267,7 @@ msgstr "Segui"
msgid "Log in to follow" msgid "Log in to follow"
msgstr "Accedi per boostare" msgstr "Accedi per boostare"
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -270,7 +270,7 @@ msgstr "フォロー"
msgid "Log in to follow" msgid "Log in to follow"
msgstr "ブーストするにはログインしてください" msgstr "ブーストするにはログインしてください"
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -277,7 +277,7 @@ msgstr "Følg"
msgid "Log in to follow" msgid "Log in to follow"
msgstr "Logg inn" msgstr "Logg inn"
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -267,7 +267,8 @@ msgstr "Obserwuj {}"
msgid "Log in to follow" msgid "Log in to follow"
msgstr "" msgstr ""
msgid "Enter your full username to follow" #, fuzzy
msgid "Enter your full username handle to follow"
msgstr "Wpisz swoją pełną nazwę użytkownika, do naśladowania" msgstr "Wpisz swoją pełną nazwę użytkownika, do naśladowania"
msgid "Edit your account" msgid "Edit your account"

View File

@ -259,7 +259,7 @@ msgstr ""
msgid "Log in to follow" msgid "Log in to follow"
msgstr "" msgstr ""
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -269,7 +269,7 @@ msgstr "Seguir"
msgid "Log in to follow" msgid "Log in to follow"
msgstr "Entrar para gostar" msgstr "Entrar para gostar"
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -266,7 +266,7 @@ msgstr ""
msgid "Log in to follow" msgid "Log in to follow"
msgstr "" msgstr ""
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -269,7 +269,7 @@ msgstr "Подписаться"
msgid "Log in to follow" msgid "Log in to follow"
msgstr "Войдите, чтобы продвигать посты" msgstr "Войдите, чтобы продвигать посты"
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -264,10 +264,12 @@ msgstr "Beží na Plume {0}"
msgid "Follow {}" msgid "Follow {}"
msgstr "Následuj {}" msgstr "Následuj {}"
#, fuzzy
msgid "Log in to follow" msgid "Log in to follow"
msgstr "Pre následovanie sa prihlás" msgstr "Pre následovanie sa prihlás"
msgid "Enter your full username to follow" #, fuzzy
msgid "Enter your full username handle to follow"
msgstr "Zadaj svoju prezývku v úplnosti, aby si následoval/a" msgstr "Zadaj svoju prezývku v úplnosti, aby si následoval/a"
msgid "Edit your account" msgid "Edit your account"

View File

@ -266,7 +266,7 @@ msgstr ""
msgid "Log in to follow" msgid "Log in to follow"
msgstr "" msgstr ""
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -265,7 +265,7 @@ msgstr ""
msgid "Log in to follow" msgid "Log in to follow"
msgstr "" msgstr ""
msgid "Enter your full username to follow" msgid "Enter your full username handle to follow"
msgstr "" msgstr ""
msgid "Edit your account" msgid "Edit your account"

View File

@ -8,14 +8,13 @@ use serde_json;
use validator::{Validate, ValidationErrors}; use validator::{Validate, ValidationErrors};
use inbox; use inbox;
use plume_common::activity_pub::inbox::FromId; use plume_common::activity_pub::{broadcast, inbox::FromId};
use plume_models::{ use plume_models::{
admin::Admin, comments::Comment, db_conn::DbConn, headers::Headers, instance::*, posts::Post, admin::Admin, comments::Comment, db_conn::DbConn, headers::Headers, instance::*, posts::Post,
safe_string::SafeString, users::User, Error, PlumeRocket, CONFIG, safe_string::SafeString, users::User, Error, PlumeRocket, CONFIG,
}; };
use routes::{errors::ErrorPage, rocket_uri_macro_static_files, Page}; use routes::{errors::ErrorPage, rocket_uri_macro_static_files, Page};
use template_utils::Ructe; use template_utils::Ructe;
use Searcher;
#[get("/")] #[get("/")]
pub fn index(conn: DbConn, user: Option<User>, intl: I18n) -> Result<Ructe, ErrorPage> { pub fn index(conn: DbConn, user: Option<User>, intl: I18n) -> Result<Ructe, ErrorPage> {
@ -197,14 +196,20 @@ pub fn admin_users(
} }
#[post("/admin/users/<id>/ban")] #[post("/admin/users/<id>/ban")]
pub fn ban( pub fn ban(_admin: Admin, id: i32, rockets: PlumeRocket) -> Result<Redirect, ErrorPage> {
_admin: Admin, if let Ok(u) = User::get(&*rockets.conn, id) {
conn: DbConn, u.delete(&*rockets.conn, &rockets.searcher)?;
id: i32,
searcher: Searcher, if Instance::get_local(&*rockets.conn)
) -> Result<Redirect, ErrorPage> { .map(|i| u.instance_id == i.id)
if let Ok(u) = User::get(&*conn, id) { .unwrap_or(false)
u.delete(&*conn, &searcher)?; {
let target = User::one_by_instance(&*rockets.conn)?;
let delete_act = u.delete_activity(&*rockets.conn)?;
rockets
.worker
.execute(move || broadcast(&u, delete_act, target));
}
} }
Ok(Redirect::to(uri!(admin_users: page = _))) Ok(Redirect::to(uri!(admin_users: page = _)))
} }

View File

@ -394,6 +394,12 @@ pub fn delete(
if user.id == account.id { if user.id == account.id {
account.delete(&*rockets.conn, &rockets.searcher)?; account.delete(&*rockets.conn, &rockets.searcher)?;
let target = User::one_by_instance(&*rockets.conn)?;
let delete_act = account.delete_activity(&*rockets.conn)?;
rockets
.worker
.execute(move || broadcast(&account, delete_act, target));
if let Some(cookie) = cookies.get_private(AUTH_COOKIE) { if let Some(cookie) = cookies.get_private(AUTH_COOKIE) {
cookies.remove_private(cookie); cookies.remove_private(cookie);
} }