Can get user information
This commit is contained in:
		@@ -6,6 +6,7 @@ use std::fmt::{Debug, Display, Formatter};
 | 
			
		||||
 | 
			
		||||
pub mod auth_controller;
 | 
			
		||||
pub mod config_controller;
 | 
			
		||||
pub mod user_controller;
 | 
			
		||||
 | 
			
		||||
/// Custom error to ease controller writing
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								geneit_backend/src/controllers/user_controller.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								geneit_backend/src/controllers/user_controller.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
//! # User controller
 | 
			
		||||
//!
 | 
			
		||||
//! The actions of the user on his account when he is authenticated.
 | 
			
		||||
 | 
			
		||||
use crate::controllers::HttpResult;
 | 
			
		||||
use crate::services::login_token_service::LoginToken;
 | 
			
		||||
use crate::services::users_service;
 | 
			
		||||
use actix_web::HttpResponse;
 | 
			
		||||
 | 
			
		||||
/// Get account information
 | 
			
		||||
pub async fn auth_info(token: LoginToken) -> HttpResult {
 | 
			
		||||
    let user = users_service::get_by_id(token.user_id).await?;
 | 
			
		||||
 | 
			
		||||
    Ok(HttpResponse::Ok().json(user))
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,7 @@ use actix_remote_ip::RemoteIPConfig;
 | 
			
		||||
use actix_web::middleware::Logger;
 | 
			
		||||
use actix_web::{web, App, HttpServer};
 | 
			
		||||
use geneit_backend::app_config::AppConfig;
 | 
			
		||||
use geneit_backend::controllers::{auth_controller, config_controller};
 | 
			
		||||
use geneit_backend::controllers::{auth_controller, config_controller, user_controller};
 | 
			
		||||
 | 
			
		||||
#[actix_web::main]
 | 
			
		||||
async fn main() -> std::io::Result<()> {
 | 
			
		||||
@@ -43,6 +43,8 @@ async fn main() -> std::io::Result<()> {
 | 
			
		||||
                "/auth/password_login",
 | 
			
		||||
                web::post().to(auth_controller::password_login),
 | 
			
		||||
            )
 | 
			
		||||
            // User controller
 | 
			
		||||
            .route("/user/info", web::get().to(user_controller::auth_info))
 | 
			
		||||
    })
 | 
			
		||||
    .bind(AppConfig::get().listen_address.as_str())?
 | 
			
		||||
    .run()
 | 
			
		||||
 
 | 
			
		||||
@@ -5,14 +5,17 @@ use diesel::prelude::*;
 | 
			
		||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
 | 
			
		||||
pub struct UserID(pub i32);
 | 
			
		||||
 | 
			
		||||
#[derive(Queryable, Debug)]
 | 
			
		||||
#[derive(Queryable, Debug, serde::Serialize)]
 | 
			
		||||
pub struct User {
 | 
			
		||||
    pub id: i32,
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub email: String,
 | 
			
		||||
    #[serde(skip_serializing)]
 | 
			
		||||
    pub password: Option<String>,
 | 
			
		||||
    #[serde(skip_serializing)]
 | 
			
		||||
    pub reset_password_token: Option<String>,
 | 
			
		||||
    pub time_create: i64,
 | 
			
		||||
    #[serde(skip_serializing)]
 | 
			
		||||
    pub time_gen_reset_token: i64,
 | 
			
		||||
    pub time_activate: i64,
 | 
			
		||||
    pub active: bool,
 | 
			
		||||
 
 | 
			
		||||
@@ -4,18 +4,20 @@ use crate::connections::redis_connection;
 | 
			
		||||
use crate::models::{User, UserID};
 | 
			
		||||
use crate::utils::string_utils;
 | 
			
		||||
use crate::utils::time_utils::time;
 | 
			
		||||
use actix_web::dev::Payload;
 | 
			
		||||
use actix_web::{FromRequest, HttpRequest};
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
 | 
			
		||||
struct LoginToken {
 | 
			
		||||
pub struct LoginToken {
 | 
			
		||||
    expire: u64,
 | 
			
		||||
    hb: u64,
 | 
			
		||||
    user_id: UserID,
 | 
			
