Skip to main content

kellnr_auth/
maybe_user.rs

1use axum::RequestPartsExt;
2use axum::extract::FromRequestParts;
3use axum::http::StatusCode;
4use axum::http::request::Parts;
5use axum_extra::extract::PrivateCookieJar;
6use kellnr_appstate::AppStateData;
7use kellnr_db::SessionInfo;
8use kellnr_settings::constants;
9
10use crate::token;
11
12#[derive(Debug, Clone)]
13pub struct MaybeUser {
14    pub name: String,
15    pub is_admin: bool,
16    pub is_read_only: bool,
17}
18
19impl MaybeUser {
20    pub fn from_token(token: token::Token) -> Self {
21        Self {
22            name: token.user,
23            is_admin: token.is_admin,
24            is_read_only: token.is_read_only,
25        }
26    }
27
28    pub fn from_session(session: SessionInfo) -> Self {
29        Self {
30            name: session.name,
31            is_admin: session.is_admin,
32            is_read_only: session.is_read_only,
33        }
34    }
35}
36
37impl FromRequestParts<AppStateData> for MaybeUser {
38    type Rejection = StatusCode;
39
40    async fn from_request_parts(
41        parts: &mut Parts,
42        state: &AppStateData,
43    ) -> Result<Self, Self::Rejection> {
44        // 1) Prefer cargo token auth if present
45        if let Ok(t) = token::Token::from_request_parts(parts, state).await {
46            return Ok(Self::from_token(t));
47        }
48
49        // 2) Fallback to session cookie auth
50        let jar: PrivateCookieJar = parts
51            .extract_with_state(state)
52            .await
53            .map_err(|_| StatusCode::UNAUTHORIZED)?;
54
55        let session_cookie = jar
56            .get(constants::COOKIE_SESSION_ID)
57            .ok_or(StatusCode::UNAUTHORIZED)?;
58
59        let session = state
60            .db
61            .validate_session(session_cookie.value())
62            .await
63            .map_err(|_| StatusCode::UNAUTHORIZED)?;
64
65        Ok(Self::from_session(session))
66    }
67}