aboutsummaryrefslogtreecommitdiff
path: root/server/src/helper
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-04-29 15:19:36 +0200
committermetamuffin <metamuffin@disroot.org>2025-04-29 15:19:36 +0200
commitf73aa32549743b2967160d38c1622199c41524a4 (patch)
tree0fa290fbf9b14d7bfd3803f8cc4618c6c9829330 /server/src/helper
parentf62c7f2a8cc143454779dc99334ca9fc80ddabd5 (diff)
downloadjellything-f73aa32549743b2967160d38c1622199c41524a4.tar
jellything-f73aa32549743b2967160d38c1622199c41524a4.tar.bz2
jellything-f73aa32549743b2967160d38c1622199c41524a4.tar.zst
aaaaaaa
Diffstat (limited to 'server/src/helper')
-rw-r--r--server/src/helper/mod.rs6
-rw-r--r--server/src/helper/node_id.rs17
-rw-r--r--server/src/helper/session.rs109
3 files changed, 131 insertions, 1 deletions
diff --git a/server/src/helper/mod.rs b/server/src/helper/mod.rs
index 856f6b7..125b159 100644
--- a/server/src/helper/mod.rs
+++ b/server/src/helper/mod.rs
@@ -3,5 +3,9 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
-pub mod cors;
pub mod cache;
+pub mod cors;
+pub mod session;
+pub mod node_id;
+
+pub struct A<T>(pub T);
diff --git a/server/src/helper/node_id.rs b/server/src/helper/node_id.rs
new file mode 100644
index 0000000..f891d62
--- /dev/null
+++ b/server/src/helper/node_id.rs
@@ -0,0 +1,17 @@
+/*
+ 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 super::A;
+use jellycommon::NodeID;
+use rocket::request::FromParam;
+use std::str::FromStr;
+
+impl<'a> FromParam<'a> for A<NodeID> {
+ type Error = ();
+ fn from_param(param: &'a str) -> Result<Self, Self::Error> {
+ NodeID::from_str(param).map_err(|_| ()).map(A)
+ }
+}
diff --git a/server/src/helper/session.rs b/server/src/helper/session.rs
new file mode 100644
index 0000000..b77f9fa
--- /dev/null
+++ b/server/src/helper/session.rs
@@ -0,0 +1,109 @@
+/*
+ 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::ui::error::MyError;
+use anyhow::anyhow;
+use jellybase::database::Database;
+use jellylogic::session::{validate, AdminSession, Session};
+use log::warn;
+use rocket::{
+ async_trait,
+ http::Status,
+ outcome::Outcome,
+ request::{self, FromRequest},
+ Request, State,
+};
+
+use super::A;
+
+impl A<Session> {
+ async fn from_request_ut(req: &Request<'_>) -> Result<Self, MyError> {
+ let username;
+
+ #[cfg(not(feature = "bypass-auth"))]
+ {
+ let token = req
+ .query_value("session")
+ .map(|e| e.unwrap())
+ .or_else(|| req.query_value("api_key").map(|e| e.unwrap()))
+ .or_else(|| req.headers().get_one("X-MediaBrowser-Token"))
+ .or_else(|| {
+ req.headers()
+ .get_one("Authorization")
+ .and_then(parse_jellyfin_auth)
+ }) // for jellyfin compat
+ .or(req.cookies().get("session").map(|cookie| cookie.value()))
+ .ok_or(anyhow!("not logged in"))?;
+
+ // jellyfin urlescapes the token for *some* requests
+ let token = token.replace("%3D", "=");
+ username = validate(&token)?;
+ };
+
+ #[cfg(feature = "bypass-auth")]
+ {
+ parse_jellyfin_auth("a"); // unused warning is annoying
+ username = "admin".to_string();
+ }
+
+ let db = req.guard::<&State<Database>>().await.unwrap();
+
+ let user = db.get_user(&username)?.ok_or(anyhow!("user not found"))?;
+
+ Ok(A(Session { user }))
+ }
+}
+
+fn parse_jellyfin_auth(h: &str) -> Option<&str> {
+ for tok in h.split(" ") {
+ if let Some(tok) = tok.strip_prefix("Token=\"") {
+ if let Some(tok) = tok.strip_suffix("\"") {
+ return Some(tok);
+ }
+ }
+ }
+ None
+}
+
+#[async_trait]
+impl<'r> FromRequest<'r> for A<Session> {
+ type Error = MyError;
+ async fn from_request<'life0>(
+ request: &'r Request<'life0>,
+ ) -> request::Outcome<Self, Self::Error> {
+ match A::<Session>::from_request_ut(request).await {
+ Ok(x) => Outcome::Success(x),
+ Err(e) => {
+ warn!("authentificated route rejected: {e:?}");
+ Outcome::Forward(Status::Unauthorized)
+ }
+ }
+ }
+}
+
+#[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 A::<Session>::from_request_ut(request).await {
+ Ok(x) => {
+ if x.0.user.admin {
+ Outcome::Success(A(AdminSession(x.0)))
+ } else {
+ Outcome::Error((
+ Status::Unauthorized,
+ MyError(anyhow!("you are not an admin")),
+ ))
+ }
+ }
+ Err(e) => {
+ warn!("authentificated route rejected: {e:?}");
+ Outcome::Forward(Status::Unauthorized)
+ }
+ }
+ }
+}