		||||
    pub user_id: UserID,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl LoginToken {
 | 
			
		||||
    pub fn new(user: &User) -> (String, Self) {
 | 
			
		||||
        let key = format!("tok-{}-{}", user.id().0, string_utils::rand_str(40));
 | 
			
		||||
        let key = format!("{}-{}", user.id().0, string_utils::rand_str(40));
 | 
			
		||||
 | 
			
		||||
        (
 | 
			
		||||
            key,
 | 
			
		||||
@@ -27,8 +29,8 @@ impl LoginToken {
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn is_valid(&self) -> bool {
 | 
			
		||||
        self.expire > time() && self.hb + 3600 > time()
 | 
			
		||||
    pub fn is_expired(&self) -> bool {
 | 
			
		||||
        self.expire < time() || self.hb + 3600 < time()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn refresh_hb(&self) -> Option<Self> {
 | 
			
		||||
@@ -41,7 +43,7 @@ impl LoginToken {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn lifetime(&self) -> Duration {
 | 
			
		||||
    pub fn redis_lifetime(&self) -> Duration {
 | 
			
		||||
        Duration::from_secs(if self.expire <= time() {
 | 
			
		||||
            0
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -50,9 +52,73 @@ impl LoginToken {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get redis key for a given login token
 | 
			
		||||
fn redis_key(tok: &str) -> String {
 | 
			
		||||
    format!("tok-{tok}")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Generate a new login token
 | 
			
		||||
pub async fn gen_new_token(user: &User) -> anyhow::Result<String> {
 | 
			
		||||
    let (key, token) = LoginToken::new(user);
 | 
			
		||||
    redis_connection::set_value(&key, &token, token.lifetime()).await?;
 | 
			
		||||
    redis_connection::set_value(&redis_key(&key), &token, token.redis_lifetime()).await?;
 | 
			
		||||
    Ok(key)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get a user information from its token
 | 
			
		||||
async fn get_token(key: &str) -> anyhow::Result<Option<LoginToken>> {
 | 
			
		||||
    let token = match redis_connection::get_value::<LoginToken>(&redis_key(key)).await? {
 | 
			
		||||
        None => {
 | 
			
		||||
            log::error!("Could not find token for key '{}' (key absent)", key);
 | 
			
		||||
            return Ok(None);
 | 
			
		||||
        }
 | 
			
		||||
        Some(t) => t,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if token.is_expired() {
 | 
			
		||||
        log::error!("Could not find token for key '{}' (token expired)", key);
 | 
			
		||||
        return Ok(None);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check if heartbeat must be updated
 | 
			
		||||
    if let Some(renew) = token.refresh_hb() {
 | 
			
		||||
        redis_connection::set_value(&redis_key(key), &renew, renew.redis_lifetime()).await?;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(Some(token))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FromRequest for LoginToken {
 | 
			
		||||
    type Error = actix_web::Error;
 | 
			
		||||
    type Future = futures_util::future::LocalBoxFuture<'static, Result<Self, Self::Error>>;
 | 
			
		||||
 | 
			
		||||
    fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
 | 
			
		||||
        let req = req.clone();
 | 
			
		||||
        Box::pin(async move {
 | 
			
		||||
            let token = match req.headers().get("X-Auth-Token") {
 | 
			
		||||
                Some(v) => v.to_str().unwrap_or(""),
 | 
			
		||||
                None => {
 | 
			
		||||
                    return Err(actix_web::error::ErrorBadRequest(
 | 
			
		||||
                        "Missing auth token header!",
 | 
			
		||||
                    ));
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            let token = match get_token(token).await {
 | 
			
		||||
                Err(e) => {
 | 
			
		||||
                    log::error!("Failed to load auth token! {}", e);
 | 
			
		||||
                    return Err(actix_web::error::ErrorInternalServerError(
 | 
			
		||||
                        "Failed to check auth token!",
 | 
			
		||||
                    ));
 | 
			
		||||
                }
 | 
			
		||||
                Ok(None) => {
 | 
			
		||||
                    return Err(actix_web::error::ErrorPreconditionFailed(
 | 
			
		||||
                        "Invalid auth token!",
 | 
			
		||||
                    ));
 | 
			
		||||
                }
 | 
			
		||||
                Ok(Some(t)) => t,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            Ok(token)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user