aboutsummaryrefslogtreecommitdiff
path: root/server/src/routes/ui/account/session.rs
blob: c41c968ff76ea6567b95d1b38b54b889663179dd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/*
    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) 2023 metamuffin <metamuffin.org>
*/
use crate::{
    database::{Database, User},
    routes::ui::error::MyError,
    CONF,
};
use anyhow::anyhow;
use chrono::{DateTime, Duration, Utc};
use rocket::{
    outcome::Outcome,
    request::{self, FromRequest},
    Request, State,
};
use serde::{Deserialize, Serialize};

pub struct Session {
    pub user: User,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionCookie {
    name: String,
    expire: DateTime<Utc>,
}

impl SessionCookie {
    pub fn new(name: String) -> Self {
        Self {
            name,
            expire: Utc::now() + Duration::days(CONF.login_expire),
        }
    }
}

impl Session {
    pub async fn from_request_ut(req: &Request<'_>) -> Result<Self, MyError> {
        #[cfg(not(feature = "bypass-auth"))]
        let cookie = req
            .cookies()
            .get_private("user")
            .ok_or(anyhow!("login required"))?;
        #[cfg(not(feature = "bypass-auth"))]
        let cookie = serde_json::from_str::<SessionCookie>(cookie.value())?;
        #[cfg(feature = "bypass-auth")]
        let cookie = SessionCookie {
            name: crate::CONF.admin_username.to_string(),
            expire: Utc::now() + Duration::days(CONF.login_expire),
        };

        if cookie.expire < Utc::now() {
            Err(anyhow!("cookie expired"))?;
        }

        let db = req.guard::<&State<Database>>().await.unwrap();
        let user = db
            .users
            .get(&cookie.name.to_string())?
            .ok_or(anyhow!("user not found"))?;

        Ok(Session { user })
    }
}

impl<'r> FromRequest<'r> for Session {
    type Error = MyError;

    fn from_request<'life0, 'async_trait>(
        request: &'r Request<'life0>,
    ) -> core::pin::Pin<
        Box<
            dyn core::future::Future<Output = request::Outcome<Self, Self::Error>>
                + core::marker::Send
                + 'async_trait,
        >,
    >
    where
        'r: 'async_trait,
        'life0: 'async_trait,
        Self: 'async_trait,
    {
        Box::pin(async move {
            match Self::from_request_ut(request).await {
                Ok(x) => Outcome::Success(x),
                Err(_) => Outcome::Forward(()),
            }
        })
    }
}