/* 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 */ use crate::ui::error::MyError; use anyhow::anyhow; use jellylogic::{ session::{validate, AdminSession, Session}, Database, }; use log::warn; use rocket::{ async_trait, http::Status, outcome::Outcome, request::{self, FromRequest}, Request, State, }; use super::A; impl A { async fn from_request_ut(req: &Request<'_>) -> Result { 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>().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 { type Error = MyError; async fn from_request<'life0>( request: &'r Request<'life0>, ) -> request::Outcome { match A::::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 { type Error = MyError; async fn from_request<'life0>( request: &'r Request<'life0>, ) -> request::Outcome { match A::::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) } } } }