From 17013a06435432ed3628cb99d0a38bb325efe58b Mon Sep 17 00:00:00 2001 From: "D. Scott Boggs" Date: Mon, 26 Jun 2023 07:55:12 -0400 Subject: [PATCH] Add user model --- server/Cargo.lock | 49 +++++++++++++++++ server/Cargo.toml | 1 + server/src/api/error.rs | 4 +- server/src/entities/mod.rs | 1 + server/src/entities/prelude.rs | 1 + server/src/entities/user.rs | 53 +++++++++++++++++++ server/src/error.rs | 3 ++ .../m20230626_083036_create_users_table.rs | 42 +++++++++++++++ server/src/migrator/mod.rs | 2 + 9 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 server/src/entities/user.rs create mode 100644 server/src/migrator/m20230626_083036_create_users_table.rs diff --git a/server/Cargo.lock b/server/Cargo.lock index 59054fd..026a33f 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -142,6 +142,19 @@ version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +[[package]] +name = "bcrypt" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df288bec72232f78c1ec5fe4e8f1d108aa0265476e93097593c803c8c02062a" +dependencies = [ + "base64 0.21.2", + "blowfish", + "getrandom", + "subtle", + "zeroize", +] + [[package]] name = "bigdecimal" version = "0.3.1" @@ -192,6 +205,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher", +] + [[package]] name = "borsh" version = "0.10.3" @@ -305,6 +328,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clap" version = "3.2.25" @@ -989,6 +1022,15 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "instant" version = "0.1.12" @@ -1049,6 +1091,7 @@ dependencies = [ name = "kalkutago-server" version = "0.1.0" dependencies = [ + "bcrypt", "chrono", "derive_builder", "either", @@ -3010,3 +3053,9 @@ name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/server/Cargo.toml b/server/Cargo.toml index eddfb63..ec1cbdb 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -13,6 +13,7 @@ path = "src/main.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bcrypt = "0.14.0" chrono = "0.4.26" femme = "2.2.1" log = { version = "0.4.19", features = ["kv_unstable", "kv_unstable_serde"] } diff --git a/server/src/api/error.rs b/server/src/api/error.rs index 602ca1d..5689ed4 100644 --- a/server/src/api/error.rs +++ b/server/src/api/error.rs @@ -2,11 +2,11 @@ use crate::error::Error; #[derive(Responder)] #[response(status = 500, content_type = "json")] -pub(crate) struct ErrorResponder { +pub struct ErrorResponder { message: String, } -pub(crate) type ApiResult = Result; +pub type ApiResult = Result; // The following impl's are for easy conversion of error types. diff --git a/server/src/entities/mod.rs b/server/src/entities/mod.rs index 7a309af..e9e8598 100644 --- a/server/src/entities/mod.rs +++ b/server/src/entities/mod.rs @@ -6,3 +6,4 @@ pub mod groups; pub mod ticks; pub mod track2_groups; pub mod tracks; +pub mod user; diff --git a/server/src/entities/prelude.rs b/server/src/entities/prelude.rs index 796df22..419d754 100644 --- a/server/src/entities/prelude.rs +++ b/server/src/entities/prelude.rs @@ -4,3 +4,4 @@ pub use super::groups::Entity as Groups; pub use super::ticks::Entity as Ticks; pub use super::track2_groups::Entity as Track2Groups; pub use super::tracks::Entity as Tracks; +pub use super::user::Entity as User; diff --git a/server/src/entities/user.rs b/server/src/entities/user.rs new file mode 100644 index 0000000..d8ba976 --- /dev/null +++ b/server/src/entities/user.rs @@ -0,0 +1,53 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use std::default::default; + +use bcrypt::*; +use either::Either::{self, Left, Right}; +use rocket::response::status::Unauthorized; +use sea_orm::entity::prelude::*; + +use crate::{ + api::ErrorResponder, + error::{self, Error}, +}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "user")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub name: String, + pub password_hash: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} + +impl ActiveModel { + pub fn new(name: String, password: String) -> error::Result { + use sea_orm::ActiveValue::Set; + let name = Set(name); + let password_hash = Set(hash(password, DEFAULT_COST + 2)?); + Ok(Self { + name, + password_hash, + ..default() + }) + } +} + +impl Model { + pub fn check_password( + self, + password: String, + ) -> std::result::Result, ErrorResponder>> { + match verify(password, &self.password_hash) { + Ok(true) => Ok(self), + Ok(false) => Err(Left(Unauthorized(None))), + Err(err) => Err(Right(Error::from(err).into())), + } + } +} diff --git a/server/src/error.rs b/server/src/error.rs index fbf5c10..fa39e1f 100644 --- a/server/src/error.rs +++ b/server/src/error.rs @@ -1,5 +1,6 @@ use std::string; +use bcrypt::BcryptError; use derive_builder::UninitializedFieldError; #[derive(Debug, thiserror::Error)] @@ -18,6 +19,8 @@ pub enum Error { Utf8(#[from] string::FromUtf8Error), #[error(transparent)] ChannelSendError(#[from] tokio::sync::broadcast::error::SendError), + #[error(transparent)] + Bcrypt(#[from] BcryptError), } pub type Result = std::result::Result; diff --git a/server/src/migrator/m20230626_083036_create_users_table.rs b/server/src/migrator/m20230626_083036_create_users_table.rs new file mode 100644 index 0000000..50ab989 --- /dev/null +++ b/server/src/migrator/m20230626_083036_create_users_table.rs @@ -0,0 +1,42 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(User::Table) + .if_not_exists() + .col( + ColumnDef::new(User::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(User::Name).string().not_null()) + .col(ColumnDef::new(User::PasswordHash).string().not_null()) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(User::Table).to_owned()) + .await + } +} + +/// Learn more at https://docs.rs/sea-query#iden +#[derive(Iden)] +enum User { + Table, + Id, + Name, + PasswordHash, +} diff --git a/server/src/migrator/mod.rs b/server/src/migrator/mod.rs index 473e061..6d4f915 100644 --- a/server/src/migrator/mod.rs +++ b/server/src/migrator/mod.rs @@ -2,6 +2,7 @@ mod m20230606_000001_create_tracks_table; mod m20230606_000002_create_ticks_table; mod m20230606_000003_create_groups_table; mod m20230606_000004_create_track2groups_table; +mod m20230626_083036_create_users_table; use sea_orm_migration::prelude::*; @@ -15,6 +16,7 @@ impl MigratorTrait for Migrator { Box::new(m20230606_000002_create_ticks_table::Migration), Box::new(m20230606_000003_create_groups_table::Migration), Box::new(m20230606_000004_create_track2groups_table::Migration), + Box::new(m20230626_083036_create_users_table::Migration), ] } }