move user structs into common
This commit is contained in:
parent
ed517b6dab
commit
8264ced7a6
6 changed files with 58 additions and 40 deletions
|
@ -1,6 +1,7 @@
|
|||
pub mod net;
|
||||
pub mod operations;
|
||||
pub mod stock;
|
||||
pub mod user;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
|
19
vtse-common/src/user.rs
Normal file
19
vtse-common/src/user.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use serde::Deserialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize, sqlx::Type)]
|
||||
#[sqlx(transparent)]
|
||||
pub struct ApiKey(pub Uuid);
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize, sqlx::Type)]
|
||||
#[sqlx(transparent)]
|
||||
pub struct Username(String);
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize)]
|
||||
pub struct Password(String);
|
||||
|
||||
impl Password {
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.0.as_bytes()
|
||||
}
|
||||
}
|
|
@ -68,7 +68,7 @@ async fn handle_stream(mut socket: TcpStream, pool: PgPool) -> Result<()> {
|
|||
let response = match parsed {
|
||||
ServerOperation::Query(op) => match op {
|
||||
QueryOperation::StockInfo { stock } => state.stock_info(stock, &pool).await?,
|
||||
QueryOperation::User(_) => todo!(),
|
||||
QueryOperation::User(username) => state.user_info(username, &pool).await?,
|
||||
},
|
||||
ServerOperation::User(op) => match op {
|
||||
UserOperation::Login { api_key } => state.login(api_key, &pool).await?,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use serde::Deserialize;
|
||||
use vtse_common::stock::StockName;
|
||||
|
||||
use crate::user::{ApiKey, Password, Username};
|
||||
use vtse_common::user::{ApiKey, Password, Username};
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq)]
|
||||
#[serde(untagged)]
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::user::{ApiKey, Password, Username};
|
||||
use crate::user::SaltedPassword;
|
||||
use sodiumoxide::crypto::pwhash::argon2id13::{pwhash_verify, HashedPassword};
|
||||
use sqlx::{query, PgPool};
|
||||
use std::convert::TryFrom;
|
||||
use thiserror::Error;
|
||||
use vtse_common::{
|
||||
net::{ServerResponse, UserError},
|
||||
stock::{Stock, StockName},
|
||||
};
|
||||
use vtse_common::net::{ServerResponse, UserError};
|
||||
use vtse_common::stock::{Stock, StockName};
|
||||
use vtse_common::user::{ApiKey, Password, Username};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub(crate) enum StateError {
|
||||
|
@ -44,7 +44,7 @@ impl AppState {
|
|||
impl AppState {
|
||||
pub(crate) async fn stock_info(&self, stock_name: StockName, pool: &PgPool) -> OperationResult {
|
||||
let stock = query!(
|
||||
"SELECT name, symbol, description, price FROM stocks WHERE name = $1::text",
|
||||
"SELECT name, symbol, description, price FROM stocks WHERE name = $1",
|
||||
stock_name as StockName
|
||||
)
|
||||
.fetch_one(pool)
|
||||
|
@ -57,6 +57,10 @@ impl AppState {
|
|||
stock.price,
|
||||
)))
|
||||
}
|
||||
|
||||
pub(crate) async fn user_info(&self, username: Username, pool: &PgPool) -> OperationResult {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// User operation implementation
|
||||
|
@ -88,12 +92,14 @@ impl AppState {
|
|||
pool: &PgPool,
|
||||
) -> OperationResult {
|
||||
self.assert_state(AppState::Unauthorized)?;
|
||||
let salted_password =
|
||||
SaltedPassword::try_from(password).map_err(|_| StateError::PasswordHash)?;
|
||||
query!(
|
||||
"INSERT INTO users
|
||||
(username, pwhash_data)
|
||||
VALUES ($1::varchar, $2::bytea)",
|
||||
VALUES ($1, $2)",
|
||||
username as Username,
|
||||
password.salt().map_err(|_| StateError::PasswordHash)?.0,
|
||||
salted_password.as_ref(),
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
@ -149,7 +155,7 @@ impl AppState {
|
|||
) -> Result<Option<i32>, StateError> {
|
||||
let result = query!(
|
||||
"SELECT user_id, pwhash_data FROM users
|
||||
WHERE username = $1::varchar",
|
||||
WHERE username = $1",
|
||||
username as &Username
|
||||
)
|
||||
.fetch_one(pool)
|
||||
|
|
|
@ -1,36 +1,28 @@
|
|||
use serde::Deserialize;
|
||||
use sodiumoxide::crypto::pwhash::argon2id13::{pwhash, MEMLIMIT_INTERACTIVE, OPSLIMIT_INTERACTIVE};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize, sqlx::Type)]
|
||||
#[sqlx(transparent)]
|
||||
pub(crate) struct ApiKey(pub(crate) Uuid);
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize, sqlx::Type)]
|
||||
#[sqlx(transparent)]
|
||||
pub(crate) struct Username(String);
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize)]
|
||||
pub(crate) struct Password(String);
|
||||
use sodiumoxide::crypto::pwhash::{
|
||||
argon2id13::HashedPassword,
|
||||
argon2id13::{pwhash, MEMLIMIT_INTERACTIVE, OPSLIMIT_INTERACTIVE},
|
||||
};
|
||||
use std::convert::TryFrom;
|
||||
use vtse_common::user::Password;
|
||||
|
||||
#[derive(sqlx::Type)]
|
||||
#[sqlx(transparent)]
|
||||
pub(crate) struct SaltedPasswordData(pub(crate) Vec<u8>);
|
||||
pub(crate) struct SaltedPassword(HashedPassword);
|
||||
|
||||
impl Password {
|
||||
pub(crate) fn salt(self) -> Result<SaltedPasswordData, ()> {
|
||||
Ok(SaltedPasswordData(
|
||||
pwhash(
|
||||
self.0.as_bytes(),
|
||||
OPSLIMIT_INTERACTIVE,
|
||||
MEMLIMIT_INTERACTIVE,
|
||||
)?
|
||||
.as_ref()
|
||||
.to_vec(),
|
||||
))
|
||||
}
|
||||
impl TryFrom<Password> for SaltedPassword {
|
||||
type Error = ();
|
||||
|
||||
pub(crate) fn as_bytes(&self) -> &[u8] {
|
||||
self.0.as_bytes()
|
||||
fn try_from(password: Password) -> Result<Self, Self::Error> {
|
||||
Ok(Self(pwhash(
|
||||
password.as_bytes(),
|
||||
OPSLIMIT_INTERACTIVE,
|
||||
MEMLIMIT_INTERACTIVE,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for SaltedPassword {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue