From 8264ced7a6e701b4e09299408e914737649a8ed1 Mon Sep 17 00:00:00 2001 From: Edward Shen Date: Mon, 8 Feb 2021 19:37:19 -0500 Subject: [PATCH] move user structs into common --- vtse-common/src/lib.rs | 1 + vtse-common/src/user.rs | 19 +++++++++++++ vtse-server/src/main.rs | 2 +- vtse-server/src/operations.rs | 2 +- vtse-server/src/state.rs | 24 ++++++++++------- vtse-server/src/user.rs | 50 +++++++++++++++-------------------- 6 files changed, 58 insertions(+), 40 deletions(-) create mode 100644 vtse-common/src/user.rs diff --git a/vtse-common/src/lib.rs b/vtse-common/src/lib.rs index 50ac410..c09bfe3 100644 --- a/vtse-common/src/lib.rs +++ b/vtse-common/src/lib.rs @@ -1,6 +1,7 @@ pub mod net; pub mod operations; pub mod stock; +pub mod user; #[cfg(test)] mod tests { diff --git a/vtse-common/src/user.rs b/vtse-common/src/user.rs new file mode 100644 index 0000000..2893a28 --- /dev/null +++ b/vtse-common/src/user.rs @@ -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() + } +} diff --git a/vtse-server/src/main.rs b/vtse-server/src/main.rs index fc65c46..46a81d2 100644 --- a/vtse-server/src/main.rs +++ b/vtse-server/src/main.rs @@ -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?, diff --git a/vtse-server/src/operations.rs b/vtse-server/src/operations.rs index 9c6f5e9..d6f2747 100644 --- a/vtse-server/src/operations.rs +++ b/vtse-server/src/operations.rs @@ -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)] diff --git a/vtse-server/src/state.rs b/vtse-server/src/state.rs index f363178..08e4d8e 100644 --- a/vtse-server/src/state.rs +++ b/vtse-server/src/state.rs @@ -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, StateError> { let result = query!( "SELECT user_id, pwhash_data FROM users - WHERE username = $1::varchar", + WHERE username = $1", username as &Username ) .fetch_one(pool) diff --git a/vtse-server/src/user.rs b/vtse-server/src/user.rs index f903452..021cf51 100644 --- a/vtse-server/src/user.rs +++ b/vtse-server/src/user.rs @@ -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); +pub(crate) struct SaltedPassword(HashedPassword); -impl Password { - pub(crate) fn salt(self) -> Result { - Ok(SaltedPasswordData( - pwhash( - self.0.as_bytes(), - OPSLIMIT_INTERACTIVE, - MEMLIMIT_INTERACTIVE, - )? - .as_ref() - .to_vec(), - )) - } +impl TryFrom for SaltedPassword { + type Error = (); - pub(crate) fn as_bytes(&self) -> &[u8] { - self.0.as_bytes() + fn try_from(password: Password) -> Result { + Ok(Self(pwhash( + password.as_bytes(), + OPSLIMIT_INTERACTIVE, + MEMLIMIT_INTERACTIVE, + )?)) + } +} + +impl AsRef<[u8]> for SaltedPassword { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() } }