Can get user information
This commit is contained in:
parent
652541cd59
commit
c0f120bb53
1
geneit_backend/Cargo.lock
generated
1
geneit_backend/Cargo.lock
generated
@ -771,6 +771,7 @@ dependencies = [
|
||||
"clap",
|
||||
"diesel",
|
||||
"env_logger",
|
||||
"futures-util",
|
||||
"lazy_static",
|
||||
"lettre",
|
||||
"log",
|
||||
|
@ -12,6 +12,7 @@ clap = { version = "4.3.0", features = ["derive", "env"] }
|
||||
lazy_static = "1.4.0"
|
||||
anyhow = "1.0.71"
|
||||
actix-web = "4.3.1"
|
||||
futures-util = "0.3.28"
|
||||
diesel = { version = "2.0.4", features = ["postgres"] }
|
||||
serde = { version = "1.0.163", features = ["derive"] }
|
||||
serde_json = "1.0.96"
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user