From e62fffe11f2627ef8be6e0fe55db4feb883c8e3f Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Mon, 7 Jun 2021 01:58:15 -0400 Subject: [PATCH] delete endpoint; revised permissions --- migrations/2021-05-15-071534_users/up.sql | 23 +- .../endpoints/users/edit_profile.rs | 2 +- .../endpoints/users/session_info.rs | 20 +- .../endpoints/w2l/delete_w2l_template.rs | 214 ++++++++++++++++++ src/http_server/web_utils/session_checker.rs | 83 +++++-- 5 files changed, 306 insertions(+), 36 deletions(-) diff --git a/migrations/2021-05-15-071534_users/up.sql b/migrations/2021-05-15-071534_users/up.sql index f6d367a8c..a36f09223 100644 --- a/migrations/2021-05-15-071534_users/up.sql +++ b/migrations/2021-05-15-071534_users/up.sql @@ -118,16 +118,25 @@ CREATE TABLE user_roles ( name VARCHAR(255) NOT NULL, - can_ban_users BOOLEAN NOT NULL DEFAULT FALSE, - can_edit_other_users_data BOOLEAN NOT NULL DEFAULT FALSE, - can_approve_w2l_templates BOOLEAN NOT NULL DEFAULT FALSE, - - can_upload_tts_models BOOLEAN NOT NULL DEFAULT FALSE, - can_upload_w2l_templates BOOLEAN NOT NULL DEFAULT FALSE, - + -- Usage can_use_tts BOOLEAN NOT NULL DEFAULT FALSE, can_use_w2l BOOLEAN NOT NULL DEFAULT FALSE, + -- Contribution + can_upload_tts_models BOOLEAN NOT NULL DEFAULT FALSE, + can_upload_w2l_templates BOOLEAN NOT NULL DEFAULT FALSE, + can_delete_own_tts_models BOOLEAN NOT NULL DEFAULT FALSE, + can_delete_own_w2l_templates BOOLEAN NOT NULL DEFAULT FALSE, + + -- Moderation + can_approve_w2l_templates BOOLEAN NOT NULL DEFAULT FALSE, + can_edit_other_users_profiles BOOLEAN NOT NULL DEFAULT FALSE, + can_edit_other_users_tts_models BOOLEAN NOT NULL DEFAULT FALSE, + can_edit_other_users_w2l_templates BOOLEAN NOT NULL DEFAULT FALSE, + can_delete_other_users_tts_models BOOLEAN NOT NULL DEFAULT FALSE, + can_delete_other_users_w2l_templates BOOLEAN NOT NULL DEFAULT FALSE, + can_ban_users BOOLEAN NOT NULL DEFAULT FALSE, + -- Incremented with every update. version INT NOT NULL DEFAULT 0, diff --git a/src/http_server/endpoints/users/edit_profile.rs b/src/http_server/endpoints/users/edit_profile.rs index 090ef8d92..243993126 100644 --- a/src/http_server/endpoints/users/edit_profile.rs +++ b/src/http_server/endpoints/users/edit_profile.rs @@ -151,7 +151,7 @@ pub async fn edit_profile_handler( editor_is_original_user = true; } - if user_session.can_edit_other_users_data { + if user_session.can_edit_other_users_profiles { editor_is_moderator = true; } diff --git a/src/http_server/endpoints/users/session_info.rs b/src/http_server/endpoints/users/session_info.rs index 22bee850f..bd09472eb 100644 --- a/src/http_server/endpoints/users/session_info.rs +++ b/src/http_server/endpoints/users/session_info.rs @@ -38,11 +38,17 @@ pub struct UserInfo { // Contribution permissions: pub can_upload_tts_models: bool, pub can_upload_w2l_templates: bool, + pub can_delete_own_tts_models: bool, + pub can_delete_own_w2l_templates: bool, // Moderation permissions: - pub can_ban_users: bool, - pub can_edit_other_users_data: bool, pub can_approve_w2l_templates: bool, + pub can_edit_other_users_profiles: bool, + pub can_edit_other_users_tts_models: bool, + pub can_edit_other_users_w2l_templates: bool, + pub can_delete_other_users_tts_models: bool, + pub can_delete_other_users_w2l_templates: bool, + pub can_ban_users: bool, } #[derive(Serialize)] @@ -124,11 +130,17 @@ pub async fn session_info_handler( // Contribution permissions: can_upload_tts_models: session_data.can_upload_tts_models, can_upload_w2l_templates: session_data.can_upload_w2l_templates, + can_delete_own_tts_models: session_data.can_delete_own_tts_models, + can_delete_own_w2l_templates: session_data.can_delete_own_w2l_templates, // Moderation permissions: - can_ban_users: session_data.can_ban_users, - can_edit_other_users_data: session_data.can_edit_other_users_data, can_approve_w2l_templates: session_data.can_approve_w2l_templates, + can_edit_other_users_profiles: session_data.can_edit_other_users_profiles, + can_edit_other_users_tts_models: session_data.can_edit_other_users_tts_models, + can_edit_other_users_w2l_templates: session_data.can_edit_other_users_w2l_templates, + can_delete_other_users_tts_models: session_data.can_delete_other_users_tts_models, + can_delete_other_users_w2l_templates: session_data.can_delete_other_users_w2l_templates, + can_ban_users: session_data.can_ban_users, }); } } diff --git a/src/http_server/endpoints/w2l/delete_w2l_template.rs b/src/http_server/endpoints/w2l/delete_w2l_template.rs index e69de29bb..b46a81af0 100644 --- a/src/http_server/endpoints/w2l/delete_w2l_template.rs +++ b/src/http_server/endpoints/w2l/delete_w2l_template.rs @@ -0,0 +1,214 @@ +use actix_http::Error; +use actix_http::http::header; +use actix_web::cookie::Cookie; +use actix_web::dev::HttpResponseBuilder; +use actix_web::error::ResponseError; +use actix_web::http::StatusCode; +use actix_web::web::Path; +use actix_web::{Responder, web, HttpResponse, error, HttpRequest}; +use crate::common_queries::query_w2l_template::select_w2l_template_by_token; +use crate::database_helpers::enums::{DownloadUrlType, CreatorSetVisibility, W2lTemplateType}; +use crate::http_server::web_utils::ip_address::get_request_ip; +use crate::server_state::ServerState; +use crate::util::random_crockford_token::random_crockford_token; +use crate::validations::model_uploads::validate_model_title; +use crate::validations::passwords::validate_passwords; +use crate::validations::username::validate_username; +use derive_more::{Display, Error}; +use log::{info, warn, log}; +use regex::Regex; +use sqlx::error::DatabaseError; +use sqlx::error::Error::Database; +use sqlx::mysql::MySqlDatabaseError; +use std::sync::Arc; +use sqlx::MySqlPool; + +/// For the URL PathInfo +#[derive(Deserialize)] +pub struct DeleteW2lTemplatePathInfo { + slug: String, +} + +#[derive(Deserialize)] +pub struct DeleteW2lTemplateRequest { + set_delete: bool, +} + +#[derive(Serialize)] +pub struct DeleteW2lTemplateSuccessResponse { + pub success: bool, +} + +#[derive(Serialize)] +pub struct ErrorResponse { + pub success: bool, + pub error_reason: String, +} + +#[derive(Debug, Display)] +pub enum DeleteW2lTemplateError { + BadInput(String), + NotAuthorized, + NotFound, + ServerError, +} + +impl ResponseError for DeleteW2lTemplateError { + fn status_code(&self) -> StatusCode { + match *self { + DeleteW2lTemplateError::BadInput(_) => StatusCode::BAD_REQUEST, + DeleteW2lTemplateError::NotAuthorized => StatusCode::UNAUTHORIZED, + DeleteW2lTemplateError::NotFound => StatusCode::NOT_FOUND, + DeleteW2lTemplateError::ServerError => StatusCode::INTERNAL_SERVER_ERROR, + } + } + + fn error_response(&self) -> HttpResponse { + let error_reason = match self { + DeleteW2lTemplateError::BadInput(reason) => reason.to_string(), + DeleteW2lTemplateError::NotAuthorized => "unauthorized".to_string(), + DeleteW2lTemplateError::NotFound => "not found".to_string(), + DeleteW2lTemplateError::ServerError => "server error".to_string(), + }; + + let response = ErrorResponse { + success: false, + error_reason, + }; + + let body = match serde_json::to_string(&response) { + Ok(json) => json, + Err(_) => "{}".to_string(), + }; + + HttpResponseBuilder::new(self.status_code()) + .set_header(header::CONTENT_TYPE, "application/json") + .body(body) + } +} + +pub async fn delete_w2l_template_handler( + http_request: HttpRequest, + path: Path, + request: web::Json, + server_state: web::Data> +) -> Result { + let maybe_user_session = server_state + .session_checker + .maybe_get_user_session(&http_request, &server_state.mysql_pool) + .await + .map_err(|e| { + warn!("Session checker error: {:?}", e); + DeleteW2lTemplateError::ServerError + })?; + + let user_session = match maybe_user_session { + Some(session) => session, + None => { + warn!("not logged in"); + return Err(DeleteW2lTemplateError::NotAuthorized); + } + }; + + if !user_session.can_delete_other_users_w2l_templates { + warn!("user is not allowed to delete templates: {}", user_session.user_token); + return Err(DeleteW2lTemplateError::NotAuthorized); + } + + let template_query_result = select_w2l_template_by_token( + &path.slug, + true, // Only mods can perform this action + &server_state.mysql_pool, + ).await; + + let w2l_template = match template_query_result { + Err(e) => { + warn!("query error: {:?}", e); + return Err(DeleteW2lTemplateError::ServerError); + } + Ok(None) => return Err(DeleteW2lTemplateError::NotFound), + Ok(Some(template)) => template, + }; + + let ip_address = get_request_ip(&http_request); + + let query_result = if request.set_delete { + delete_template( + &path.slug, + &user_session.user_token, + &server_state.mysql_pool + ).await + } else { + undelete_template( + &path.slug, + &user_session.user_token, + &server_state.mysql_pool + ).await + }; + + match query_result { + Ok(_) => {}, + Err(err) => { + warn!("Update w2l mod approval status DB error: {:?}", err); + return Err(DeleteW2lTemplateError::ServerError); + } + }; + + let response = DeleteW2lTemplateSuccessResponse { + success: true, + }; + + let body = serde_json::to_string(&response) + .map_err(|e| DeleteW2lTemplateError::ServerError)?; + + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(body)) +} + +async fn delete_template( + template_token: &str, + mod_user_token: &str, + mysql_pool: &MySqlPool +) -> Result<(), sqlx::Error> { + let _r = sqlx::query!( + r#" +UPDATE w2l_templates +SET + deleted_at = CURRENT_TIMESTAMP, + maybe_mod_user_token = ? +WHERE + token = ? +LIMIT 1 + "#, + mod_user_token, + template_token, + ) + .execute(mysql_pool) + .await?; + Ok(()) +} + +async fn undelete_template( + template_token: &str, + mod_user_token: &str, + mysql_pool: &MySqlPool +) -> Result<(), sqlx::Error> { + + let _r = sqlx::query!( + r#" +UPDATE w2l_templates +SET + deleted_at = NULL, + maybe_mod_user_token = ? +WHERE + token = ? +LIMIT 1 + "#, + mod_user_token, + template_token, + ) + .execute(mysql_pool) + .await?; + Ok(()) +} diff --git a/src/http_server/web_utils/session_checker.rs b/src/http_server/web_utils/session_checker.rs index 605b42397..d9ff4c4b6 100644 --- a/src/http_server/web_utils/session_checker.rs +++ b/src/http_server/web_utils/session_checker.rs @@ -43,15 +43,24 @@ pub struct SessionUserRecord { // ===== PERMISSIONS FLAGS ===== // - pub can_ban_users: bool, - pub can_edit_other_users_data: bool, - pub can_approve_w2l_templates: bool, - - pub can_upload_tts_models: bool, - pub can_upload_w2l_templates: bool, - + // Usage pub can_use_tts: bool, pub can_use_w2l: bool, + + // Contribution + pub can_upload_tts_models: bool, + pub can_upload_w2l_templates: bool, + pub can_delete_own_tts_models: bool, + pub can_delete_own_w2l_templates: bool, + + // Moderation + pub can_approve_w2l_templates: bool, + pub can_edit_other_users_profiles: bool, + pub can_edit_other_users_tts_models: bool, + pub can_edit_other_users_w2l_templates: bool, + pub can_delete_other_users_tts_models: bool, + pub can_delete_other_users_w2l_templates: bool, + pub can_ban_users: bool, } pub struct SessionUserRawDbRecord { @@ -75,15 +84,25 @@ pub struct SessionUserRawDbRecord { pub user_role_slug: String, pub banned: i8, - pub can_ban_users: Option, - pub can_edit_other_users_data: Option, - pub can_approve_w2l_templates: Option, - - pub can_upload_tts_models: Option, - pub can_upload_w2l_templates: Option, - + // NB: These are `Option` due to the JOIN not being compile-time assured. + // Usage pub can_use_tts: Option, pub can_use_w2l: Option, + + // Contribution + pub can_upload_tts_models: Option, + pub can_upload_w2l_templates: Option, + pub can_delete_own_tts_models: Option, + pub can_delete_own_w2l_templates: Option, + + // Moderation + pub can_approve_w2l_templates: Option, + pub can_edit_other_users_profiles: Option, + pub can_edit_other_users_tts_models: Option, + pub can_edit_other_users_w2l_templates: Option, + pub can_delete_other_users_tts_models: Option, + pub can_delete_other_users_w2l_templates: Option, + pub can_ban_users: Option, } impl SessionChecker { @@ -171,15 +190,21 @@ SELECT users.user_role_slug, users.banned, - user_roles.can_ban_users, - user_roles.can_edit_other_users_data, - user_roles.can_approve_w2l_templates, + user_roles.can_use_tts, + user_roles.can_use_w2l, user_roles.can_upload_tts_models, user_roles.can_upload_w2l_templates, + user_roles.can_delete_own_tts_models, + user_roles.can_delete_own_w2l_templates, - user_roles.can_use_tts, - user_roles.can_use_w2l + user_roles.can_approve_w2l_templates, + user_roles.can_edit_other_users_profiles, + user_roles.can_edit_other_users_tts_models, + user_roles.can_edit_other_users_w2l_templates, + user_roles.can_delete_other_users_tts_models, + user_roles.can_delete_other_users_w2l_templates, + user_roles.can_ban_users FROM users LEFT OUTER JOIN user_sessions @@ -214,13 +239,23 @@ WHERE user_sessions.token = ? maybe_preferred_w2l_template_token: raw_user_record.maybe_preferred_w2l_template_token.clone(), user_role_slug: raw_user_record.user_role_slug.clone(), banned: if raw_user_record.banned == 0 { false } else { true }, - can_ban_users: convert_optional_db_bool_default_false(raw_user_record.can_ban_users), - can_edit_other_users_data: convert_optional_db_bool_default_false(raw_user_record.can_edit_other_users_data), - can_approve_w2l_templates: convert_optional_db_bool_default_false(raw_user_record.can_approve_w2l_templates), - can_upload_tts_models: convert_optional_db_bool_default_false(raw_user_record.can_upload_tts_models), - can_upload_w2l_templates: convert_optional_db_bool_default_false(raw_user_record.can_upload_w2l_templates), + + // Usage can_use_tts: convert_optional_db_bool_default_false(raw_user_record.can_use_tts), can_use_w2l: convert_optional_db_bool_default_false(raw_user_record.can_use_w2l), + // Contribution + can_upload_tts_models: convert_optional_db_bool_default_false(raw_user_record.can_upload_tts_models), + can_upload_w2l_templates: convert_optional_db_bool_default_false(raw_user_record.can_upload_w2l_templates), + can_delete_own_tts_models: convert_optional_db_bool_default_false(raw_user_record.can_delete_own_tts_models), + can_delete_own_w2l_templates: convert_optional_db_bool_default_false(raw_user_record.can_delete_own_w2l_templates), + // Moderation + can_approve_w2l_templates: convert_optional_db_bool_default_false(raw_user_record.can_approve_w2l_templates), + can_edit_other_users_profiles: convert_optional_db_bool_default_false(raw_user_record.can_edit_other_users_profiles), + can_edit_other_users_tts_models: convert_optional_db_bool_default_false(raw_user_record.can_edit_other_users_tts_models), + can_edit_other_users_w2l_templates: convert_optional_db_bool_default_false(raw_user_record.can_edit_other_users_w2l_templates), + can_delete_other_users_tts_models: convert_optional_db_bool_default_false(raw_user_record.can_delete_other_users_tts_models), + can_delete_other_users_w2l_templates: convert_optional_db_bool_default_false(raw_user_record.can_delete_other_users_w2l_templates), + can_ban_users: convert_optional_db_bool_default_false(raw_user_record.can_ban_users), }; Ok(Some(result_user_record))