Added lots of debugging + token auth
Now you dont have to send your password over the wire every time and can make do with a token instead. There's lots of debugging info now to make it easier for me to fix bugs with the server
This commit is contained in:
+82
-23
@@ -7,9 +7,15 @@ use bcrypt::{DEFAULT_COST, hash, verify};
|
||||
use diesel_async::AsyncPgConnection;
|
||||
use diesel_async::RunQueryDsl;//, AsyncConnection};
|
||||
use diesel_async::pooled_connection::deadpool::Object;
|
||||
use fedichat::client::{ServerMessage,ServerError};
|
||||
use fedichat::client::{ServerMessage,ServerError,AuthMethod};
|
||||
use rand::distr::{Alphanumeric, SampleString};
|
||||
use rand::rngs::{SysRng,StdRng};
|
||||
use rand::prelude::*;
|
||||
|
||||
use time::{OffsetDateTime,Duration};
|
||||
use tracing::{instrument,warn};
|
||||
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn maybe_create_user(mut connection: Object<AsyncPgConnection>, username: &str, password: &str) -> Result<ServerMessage,ServerError> {
|
||||
let password = match hash(password,DEFAULT_COST) {
|
||||
@@ -37,32 +43,85 @@ pub async fn maybe_create_user(mut connection: Object<AsyncPgConnection>, userna
|
||||
|
||||
/// Try to authenticate a user against information in the database
|
||||
#[instrument(skip_all)]
|
||||
pub async fn verify_user(mut connection: Object<AsyncPgConnection>, user: &str, pass: &str) -> Result<bool,ServerError> {
|
||||
use schema::users::dsl::*;
|
||||
pub async fn verify_user(mut connection: Object<AsyncPgConnection>, user: &str, pass: &AuthMethod) -> Result<bool,ServerError> {
|
||||
|
||||
let result = users
|
||||
.filter(username.eq(user))
|
||||
.select(models::User::as_select())
|
||||
.first(&mut *connection)
|
||||
match pass {
|
||||
AuthMethod::Password(pass) => {
|
||||
use schema::users::dsl::*;
|
||||
let result = users
|
||||
.filter(username.eq(user))
|
||||
.select(models::User::as_select())
|
||||
.first(&mut *connection)
|
||||
.await;
|
||||
|
||||
|
||||
|
||||
match result {
|
||||
// If we have more than 0 rows then authentication is successful
|
||||
Ok(user) => match verify(pass,&user.password) {
|
||||
Ok(val) => Ok(val),
|
||||
Err(e) => {
|
||||
warn!("Error encountered while generating password hash");
|
||||
warn!("{e}");
|
||||
return Err(ServerError::Generic)
|
||||
}
|
||||
|
||||
},
|
||||
// TODO: Probably actually check the error
|
||||
_ => Err(ServerError::AuthenticationFailed)
|
||||
}
|
||||
},
|
||||
AuthMethod::Token(tk) => {
|
||||
use schema::tokens::dsl::*;
|
||||
let result = tokens
|
||||
.filter(username.eq(user))
|
||||
.filter(token.eq(tk))
|
||||
// Check that expiry is in the future
|
||||
.filter(expiry.gt(OffsetDateTime::now_utc().unix_timestamp()))
|
||||
.select(models::Token::as_select())
|
||||
.first(&mut *connection)
|
||||
.await;
|
||||
|
||||
|
||||
|
||||
match result {
|
||||
// If we have more than 0 rows then authentication is successful
|
||||
// Only need one token, no need to hash or do anything else
|
||||
Ok(_) => Ok(true),
|
||||
// TODO: Probably actually check the error
|
||||
_ => Err(ServerError::AuthenticationFailed)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new token for the current user with an expiry set 3 months into the future
|
||||
pub async fn gen_token(mut connection: Object<AsyncPgConnection>, user: String)
|
||||
-> Result<String,ServerError>
|
||||
{
|
||||
let mut rng = StdRng::try_from_rng(&mut SysRng).unwrap();
|
||||
let new_token = Alphanumeric.sample_string(&mut rng, 32);
|
||||
let expiry = OffsetDateTime::now_utc() + Duration::days(90);
|
||||
|
||||
|
||||
let insertable = models::NewToken {
|
||||
username: user,
|
||||
token: new_token.clone(),
|
||||
expiry: expiry.unix_timestamp()
|
||||
};
|
||||
|
||||
let result = diesel::insert_into(schema::tokens::table)
|
||||
.values(&insertable)
|
||||
.execute(&mut *connection)
|
||||
.await;
|
||||
|
||||
|
||||
|
||||
match result {
|
||||
// If we have more than 0 rows then authentication is successful
|
||||
Ok(user) => match verify(pass,&user.password) {
|
||||
Ok(val) => Ok(val),
|
||||
Err(e) => {
|
||||
warn!("Error encountered while generating password hash");
|
||||
warn!("{e}");
|
||||
return Err(ServerError::Generic)
|
||||
}
|
||||
|
||||
},
|
||||
// TODO: might need to check this? I don't know what it means
|
||||
Ok(_) => Ok(new_token),
|
||||
// TODO: Probably actually check the error
|
||||
_ => Err(ServerError::AuthenticationFailed)
|
||||
Err(_e) => Err(ServerError::UserAlreadyExists)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -73,3 +73,19 @@ pub struct Messages {
|
||||
pub server_timestamp: i64,
|
||||
pub username: UserT
|
||||
}
|
||||
|
||||
#[derive(Queryable, Selectable)]
|
||||
#[diesel(table_name = crate::db::schema::tokens)]
|
||||
pub struct Token {
|
||||
pub id: i32,
|
||||
pub username: String,
|
||||
pub token: String,
|
||||
pub expiry: i64,
|
||||
}
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = crate::db::schema::tokens)]
|
||||
pub struct NewToken {
|
||||
pub username: String,
|
||||
pub token: String,
|
||||
pub expiry: i64,
|
||||
}
|
||||
|
||||
+10
-1
@@ -48,6 +48,15 @@ diesel::table! {
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
tokens (id) {
|
||||
id -> Int4,
|
||||
username -> Text,
|
||||
token -> Text,
|
||||
expiry -> Int8,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
users (id) {
|
||||
id -> Int4,
|
||||
@@ -56,4 +65,4 @@ diesel::table! {
|
||||
}
|
||||
}
|
||||
|
||||
diesel::allow_tables_to_appear_in_same_query!(groups, messages, roles, users,);
|
||||
diesel::allow_tables_to_appear_in_same_query!(groups, messages, roles, tokens, users,);
|
||||
|
||||
Reference in New Issue
Block a user