From 411b8e3cb64bd2b38df5b6d51bd3262c27037a10 Mon Sep 17 00:00:00 2001 From: wafflecoffee Date: Wed, 3 Aug 2022 02:31:56 -0400 Subject: [PATCH] Initial work on site moderation improvements (#410) --- migrations/20220801184215_banned-users.sql | 3 ++ sqlx-data.json | 44 ++++++++++++++++++++++ src/routes/auth.rs | 15 ++++++++ src/routes/mod.rs | 7 +++- src/routes/moderation.rs | 37 +++++++++++++++++- src/search/indexing/mod.rs | 2 +- 6 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 migrations/20220801184215_banned-users.sql diff --git a/migrations/20220801184215_banned-users.sql b/migrations/20220801184215_banned-users.sql new file mode 100644 index 00000000..2b8d327a --- /dev/null +++ b/migrations/20220801184215_banned-users.sql @@ -0,0 +1,3 @@ +CREATE TABLE banned_users ( + github_id bigint NOT NULL PRIMARY KEY UNIQUE +) diff --git a/sqlx-data.json b/sqlx-data.json index 5c7cb117..aa9f717a 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -838,6 +838,18 @@ }, "query": "\n DELETE FROM notifications_actions\n WHERE notification_id = ANY($1)\n " }, + "28d5825964b0fddc43bd7d6851daf91845b79c9e88c82d5c7d97ae02502d0b4f": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Int8" + ] + } + }, + "query": "INSERT INTO banned_users (github_id) VALUES ($1);" + }, "292da3eec2cc7d7eb635fa123be1b1387e9e91466f007e10101053fdb9874e3f": { "describe": { "columns": [ @@ -933,6 +945,18 @@ }, "query": "\n INSERT INTO team_members (id, team_id, user_id, role, permissions, accepted)\n VALUES ($1, $2, $3, $4, $5, $6)\n " }, + "2f7c011654d15c85dbb614ac01ed5613a6872ea8c172ab38fdaa0eb38a7d6e4f": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Int8" + ] + } + }, + "query": "DELETE FROM banned_users WHERE github_id = $1;" + }, "33a965c7dc615d3b701c05299889357db8dd36d378850625d2602ba471af4885": { "describe": { "columns": [], @@ -2520,6 +2544,26 @@ }, "query": "\n SELECT v.id id, v.mod_id project_id FROM files f\n INNER JOIN versions v ON v.id = f.version_id\n WHERE f.url = $1\n " }, + "69bb839ea7fd5687538656e1907599d75e2c4948a54d58446bec8a90170ee618": { + "describe": { + "columns": [ + { + "name": "user", + "ordinal": 0, + "type_info": "Name" + } + ], + "nullable": [ + null + ], + "parameters": { + "Left": [ + "Int8" + ] + } + }, + "query": "SELECT user FROM banned_users WHERE github_id = $1" + }, "6a7b7704c2a0c52a70f5d881a1e6d3e8e77ddaa83ecc5688cd86bf327775fb76": { "describe": { "columns": [ diff --git a/src/routes/auth.rs b/src/routes/auth.rs index eebb88b3..1d189c38 100644 --- a/src/routes/auth.rs +++ b/src/routes/auth.rs @@ -50,6 +50,8 @@ pub enum AuthorizationError { Decoding(#[from] DecodingError), #[error("Invalid callback URL specified")] Url, + #[error("User is not allowed to access Modrinth services")] + Banned, } impl actix_web::ResponseError for AuthorizationError { fn status_code(&self) -> StatusCode { @@ -67,6 +69,7 @@ impl actix_web::ResponseError for AuthorizationError { AuthorizationError::Decoding(..) => StatusCode::BAD_REQUEST, AuthorizationError::Authentication(..) => StatusCode::UNAUTHORIZED, AuthorizationError::Url => StatusCode::BAD_REQUEST, + AuthorizationError::Banned => StatusCode::FORBIDDEN, } } @@ -84,6 +87,7 @@ impl actix_web::ResponseError for AuthorizationError { "authentication_error" } AuthorizationError::Url => "url_error", + AuthorizationError::Banned => "user_banned", }, description: &self.to_string(), }) @@ -215,6 +219,17 @@ pub async fn auth_callback( match user_result { Some(_) => {} None => { + let banned_user = sqlx::query!( + "SELECT user FROM banned_users WHERE github_id = $1", + user.id as i64 + ) + .fetch_optional(&mut *transaction) + .await?; + + if banned_user.is_some() { + return Err(AuthorizationError::Banned); + } + let user_id = crate::database::models::generate_user_id(&mut transaction) .await?; diff --git a/src/routes/mod.rs b/src/routes/mod.rs index ccc495ba..cd4b08ef 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -151,7 +151,12 @@ pub fn notifications_config(cfg: &mut web::ServiceConfig) { } pub fn moderation_config(cfg: &mut web::ServiceConfig) { - cfg.service(web::scope("moderation").service(moderation::get_projects)); + cfg.service( + web::scope("moderation") + .service(moderation::get_projects) + .service(moderation::ban_user) + .service(moderation::unban_user), + ); } pub fn reports_config(cfg: &mut web::ServiceConfig) { diff --git a/src/routes/moderation.rs b/src/routes/moderation.rs index 834b157b..44a09fe5 100644 --- a/src/routes/moderation.rs +++ b/src/routes/moderation.rs @@ -2,7 +2,7 @@ use super::ApiError; use crate::database; use crate::models::projects::ProjectStatus; use crate::util::auth::check_is_moderator_from_headers; -use actix_web::{get, web, HttpRequest, HttpResponse}; +use actix_web::{delete, get, web, HttpRequest, HttpResponse}; use serde::Deserialize; use sqlx::PgPool; @@ -54,3 +54,38 @@ pub async fn get_projects( Ok(HttpResponse::Ok().json(projects)) } + +#[derive(Deserialize)] +pub struct BanUser { + pub id: i64, +} + +#[get("ban")] +pub async fn ban_user( + req: HttpRequest, + pool: web::Data, + id: web::Query, +) -> Result { + check_is_moderator_from_headers(req.headers(), &**pool).await?; + + sqlx::query!("INSERT INTO banned_users (github_id) VALUES ($1);", id.id) + .execute(&**pool) + .await?; + + Ok(HttpResponse::NoContent().body("")) +} + +#[delete("ban")] +pub async fn unban_user( + req: HttpRequest, + pool: web::Data, + id: web::Query, +) -> Result { + check_is_moderator_from_headers(req.headers(), &**pool).await?; + + sqlx::query!("DELETE FROM banned_users WHERE github_id = $1;", id.id) + .execute(&**pool) + .await?; + + Ok(HttpResponse::NoContent().body("")) +} diff --git a/src/search/indexing/mod.rs b/src/search/indexing/mod.rs index 083277a1..a845e5b6 100644 --- a/src/search/indexing/mod.rs +++ b/src/search/indexing/mod.rs @@ -206,7 +206,7 @@ const DEFAULT_DISPLAYED_ATTRIBUTES: &[&str] = &[ "license", "client_side", "server_side", - "gallery" + "gallery", ]; const DEFAULT_SEARCHABLE_ATTRIBUTES: &[&str] =