From 69e84b42660ce8e8778a6fc0724481d40b00412b Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Thu, 28 Aug 2025 13:46:43 +0200 Subject: [PATCH] feat(server): add config for enabling public signup view --- CHANGELOG.md | 1 + otter.toml | 2 ++ otter/src/cli/serve.rs | 1 + otter/src/config.rs | 6 ++++-- otter/src/server/mod.rs | 1 + otter/src/server/web/auth.rs | 23 ++++++++++++++++------- otter/src/web/mod.rs | 2 +- otter/src/web/templates/views/login.html | 4 ++++ otter/src/web/view.rs | 9 +++++++-- 9 files changed, 37 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 135063c..5ac346d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://git.rustybever.be/Chewing_Bever/otter) * CLI command to add new users +* Added public sign-up page (disabled by default) ## [0.2.1](https://git.rustybever.be/Chewing_Bever/otter/src/tag/0.2.1) diff --git a/otter.toml b/otter.toml index 70b52a4..00c516b 100644 --- a/otter.toml +++ b/otter.toml @@ -1,6 +1,8 @@ data_dir = "./data" log_level = "debug" +allow_public_signup = true + [net] type = "tcp" domain = "127.0.0.1" diff --git a/otter/src/cli/serve.rs b/otter/src/cli/serve.rs index 4bb000a..d2fe1c7 100644 --- a/otter/src/cli/serve.rs +++ b/otter/src/cli/serve.rs @@ -23,6 +23,7 @@ pub fn serve(config: &crate::config::Config) -> Result<(), CliError> { let ctx = server::Context { store, tera: Arc::new(tera), + config: config.clone(), }; let app = server::app(ctx.clone()); diff --git a/otter/src/config.rs b/otter/src/config.rs index 25b4195..e71de9a 100644 --- a/otter/src/config.rs +++ b/otter/src/config.rs @@ -23,7 +23,7 @@ impl From for tracing::Level { } } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] #[serde(rename_all = "lowercase")] #[serde(tag = "type")] pub enum NetConfig { @@ -31,12 +31,13 @@ pub enum NetConfig { Unix { path: PathBuf }, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub struct Config { pub net: NetConfig, pub data_dir: PathBuf, pub session_cleanup_interval: u64, pub log_level: LogLevel, + pub allow_public_signup: bool, } impl Default for Config { @@ -50,6 +51,7 @@ impl Default for Config { // Once per day session_cleanup_interval: 60 * 60 * 24, log_level: LogLevel::Warn, + allow_public_signup: false, } } } diff --git a/otter/src/server/mod.rs b/otter/src/server/mod.rs index 20886c6..97d30c6 100644 --- a/otter/src/server/mod.rs +++ b/otter/src/server/mod.rs @@ -20,6 +20,7 @@ use tower_http::trace::TraceLayer; pub struct Context { pub store: ::gpodder::GpodderRepository, pub tera: Arc, + pub config: crate::config::Config, } pub fn app(ctx: Context) -> Router { diff --git a/otter/src/server/web/auth.rs b/otter/src/server/web/auth.rs index 51f48f0..34b84af 100644 --- a/otter/src/server/web/auth.rs +++ b/otter/src/server/web/auth.rs @@ -21,7 +21,7 @@ use crate::{ }; pub fn router(ctx: Context) -> Router { - Router::new() + let mut router = Router::new() // .layer(middleware::from_fn_with_state( // ctx.clone(), // auth_web_middleware, @@ -29,8 +29,15 @@ pub fn router(ctx: Context) -> Router { // Login route needs to be handled differently, as the middleware turns it into a redirect // loop .route("/login", get(get_login).post(post_login)) - .route("/logout", post(post_logout)) - .route("/signup", get(get_signup).post(post_signup)) + .route("/logout", post(post_logout)); + + // If public signups aren't allowed, we don't even register the route to prevent any dumb + // security mistakes + if ctx.config.allow_public_signup { + router = router.route("/signup", get(get_signup).post(post_signup)) + } + + router } /// Middleware that authenticates the current user via the session token. If the credentials are @@ -87,10 +94,12 @@ async fn get_login(State(ctx): State, headers: HeaderMap, jar: CookieJa { Redirect::to("/").into_response() } else { - View::Login - .page(&headers) - .response(&ctx.tera) - .into_response() + View::Login { + signup_note: ctx.config.allow_public_signup, + } + .page(&headers) + .response(&ctx.tera) + .into_response() } } diff --git a/otter/src/web/mod.rs b/otter/src/web/mod.rs index 7abb954..d2977eb 100644 --- a/otter/src/web/mod.rs +++ b/otter/src/web/mod.rs @@ -78,7 +78,7 @@ pub fn initialize_tera() -> tera::Result { include_str!("templates/views/index.html"), ), ( - View::Login.template(), + View::Login { signup_note: false }.template(), include_str!("templates/views/login.html"), ), ( diff --git a/otter/src/web/templates/views/login.html b/otter/src/web/templates/views/login.html index b81094c..8dd61a2 100644 --- a/otter/src/web/templates/views/login.html +++ b/otter/src/web/templates/views/login.html @@ -6,4 +6,8 @@ + + {% if signup_note %} +

Don't have an account yet? Create one here!

+ {% endif %} diff --git a/otter/src/web/view.rs b/otter/src/web/view.rs index ce1e54a..101d13e 100644 --- a/otter/src/web/view.rs +++ b/otter/src/web/view.rs @@ -5,7 +5,9 @@ use super::{Query, Template}; pub enum View { Index, - Login, + Login { + signup_note: bool, + }, Sessions(Vec, i64, Option), Users(Vec, i64, Option), Signup { @@ -32,7 +34,7 @@ impl Template for View { fn template(&self) -> &'static str { match self { Self::Index => "views/index.html", - Self::Login => "views/login.html", + Self::Login { .. } => "views/login.html", Self::Sessions(..) => "views/sessions.html", Self::Users(..) => "views/users.html", Self::Signup { .. } => "views/signup.html", @@ -76,6 +78,9 @@ impl Template for View { ctx.insert("username_available", &username_available); ctx.insert("passwords_match", &passwords_match); } + Self::Login { signup_note } => { + ctx.insert("signup_note", &signup_note); + } _ => {} };