headless_lms_server/domain/langs/
token.rs1use 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}