Merge pull request 'Fix #1121: Check email block list when email sign-up' (#1122) from block-on-email-signup into main

Reviewed-on: https://git.joinplu.me/Plume/Plume/pulls/1122
This commit is contained in:
KitaitiMakoto 2023-01-03 09:11:20 +00:00
commit 85c1bfa300
7 changed files with 120 additions and 44 deletions

View file

@ -126,11 +126,9 @@ pub(crate) mod tests {
.id, .id,
various[1].id various[1].id
); );
assert!( assert!(BlocklistedEmail::matches_blocklist(&conn, no_match)
BlocklistedEmail::matches_blocklist(&conn, no_match)
.unwrap() .unwrap()
.is_none() .is_none());
);
Ok(()) Ok(())
}); });
} }

View file

@ -1,4 +1,5 @@
use crate::{ use crate::{
blocklisted_emails::BlocklistedEmail,
db_conn::DbConn, db_conn::DbConn,
schema::email_signups, schema::email_signups,
users::{NewUser, Role, User}, users::{NewUser, Role, User},
@ -60,6 +61,8 @@ pub struct NewEmailSignup<'a> {
impl EmailSignup { impl EmailSignup {
pub fn start(conn: &DbConn, email: &str) -> Result<Token> { pub fn start(conn: &DbConn, email: &str) -> Result<Token> {
Self::ensure_email_not_blocked(conn, email)?;
conn.transaction(|| { conn.transaction(|| {
Self::ensure_user_not_exist_by_email(conn, email)?; Self::ensure_user_not_exist_by_email(conn, email)?;
let _rows = Self::delete_existings_by_email(conn, email)?; let _rows = Self::delete_existings_by_email(conn, email)?;
@ -90,6 +93,8 @@ impl EmailSignup {
} }
pub fn confirm(&self, conn: &DbConn) -> Result<()> { pub fn confirm(&self, conn: &DbConn) -> Result<()> {
Self::ensure_email_not_blocked(conn, &self.email)?;
conn.transaction(|| { conn.transaction(|| {
Self::ensure_user_not_exist_by_email(conn, &self.email)?; Self::ensure_user_not_exist_by_email(conn, &self.email)?;
if self.expired() { if self.expired() {
@ -101,6 +106,8 @@ impl EmailSignup {
} }
pub fn complete(&self, conn: &DbConn, username: String, password: String) -> Result<User> { pub fn complete(&self, conn: &DbConn, username: String, password: String) -> Result<User> {
Self::ensure_email_not_blocked(conn, &self.email)?;
conn.transaction(|| { conn.transaction(|| {
Self::ensure_user_not_exist_by_email(conn, &self.email)?; Self::ensure_user_not_exist_by_email(conn, &self.email)?;
let user = NewUser::new_local( let user = NewUser::new_local(
@ -122,6 +129,14 @@ impl EmailSignup {
Ok(()) Ok(())
} }
fn ensure_email_not_blocked(conn: &DbConn, email: &str) -> Result<()> {
if let Some(x) = BlocklistedEmail::matches_blocklist(conn, email)? {
Err(Error::Blocklisted(x.notify_user, x.notification_text))
} else {
Ok(())
}
}
fn ensure_user_not_exist_by_email(conn: &DbConn, email: &str) -> Result<()> { fn ensure_user_not_exist_by_email(conn: &DbConn, email: &str) -> Result<()> {
if User::email_used(conn, email)? { if User::email_used(conn, email)? {
let _rows = Self::delete_existings_by_email(conn, email)?; let _rows = Self::delete_existings_by_email(conn, email)?;

View file

@ -107,12 +107,7 @@ impl Follow {
res.notify(conn)?; res.notify(conn)?;
let accept = res.build_accept(from, target, follow)?; let accept = res.build_accept(from, target, follow)?;
broadcast( broadcast(target, accept, vec![from.clone()], CONFIG.proxy().cloned());
target,
accept,
vec![from.clone()],
CONFIG.proxy().cloned(),
);
Ok(res) Ok(res)
} }

View file

@ -133,7 +133,8 @@ impl Post {
.filter(posts::id.eq_any(ids)) .filter(posts::id.eq_any(ids))
.filter(posts::published.eq(true)) .filter(posts::published.eq(true))
.count() .count()
.load(conn)?.first() .load(conn)?
.first()
.cloned() .cloned()
.ok_or(Error::NotFound) .ok_or(Error::NotFound)
} }

View file

@ -291,13 +291,7 @@ mod tests {
"all".to_owned(), "all".to_owned(),
) )
.unwrap(); .unwrap();
List::new( List::new(conn, "languages I speak", Some(&users[1]), ListType::Prefix).unwrap();
conn,
"languages I speak",
Some(&users[1]),
ListType::Prefix,
)
.unwrap();
let tl2_u1 = Timeline::new_for_user( let tl2_u1 = Timeline::new_for_user(
conn, conn,
users[0].id, users[0].id,
@ -337,18 +331,14 @@ mod tests {
let tl_instance = Timeline::list_for_user(conn, None).unwrap(); let tl_instance = Timeline::list_for_user(conn, None).unwrap();
assert_eq!(3, tl_instance.len()); // there are also the local and federated feed by default assert_eq!(3, tl_instance.len()); // there are also the local and federated feed by default
assert!(tl_instance assert!(tl_instance.iter().any(|tl| *tl == tl1_instance));
.iter()
.any(|tl| *tl == tl1_instance));
tl1_u1.name = "My Super TL".to_owned(); tl1_u1.name = "My Super TL".to_owned();
let new_tl1_u2 = tl1_u2.update(conn).unwrap(); let new_tl1_u2 = tl1_u2.update(conn).unwrap();
let tl_u2 = Timeline::list_for_user(conn, Some(users[1].id)).unwrap(); let tl_u2 = Timeline::list_for_user(conn, Some(users[1].id)).unwrap();
assert_eq!(2, tl_u2.len()); // same here assert_eq!(2, tl_u2.len()); // same here
assert!(tl_u2 assert!(tl_u2.iter().any(|tl| *tl == new_tl1_u2));
.iter()
.any(|tl| *tl == new_tl1_u2));
Ok(()) Ok(())
}); });

View file

@ -463,20 +463,24 @@ mod tests {
Instance::cache_local(conn); Instance::cache_local(conn);
instance instance
}); });
let mut user = NewUser::default(); let user = NewUser {
user.instance_id = instance.id; instance_id: instance.id,
user.username = random_hex(); username: random_hex(),
user.ap_url = random_hex(); ap_url: random_hex(),
user.inbox_url = random_hex(); inbox_url: random_hex(),
user.outbox_url = random_hex(); outbox_url: random_hex(),
user.followers_endpoint = random_hex(); followers_endpoint: random_hex(),
..Default::default()
};
let user = User::insert(conn, user).unwrap(); let user = User::insert(conn, user).unwrap();
let mut blog = NewBlog::default(); let blog = NewBlog {
blog.instance_id = instance.id; instance_id: instance.id,
blog.actor_id = random_hex(); actor_id: random_hex(),
blog.ap_url = random_hex(); ap_url: random_hex(),
blog.inbox_url = random_hex(); inbox_url: random_hex(),
blog.outbox_url = random_hex(); outbox_url: random_hex(),
..Default::default()
};
let blog = Blog::insert(conn, blog).unwrap(); let blog = Blog::insert(conn, blog).unwrap();
BlogAuthor::insert( BlogAuthor::insert(
conn, conn,

View file

@ -3,6 +3,7 @@ use crate::{
routes::{errors::ErrorPage, RespondOrRedirect}, routes::{errors::ErrorPage, RespondOrRedirect},
template_utils::{IntoContext, Ructe}, template_utils::{IntoContext, Ructe},
}; };
use plume_models::{ use plume_models::{
db_conn::DbConn, email_signups::EmailSignup, instance::Instance, lettre::Transport, signups, db_conn::DbConn, email_signups::EmailSignup, instance::Instance, lettre::Transport, signups,
Error, PlumeRocket, CONFIG, Error, PlumeRocket, CONFIG,
@ -13,7 +14,11 @@ use rocket::{
response::{Flash, Redirect}, response::{Flash, Redirect},
State, State,
}; };
use std::sync::{Arc, Mutex}; use std::{
borrow::Cow,
collections::HashMap,
sync::{Arc, Mutex},
};
use tracing::warn; use tracing::warn;
use validator::{Validate, ValidationError, ValidationErrors}; use validator::{Validate, ValidationError, ValidationErrors};
@ -105,6 +110,26 @@ pub fn create(
render!(email_signups::create(&(&conn, &rockets).to_context())).into() render!(email_signups::create(&(&conn, &rockets).to_context())).into()
} }
Error::NotFound => render!(errors::not_found(&(&conn, &rockets).to_context())).into(), Error::NotFound => render!(errors::not_found(&(&conn, &rockets).to_context())).into(),
Error::Blocklisted(show, msg) => {
let mut errors = ValidationErrors::new();
if *show {
errors.add(
"email",
ValidationError {
code: Cow::from("blocklisted"),
message: Some(Cow::from(msg.clone())),
params: HashMap::new(),
},
);
}
render!(email_signups::new(
&(&conn, &rockets).to_context(),
registration_open,
&form,
errors
))
.into()
}
_ => render!(errors::not_found(&(&conn, &rockets).to_context())).into(), // FIXME _ => render!(errors::not_found(&(&conn, &rockets).to_context())).into(), // FIXME
}); });
} }
@ -153,6 +178,28 @@ pub fn show(
))) )))
} // TODO: Flash and redirect } // TODO: Flash and redirect
Error::NotFound => return Err(Error::NotFound.into()), Error::NotFound => return Err(Error::NotFound.into()),
Error::Blocklisted(show, msg) => {
let mut errors = ValidationErrors::new();
if show {
errors.add(
"email",
ValidationError {
code: Cow::from("blocklisted"),
message: Some(Cow::from(msg)),
params: HashMap::new(),
},
);
}
return Ok(render!(email_signups::new(
&(&conn, &rockets).to_context(),
Instance::get_local()?.open_registrations,
&EmailSignupForm {
email: signup.email.clone(),
email_confirmation: signup.email
},
errors
)));
}
_ => return Err(Error::NotFound.into()), // FIXME _ => return Err(Error::NotFound.into()), // FIXME
} }
} }
@ -207,12 +254,38 @@ pub fn signup(
err err
)))); ))));
} }
let _user = signup let user = signup.complete(&conn, form.username.clone(), form.password.clone());
.complete(&conn, form.username.clone(), form.password.clone()) match user {
.map_err(|e| { Err(Error::Blocklisted(show, msg)) => {
let instance = Instance::get_local().map_err(|_| Status::UnprocessableEntity)?;
let mut errors = ValidationErrors::new();
if show {
errors.add(
"email",
ValidationError {
code: Cow::from("blocklisted"),
message: Some(Cow::from(msg)),
params: HashMap::new(),
},
);
}
return Ok(render!(email_signups::new(
&(&conn, &rockets).to_context(),
instance.open_registrations,
&EmailSignupForm {
email: signup.email.clone(),
email_confirmation: signup.email
},
errors
))
.into());
}
Err(e) => {
warn!("{:?}", e); warn!("{:?}", e);
Status::UnprocessableEntity return Err(Status::UnprocessableEntity);
})?; }
_ => {}
}
Ok(FlashRedirect(Flash::success( Ok(FlashRedirect(Flash::success(
Redirect::to(uri!(super::session::new: m = _)), Redirect::to(uri!(super::session::new: m = _)),
i18n!( i18n!(