diff options
author | metamuffin <metamuffin@disroot.org> | 2025-05-31 03:26:45 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-05-31 03:26:45 +0200 |
commit | 3e834092ba230ee081065a3b80ad227d585b5a13 (patch) | |
tree | b168d1524045716e6922c137f1ad92bdf3f47994 | |
parent | 3b15caade07e8fbe351fed9aceb3f435bf58368e (diff) | |
download | jellything-3e834092ba230ee081065a3b80ad227d585b5a13.tar jellything-3e834092ba230ee081065a3b80ad227d585b5a13.tar.bz2 jellything-3e834092ba230ee081065a3b80ad227d585b5a13.tar.zst |
get rid of admin session; checking manually instead
-rw-r--r-- | locale/en.ini | 1 | ||||
-rw-r--r-- | logic/src/admin/mod.rs | 22 | ||||
-rw-r--r-- | logic/src/admin/user.rs | 14 | ||||
-rw-r--r-- | logic/src/lib.rs | 1 | ||||
-rw-r--r-- | logic/src/permission.rs | 18 | ||||
-rw-r--r-- | logic/src/session.rs | 2 | ||||
-rw-r--r-- | server/src/api.rs | 9 | ||||
-rw-r--r-- | server/src/helper/session.rs | 27 | ||||
-rw-r--r-- | server/src/ui/admin/log.rs | 15 | ||||
-rw-r--r-- | server/src/ui/admin/mod.rs | 30 | ||||
-rw-r--r-- | server/src/ui/admin/user.rs | 22 | ||||
-rw-r--r-- | ui/src/admin/mod.rs | 2 |
12 files changed, 88 insertions, 75 deletions
diff --git a/locale/en.ini b/locale/en.ini index 944a29d..58433a4 100644 --- a/locale/en.ini +++ b/locale/en.ini @@ -106,6 +106,7 @@ prop.vis.reduced=Reduced visibility admin.dashboard.title=Admin Panel admin.dashboard.import.inc=Start incremental import admin.dashboard.import.full=Start full import +admin.invite_create_success=Invite created: {invite} page.curr=Page {cur} of {max} page.prev=Previous page diff --git a/logic/src/admin/mod.rs b/logic/src/admin/mod.rs index 804cb2b..d8b21b6 100644 --- a/logic/src/admin/mod.rs +++ b/logic/src/admin/mod.rs @@ -7,38 +7,40 @@ pub mod log; pub mod user; -use crate::{DATABASE, session::AdminSession}; +use crate::{DATABASE, session::Session}; use anyhow::{Result, anyhow}; use jellyimport::{IMPORT_ERRORS, import_wrap}; use rand::Rng; use std::time::{Duration, Instant}; use tokio::task::spawn_blocking; -pub async fn get_import_errors(_session: &AdminSession) -> Vec<String> { +pub async fn get_import_errors(_session: &Session) -> Vec<String> { IMPORT_ERRORS.read().await.to_owned() } -pub fn list_invites(_session: &AdminSession) -> Result<Vec<String>> { +pub fn list_invites(session: &Session) -> Result<Vec<String>> { + session.assert_admin()?; DATABASE.list_invites() } -pub fn create_invite(_session: &AdminSession) -> Result<String> { +pub fn create_invite(session: &Session) -> Result<String> { + session.assert_admin()?; let i = format!("{}", rand::rng().random::<u128>()); DATABASE.create_invite(&i)?; Ok(i) } -pub fn delete_invite(_session: &AdminSession, invite: &str) -> Result<()> { +pub fn delete_invite(session: &Session, invite: &str) -> Result<()> { + session.assert_admin()?; if !DATABASE.delete_invite(invite)? { Err(anyhow!("invite does not exist"))?; }; Ok(()) } -pub async fn update_search_index(_session: &AdminSession) -> Result<()> { +pub async fn update_search_index(session: &Session) -> Result<()> { + session.assert_admin()?; spawn_blocking(move || DATABASE.search_create_index()).await? } -pub async fn do_import( - _session: &AdminSession, - incremental: bool, -) -> Result<(Duration, Result<()>)> { +pub async fn do_import(session: &Session, incremental: bool) -> Result<(Duration, Result<()>)> { + session.assert_admin()?; let t = Instant::now(); if !incremental { DATABASE.clear_nodes()?; diff --git a/logic/src/admin/user.rs b/logic/src/admin/user.rs index e277077..15356a8 100644 --- a/logic/src/admin/user.rs +++ b/logic/src/admin/user.rs @@ -4,25 +4,28 @@ Copyright (C) 2025 metamuffin <metamuffin.org> */ -use crate::{DATABASE, session::AdminSession}; +use crate::{DATABASE, session::Session}; use anyhow::{Result, anyhow}; use jellycommon::{ api::ApiAdminUsersResponse, user::{User, UserPermission}, }; -pub fn admin_users(_session: &AdminSession) -> Result<ApiAdminUsersResponse> { +pub fn admin_users(session: &Session) -> Result<ApiAdminUsersResponse> { + session.assert_admin()?; // TODO dont return useless info like passwords Ok(ApiAdminUsersResponse { users: DATABASE.list_users()?, }) } -pub fn get_user(_session: &AdminSession, username: &str) -> Result<User> { +pub fn get_user(session: &Session, username: &str) -> Result<User> { + session.assert_admin()?; DATABASE .get_user(username)? .ok_or(anyhow!("user not found")) } -pub fn delete_user(_session: &AdminSession, username: &str) -> Result<()> { +pub fn delete_user(session: &Session, username: &str) -> Result<()> { + session.assert_admin()?; if !DATABASE.delete_user(&username)? { Err(anyhow!("user did not exist"))?; } @@ -35,11 +38,12 @@ pub enum GrantState { Unset, } pub fn update_user_perms( - _session: &AdminSession, + session: &Session, username: &str, perm: UserPermission, action: GrantState, ) -> Result<()> { + session.assert_admin()?; DATABASE.update_user(username, |user| { match action { GrantState::Grant => drop(user.permissions.0.insert(perm.clone(), true)), diff --git a/logic/src/lib.rs b/logic/src/lib.rs index 9988ed2..0bd44d7 100644 --- a/logic/src/lib.rs +++ b/logic/src/lib.rs @@ -16,6 +16,7 @@ pub mod search; pub mod session; pub mod stats; pub mod account; +pub mod permission; use anyhow::Context; use anyhow::Result; diff --git a/logic/src/permission.rs b/logic/src/permission.rs new file mode 100644 index 0000000..c23ad41 --- /dev/null +++ b/logic/src/permission.rs @@ -0,0 +1,18 @@ +/* + This file is part of jellything (https://codeberg.org/metamuffin/jellything) + which is licensed under the GNU Affero General Public License (version 3); see /COPYING. + Copyright (C) 2025 metamuffin <metamuffin.org> +*/ + +use crate::session::Session; +use anyhow::{Result, anyhow}; + +impl Session { + pub fn assert_admin(&self) -> Result<()> { + if self.user.admin { + Ok(()) + } else { + Err(anyhow!("Permission denied.")) + } + } +} diff --git a/logic/src/session.rs b/logic/src/session.rs index 615694c..6f168e3 100644 --- a/logic/src/session.rs +++ b/logic/src/session.rs @@ -22,8 +22,6 @@ pub struct Session { pub user: User, } -pub struct AdminSession(pub Session); - #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SessionData { username: String, diff --git a/server/src/api.rs b/server/src/api.rs index d983548..4fecfb6 100644 --- a/server/src/api.rs +++ b/server/src/api.rs @@ -7,11 +7,7 @@ use super::ui::error::MyResult; use crate::helper::{accept::AcceptJson, language::AcceptLanguage, A}; use jellycommon::{user::CreateSessionParams, NodeID}; use jellyimport::asset_token::AssetInner; -use jellylogic::{ - login::login_logic, - node::get_nodes_modified_since, - session::{AdminSession, Session}, -}; +use jellylogic::{login::login_logic, node::get_nodes_modified_since, session::Session}; use jellyui::locale::get_translation_table; use rocket::{get, post, response::Redirect, serde::json::Json, Either}; use serde_json::{json, Value}; @@ -60,7 +56,8 @@ pub fn r_api_account_login(data: Json<CreateSessionParams>) -> MyResult<Value> { } #[get("/api/asset_token_raw/<token>")] -pub fn r_api_asset_token_raw(_admin: A<AdminSession>, token: &str) -> MyResult<Json<AssetInner>> { +pub fn r_api_asset_token_raw(session: A<Session>, token: &str) -> MyResult<Json<AssetInner>> { + session.0.assert_admin()?; Ok(Json(AssetInner::deser(token)?)) } diff --git a/server/src/helper/session.rs b/server/src/helper/session.rs index 090330b..1417df0 100644 --- a/server/src/helper/session.rs +++ b/server/src/helper/session.rs @@ -6,7 +6,7 @@ use super::A; use crate::ui::error::MyError; use anyhow::anyhow; -use jellylogic::session::{bypass_auth_session, token_to_session, AdminSession, Session}; +use jellylogic::session::{bypass_auth_session, token_to_session, Session}; use log::warn; use rocket::{ async_trait, @@ -65,28 +65,3 @@ impl<'r> FromRequest<'r> for A<Session> { } } } - -#[async_trait] -impl<'r> FromRequest<'r> for A<AdminSession> { - type Error = MyError; - async fn from_request<'life0>( - request: &'r Request<'life0>, - ) -> request::Outcome<Self, Self::Error> { - match session_from_request(request).await { - Ok(x) => { - if x.user.admin { - Outcome::Success(A(AdminSession(x))) - } else { - Outcome::Error(( - Status::Unauthorized, - MyError(anyhow!("you are not an admin")), - )) - } - } - Err(e) => { - warn!("authentificated route rejected: {e:?}"); - Outcome::Forward(Status::Unauthorized) - } - } - } -} diff --git a/server/src/ui/admin/log.rs b/server/src/ui/admin/log.rs index f0a85f2..c26b697 100644 --- a/server/src/ui/admin/log.rs +++ b/server/src/ui/admin/log.rs @@ -10,7 +10,7 @@ use crate::{ use jellyimport::is_importing; use jellylogic::{ admin::log::{get_log_buffer, get_log_stream}, - session::AdminSession, + session::Session, }; use jellyui::{ admin::log::{render_log_line, ServerLogPage}, @@ -23,10 +23,11 @@ use serde_json::json; #[get("/admin/log?<warnonly>", rank = 2)] pub fn r_admin_log<'a>( - session: A<AdminSession>, + session: A<Session>, warnonly: bool, lang: AcceptLanguage, ) -> MyResult<RawHtml<String>> { + session.0.assert_admin()?; let AcceptLanguage(lang) = lang; let messages = get_log_buffer(warnonly) .into_iter() @@ -40,7 +41,7 @@ pub fn r_admin_log<'a>( RenderInfo { importing: is_importing(), session: Some(SessionInfo { - user: session.0 .0.user, + user: session.0.user, }), }, lang, @@ -49,14 +50,18 @@ pub fn r_admin_log<'a>( #[get("/admin/log?stream&<warnonly>&<html>", rank = 1)] pub fn r_admin_log_stream( - _session: A<AdminSession>, + session: A<Session>, ws: WebSocket, warnonly: bool, html: bool, ) -> Stream!['static] { + // TODO type problems + let ok = session.0.assert_admin().is_ok(); let mut stream = get_log_stream(warnonly); Stream! { ws => - if html { + if !ok { + yield Message::Text("unauthorized".to_string()); + } else if html { let _ = ws; while let Ok(line) = stream.recv().await { yield Message::Text(render_log_line(&line)); diff --git a/server/src/ui/admin/mod.rs b/server/src/ui/admin/mod.rs index e3eb2d6..4e07afb 100644 --- a/server/src/ui/admin/mod.rs +++ b/server/src/ui/admin/mod.rs @@ -7,7 +7,7 @@ pub mod log; pub mod user; use super::error::MyResult; -use crate::helper::{language::AcceptLanguage, A}; +use crate::helper::{language::AcceptLanguage, RequestInfo, A}; use jellycommon::routes::u_admin_dashboard; use jellyimport::is_importing; use jellylogic::{ @@ -15,25 +15,27 @@ use jellylogic::{ create_invite, delete_invite, do_import, get_import_errors, list_invites, update_search_index, }, - session::AdminSession, + session::Session, }; use jellyui::{ admin::AdminDashboardPage, + locale::tr, render_page, scaffold::{RenderInfo, SessionInfo}, }; use rocket::{ form::Form, get, post, - response::{content::RawHtml, Redirect}, + response::{content::RawHtml, Flash, Redirect}, FromForm, }; #[get("/admin/dashboard")] pub async fn r_admin_dashboard( - session: A<AdminSession>, + session: A<Session>, lang: AcceptLanguage, ) -> MyResult<RawHtml<String>> { + session.0.assert_admin()?; let AcceptLanguage(lang) = lang; let flash = None; @@ -57,7 +59,7 @@ pub async fn r_admin_dashboard( RenderInfo { importing: is_importing(), session: Some(SessionInfo { - user: session.0 .0.user, + user: session.0.user, }), }, lang, @@ -65,9 +67,12 @@ pub async fn r_admin_dashboard( } #[post("/admin/generate_invite")] -pub async fn r_admin_invite(session: A<AdminSession>) -> MyResult<Redirect> { - let _ = create_invite(&session.0)?; - Ok(Redirect::temporary(u_admin_dashboard())) +pub async fn r_admin_invite(ri: RequestInfo) -> MyResult<Flash<Redirect>> { + let i = create_invite(&ri.session)?; + Ok(Flash::success( + Redirect::to(u_admin_dashboard()), + tr(ri.lang, "admin.invite_create_success").replace("{invite}", &i), + )) } #[derive(FromForm)] @@ -77,21 +82,24 @@ pub struct DeleteInvite { #[post("/admin/remove_invite", data = "<form>")] pub async fn r_admin_remove_invite( - session: A<AdminSession>, + session: A<Session>, form: Form<DeleteInvite>, ) -> MyResult<Redirect> { + session.0.assert_admin()?; delete_invite(&session.0, &form.invite)?; Ok(Redirect::temporary(u_admin_dashboard())) } #[post("/admin/import?<incremental>")] -pub async fn r_admin_import(session: A<AdminSession>, incremental: bool) -> MyResult<Redirect> { +pub async fn r_admin_import(session: A<Session>, incremental: bool) -> MyResult<Redirect> { + session.0.assert_admin()?; do_import(&session.0, incremental).await?.1?; Ok(Redirect::temporary(u_admin_dashboard())) } #[post("/admin/update_search")] -pub async fn r_admin_update_search(session: A<AdminSession>) -> MyResult<Redirect> { +pub async fn r_admin_update_search(session: A<Session>) -> MyResult<Redirect> { + session.0.assert_admin()?; update_search_index(&session.0).await?; Ok(Redirect::temporary(u_admin_dashboard())) } diff --git a/server/src/ui/admin/user.rs b/server/src/ui/admin/user.rs index 27d5256..e8dc332 100644 --- a/server/src/ui/admin/user.rs +++ b/server/src/ui/admin/user.rs @@ -12,7 +12,7 @@ use jellycommon::user::UserPermission; use jellyimport::is_importing; use jellylogic::{ admin::user::{admin_users, delete_user, get_user, update_user_perms, GrantState}, - session::AdminSession, + session::Session, }; use jellyui::{ admin::user::{AdminUserPage, AdminUsersPage}, @@ -22,7 +22,8 @@ use jellyui::{ use rocket::{form::Form, get, post, response::content::RawHtml, FromForm, FromFormField}; #[get("/admin/users")] -pub fn r_admin_users(session: A<AdminSession>, lang: AcceptLanguage) -> MyResult<RawHtml<String>> { +pub fn r_admin_users(session: A<Session>, lang: AcceptLanguage) -> MyResult<RawHtml<String>> { + session.0.assert_admin()?; let AcceptLanguage(lang) = lang; let r = admin_users(&session.0)?; Ok(RawHtml(render_page( @@ -34,7 +35,7 @@ pub fn r_admin_users(session: A<AdminSession>, lang: AcceptLanguage) -> MyResult RenderInfo { importing: is_importing(), session: Some(SessionInfo { - user: session.0 .0.user, + user: session.0.user, }), }, lang, @@ -43,10 +44,11 @@ pub fn r_admin_users(session: A<AdminSession>, lang: AcceptLanguage) -> MyResult #[get("/admin/user/<name>")] pub fn r_admin_user<'a>( - session: A<AdminSession>, + session: A<Session>, name: &'a str, lang: AcceptLanguage, ) -> MyResult<RawHtml<String>> { + session.0.assert_admin()?; let AcceptLanguage(lang) = lang; let user = get_user(&session.0, name)?; @@ -59,7 +61,7 @@ pub fn r_admin_user<'a>( RenderInfo { importing: is_importing(), session: Some(SessionInfo { - user: session.0 .0.user, + user: session.0.user, }), }, lang, @@ -81,11 +83,12 @@ pub enum UrlGrantState { #[post("/admin/user/<name>/update_permission", data = "<form>")] pub fn r_admin_user_permission( - session: A<AdminSession>, + session: A<Session>, form: Form<UserPermissionForm>, name: &str, lang: AcceptLanguage, ) -> MyResult<RawHtml<String>> { + session.0.assert_admin()?; let AcceptLanguage(lang) = lang; let perm = serde_json::from_str::<UserPermission>(&form.permission) .context("parsing provided permission")?; @@ -112,7 +115,7 @@ pub fn r_admin_user_permission( RenderInfo { importing: is_importing(), session: Some(SessionInfo { - user: session.0 .0.user, + user: session.0.user, }), }, lang, @@ -121,10 +124,11 @@ pub fn r_admin_user_permission( #[post("/admin/<name>/remove")] pub fn r_admin_remove_user( - session: A<AdminSession>, + session: A<Session>, name: &str, lang: AcceptLanguage, ) -> MyResult<RawHtml<String>> { + session.0.assert_admin()?; let AcceptLanguage(lang) = lang; delete_user(&session.0, name)?; let r = admin_users(&session.0)?; @@ -138,7 +142,7 @@ pub fn r_admin_remove_user( RenderInfo { importing: is_importing(), session: Some(SessionInfo { - user: session.0 .0.user, + user: session.0.user, }), }, lang, diff --git a/ui/src/admin/mod.rs b/ui/src/admin/mod.rs index 5898f45..394f1c6 100644 --- a/ui/src/admin/mod.rs +++ b/ui/src/admin/mod.rs @@ -7,7 +7,7 @@ pub mod log; pub mod user; -use crate::{Page, locale::Language, scaffold::FlashDisplay}; +use crate::{locale::{tr, Language}, scaffold::FlashDisplay, Page}; use jellycommon::routes::{ u_admin_import, u_admin_invite_create, u_admin_invite_remove, u_admin_log, u_admin_update_search, u_admin_users, |