diff --git a/backend/api/src/api_doc.rs b/backend/api/src/api_doc.rs index 5bc14761..b97b3c4c 100644 --- a/backend/api/src/api_doc.rs +++ b/backend/api/src/api_doc.rs @@ -124,6 +124,8 @@ use crate::handlers::user_applications::get_user_applications::GetUserApplicatio crate::handlers::css_sheets::get_css_sheet_content::exec, crate::handlers::css_sheets::get_css_sheets::exec, crate::handlers::css_sheets::get_css_sheet::exec, + crate::handlers::arcadia_settings::get_arcadia_settings::exec, + crate::handlers::arcadia_settings::update_arcadia_settings::exec, crate::handlers::external_db::get_isbn_data::exec, crate::handlers::external_db::get_musicbrainz_data::exec, crate::handlers::external_db::get_tmdb_data::exec, diff --git a/backend/api/src/handlers/arcadia_settings/get_arcadia_settings.rs b/backend/api/src/handlers/arcadia_settings/get_arcadia_settings.rs new file mode 100644 index 00000000..db98f5a1 --- /dev/null +++ b/backend/api/src/handlers/arcadia_settings/get_arcadia_settings.rs @@ -0,0 +1,35 @@ +use crate::{middlewares::auth_middleware::Authdata, Arcadia}; +use actix_web::{web::Data, HttpResponse}; +use arcadia_common::error::{Error, Result}; +use arcadia_storage::{ + models::{arcadia_settings::ArcadiaSettings, user::UserPermission}, + redis::RedisPoolInterface, +}; + +#[utoipa::path( + get, + operation_id = "Get Arcadia settings", + tag = "Arcadia Settings", + path = "/api/arcadia-settings", + security( + ("http" = ["Bearer"]) + ), + responses( + (status = 200, description = "Successfully retrieved Arcadia settings", body=ArcadiaSettings), + ) +)] +pub async fn exec( + arc: Data>, + user: Authdata, +) -> Result { + if !arc + .pool + .user_has_permission(user.sub, &UserPermission::EditArcadiaSettings) + .await? + { + return Err(Error::InsufficientPrivileges); + } + + let settings = arc.settings.lock().unwrap().clone(); + Ok(HttpResponse::Ok().json(settings)) +} diff --git a/backend/api/src/handlers/arcadia_settings/mod.rs b/backend/api/src/handlers/arcadia_settings/mod.rs new file mode 100644 index 00000000..ea8b0a21 --- /dev/null +++ b/backend/api/src/handlers/arcadia_settings/mod.rs @@ -0,0 +1,13 @@ +pub mod get_arcadia_settings; +pub mod update_arcadia_settings; + +use actix_web::web::{get, put, resource, ServiceConfig}; +use arcadia_storage::redis::RedisPoolInterface; + +pub fn config(cfg: &mut ServiceConfig) { + cfg.service( + resource("") + .route(get().to(self::get_arcadia_settings::exec::)) + .route(put().to(self::update_arcadia_settings::exec::)), + ); +} diff --git a/backend/api/src/handlers/arcadia_settings/update_arcadia_settings.rs b/backend/api/src/handlers/arcadia_settings/update_arcadia_settings.rs new file mode 100644 index 00000000..a87bd154 --- /dev/null +++ b/backend/api/src/handlers/arcadia_settings/update_arcadia_settings.rs @@ -0,0 +1,44 @@ +use crate::{middlewares::auth_middleware::Authdata, Arcadia}; +use actix_web::{ + web::{Data, Json}, + HttpResponse, +}; +use arcadia_common::error::{Error, Result}; +use arcadia_storage::{ + models::{arcadia_settings::ArcadiaSettings, user::UserPermission}, + redis::RedisPoolInterface, +}; + +#[utoipa::path( + put, + operation_id = "Update Arcadia settings", + tag = "Arcadia Settings", + path = "/api/arcadia-settings", + security( + ("http" = ["Bearer"]) + ), + request_body = ArcadiaSettings, + responses( + (status = 200, description = "Successfully updated Arcadia settings", body=ArcadiaSettings), + ) +)] +pub async fn exec( + settings: Json, + arc: Data>, + user: Authdata, +) -> Result { + if !arc + .pool + .user_has_permission(user.sub, &UserPermission::EditArcadiaSettings) + .await? + { + return Err(Error::InsufficientPrivileges); + } + + let updated_settings = arc.pool.update_arcadia_settings(&settings).await?; + + // Update the in-memory settings + *arc.settings.lock().unwrap() = updated_settings.clone(); + + Ok(HttpResponse::Ok().json(updated_settings)) +} diff --git a/backend/api/src/handlers/auth/register.rs b/backend/api/src/handlers/auth/register.rs index fe935112..79d8bb8e 100644 --- a/backend/api/src/handlers/auth/register.rs +++ b/backend/api/src/handlers/auth/register.rs @@ -97,6 +97,7 @@ pub async fn exec( &invitation, &arc.is_open_signups(), &arc.env.user_class_name_on_signup, + &arc.settings.lock().unwrap().default_css_sheet_name, ) .await?; diff --git a/backend/api/src/handlers/css_sheets/edit_css_sheet.rs b/backend/api/src/handlers/css_sheets/edit_css_sheet.rs index c510e1c5..7155f453 100644 --- a/backend/api/src/handlers/css_sheets/edit_css_sheet.rs +++ b/backend/api/src/handlers/css_sheets/edit_css_sheet.rs @@ -37,6 +37,17 @@ pub async fn exec( return Err(Error::InsufficientPrivileges); } - let updated = arc.pool.update_css_sheet(&css_sheet).await?; - Ok(HttpResponse::Ok().json(updated)) + let old_name_was_default = + css_sheet.old_name == arc.settings.lock().unwrap().default_css_sheet_name; + + let updated_css_sheet = arc.pool.update_css_sheet(&css_sheet).await?; + + // If the old name was the default, the CASCADE has already updated the database. + // We just need to reload the settings from the database to update the in-memory cache. + if old_name_was_default { + let updated_settings = arc.pool.get_arcadia_settings().await?; + *arc.settings.lock().unwrap() = updated_settings; + } + + Ok(HttpResponse::Ok().json(updated_css_sheet)) } diff --git a/backend/api/src/handlers/css_sheets/get_css_sheets.rs b/backend/api/src/handlers/css_sheets/get_css_sheets.rs index d3af4bb5..b15f55c0 100644 --- a/backend/api/src/handlers/css_sheets/get_css_sheets.rs +++ b/backend/api/src/handlers/css_sheets/get_css_sheets.rs @@ -17,5 +17,8 @@ use arcadia_storage::{models::css_sheet::CssSheetsEnriched, redis::RedisPoolInte )] pub async fn exec(arc: Data>) -> Result { let sheets = arc.pool.find_css_sheets().await?; - Ok(HttpResponse::Ok().json(sheets)) + Ok(HttpResponse::Ok().json(CssSheetsEnriched { + css_sheets: sheets, + default_sheet_name: arc.settings.lock().unwrap().default_css_sheet_name.clone(), + })) } diff --git a/backend/api/src/handlers/css_sheets/mod.rs b/backend/api/src/handlers/css_sheets/mod.rs index 4d069640..e7107e13 100644 --- a/backend/api/src/handlers/css_sheets/mod.rs +++ b/backend/api/src/handlers/css_sheets/mod.rs @@ -3,7 +3,6 @@ pub mod edit_css_sheet; pub mod get_css_sheet; pub mod get_css_sheet_content; pub mod get_css_sheets; -pub mod set_default_css_sheet; use actix_web::web::{get, post, put, resource, ServiceConfig}; use arcadia_storage::redis::RedisPoolInterface; @@ -16,9 +15,6 @@ pub fn config(cfg: &mut ServiceConfig) { .route(put().to(self::edit_css_sheet::exec::)), ); cfg.service(resource("/{name}").route(get().to(self::get_css_sheet::exec::))); - cfg.service( - resource("/{name}/default").route(put().to(self::set_default_css_sheet::exec::)), - ); } pub fn config_public(cfg: &mut ServiceConfig) { diff --git a/backend/api/src/handlers/css_sheets/set_default_css_sheet.rs b/backend/api/src/handlers/css_sheets/set_default_css_sheet.rs deleted file mode 100644 index d9b2ded8..00000000 --- a/backend/api/src/handlers/css_sheets/set_default_css_sheet.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::{middlewares::auth_middleware::Authdata, Arcadia}; -use actix_web::{ - web::{Data, Path}, - HttpResponse, -}; -use arcadia_common::error::{Error, Result}; -use arcadia_storage::{models::user::UserPermission, redis::RedisPoolInterface}; - -#[utoipa::path( - put, - operation_id = "Set default CSS sheet", - tag = "Css Sheet", - path = "/api/css-sheets/{name}/default", - security( - ("http" = ["Bearer"]) - ), - responses( - (status = 200, description = "Successfully changed the default CSS sheet"), - ) -)] -pub async fn exec( - name: Path, - arc: Data>, - user: Authdata, -) -> Result { - if !arc - .pool - .user_has_permission(user.sub, &UserPermission::SetDefaultCssSheet) - .await? - { - return Err(Error::InsufficientPrivileges); - } - - arc.pool.set_default_css_sheet(&name).await?; - Ok(HttpResponse::Ok().json(serde_json::json!({ "result": "success" }))) -} diff --git a/backend/api/src/handlers/mod.rs b/backend/api/src/handlers/mod.rs index e0994f63..21d5b47f 100644 --- a/backend/api/src/handlers/mod.rs +++ b/backend/api/src/handlers/mod.rs @@ -1,4 +1,5 @@ pub mod affiliated_artists; +pub mod arcadia_settings; pub mod artists; pub mod auth; pub mod collages; diff --git a/backend/api/src/lib.rs b/backend/api/src/lib.rs index c729e6f1..638f1385 100644 --- a/backend/api/src/lib.rs +++ b/backend/api/src/lib.rs @@ -1,5 +1,12 @@ -use arcadia_storage::{connection_pool::ConnectionPool, redis::RedisPoolInterface}; -use std::{ops::Deref, str::FromStr, sync::Arc}; +use arcadia_storage::{ + connection_pool::ConnectionPool, models::arcadia_settings::ArcadiaSettings, + redis::RedisPoolInterface, +}; +use std::{ + ops::Deref, + str::FromStr, + sync::{Arc, Mutex}, +}; use crate::{env::Env, services::auth::Auth}; @@ -34,6 +41,7 @@ pub struct Arcadia { pub pool: Arc, pub redis_pool: Arc, pub auth: Auth, + pub settings: Arc>, env: Env, } @@ -46,11 +54,17 @@ impl Deref for Arcadia { } impl Arcadia { - pub fn new(pool: Arc, redis_pool: Arc, env: Env) -> Self { + pub fn new( + pool: Arc, + redis_pool: Arc, + env: Env, + settings: ArcadiaSettings, + ) -> Self { Self { pool, redis_pool: Arc::clone(&redis_pool), auth: Auth::new(Arc::clone(&redis_pool)), + settings: Arc::new(Mutex::new(settings)), env, } } diff --git a/backend/api/src/main.rs b/backend/api/src/main.rs index 1aad110d..bd004b9b 100644 --- a/backend/api/src/main.rs +++ b/backend/api/src/main.rs @@ -49,10 +49,18 @@ async fn main() -> std::io::Result<()> { &env.redis.password, env.redis.port, )); + + // Load settings from database on startup + let settings = pool + .get_arcadia_settings() + .await + .expect("failed to load arcadia settings from database"); + let arc = Data::new(Arcadia::new( Arc::clone(&pool), Arc::clone(&redis_pool), env, + settings, )); let server = HttpServer::new(move || { let cors = Cors::permissive(); diff --git a/backend/api/src/routes.rs b/backend/api/src/routes.rs index ff7b30b5..e39db84b 100644 --- a/backend/api/src/routes.rs +++ b/backend/api/src/routes.rs @@ -3,6 +3,7 @@ use actix_web_httpauth::middleware::HttpAuthentication; use arcadia_storage::redis::RedisPoolInterface; use crate::handlers::affiliated_artists::config as AffiliatedArtistsConfig; +use crate::handlers::arcadia_settings::config as ArcadiaSettingsConfig; use crate::handlers::artists::config as ArtistsConfig; use crate::handlers::auth::config as AuthConfig; use crate::handlers::collages::config as CollagesConfig; @@ -70,6 +71,7 @@ pub fn init(cfg: &mut web::ServiceConfig) { .service(scope("/gifts").configure(GiftsConfig::)) .service(scope("/collages").configure(CollagesConfig::)) .service(scope("/css-sheets").configure(CssSheetsConfig::)) + .service(scope("/arcadia-settings").configure(ArcadiaSettingsConfig::)) .service(scope("/tracker").configure(TrackerConfig::)), ); } diff --git a/backend/api/tests/common.rs b/backend/api/tests/common.rs index 8389ae25..acc3aefb 100644 --- a/backend/api/tests/common.rs +++ b/backend/api/tests/common.rs @@ -36,7 +36,13 @@ pub async fn create_test_app( env.global_upload_factor = global_upload_factor; env.global_download_factor = global_download_factor; - let arc = Arcadia::::new(pool, Arc::new(redis_pool), env); + // Load settings from database for tests + let settings = pool + .get_arcadia_settings() + .await + .expect("failed to load arcadia settings from database"); + + let arc = Arcadia::::new(pool, Arc::new(redis_pool), env, settings); // TODO: CORS? test::init_service( @@ -70,6 +76,7 @@ pub enum TestUser { EditUserPermissions, LockUserClass, ChangeUserClass, + EditArcadiaSettings, } impl TestUser { @@ -96,6 +103,7 @@ impl TestUser { TestUser::EditUserPermissions => "user_perm_edit", TestUser::LockUserClass => "user_lock_cls", TestUser::ChangeUserClass => "user_cls_chg", + TestUser::EditArcadiaSettings => "user_arc_set", }; Login { diff --git a/backend/api/tests/fixtures/with_locked_user.sql b/backend/api/tests/fixtures/with_locked_user.sql index 53c720e8..b49dc858 100644 --- a/backend/api/tests/fixtures/with_locked_user.sql +++ b/backend/api/tests/fixtures/with_locked_user.sql @@ -1,3 +1,3 @@ -- User with locked class -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, class_locked, permissions) -VALUES (999, 'locked_user', 'locked@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c9999', 'newbie', TRUE, '{download_torrent}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, class_locked, permissions) +VALUES (999, 'locked_user', 'locked@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c9999', 'newbie', 'arcadia', TRUE, '{download_torrent}'); diff --git a/backend/api/tests/fixtures/with_test_banned_user.sql b/backend/api/tests/fixtures/with_test_banned_user.sql index 624654be..5096ab99 100644 --- a/backend/api/tests/fixtures/with_test_banned_user.sql +++ b/backend/api/tests/fixtures/with_test_banned_user.sql @@ -1,5 +1,5 @@ INSERT INTO - users (banned, username, email, password_hash, registered_from_ip, passkey, class_name) + users (banned, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name) VALUES -- passkey d2037c66dd3e13044e0d2f9b891c3837 - (true, 'test_user', 'test_email@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'mqdfkjqmsdkf', 'newbie') + (true, 'test_user', 'test_email@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'mqdfkjqmsdkf', 'newbie', 'arcadia') diff --git a/backend/api/tests/fixtures/with_test_users.sql b/backend/api/tests/fixtures/with_test_users.sql index 8f642dba..668ecb15 100644 --- a/backend/api/tests/fixtures/with_test_users.sql +++ b/backend/api/tests/fixtures/with_test_users.sql @@ -1,83 +1,87 @@ -- Basic user for read operations and permission denial tests -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (100, 'user_basic', 'test_user@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3837', 'newbie', '{download_torrent}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (100, 'user_basic', 'test_user@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3837', 'newbie', 'arcadia', '{download_torrent}'); -- User with edit_artist permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (101, 'user_edit_art', 'test_user_edit_artist@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3838', 'newbie', '{edit_artist}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (101, 'user_edit_art', 'test_user_edit_artist@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3838', 'newbie', 'arcadia', '{edit_artist}'); -- User with edit_series permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (102, 'user_edit_ser', 'test_user_edit_series@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3839', 'newbie', '{edit_series}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (102, 'user_edit_ser', 'test_user_edit_series@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3839', 'newbie', 'arcadia', '{edit_series}'); -- User with edit_title_group_comment permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (103, 'user_edit_tgc', 'test_user_edit_title_group_comment@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383a', 'newbie', '{edit_title_group_comment}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (103, 'user_edit_tgc', 'test_user_edit_title_group_comment@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383a', 'newbie', 'arcadia', '{edit_title_group_comment}'); -- User with create_css_sheet permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (104, 'user_css_crt', 'test_user_create_css_sheet@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383b', 'newbie', '{create_css_sheet}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (104, 'user_css_crt', 'test_user_create_css_sheet@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383b', 'newbie', 'arcadia', '{create_css_sheet}'); -- User with edit_css_sheet permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (105, 'user_css_edit', 'test_user_edit_css_sheet@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383c', 'newbie', '{edit_css_sheet}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (105, 'user_css_edit', 'test_user_edit_css_sheet@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383c', 'newbie', 'arcadia', '{edit_css_sheet}'); -- User with set_default_css_sheet permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (106, 'user_css_def', 'test_user_set_default_css_sheet@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383d', 'newbie', '{set_default_css_sheet}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (106, 'user_css_def', 'test_user_set_default_css_sheet@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383d', 'newbie', 'arcadia', '{set_default_css_sheet}'); -- User with create_forum_category permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (107, 'user_cat_crt', 'test_user_create_forum_category@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383e', 'newbie', '{create_forum_category}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (107, 'user_cat_crt', 'test_user_create_forum_category@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383e', 'newbie', 'arcadia', '{create_forum_category}'); -- User with edit_forum_category permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (108, 'user_cat_edit', 'test_user_edit_forum_category@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383f', 'newbie', '{edit_forum_category}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (108, 'user_cat_edit', 'test_user_edit_forum_category@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c383f', 'newbie', 'arcadia', '{edit_forum_category}'); -- User with create_forum_sub_category permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (109, 'user_sub_crt', 'test_user_create_forum_sub_category@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3840', 'newbie', '{create_forum_sub_category}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (109, 'user_sub_crt', 'test_user_create_forum_sub_category@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3840', 'newbie', 'arcadia', '{create_forum_sub_category}'); -- User with edit_forum_sub_category permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (110, 'user_sub_edit', 'test_user_edit_forum_sub_category@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3841', 'newbie', '{edit_forum_sub_category}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (110, 'user_sub_edit', 'test_user_edit_forum_sub_category@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3841', 'newbie', 'arcadia', '{edit_forum_sub_category}'); -- User with edit_forum_thread permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (111, 'user_thr_edit', 'test_user_edit_forum_thread@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3842', 'newbie', '{edit_forum_thread}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (111, 'user_thr_edit', 'test_user_edit_forum_thread@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3842', 'newbie', 'arcadia', '{edit_forum_thread}'); -- User with edit_forum_post permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (112, 'user_post_edit', 'test_user_edit_forum_post@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3843', 'newbie', '{edit_forum_post}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (112, 'user_post_edit', 'test_user_edit_forum_post@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3843', 'newbie', 'arcadia', '{edit_forum_post}'); -- User with both create and edit forum category permissions (for flow tests) -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (113, 'user_cat_flow', 'test_user_cat_flow@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3844', 'newbie', '{create_forum_category,edit_forum_category}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (113, 'user_cat_flow', 'test_user_cat_flow@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3844', 'newbie', 'arcadia', '{create_forum_category,edit_forum_category}'); -- User with both create and edit forum sub category permissions (for flow tests) -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (114, 'user_sub_flow', 'test_user_sub_flow@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3845', 'newbie', '{create_forum_sub_category,edit_forum_sub_category}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (114, 'user_sub_flow', 'test_user_sub_flow@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3845', 'newbie', 'arcadia', '{create_forum_sub_category,edit_forum_sub_category}'); -- User with create_user_class permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (115, 'user_cls_crt', 'test_user_create_user_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3846', 'newbie', '{create_user_class}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (115, 'user_cls_crt', 'test_user_create_user_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3846', 'newbie', 'arcadia', '{create_user_class}'); -- User with edit_user_class permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (116, 'user_cls_edit', 'test_user_edit_user_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3847', 'newbie', '{edit_user_class}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (116, 'user_cls_edit', 'test_user_edit_user_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3847', 'newbie', 'arcadia', '{edit_user_class}'); -- User with delete_user_class permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (117, 'user_cls_del', 'test_user_delete_user_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3848', 'newbie', '{delete_user_class}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (117, 'user_cls_del', 'test_user_delete_user_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3848', 'newbie', 'arcadia', '{delete_user_class}'); -- User with edit_user_permissions permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (118, 'user_perm_edit', 'test_user_edit_permissions@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3849', 'newbie', '{edit_user_permissions}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (118, 'user_perm_edit', 'test_user_edit_permissions@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3849', 'newbie', 'arcadia', '{edit_user_permissions}'); -- User with lock_user_class permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (119, 'user_lock_cls', 'test_user_lock_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c384a', 'newbie', '{lock_user_class}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (119, 'user_lock_cls', 'test_user_lock_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c384a', 'newbie', 'arcadia', '{lock_user_class}'); -- User with change_user_class permission -INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, permissions) -VALUES (120, 'user_cls_chg', 'test_user_change_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c384b', 'newbie', '{change_user_class}'); +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (120, 'user_cls_chg', 'test_user_change_class@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c384b', 'newbie', 'arcadia', '{change_user_class}'); + +-- User with edit_arcadia_settings permission +INSERT INTO users (id, username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name, permissions) +VALUES (121, 'user_arc_set', 'test_user_arcadia_settings@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c384c', 'newbie', 'arcadia', '{edit_arcadia_settings}'); diff --git a/backend/api/tests/test_arcadia_settings.rs b/backend/api/tests/test_arcadia_settings.rs new file mode 100644 index 00000000..b233805a --- /dev/null +++ b/backend/api/tests/test_arcadia_settings.rs @@ -0,0 +1,210 @@ +pub mod common; +pub mod mocks; + +use actix_web::{http::StatusCode, test}; +use arcadia_api::OpenSignups; +use arcadia_storage::{connection_pool::ConnectionPool, models::arcadia_settings::ArcadiaSettings}; +use common::{ + auth_header, call_and_read_body_json, create_test_app, create_test_app_and_login, TestUser, +}; +use mocks::mock_redis::MockRedisPool; +use sqlx::PgPool; +use std::sync::Arc; + +#[sqlx::test(fixtures("with_test_users"), migrations = "../storage/migrations")] +async fn test_staff_can_get_arcadia_settings(pool: PgPool) { + let pool = Arc::new(ConnectionPool::with_pg_pool(pool)); + let (service, user) = create_test_app_and_login( + pool, + MockRedisPool::default(), + 100, + 100, + TestUser::EditArcadiaSettings, + ) + .await; + + let req = test::TestRequest::get() + .insert_header(("X-Forwarded-For", "10.10.4.88")) + .insert_header(auth_header(&user.token)) + .uri("/api/arcadia-settings") + .to_request(); + + let settings = call_and_read_body_json::(&service, req).await; + + assert_eq!(settings.user_class_name_on_signup, "newbie"); + assert_eq!(settings.default_css_sheet_name, "arcadia"); +} + +#[sqlx::test(fixtures("with_test_users"), migrations = "../storage/migrations")] +async fn test_regular_user_cannot_get_arcadia_settings(pool: PgPool) { + let pool = Arc::new(ConnectionPool::with_pg_pool(pool)); + let (service, user) = + create_test_app_and_login(pool, MockRedisPool::default(), 100, 100, TestUser::Standard) + .await; + + let req = test::TestRequest::get() + .insert_header(("X-Forwarded-For", "10.10.4.88")) + .insert_header(auth_header(&user.token)) + .uri("/api/arcadia-settings") + .to_request(); + + let resp = test::call_service(&service, req).await; + assert_eq!(resp.status(), StatusCode::FORBIDDEN); +} + +#[sqlx::test(fixtures("with_test_users"), migrations = "../storage/migrations")] +async fn test_get_arcadia_settings_requires_auth(pool: PgPool) { + let pool = Arc::new(ConnectionPool::with_pg_pool(pool)); + let service = create_test_app( + pool, + MockRedisPool::default(), + OpenSignups::Disabled, + 100, + 100, + ) + .await; + + let req = test::TestRequest::get() + .insert_header(("X-Forwarded-For", "10.10.4.88")) + .uri("/api/arcadia-settings") + .to_request(); + + let resp = test::call_service(&service, req).await; + assert_eq!(resp.status(), StatusCode::UNAUTHORIZED); +} + +#[sqlx::test(fixtures("with_test_users"), migrations = "../storage/migrations")] +async fn test_staff_can_update_arcadia_settings(pool: PgPool) { + let pool = Arc::new(ConnectionPool::with_pg_pool(pool)); + let (service, user) = create_test_app_and_login( + pool.clone(), + MockRedisPool::default(), + 100, + 100, + TestUser::EditArcadiaSettings, + ) + .await; + + let updated_settings = ArcadiaSettings { + user_class_name_on_signup: "newbie".to_string(), + default_css_sheet_name: "arcadia".to_string(), + }; + + let req = test::TestRequest::put() + .insert_header(("X-Forwarded-For", "10.10.4.88")) + .insert_header(auth_header(&user.token)) + .uri("/api/arcadia-settings") + .set_json(&updated_settings) + .to_request(); + + let settings = call_and_read_body_json::(&service, req).await; + + assert_eq!(settings.user_class_name_on_signup, "newbie"); + assert_eq!(settings.default_css_sheet_name, "arcadia"); + + // Verify the settings were actually updated in the database + let db_settings = pool.get_arcadia_settings().await.unwrap(); + assert_eq!(db_settings.user_class_name_on_signup, "newbie"); + assert_eq!(db_settings.default_css_sheet_name, "arcadia"); +} + +#[sqlx::test(fixtures("with_test_users"), migrations = "../storage/migrations")] +async fn test_regular_user_cannot_update_arcadia_settings(pool: PgPool) { + let pool = Arc::new(ConnectionPool::with_pg_pool(pool)); + let (service, user) = + create_test_app_and_login(pool, MockRedisPool::default(), 100, 100, TestUser::Standard) + .await; + + let updated_settings = ArcadiaSettings { + user_class_name_on_signup: "newbie".to_string(), + default_css_sheet_name: "arcadia".to_string(), + }; + + let req = test::TestRequest::put() + .insert_header(("X-Forwarded-For", "10.10.4.88")) + .insert_header(auth_header(&user.token)) + .uri("/api/arcadia-settings") + .set_json(&updated_settings) + .to_request(); + + let resp = test::call_service(&service, req).await; + assert_eq!(resp.status(), StatusCode::FORBIDDEN); +} + +#[sqlx::test(fixtures("with_test_users"), migrations = "../storage/migrations")] +async fn test_update_arcadia_settings_requires_auth(pool: PgPool) { + let pool = Arc::new(ConnectionPool::with_pg_pool(pool)); + let service = create_test_app( + pool, + MockRedisPool::default(), + OpenSignups::Disabled, + 100, + 100, + ) + .await; + + let updated_settings = ArcadiaSettings { + user_class_name_on_signup: "newbie".to_string(), + default_css_sheet_name: "arcadia".to_string(), + }; + + let req = test::TestRequest::put() + .insert_header(("X-Forwarded-For", "10.10.4.88")) + .uri("/api/arcadia-settings") + .set_json(&updated_settings) + .to_request(); + + let resp = test::call_service(&service, req).await; + assert_eq!(resp.status(), StatusCode::UNAUTHORIZED); +} + +#[sqlx::test(fixtures("with_test_users"), migrations = "../storage/migrations")] +async fn test_update_arcadia_settings_updates_in_memory_cache(pool: PgPool) { + let pool = Arc::new(ConnectionPool::with_pg_pool(pool)); + let (service, user) = create_test_app_and_login( + pool, + MockRedisPool::default(), + 100, + 100, + TestUser::EditArcadiaSettings, + ) + .await; + + // First, get the current settings + let get_req = test::TestRequest::get() + .insert_header(("X-Forwarded-For", "10.10.4.88")) + .insert_header(auth_header(&user.token)) + .uri("/api/arcadia-settings") + .to_request(); + + let initial_settings = call_and_read_body_json::(&service, get_req).await; + assert_eq!(initial_settings.user_class_name_on_signup, "newbie"); + + // Update the settings + let updated_settings = ArcadiaSettings { + user_class_name_on_signup: "newbie".to_string(), + default_css_sheet_name: "arcadia".to_string(), + }; + + let update_req = test::TestRequest::put() + .insert_header(("X-Forwarded-For", "10.10.4.88")) + .insert_header(auth_header(&user.token)) + .uri("/api/arcadia-settings") + .set_json(&updated_settings) + .to_request(); + + let updated = call_and_read_body_json::(&service, update_req).await; + assert_eq!(updated.user_class_name_on_signup, "newbie"); + + // Get the settings again to verify the in-memory cache was updated + let get_req_after = test::TestRequest::get() + .insert_header(("X-Forwarded-For", "10.10.4.88")) + .insert_header(auth_header(&user.token)) + .uri("/api/arcadia-settings") + .to_request(); + + let final_settings = + call_and_read_body_json::(&service, get_req_after).await; + assert_eq!(final_settings.user_class_name_on_signup, "newbie"); + assert_eq!(final_settings.default_css_sheet_name, "arcadia"); +} diff --git a/backend/api/tests/test_css_sheets.rs b/backend/api/tests/test_css_sheets.rs index 5172549a..870b3408 100644 --- a/backend/api/tests/test_css_sheets.rs +++ b/backend/api/tests/test_css_sheets.rs @@ -203,63 +203,6 @@ async fn test_regular_user_cannot_edit_css_sheet(pool: PgPool) { assert_eq!(resp.status(), StatusCode::FORBIDDEN); } -#[sqlx::test( - fixtures("with_test_users", "with_test_css_sheets"), - migrations = "../storage/migrations" -)] -async fn test_staff_can_set_default_css_sheet(pool: PgPool) { - let pool = Arc::new(ConnectionPool::with_pg_pool(pool)); - let (service, user) = create_test_app_and_login( - pool, - MockRedisPool::default(), - 100, - 100, - TestUser::SetDefaultCssSheet, - ) - .await; - - // Set a fixture sheet as default - let req = test::TestRequest::put() - .insert_header(("X-Forwarded-For", "10.10.4.88")) - .insert_header(auth_header(&user.token)) - .uri("/api/css-sheets/test_sheet_1/default") - .to_request(); - - let resp = test::call_service(&service, req).await; - assert_eq!(resp.status(), StatusCode::OK); - - // Get the CSS sheets list and verify the default name is updated - let req = test::TestRequest::get() - .insert_header(("X-Forwarded-For", "10.10.4.88")) - .insert_header(auth_header(&user.token)) - .uri("/api/css-sheets") - .to_request(); - - let sheets = call_and_read_body_json::(&service, req).await; - - assert_eq!(sheets.default_sheet_name, "test_sheet_1"); -} - -#[sqlx::test( - fixtures("with_test_users", "with_test_css_sheets"), - migrations = "../storage/migrations" -)] -async fn test_regular_user_cannot_set_default_css_sheet(pool: PgPool) { - let pool = Arc::new(ConnectionPool::with_pg_pool(pool)); - let (service, user) = - create_test_app_and_login(pool, MockRedisPool::default(), 100, 100, TestUser::Standard) - .await; - - let req = test::TestRequest::put() - .insert_header(("X-Forwarded-For", "10.10.4.88")) - .insert_header(auth_header(&user.token)) - .uri("/api/css-sheets/test_sheet_1/default") - .to_request(); - - let resp = test::call_service(&service, req).await; - assert_eq!(resp.status(), StatusCode::FORBIDDEN); -} - #[sqlx::test( fixtures("with_test_users", "with_test_css_sheets"), migrations = "../storage/migrations" diff --git a/backend/common/src/error/mod.rs b/backend/common/src/error/mod.rs index 1f68b470..45a3067e 100644 --- a/backend/common/src/error/mod.rs +++ b/backend/common/src/error/mod.rs @@ -341,6 +341,12 @@ pub enum Error { #[error("could not find css sheets")] CouldNotFindCssSheets(#[source] sqlx::Error), + #[error("could not find arcadia settings")] + CouldNotFindArcadiaSettings(#[source] sqlx::Error), + + #[error("could not update arcadia settings")] + CouldNotUpdateArcadiaSettings(#[source] sqlx::Error), + #[error("error getting musicbrainz data")] ErrorGettingMusicbrainzData(#[source] musicbrainz_rs::Error), diff --git a/backend/storage/.sqlx/query-0c66730eb56aa805ad572f660ab028a345d78da018b6ad75d1b4e629c763a9c4.json b/backend/storage/.sqlx/query-0c66730eb56aa805ad572f660ab028a345d78da018b6ad75d1b4e629c763a9c4.json index d495e74b..e9102bf9 100644 --- a/backend/storage/.sqlx/query-0c66730eb56aa805ad572f660ab028a345d78da018b6ad75d1b4e629c763a9c4.json +++ b/backend/storage/.sqlx/query-0c66730eb56aa805ad572f660ab028a345d78da018b6ad75d1b4e629c763a9c4.json @@ -62,7 +62,8 @@ "warn_user", "edit_user", "create_wiki_article", - "edit_wiki_article" + "edit_wiki_article", + "edit_arcadia_settings" ] } } diff --git a/backend/storage/.sqlx/query-0ce494c056dbc6166b9a68fd0ddf6e0319adf7db044ea27e1c391c1d5a29f230.json b/backend/storage/.sqlx/query-0ce494c056dbc6166b9a68fd0ddf6e0319adf7db044ea27e1c391c1d5a29f230.json new file mode 100644 index 00000000..23eb532e --- /dev/null +++ b/backend/storage/.sqlx/query-0ce494c056dbc6166b9a68fd0ddf6e0319adf7db044ea27e1c391c1d5a29f230.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT user_class_name_on_signup, default_css_sheet_name\n FROM arcadia_settings\n LIMIT 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "user_class_name_on_signup", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "default_css_sheet_name", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false + ] + }, + "hash": "0ce494c056dbc6166b9a68fd0ddf6e0319adf7db044ea27e1c391c1d5a29f230" +} diff --git a/backend/storage/.sqlx/query-79ed9d5eee7f6d19a6ad85df3666c83828e0e162b8496533552b92efd807cd29.json b/backend/storage/.sqlx/query-1a38ee4ed6ef71e6720ca19ab50bb2e53a2dec75c9fc53bb39135c07226e7563.json similarity index 95% rename from backend/storage/.sqlx/query-79ed9d5eee7f6d19a6ad85df3666c83828e0e162b8496533552b92efd807cd29.json rename to backend/storage/.sqlx/query-1a38ee4ed6ef71e6720ca19ab50bb2e53a2dec75c9fc53bb39135c07226e7563.json index eebffd6b..2b6a0d00 100644 --- a/backend/storage/.sqlx/query-79ed9d5eee7f6d19a6ad85df3666c83828e0e162b8496533552b92efd807cd29.json +++ b/backend/storage/.sqlx/query-1a38ee4ed6ef71e6720ca19ab50bb2e53a2dec75c9fc53bb39135c07226e7563.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n INSERT INTO users (username, email, password_hash, registered_from_ip, passkey, class_name)\n VALUES ($1, $2, $3, $4, $5, $6)\n RETURNING *\n ", + "query": "\n INSERT INTO users (username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n RETURNING *\n ", "describe": { "columns": [ { @@ -142,7 +142,8 @@ "warn_user", "edit_user", "create_wiki_article", - "edit_wiki_article" + "edit_wiki_article", + "edit_arcadia_settings" ] } } @@ -269,6 +270,7 @@ "Varchar", "Inet", "Varchar", + "Varchar", "Varchar" ] }, @@ -315,5 +317,5 @@ false ] }, - "hash": "79ed9d5eee7f6d19a6ad85df3666c83828e0e162b8496533552b92efd807cd29" + "hash": "1a38ee4ed6ef71e6720ca19ab50bb2e53a2dec75c9fc53bb39135c07226e7563" } diff --git a/backend/storage/.sqlx/query-4e362764b8f74fdced061ff73a3f59612e03035f1a9edaa33449602322fc9af6.json b/backend/storage/.sqlx/query-4e362764b8f74fdced061ff73a3f59612e03035f1a9edaa33449602322fc9af6.json index 1d9773b2..af83a2e9 100644 --- a/backend/storage/.sqlx/query-4e362764b8f74fdced061ff73a3f59612e03035f1a9edaa33449602322fc9af6.json +++ b/backend/storage/.sqlx/query-4e362764b8f74fdced061ff73a3f59612e03035f1a9edaa33449602322fc9af6.json @@ -142,7 +142,8 @@ "warn_user", "edit_user", "create_wiki_article", - "edit_wiki_article" + "edit_wiki_article", + "edit_arcadia_settings" ] } } diff --git a/backend/storage/.sqlx/query-68e3b62b9a7961d2145823f7980576bf90ffc9f09fcb8ae7e25d63627b8a9648.json b/backend/storage/.sqlx/query-68e3b62b9a7961d2145823f7980576bf90ffc9f09fcb8ae7e25d63627b8a9648.json deleted file mode 100644 index 5e2b84bf..00000000 --- a/backend/storage/.sqlx/query-68e3b62b9a7961d2145823f7980576bf90ffc9f09fcb8ae7e25d63627b8a9648.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT column_default\n FROM information_schema.columns\n WHERE table_name='users' AND column_name='css_sheet_name';\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "column_default", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - true - ] - }, - "hash": "68e3b62b9a7961d2145823f7980576bf90ffc9f09fcb8ae7e25d63627b8a9648" -} diff --git a/backend/storage/.sqlx/query-6c7d0af18b8564fefa86a9df0e64e25cfc79700d38b516b2e008a9acd969b161.json b/backend/storage/.sqlx/query-6c7d0af18b8564fefa86a9df0e64e25cfc79700d38b516b2e008a9acd969b161.json index 6c14654d..97be9cff 100644 --- a/backend/storage/.sqlx/query-6c7d0af18b8564fefa86a9df0e64e25cfc79700d38b516b2e008a9acd969b161.json +++ b/backend/storage/.sqlx/query-6c7d0af18b8564fefa86a9df0e64e25cfc79700d38b516b2e008a9acd969b161.json @@ -62,7 +62,8 @@ "warn_user", "edit_user", "create_wiki_article", - "edit_wiki_article" + "edit_wiki_article", + "edit_arcadia_settings" ] } } @@ -127,7 +128,8 @@ "warn_user", "edit_user", "create_wiki_article", - "edit_wiki_article" + "edit_wiki_article", + "edit_arcadia_settings" ] } } diff --git a/backend/storage/.sqlx/query-7bd34b94616ece1e265df0a103f810e74a0d758195a7eb7eeaf42e99b6d45103.json b/backend/storage/.sqlx/query-7bd34b94616ece1e265df0a103f810e74a0d758195a7eb7eeaf42e99b6d45103.json index 724911c8..c8fd49d4 100644 --- a/backend/storage/.sqlx/query-7bd34b94616ece1e265df0a103f810e74a0d758195a7eb7eeaf42e99b6d45103.json +++ b/backend/storage/.sqlx/query-7bd34b94616ece1e265df0a103f810e74a0d758195a7eb7eeaf42e99b6d45103.json @@ -57,7 +57,8 @@ "warn_user", "edit_user", "create_wiki_article", - "edit_wiki_article" + "edit_wiki_article", + "edit_arcadia_settings" ] } } diff --git a/backend/storage/.sqlx/query-8a8c1264be51c4286ad8a85a2abb158ee499582e3212920d741cbd8b4494ee2b.json b/backend/storage/.sqlx/query-8a8c1264be51c4286ad8a85a2abb158ee499582e3212920d741cbd8b4494ee2b.json new file mode 100644 index 00000000..d9f4e54b --- /dev/null +++ b/backend/storage/.sqlx/query-8a8c1264be51c4286ad8a85a2abb158ee499582e3212920d741cbd8b4494ee2b.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE arcadia_settings\n SET user_class_name_on_signup = $1,\n default_css_sheet_name = $2\n RETURNING *\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "user_class_name_on_signup", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "default_css_sheet_name", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Varchar" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "8a8c1264be51c4286ad8a85a2abb158ee499582e3212920d741cbd8b4494ee2b" +} diff --git a/backend/storage/.sqlx/query-a3a1ac6a31364650446c2dd008a3df96617ad9aaa623d3ca128e43159eaf8779.json b/backend/storage/.sqlx/query-a3a1ac6a31364650446c2dd008a3df96617ad9aaa623d3ca128e43159eaf8779.json index 9b653f4c..62a82536 100644 --- a/backend/storage/.sqlx/query-a3a1ac6a31364650446c2dd008a3df96617ad9aaa623d3ca128e43159eaf8779.json +++ b/backend/storage/.sqlx/query-a3a1ac6a31364650446c2dd008a3df96617ad9aaa623d3ca128e43159eaf8779.json @@ -59,7 +59,8 @@ "warn_user", "edit_user", "create_wiki_article", - "edit_wiki_article" + "edit_wiki_article", + "edit_arcadia_settings" ] } } diff --git a/backend/storage/.sqlx/query-b2f82503135861043c852764286a61813f7e25f5dac44c1837f959f3dfb22faa.json b/backend/storage/.sqlx/query-b2f82503135861043c852764286a61813f7e25f5dac44c1837f959f3dfb22faa.json index e0f235db..3e09acb8 100644 --- a/backend/storage/.sqlx/query-b2f82503135861043c852764286a61813f7e25f5dac44c1837f959f3dfb22faa.json +++ b/backend/storage/.sqlx/query-b2f82503135861043c852764286a61813f7e25f5dac44c1837f959f3dfb22faa.json @@ -142,7 +142,8 @@ "warn_user", "edit_user", "create_wiki_article", - "edit_wiki_article" + "edit_wiki_article", + "edit_arcadia_settings" ] } } diff --git a/backend/storage/.sqlx/query-b4553e52e21961501368aa72bd9986dc8f6d205b09d98843fd17560554ba9d69.json b/backend/storage/.sqlx/query-b4553e52e21961501368aa72bd9986dc8f6d205b09d98843fd17560554ba9d69.json index 2c837b83..80118543 100644 --- a/backend/storage/.sqlx/query-b4553e52e21961501368aa72bd9986dc8f6d205b09d98843fd17560554ba9d69.json +++ b/backend/storage/.sqlx/query-b4553e52e21961501368aa72bd9986dc8f6d205b09d98843fd17560554ba9d69.json @@ -62,7 +62,8 @@ "warn_user", "edit_user", "create_wiki_article", - "edit_wiki_article" + "edit_wiki_article", + "edit_arcadia_settings" ] } } @@ -126,7 +127,8 @@ "warn_user", "edit_user", "create_wiki_article", - "edit_wiki_article" + "edit_wiki_article", + "edit_arcadia_settings" ] } } diff --git a/backend/storage/.sqlx/query-b4cf1c2d3f776bb107726953676c7b384eced255e96a5486fdd2fb1a90d6883e.json b/backend/storage/.sqlx/query-b4cf1c2d3f776bb107726953676c7b384eced255e96a5486fdd2fb1a90d6883e.json index e9baf89a..ae0acd8d 100644 --- a/backend/storage/.sqlx/query-b4cf1c2d3f776bb107726953676c7b384eced255e96a5486fdd2fb1a90d6883e.json +++ b/backend/storage/.sqlx/query-b4cf1c2d3f776bb107726953676c7b384eced255e96a5486fdd2fb1a90d6883e.json @@ -62,7 +62,8 @@ "warn_user", "edit_user", "create_wiki_article", - "edit_wiki_article" + "edit_wiki_article", + "edit_arcadia_settings" ] } } diff --git a/backend/storage/.sqlx/query-bc7a8046601ae0830b7702bc03b83ddd0f15cc6fc05a317246a06aa293e0d93f.json b/backend/storage/.sqlx/query-bc7a8046601ae0830b7702bc03b83ddd0f15cc6fc05a317246a06aa293e0d93f.json deleted file mode 100644 index ff0b4ee7..00000000 --- a/backend/storage/.sqlx/query-bc7a8046601ae0830b7702bc03b83ddd0f15cc6fc05a317246a06aa293e0d93f.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT quote_literal($1) AS quoted", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "quoted", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "bc7a8046601ae0830b7702bc03b83ddd0f15cc6fc05a317246a06aa293e0d93f" -} diff --git a/backend/storage/.sqlx/query-c5ef39624904dbc651eb31e96568a2d98359844aa26ccb5f9b99952d3ccc3d3c.json b/backend/storage/.sqlx/query-c5ef39624904dbc651eb31e96568a2d98359844aa26ccb5f9b99952d3ccc3d3c.json index 84bc1e43..ee020e6a 100644 --- a/backend/storage/.sqlx/query-c5ef39624904dbc651eb31e96568a2d98359844aa26ccb5f9b99952d3ccc3d3c.json +++ b/backend/storage/.sqlx/query-c5ef39624904dbc651eb31e96568a2d98359844aa26ccb5f9b99952d3ccc3d3c.json @@ -142,7 +142,8 @@ "warn_user", "edit_user", "create_wiki_article", - "edit_wiki_article" + "edit_wiki_article", + "edit_arcadia_settings" ] } } diff --git a/backend/storage/migrations/20250312215600_initdb.sql b/backend/storage/migrations/20250312215600_initdb.sql index 1d859acd..c026cdd9 100644 --- a/backend/storage/migrations/20250312215600_initdb.sql +++ b/backend/storage/migrations/20250312215600_initdb.sql @@ -41,7 +41,8 @@ CREATE TYPE user_permissions_enum AS ENUM ( 'warn_user', 'edit_user', 'create_wiki_article', - 'edit_wiki_article' + 'edit_wiki_article', + 'edit_arcadia_settings' ); CREATE TABLE user_classes ( name VARCHAR(30) UNIQUE NOT NULL, @@ -90,13 +91,12 @@ CREATE TABLE users ( warned BOOLEAN NOT NULL DEFAULT FALSE, banned BOOLEAN NOT NULL DEFAULT FALSE, staff_note TEXT NOT NULL DEFAULT '', - -- the default sheet for a new user is updated in the rust repository as it's too tricky/impossible with triggers - css_sheet_name VARCHAR(30) NOT NULL DEFAULT 'arcadia', + css_sheet_name VARCHAR(30) NOT NULL, UNIQUE(passkey) ); -INSERT INTO users (username, email, password_hash, registered_from_ip, passkey, class_name) -VALUES ('creator', 'none@domain.com', 'none', '127.0.0.1', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'newbie'); +INSERT INTO users (username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name) +VALUES ('creator', 'none@domain.com', 'none', '127.0.0.1', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'newbie', 'arcadia'); CREATE TABLE css_sheets ( created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), created_by_id INT NOT NULL REFERENCES users(id), @@ -113,6 +113,12 @@ ADD CONSTRAINT fk_users_css_sheet FOREIGN KEY (css_sheet_name) REFERENCES css_sheets(name) ON UPDATE CASCADE; +CREATE TABLE arcadia_settings ( + user_class_name_on_signup VARCHAR(30) NOT NULL REFERENCES user_classes(name) ON UPDATE CASCADE, + default_css_sheet_name VARCHAR(30) NOT NULL REFERENCES css_sheets(name) ON UPDATE CASCADE +); +INSERT INTO arcadia_settings (user_class_name_on_signup, default_css_sheet_name) +VALUES ('newbie', 'arcadia'); CREATE TABLE api_keys ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), diff --git a/backend/storage/migrations/fixtures/fixtures.sql b/backend/storage/migrations/fixtures/fixtures.sql index 2275568f..1229d51e 100644 --- a/backend/storage/migrations/fixtures/fixtures.sql +++ b/backend/storage/migrations/fixtures/fixtures.sql @@ -29,11 +29,11 @@ VALUES ('staff', '{create_user_class,edit_user_class,delete_user_class,edit_user -- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: arcadia -- -INSERT INTO public.users VALUES (1, 'creator', NULL, 'none@domain.com', 'none', '127.0.0.1', '2025-09-17 12:42:13.702455+00', '', 0, 0, 1, 1, 0, 0, '2025-09-17 12:42:13.702455+00', 'newbie', false, '{}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '111111111111111111111111111111111', false, false, ''); -INSERT INTO public.users VALUES (5, 'waterbottle', 'https://i.pinimg.com/736x/a6/27/12/a6271204df8d387c3e614986c106f549.jpg', 'user2@example.com', 'hashedpassword2', '192.168.1.2', '2025-03-30 16:24:57.388152+00', '', 0, 0, 1, 1, 0, 0, '2025-03-30 16:24:57.388152+00', 'newbie', false, '{}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '22222222222222222222222222222222', false, false, ''''''); -INSERT INTO public.users VALUES (3, 'coolguy', 'https://i.pinimg.com/474x/c1/5a/6c/c15a6c91515e22f6ea8b766f89c12f0c.jpg', 'user3@example.com', 'hashedpassword3', '192.168.1.3', '2025-03-30 16:24:57.388152+00', '', 0, 0, 1, 1, 0, 0, '2025-03-30 16:24:57.388152+00', 'newbie', false, '{}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '33333333333333333333333333333333', false, false, ''''''); -INSERT INTO public.users VALUES (4, 'test', NULL, 'test@test.tsttt', '$argon2id$v=19$m=19456,t=2,p=1$yaA+WqA4OfSyAqR3iXhDng$/Ngv7VeJvVNHli9rBgQG0d/O2W+qoI2yHhQxZSxxW2M', '127.0.0.1', '2025-04-10 19:15:51.036818+00', '', 979900000000, 0, 1, 1, 0, 0, '2025-09-17 09:15:44.322914+00', 'newbie', false, '{}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99999000, 0, '55555555555555555555555555555555', false, false, ''''''); -INSERT INTO public.users VALUES (2, 'picolo', 'https://img.freepik.com/premium-vector/random-people-line-art-vector_567805-63.jpg', 'user1@example.com', '$argon2id$v=19$m=19456,t=2,p=1$s4XJtCUk9IrGgNsTfP6Ofw$ktoGbBEoFaVgdiTn19Gh9h45LjFiv7AUEL5KHhzm4d0', '192.168.1.1', '2025-03-30 16:24:57.388152+00', '', 10000, 0, 1, 1, 0, 0, '2025-11-28 17:29:24.968105+00', 'newbie', false, '{create_user_class,edit_user_class,delete_user_class,edit_user_permissions,change_user_class,lock_user_class,upload_torrent,download_torrent,create_torrent_request,immune_activity_pruning,edit_title_group,edit_title_group_comment,edit_edition_group,edit_torrent,edit_artist,edit_collage,edit_series,edit_torrent_request,edit_forum_post,edit_forum_thread,edit_forum_sub_category,edit_forum_category,create_forum_category,create_forum_sub_category,create_forum_thread,create_forum_post,send_pm,create_css_sheet,edit_css_sheet,set_default_css_sheet,read_staff_pm,reply_staff_pm,resolve_staff_pm,unresolve_staff_pm,delete_title_group_tag,edit_title_group_tag,delete_torrent,get_user_application,update_user_application,warn_user,edit_user,create_wiki_article,edit_wiki_article}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 999999410, 0, '44444444444444444444444444444444', false, false, ''''''); +INSERT INTO public.users VALUES (1, 'creator', NULL, 'none@domain.com', 'none', '127.0.0.1', '2025-09-17 12:42:13.702455+00', '', 0, 0, 1, 1, 0, 0, '2025-09-17 12:42:13.702455+00', 'newbie', false, '{}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '111111111111111111111111111111111', false, false, '', 'arcadia'); +INSERT INTO public.users VALUES (5, 'waterbottle', 'https://i.pinimg.com/736x/a6/27/12/a6271204df8d387c3e614986c106f549.jpg', 'user2@example.com', 'hashedpassword2', '192.168.1.2', '2025-03-30 16:24:57.388152+00', '', 0, 0, 1, 1, 0, 0, '2025-03-30 16:24:57.388152+00', 'newbie', false, '{}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '22222222222222222222222222222222', false, false, '''''', 'arcadia'); +INSERT INTO public.users VALUES (3, 'coolguy', 'https://i.pinimg.com/474x/c1/5a/6c/c15a6c91515e22f6ea8b766f89c12f0c.jpg', 'user3@example.com', 'hashedpassword3', '192.168.1.3', '2025-03-30 16:24:57.388152+00', '', 0, 0, 1, 1, 0, 0, '2025-03-30 16:24:57.388152+00', 'newbie', false, '{}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '33333333333333333333333333333333', false, false, '''''', 'arcadia'); +INSERT INTO public.users VALUES (4, 'test', NULL, 'test@test.tsttt', '$argon2id$v=19$m=19456,t=2,p=1$yaA+WqA4OfSyAqR3iXhDng$/Ngv7VeJvVNHli9rBgQG0d/O2W+qoI2yHhQxZSxxW2M', '127.0.0.1', '2025-04-10 19:15:51.036818+00', '', 979900000000, 0, 1, 1, 0, 0, '2025-09-17 09:15:44.322914+00', 'newbie', false, '{}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99999000, 0, '55555555555555555555555555555555', false, false, '''''', 'arcadia'); +INSERT INTO public.users VALUES (2, 'picolo', 'https://img.freepik.com/premium-vector/random-people-line-art-vector_567805-63.jpg', 'user1@example.com', '$argon2id$v=19$m=19456,t=2,p=1$s4XJtCUk9IrGgNsTfP6Ofw$ktoGbBEoFaVgdiTn19Gh9h45LjFiv7AUEL5KHhzm4d0', '192.168.1.1', '2025-03-30 16:24:57.388152+00', '', 10000, 0, 1, 1, 0, 0, '2025-11-28 17:29:24.968105+00', 'newbie', false, '{create_user_class,edit_user_class,delete_user_class,edit_user_permissions,change_user_class,lock_user_class,upload_torrent,download_torrent,create_torrent_request,immune_activity_pruning,edit_title_group,edit_title_group_comment,edit_edition_group,edit_torrent,edit_artist,edit_collage,edit_series,edit_torrent_request,edit_forum_post,edit_forum_thread,edit_forum_sub_category,edit_forum_category,create_forum_category,create_forum_sub_category,create_forum_thread,create_forum_post,send_pm,create_css_sheet,edit_css_sheet,set_default_css_sheet,read_staff_pm,reply_staff_pm,resolve_staff_pm,unresolve_staff_pm,delete_title_group_tag,edit_title_group_tag,delete_torrent,get_user_application,update_user_application,warn_user,edit_user,create_wiki_article,edit_wiki_article}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 999999410, 0, '44444444444444444444444444444444', false, false, '''''', 'arcadia'); -- diff --git a/backend/storage/src/models/arcadia_settings.rs b/backend/storage/src/models/arcadia_settings.rs new file mode 100644 index 00000000..dbe34b75 --- /dev/null +++ b/backend/storage/src/models/arcadia_settings.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; +use sqlx::prelude::FromRow; +use utoipa::ToSchema; + +#[derive(Debug, Clone, Serialize, Deserialize, FromRow, ToSchema)] +pub struct ArcadiaSettings { + pub user_class_name_on_signup: String, + pub default_css_sheet_name: String, +} diff --git a/backend/storage/src/models/mod.rs b/backend/storage/src/models/mod.rs index 71508a12..4825f326 100644 --- a/backend/storage/src/models/mod.rs +++ b/backend/storage/src/models/mod.rs @@ -1,3 +1,4 @@ +pub mod arcadia_settings; pub mod artist; pub mod collage; pub mod common; diff --git a/backend/storage/src/models/user.rs b/backend/storage/src/models/user.rs index b354e455..ff1ba161 100644 --- a/backend/storage/src/models/user.rs +++ b/backend/storage/src/models/user.rs @@ -102,6 +102,7 @@ pub enum UserPermission { EditUserPermissions, LockUserClass, ChangeUserClass, + EditArcadiaSettings, } #[derive(Debug, Serialize, Deserialize, ToSchema)] diff --git a/backend/storage/src/repositories/arcadia_settings_repository.rs b/backend/storage/src/repositories/arcadia_settings_repository.rs new file mode 100644 index 00000000..da732fc5 --- /dev/null +++ b/backend/storage/src/repositories/arcadia_settings_repository.rs @@ -0,0 +1,43 @@ +use crate::{connection_pool::ConnectionPool, models::arcadia_settings::ArcadiaSettings}; +use arcadia_common::error::{Error, Result}; +use std::borrow::Borrow; + +impl ConnectionPool { + pub async fn get_arcadia_settings(&self) -> Result { + let settings = sqlx::query_as!( + ArcadiaSettings, + r#" + SELECT user_class_name_on_signup, default_css_sheet_name + FROM arcadia_settings + LIMIT 1 + "#, + ) + .fetch_one(self.borrow()) + .await + .map_err(Error::CouldNotFindArcadiaSettings)?; + + Ok(settings) + } + + pub async fn update_arcadia_settings( + &self, + settings: &ArcadiaSettings, + ) -> Result { + let updated_settings = sqlx::query_as!( + ArcadiaSettings, + r#" + UPDATE arcadia_settings + SET user_class_name_on_signup = $1, + default_css_sheet_name = $2 + RETURNING * + "#, + settings.user_class_name_on_signup, + settings.default_css_sheet_name + ) + .fetch_one(self.borrow()) + .await + .map_err(Error::CouldNotUpdateArcadiaSettings)?; + + Ok(updated_settings) + } +} diff --git a/backend/storage/src/repositories/auth_repository.rs b/backend/storage/src/repositories/auth_repository.rs index ab1022a0..73b5ac0a 100644 --- a/backend/storage/src/repositories/auth_repository.rs +++ b/backend/storage/src/repositories/auth_repository.rs @@ -37,6 +37,7 @@ impl ConnectionPool { invitation: &Invitation, open_signups: &bool, user_class_name: &str, + css_sheet_name: &str, ) -> Result { let rng = rand::rng(); @@ -55,8 +56,8 @@ impl ConnectionPool { let registered_user = sqlx::query_as_unchecked!( User, r#" - INSERT INTO users (username, email, password_hash, registered_from_ip, passkey, class_name) - VALUES ($1, $2, $3, $4, $5, $6) + INSERT INTO users (username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name) + VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING * "#, &user.username, @@ -64,7 +65,8 @@ impl ConnectionPool { password_hash, from_ip, passkey, - user_class_name + user_class_name, + css_sheet_name ) .fetch_one(self.borrow()) .await diff --git a/backend/storage/src/repositories/css_sheet_repository.rs b/backend/storage/src/repositories/css_sheet_repository.rs index 50c04bc0..76d22af2 100644 --- a/backend/storage/src/repositories/css_sheet_repository.rs +++ b/backend/storage/src/repositories/css_sheet_repository.rs @@ -1,6 +1,6 @@ use crate::{ connection_pool::ConnectionPool, - models::css_sheet::{CssSheet, CssSheetsEnriched, EditedCssSheet, UserCreatedCssSheet}, + models::css_sheet::{CssSheet, EditedCssSheet, UserCreatedCssSheet}, }; use arcadia_common::error::{Error, Result}; use std::borrow::Borrow; @@ -30,7 +30,7 @@ impl ConnectionPool { Ok(css_sheet) } - pub async fn find_css_sheets(&self) -> Result { + pub async fn find_css_sheets(&self) -> Result> { let sheets = sqlx::query_as!( CssSheet, r#" @@ -41,35 +41,7 @@ impl ConnectionPool { .await .map_err(Error::CouldNotFindCssSheets)?; - let default_sheet_name = Self::find_default_css_sheet_name(self).await?; - - Ok(CssSheetsEnriched { - css_sheets: sheets, - default_sheet_name, - }) - } - - pub async fn find_default_css_sheet_name(&self) -> Result { - let mut default_sheet_name = sqlx::query_scalar!( - r#" - SELECT column_default - FROM information_schema.columns - WHERE table_name='users' AND column_name='css_sheet_name'; - "#, - ) - .fetch_one(self.borrow()) - .await - .map_err(Error::CouldNotFindCssSheets)? - .unwrap(); - - default_sheet_name = regex::Regex::new(r#"^'(.*)'::\s*character varying$"#) - .unwrap() - .captures(&default_sheet_name) - .and_then(|caps| caps.get(1)) - .map(|m| m.as_str().to_string()) - .unwrap(); - - Ok(default_sheet_name) + Ok(sheets) } pub async fn find_css_sheet(&self, name: &str) -> Result { @@ -88,8 +60,6 @@ impl ConnectionPool { } pub async fn update_css_sheet(&self, form: &EditedCssSheet) -> Result { - let default_sheet_name = Self::find_default_css_sheet_name(self).await?; - let css_sheet = sqlx::query_as!( CssSheet, r#" @@ -107,31 +77,9 @@ impl ConnectionPool { .await .map_err(Error::CssSheetNotFound)?; - if form.old_name == default_sheet_name { - Self::set_default_css_sheet(self, &form.name).await?; - } - Ok(css_sheet) } - pub async fn set_default_css_sheet(&self, sheet_name: &str) -> Result<()> { - let sql = format!( - "ALTER TABLE users ALTER COLUMN css_sheet_name SET DEFAULT {}", - sqlx::query_scalar!("SELECT quote_literal($1) AS quoted", sheet_name) - .fetch_one(self.borrow()) - .await - .map_err(Error::CouldNotUpdateDefaultCssSheet)? - .unwrap() - ); - - sqlx::query(&sql) - .execute(self.borrow()) - .await - .map_err(Error::CouldNotUpdateDefaultCssSheet)?; - - Ok(()) - } - pub async fn set_css_sheet_for_user( &self, user_id: i32, diff --git a/backend/storage/src/repositories/mod.rs b/backend/storage/src/repositories/mod.rs index a0f5b42c..d0afe96a 100644 --- a/backend/storage/src/repositories/mod.rs +++ b/backend/storage/src/repositories/mod.rs @@ -1,3 +1,4 @@ +pub mod arcadia_settings_repository; pub mod artist_repository; pub mod auth_repository; pub mod collage_repository; diff --git a/tracker/arcadia_tracker/tests/fixtures/with_test_user.sql b/tracker/arcadia_tracker/tests/fixtures/with_test_user.sql index e6d95f77..9ed9ec23 100644 --- a/tracker/arcadia_tracker/tests/fixtures/with_test_user.sql +++ b/tracker/arcadia_tracker/tests/fixtures/with_test_user.sql @@ -1,6 +1,6 @@ INSERT INTO - users (username, email, password_hash, registered_from_ip, passkey, class_name) + users (username, email, password_hash, registered_from_ip, passkey, class_name, css_sheet_name) VALUES - ('test_user', 'test_email@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3837', 'newbie'); + ('test_user', 'test_email@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'd2037c66dd3e13044e0d2f9b891c3837', 'newbie', 'arcadia');