headless_lms_server/domain/langs/
token.rs

1use crate::{
2    domain::authorization::{self, LoginToken},
3    prelude::*,
4};
5use actix_web::{FromRequest, http::header};
6use futures_util::{FutureExt, future::LocalBoxFuture};
7use headless_lms_utils::cache::Cache;
8use models::users::User;
9use oauth2::TokenResponse;
10use std::ops::{Deref, DerefMut};
11use std::time::Duration;
12
13#[derive(Debug, Clone)]
14pub struct AuthToken(User);
15
16impl Deref for AuthToken {
17    type Target = User;
18    fn deref(&self) -> &Self::Target {
19        &self.0
20    }
21}
22
23impl DerefMut for AuthToken {
24    fn deref_mut(&mut self) -> &mut Self::Target {
25        &mut self.0
26    }
27}
28
29impl FromRequest for AuthToken {
30    type Error = ControllerError;
31    type Future = LocalBoxFuture<'static, Result<Self, ControllerError>>;
32
33    fn from_request(req: &HttpRequest, _payload: &mut actix_http::Payload) -> Self::Future {
34        let pool = req
35            .app_data::<web::Data<PgPool>>()
36            .expect("Missing database pool")
37            .clone();
38        let app_conf = req
39            .app_data::<web::Data<ApplicationConfiguration>>()
40            .expect("Missing application configuration")
41            .clone();
42        let cache = req
43            .app_data::<web::Data<Cache>>()
44            .expect("Missing cache")
45            .clone();
46
47        let auth_header = req
48            .headers()
49            .get(header::AUTHORIZATION)
50            .map(|hv| String::from_utf8_lossy(hv.as_bytes()))
51            .and_then(|h| h.strip_prefix("Bearer ").map(str::to_string));
52        async move {
53            let Some(token) = auth_header else {
54                return Err(ControllerError::new(
55                    ControllerErrorType::Unauthorized,
56                    "Missing bearer token".to_string(),
57                    None,
58                ));
59            };
60            let mut conn = pool.acquire().await?;
61
62            let user = if app_conf.test_mode {
63                warn!("Using test credentials. Normal accounts won't work.");
64                authorization::authenticate_test_token(&mut conn, &token, &app_conf)
65                    .await
66                    .map_err(|err| {
67                        ControllerError::new(
68                            ControllerErrorType::Unauthorized,
69                            "Could not find user".to_string(),
70                            Some(err),
71                        )
72                    })?
73            } else {
74                match load_user(&cache, &token).await {
75                    Some(user) => user,
76                    None => {
77                        let token = LoginToken::new(
78                            oauth2::AccessToken::new(token),
79                            oauth2::basic::BasicTokenType::Bearer,
80                            oauth2::EmptyExtraTokenFields {},
81                        );
82                        let user =
83                            authorization::get_user_from_moocfi_by_login_token(&token, &mut conn)
84                                .await?;
85                        cache_user(&cache, &token, &user).await;
86                        user
87                    }
88                }
89            };
90
91            Ok(Self(user))
92        }
93        .boxed_local()
94    }
95}
96
97#[derive(Debug, Deserialize)]
98#[allow(unused)]
99struct TmcUser {
100    id: i32,
101    username: String,
102    email: String,
103    administrator: bool,
104}
105
106pub async fn cache_user(cache: &Cache, token: &LoginToken, user: &User) {
107    cache
108        .cache_json(
109            token.access_token().secret(),
110            user,
111            Duration::from_secs(60 * 60),
112        )
113        .await;
114}
115
116pub async fn load_user(cache: &Cache, token: &str) -> Option<User> {
117    cache.get_json(token).await
118}