move user structs into common

master
Edward Shen 2021-02-08 19:37:19 -05:00
parent ed517b6dab
commit 8264ced7a6
Signed by: edward
GPG Key ID: 19182661E818369F
6 changed files with 58 additions and 40 deletions

View File

@ -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
View 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()
}
}

View File

@ -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?,

View File

@ -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)]

View File

@ -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)

View File

@ -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()
}
}