mirror of
https://github.com/Jackzmc/storage.git
synced 2025-05-06 05:53:20 +00:00
Use new login method for normal login
This commit is contained in:
parent
fe17ca5633
commit
3f222dfd3c
5 changed files with 47 additions and 35 deletions
|
@ -10,9 +10,9 @@ use rocket_session_store::memory::MemoryStore;
|
||||||
use sqlx::{query, query_as, Pool, QueryBuilder};
|
use sqlx::{query, query_as, Pool, QueryBuilder};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use crate::config::AppConfig;
|
use crate::config::AppConfig;
|
||||||
use crate::consts::ENCRYPTION_ROUNDS;
|
use crate::consts::{DISABLE_LOGIN_CHECK, ENCRYPTION_ROUNDS};
|
||||||
use crate::{LoginSessionData, SessionData, DB};
|
use crate::{LoginSessionData, SessionData, DB};
|
||||||
use crate::models::user::{UserAuthError, UserModel};
|
use crate::models::user::{UserAuthError, UserModel, UserModelWithPassword};
|
||||||
|
|
||||||
pub struct UserManager {
|
pub struct UserManager {
|
||||||
pool: DB,
|
pool: DB,
|
||||||
|
@ -36,7 +36,7 @@ pub enum FindUserOption {
|
||||||
|
|
||||||
#[derive(Hash)]
|
#[derive(Hash)]
|
||||||
pub struct SSOData {
|
pub struct SSOData {
|
||||||
pub provider_id: String,
|
pub(crate) provider_id: String,
|
||||||
pub(crate) sub: String
|
pub(crate) sub: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ impl UserManager {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn login_user(&self, user: UserModel, ip_address: IpAddr, sessions: Session<'_, SessionData>) {
|
pub async fn login_user_session(&self, user: UserModel, ip_address: IpAddr, sessions: &Session<'_, SessionData>) {
|
||||||
sessions.set(SessionData {
|
sessions.set(SessionData {
|
||||||
csrf_token: None,
|
csrf_token: None,
|
||||||
login: Some(LoginSessionData {
|
login: Some(LoginSessionData {
|
||||||
|
@ -120,4 +120,30 @@ impl UserManager {
|
||||||
}),
|
}),
|
||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn login_normal_user(&self, email_or_usrname: &str, password: &str, ip: IpAddr, session: &Session<'_, SessionData>) -> Result<UserModel, UserAuthError> {
|
||||||
|
let user = query_as!(UserModelWithPassword,
|
||||||
|
"select id, username, password, created_at, email, name from storage.users where email = $1 OR username = $1", email_or_usrname
|
||||||
|
)
|
||||||
|
.fetch_optional(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| UserAuthError::DatabaseError(e))?;
|
||||||
|
let Some(user) = user else {
|
||||||
|
return Err(UserAuthError::UserNotFound);
|
||||||
|
};
|
||||||
|
if let Some(db_password) = user.password {
|
||||||
|
if !*DISABLE_LOGIN_CHECK || bcrypt::verify(password, &db_password).map_err(|e| UserAuthError::EncryptionError(e))? {
|
||||||
|
let model = UserModel {
|
||||||
|
id: user.id,
|
||||||
|
email: user.email,
|
||||||
|
username: user.username,
|
||||||
|
created_at: user.created_at,
|
||||||
|
name: user.name
|
||||||
|
};
|
||||||
|
self.login_user_session(model.clone(), ip, session).await;
|
||||||
|
return Ok(model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(UserAuthError::PasswordInvalid)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::net::IpAddr;
|
||||||
use bcrypt::BcryptError;
|
use bcrypt::BcryptError;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use rocket::form::Context;
|
use rocket::form::Context;
|
||||||
|
@ -9,9 +10,11 @@ use rocket::form::error::Entity;
|
||||||
use rocket::response::Responder;
|
use rocket::response::Responder;
|
||||||
use rocket::serde::Serialize;
|
use rocket::serde::Serialize;
|
||||||
use rocket::serde::uuid::Uuid;
|
use rocket::serde::uuid::Uuid;
|
||||||
|
use rocket_session_store::Session;
|
||||||
use sqlx::{query_as, FromRow};
|
use sqlx::{query_as, FromRow};
|
||||||
use crate::consts::{DISABLE_LOGIN_CHECK, ENCRYPTION_ROUNDS};
|
use crate::consts::{DISABLE_LOGIN_CHECK, ENCRYPTION_ROUNDS};
|
||||||
use crate::{LoginSessionData, SessionData, DB};
|
use crate::{LoginSessionData, SessionData, DB};
|
||||||
|
use crate::managers::user::UsersState;
|
||||||
use crate::models::repo::RepoModel;
|
use crate::models::repo::RepoModel;
|
||||||
use crate::util::JsonErrorResponse;
|
use crate::util::JsonErrorResponse;
|
||||||
|
|
||||||
|
@ -95,22 +98,11 @@ pub async fn get_user(pool: &DB, user_id: &str) -> Result<Option<UserModel>, any
|
||||||
.fetch_optional(pool)
|
.fetch_optional(pool)
|
||||||
.await.map_err(anyhow::Error::from)
|
.await.map_err(anyhow::Error::from)
|
||||||
}
|
}
|
||||||
/// Validates user login form, returning Some on success or None (with ctx containing errors) on failure
|
/// Validates user login form
|
||||||
pub async fn validate_user_form(ctx: &mut Context<'_>, pool: &DB) -> Option<UserModel> {
|
pub async fn try_login_user_form(ctx: &mut Context<'_>, users: &UsersState, ip: IpAddr, session: &Session<'_, SessionData>) -> Result<UserModel, UserAuthError> {
|
||||||
let username = ctx.field_value("username").unwrap();
|
let username = ctx.field_value("username").unwrap();
|
||||||
let password = ctx.field_value("password").unwrap(); // TODO: no unwrap
|
let password = ctx.field_value("password").unwrap(); // TODO: no unwrap
|
||||||
match validate_user(pool, username, password).await {
|
users.login_normal_user(username, password, ip, session).await
|
||||||
Ok(u) => Some(u),
|
|
||||||
Err(UserAuthError::PasswordInvalid | UserAuthError::UserNotFound) => {
|
|
||||||
ctx.push_error(form::Error::validation("Username or password is incorrect").with_entity(Entity::Form));
|
|
||||||
None
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
ctx.push_error(form::Error::custom(e));
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
pub async fn validate_user(pool: &DB, email_or_usrname: &str, password: &str) -> Result<UserModel, UserAuthError> {
|
pub async fn validate_user(pool: &DB, email_or_usrname: &str, password: &str) -> Result<UserModel, UserAuthError> {
|
||||||
let user = query_as!(UserModelWithPassword,
|
let user = query_as!(UserModelWithPassword,
|
||||||
|
|
|
@ -9,7 +9,7 @@ use rocket::http::uri::{Origin, Reference, Uri};
|
||||||
use rocket::response::Redirect;
|
use rocket::response::Redirect;
|
||||||
use rocket_dyn_templates::{context, Template};
|
use rocket_dyn_templates::{context, Template};
|
||||||
use rocket_session_store::Session;
|
use rocket_session_store::Session;
|
||||||
use crate::models::user::{validate_user, validate_user_form, UserAuthError, UserModel};
|
use crate::models::user::{validate_user, try_login_user_form, UserAuthError, UserModel};
|
||||||
use crate::{GlobalMetadata, LoginSessionData, SessionData, DB};
|
use crate::{GlobalMetadata, LoginSessionData, SessionData, DB};
|
||||||
use crate::guards::AuthUser;
|
use crate::guards::AuthUser;
|
||||||
use crate::routes::ui;
|
use crate::routes::ui;
|
||||||
|
|
|
@ -8,7 +8,8 @@ use rocket_session_store::Session;
|
||||||
use crate::{GlobalMetadata, LoginSessionData, SessionData, DB};
|
use crate::{GlobalMetadata, LoginSessionData, SessionData, DB};
|
||||||
use crate::config::AppConfig;
|
use crate::config::AppConfig;
|
||||||
use crate::consts::{APP_METADATA, DISABLE_LOGIN_CHECK};
|
use crate::consts::{APP_METADATA, DISABLE_LOGIN_CHECK};
|
||||||
use crate::models::user::validate_user_form;
|
use crate::managers::user::UsersState;
|
||||||
|
use crate::models::user::try_login_user_form;
|
||||||
use crate::routes::ui::auth::HackyRedirectBecauseRocketBug;
|
use crate::routes::ui::auth::HackyRedirectBecauseRocketBug;
|
||||||
use crate::util::{set_csrf, validate_csrf_form};
|
use crate::util::{set_csrf, validate_csrf_form};
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@ pub async fn page(
|
||||||
return_to: Option<String>,
|
return_to: Option<String>,
|
||||||
logged_out: Option<bool>,
|
logged_out: Option<bool>,
|
||||||
settings: &State<AppConfig>,
|
settings: &State<AppConfig>,
|
||||||
|
|
||||||
) -> Template {
|
) -> Template {
|
||||||
// TODO: redirect if already logged in
|
// TODO: redirect if already logged in
|
||||||
let csrf_token = set_csrf(&session).await;
|
let csrf_token = set_csrf(&session).await;
|
||||||
|
@ -48,29 +50,21 @@ struct LoginForm<'r> {
|
||||||
|
|
||||||
#[post("/auth/login?<return_to>", data = "<form>")]
|
#[post("/auth/login?<return_to>", data = "<form>")]
|
||||||
pub async fn handler(
|
pub async fn handler(
|
||||||
pool: &State<DB>,
|
|
||||||
route: &Route,
|
route: &Route,
|
||||||
ip_addr: IpAddr,
|
ip_addr: IpAddr,
|
||||||
session: Session<'_, SessionData>,
|
session: Session<'_, SessionData>,
|
||||||
mut form: Form<Contextual<'_, LoginForm<'_>>>,
|
mut form: Form<Contextual<'_, LoginForm<'_>>>,
|
||||||
|
users: &State<UsersState>,
|
||||||
settings: &State<AppConfig>,
|
settings: &State<AppConfig>,
|
||||||
return_to: Option<String>,
|
return_to: Option<String>,
|
||||||
) -> Result<HackyRedirectBecauseRocketBug, Template> {
|
) -> Result<HackyRedirectBecauseRocketBug, Template> {
|
||||||
trace!("handler");
|
trace!("handler");
|
||||||
if !*DISABLE_LOGIN_CHECK {
|
validate_csrf_form(&mut form.context, &session).await;
|
||||||
validate_csrf_form(&mut form.context, &session).await;
|
let user = try_login_user_form(&mut form.context, users.inner(), ip_addr, &session).await.ok();
|
||||||
}
|
// TODO: use new users fetch user
|
||||||
let user = validate_user_form(&mut form.context, &pool).await;
|
|
||||||
trace!("check form");
|
trace!("check form");
|
||||||
if form.context.status() == Status::Ok {
|
if form.context.status() == Status::Ok {
|
||||||
if let Some(submission) = &form.value {
|
if let Some(_) = &form.value {
|
||||||
session.set(SessionData {
|
|
||||||
csrf_token: None,
|
|
||||||
login: Some(LoginSessionData {
|
|
||||||
user: user.expect("failed to acquire user but no errors"), // if validate_user_form returned None, form had errors, this shouldnt run,
|
|
||||||
ip_address: ip_addr,
|
|
||||||
}),
|
|
||||||
}).await.unwrap();
|
|
||||||
let mut return_to_path = return_to.unwrap_or("/".to_string());
|
let mut return_to_path = return_to.unwrap_or("/".to_string());
|
||||||
if return_to_path == "" { return_to_path.push_str("/"); }
|
if return_to_path == "" { return_to_path.push_str("/"); }
|
||||||
debug!("returning user to {:?}", return_to_path);
|
debug!("returning user to {:?}", return_to_path);
|
||||||
|
|
|
@ -101,7 +101,7 @@ async fn callback_handler(sso: &State<SSOState>, ip: IpAddr, code: String, state
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/auth/sso/cb?<code>&<state>")]
|
#[get("/auth/sso/cb?<code>&<state>")]
|
||||||
pub async fn callback(sessions: Session<'_, SessionData>, config: &State<AppConfig>, users: &State<UsersState>, ip: IpAddr, sso: &State<SSOState>, code: String, state: String) -> Result<HackyRedirectBecauseRocketBug, (Status, Template)> {
|
pub async fn callback(session: Session<'_, SessionData>, config: &State<AppConfig>, users: &State<UsersState>, ip: IpAddr, sso: &State<SSOState>, code: String, state: String) -> Result<HackyRedirectBecauseRocketBug, (Status, Template)> {
|
||||||
let (userinfo, provider_id, return_to) = callback_handler(sso, ip, code, state).await
|
let (userinfo, provider_id, return_to) = callback_handler(sso, ip, code, state).await
|
||||||
.map_err(|e| (Status::InternalServerError, Template::render("errors/500", context! {
|
.map_err(|e| (Status::InternalServerError, Template::render("errors/500", context! {
|
||||||
error: e.to_string()
|
error: e.to_string()
|
||||||
|
@ -141,7 +141,7 @@ pub async fn callback(sessions: Session<'_, SessionData>, config: &State<AppConf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let user = user.unwrap();
|
let user = user.unwrap();
|
||||||
users.login_user(user, ip, sessions).await;
|
users.login_user_session(user, ip, &session).await;
|
||||||
debug!("user={:?}\nemail={:?}\nname={:?}", userinfo.subject(), userinfo.email(), userinfo.name());
|
debug!("user={:?}\nemail={:?}\nname={:?}", userinfo.subject(), userinfo.email(), userinfo.name());
|
||||||
// TODO: login user to session, prob through UserManager/users
|
// TODO: login user to session, prob through UserManager/users
|
||||||
let return_to = return_to.unwrap_or("/".to_string());
|
let return_to = return_to.unwrap_or("/".to_string());
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue