diff options
Diffstat (limited to 'server/src/ui/account')
-rw-r--r-- | server/src/ui/account/mod.rs | 171 | ||||
-rw-r--r-- | server/src/ui/account/settings.rs | 47 |
2 files changed, 90 insertions, 128 deletions
diff --git a/server/src/ui/account/mod.rs b/server/src/ui/account/mod.rs index 54fa4d0..a9c28ea 100644 --- a/server/src/ui/account/mod.rs +++ b/server/src/ui/account/mod.rs @@ -8,22 +8,31 @@ pub mod settings; use super::error::MyError; use crate::{ database::Database, + helper::A, locale::AcceptLanguage, ui::{error::MyResult, home::rocket_uri_macro_r_home}, }; use anyhow::anyhow; -use chrono::Duration; -use jellycommon::user::{User, UserPermission}; +use jellycommon::user::User; +use jellyimport::is_importing; +use jellylogic::{ + login::{hash_password, login_logic}, + session::Session, +}; +use jellyui::{ + account::{AccountLogin, AccountLogout, AccountRegister, AccountRegisterSuccess}, + render_page, + scaffold::{RenderInfo, SessionInfo}, +}; use rocket::{ form::{Contextual, Form}, get, http::{Cookie, CookieJar}, post, - response::Redirect, + response::{content::RawHtml, Redirect}, FromForm, State, }; use serde::{Deserialize, Serialize}; -use std::collections::HashSet; #[derive(FromForm)] pub struct RegisterForm { @@ -36,15 +45,16 @@ pub struct RegisterForm { } #[get("/account/register")] -pub async fn r_account_register(lang: AcceptLanguage) -> DynLayoutPage<'static> { +pub async fn r_account_register(lang: AcceptLanguage) -> RawHtml<String> { let AcceptLanguage(lang) = lang; - LayoutPage { - title: tr(lang, "account.register").to_string(), - content: markup::new! { - + RawHtml(render_page( + &AccountRegister { lang: &lang }, + RenderInfo { + importing: false, + session: None, }, - ..Default::default() - } + lang, + )) } #[derive(FromForm, Serialize, Deserialize)] @@ -58,69 +68,47 @@ pub struct LoginForm { } #[get("/account/login")] -pub fn r_account_login(sess: Option<Session>, lang: AcceptLanguage) -> DynLayoutPage<'static> { +pub fn r_account_login(session: Option<A<Session>>, lang: AcceptLanguage) -> RawHtml<String> { let AcceptLanguage(lang) = lang; - let logged_in = sess.is_some(); - let title = tr( - lang, - if logged_in { - "account.login.switch" - } else { - "account.login" + let logged_in = session.is_some(); + RawHtml(render_page( + &AccountLogin { + lang: &lang, + logged_in, }, - ); - LayoutPage { - title: title.to_string(), - content: markup::new! { - form.account[method="POST", action=""] { - h1 { @title.to_string() } - - label[for="inp-username"] { @trs(&lang, "account.username") } - input[type="text", id="inp-username", name="username"]; br; - label[for="inp-password"] { @trs(&lang, "account.password") } - input[type="password", id="inp-password", name="password"]; br; - - input[type="submit", value=&*tr(lang, if logged_in { "account.login.submit.switch" } else { "account.login.submit" })]; - - @if logged_in { - p { @trs(&lang, "account.login.register.switch") " " a[href=uri!(r_account_register())] { @trs(&lang, "account.login.register_here") } } - } else { - p { @trs(&lang, "account.login.cookie_note") } - p { @trs(&lang, "account.login.register") " " a[href=uri!(r_account_register())] { @trs(&lang, "account.login.register_here") } } - } - } + RenderInfo { + session: session.map(|s| SessionInfo { user: s.0.user }), + importing: is_importing(), }, - ..Default::default() - } + lang, + )) } #[get("/account/logout")] -pub fn r_account_logout(lang: AcceptLanguage) -> DynLayoutPage<'static> { +pub fn r_account_logout(session: Option<A<Session>>, lang: AcceptLanguage) -> RawHtml<String> { let AcceptLanguage(lang) = lang; - LayoutPage { - title: tr(lang, "account.logout").to_string(), - content: markup::new! { - form.account[method="POST", action=""] { - h1 { @trs(&lang, "account.logout") } - input[type="submit", value=&*tr(lang, "account.logout.submit")]; - } + RawHtml(render_page( + &AccountLogout { lang: &lang }, + RenderInfo { + session: session.map(|s| SessionInfo { user: s.0.user }), + importing: is_importing(), }, - ..Default::default() - } + lang, + )) } #[post("/account/register", data = "<form>")] pub fn r_account_register_post<'a>( database: &'a State<Database>, - _sess: Option<Session>, + session: Option<A<Session>>, form: Form<Contextual<'a, RegisterForm>>, lang: AcceptLanguage, -) -> MyResult<DynLayoutPage<'a>> { +) -> MyResult<RawHtml<String>> { let AcceptLanguage(lang) = lang; - let logged_in = _sess.is_some(); + let logged_in = session.is_some(); let form = match &form.value { Some(v) => v, - None => return Err(format_form_error(form)), + None => return Err(MyError(anyhow!(format_form_error(form)))), }; database.register_user( @@ -134,17 +122,17 @@ pub fn r_account_register_post<'a>( }, )?; - Ok(LayoutPage { - title: tr(lang, "account.register.success.title").to_string(), - content: markup::new! { - h1 { @trs(&lang, if logged_in { - "account.register.success.switch" - } else { - "account.register.success" - })} + Ok(RawHtml(render_page( + &AccountRegisterSuccess { + lang: &lang, + logged_in, + }, + RenderInfo { + session: session.map(|s| SessionInfo { user: s.0.user }), + importing: is_importing(), }, - ..Default::default() - }) + lang, + ))) } #[post("/account/login", data = "<form>")] @@ -155,7 +143,7 @@ pub fn r_account_login_post( ) -> MyResult<Redirect> { let form = match &form.value { Some(v) => v, - None => return Err(format_form_error(form)), + None => return Err(MyError(anyhow!(format_form_error(form)))), }; jar.add( Cookie::build(( @@ -175,39 +163,7 @@ pub fn r_account_logout_post(jar: &CookieJar) -> MyResult<Redirect> { Ok(Redirect::found(rocket::uri!(r_home()))) } -pub fn login_logic( - database: &Database, - username: &str, - password: &str, - expire: Option<i64>, - drop_permissions: Option<HashSet<UserPermission>>, -) -> MyResult<String> { - // hashing the password regardless if the accounts exists to better resist timing attacks - let password = hash_password(username, password); - - let mut user = database - .get_user(username)? - .ok_or(anyhow!("invalid password"))?; - - if user.password != password { - Err(anyhow!("invalid password"))? - } - - if let Some(ep) = drop_permissions { - // remove all grant perms that are in `ep` - user.permissions - .0 - .retain(|p, val| if *val { !ep.contains(p) } else { true }) - } - - Ok(session::create( - user.name, - user.permissions, - Duration::days(CONF.login_expire.min(expire.unwrap_or(i64::MAX))), - )) -} - -pub fn format_form_error<T>(form: Form<Contextual<T>>) -> MyError { +pub fn format_form_error<T>(form: Form<Contextual<T>>) -> String { let mut k = String::from("form validation failed:"); for e in form.context.errors() { k += &format!( @@ -218,18 +174,5 @@ pub fn format_form_error<T>(form: Form<Contextual<T>>) -> MyError { .unwrap_or("<unknown>".to_string()) ) } - MyError(anyhow!(k)) -} - -pub fn hash_password(username: &str, password: &str) -> Vec<u8> { - Argon2::default() - .hash_password( - format!("{username}\0{password}").as_bytes(), - <&str as TryInto<Salt>>::try_into("IYMa13osbNeLJKnQ1T8LlA").unwrap(), - ) - .unwrap() - .hash - .unwrap() - .as_bytes() - .to_vec() + k } diff --git a/server/src/ui/account/settings.rs b/server/src/ui/account/settings.rs index e6d096f..5355321 100644 --- a/server/src/ui/account/settings.rs +++ b/server/src/ui/account/settings.rs @@ -4,16 +4,20 @@ Copyright (C) 2025 metamuffin <metamuffin.org> */ use super::{format_form_error, hash_password}; -use crate::{database::Database, locale::AcceptLanguage, ui::error::MyResult}; +use crate::{database::Database, helper::A, locale::AcceptLanguage, ui::error::MyResult}; use jellybase::permission::PermissionSetExt; use jellycommon::user::{PlayerKind, Theme, UserPermission}; +use jellyimport::is_importing; use jellylogic::session::Session; -use jellyui::locale::Language; +use jellyui::{ + account::settings::SettingsPage, + locale::{tr, Language}, + render_page, + scaffold::{RenderInfo, SessionInfo}, +}; use rocket::{ form::{self, validate::len, Contextual, Form}, - get, - http::uri::fmt::{Query, UriDisplay}, - post, + get, post, response::content::RawHtml, FromForm, State, }; @@ -25,8 +29,8 @@ pub struct SettingsForm { password: Option<String>, #[field(validate = option_len(4..32))] display_name: Option<String>, - theme: Option<Theme>, - player_preference: Option<PlayerKind>, + theme: Option<A<Theme>>, + player_preference: Option<A<PlayerKind>>, native_secret: Option<String>, } @@ -36,25 +40,40 @@ fn option_len<'v>(value: &Option<String>, range: Range<usize>) -> form::Result<' fn settings_page( session: Session, - flash: Option<MyResult<String>>, + flash: Option<Result<String, String>>, lang: Language, ) -> RawHtml<String> { + RawHtml(render_page( + &SettingsPage { + flash, + session: &SessionInfo { + user: session.user.clone(), + }, + lang: &lang, + }, + RenderInfo { + importing: is_importing(), + session: Some(SessionInfo { user: session.user }), + }, + lang, + )) } #[get("/account/settings")] -pub fn r_account_settings(session: Session, lang: AcceptLanguage) -> RawHtml<String> { +pub fn r_account_settings(session: A<Session>, lang: AcceptLanguage) -> RawHtml<String> { let AcceptLanguage(lang) = lang; - settings_page(session, None, lang) + settings_page(session.0, None, lang) } #[post("/account/settings", data = "<form>")] pub fn r_account_settings_post( - session: Session, + session: A<Session>, database: &State<Database>, form: Form<Contextual<SettingsForm>>, lang: AcceptLanguage, ) -> MyResult<RawHtml<String>> { let AcceptLanguage(lang) = lang; + let A(session) = session; session .user .permissions @@ -65,7 +84,7 @@ pub fn r_account_settings_post( None => { return Ok(settings_page( session, - Some(Err(format_form_error(form))), + Some(Err(format_form_error(form).to_string())), lang, )) } @@ -85,12 +104,12 @@ pub fn r_account_settings_post( out += "\n"; } if let Some(theme) = form.theme { - user.theme = theme; + user.theme = theme.0; out += &*tr(lang, "settings.account.theme.changed"); out += "\n"; } if let Some(player_preference) = form.player_preference { - user.player_preference = player_preference; + user.player_preference = player_preference.0; out += &*tr(lang, "settings.player_preference.changed"); out += "\n"; } |