diff --git a/api/src/lib.rs b/api/src/lib.rs index df560b8..5ba1fbd 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -1,15 +1,12 @@ #[macro_use] extern crate rocket; +use rocket::{Rocket, Build}; use rocket::serde::json::Json; - use rocket::fairing::{self, AdHoc}; -use rocket::form::{ Form}; -use rocket::fs::{relative, FileServer}; -use rocket::response::{Flash, Redirect}; -use rocket::{Build, Rocket}; -use portfolio_core::{Mutation, Query}; +use rocket::response::status::Custom; +use portfolio_core::{Mutation}; -use migration::MigratorTrait; +use migration::{MigratorTrait}; use sea_orm_rocket::{Connection, Database}; mod pool; @@ -18,18 +15,21 @@ use pool::Db; pub use entity::candidate; pub use entity::candidate::Entity as Candidate; +use portfolio_core::crypto::random_8_char_string; + #[post("/", data = "")] -async fn create(conn: Connection<'_, Db>, post_form: Json) -> Flash { +async fn create(conn: Connection<'_, Db>, post_form: Json) -> Result> { let db = conn.into_inner(); - let form = post_form.into_inner(); - Mutation::create_candidate(db, form) - .await - .expect("could not insert post"); + let plain_text_password = random_8_char_string(); - Flash::success(Redirect::to("/"), "Post successfully added.") + Mutation::create_candidate(db, form, &plain_text_password) + .await + .expect("Could not insert candidate"); + + Ok(plain_text_password) } #[get("/hello")] diff --git a/core/Cargo.toml b/core/Cargo.toml index 3793774..4b41cec 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" [dependencies] portfolio-entity = { path = "../entity" } +rand = "0.8.5" +argon2 = "0.4.1" +chrono = "0.4.22" [dependencies.sea-orm] version = "^0.10.0" diff --git a/core/src/crypto.rs b/core/src/crypto.rs new file mode 100644 index 0000000..7047e55 --- /dev/null +++ b/core/src/crypto.rs @@ -0,0 +1,53 @@ +use argon2::{ + Argon2, PasswordHasher as ArgonPasswordHasher, PasswordVerifier as ArgonPasswordVerifier, +}; +use rand::Rng; + + +/// Foolproof random 8 char string +/// only uppercase letters (except for 0 and O) and numbers +/// TODO tests +pub fn random_8_char_string() -> String { + let iterator = rand::thread_rng() + .sample_iter(&rand::distributions::Alphanumeric) + .map(char::from); + + + let mut s = String::new(); + for c in iterator { // add all characters except for: lowercase chars, 0 and O + if ('1'..='9').contains(&c) || + ('A'..='N').contains(&c) || + ('P'..'Z').contains(&c) + { + s.push(c); + if s.len() == 8 { + break; + } + } + } + s +} + +pub fn hash_password(password_plaint_text: &str) -> Result { + let password = password_plaint_text.as_bytes(); + let salt = "c2VjcmV0bHl0ZXN0aW5nZXZlcnl0aGluZw"; + + let argon_config = Argon2::default(); + + let hash = argon_config.hash_password(password, salt)?; + + return Ok(hash.to_string()); +} + +pub fn verify_password( + password_plaint_text: &str, + hash: &str, +) -> Result { + let argon_config = Argon2::default(); + + let parsed_hash = argon2::PasswordHash::new(&hash)?; + + return Ok(argon_config + .verify_password(password_plaint_text.as_bytes(), &parsed_hash) + .is_ok()); +} diff --git a/core/src/lib.rs b/core/src/lib.rs index 4a80f23..0ebcef0 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,5 +1,6 @@ mod mutation; mod query; +pub mod crypto; pub use mutation::*; pub use query::*; diff --git a/core/src/mutation.rs b/core/src/mutation.rs index f3f9e61..ac04626 100644 --- a/core/src/mutation.rs +++ b/core/src/mutation.rs @@ -1,5 +1,6 @@ -use ::entity::{candidate, candidate::Entity as Candidate}; +use ::entity::{candidate}; use sea_orm::*; +use crate::crypto::hash_password; pub struct Mutation; @@ -7,7 +8,20 @@ impl Mutation { pub async fn create_candidate( db: &DbConn, form_data: candidate::Model, - ) -> Result { - todo!() + plain_text_password: &String, + ) -> Result { + // TODO: unwrap pro testing.. + let hashed_password = hash_password(plain_text_password).unwrap(); + candidate::ActiveModel { + application: Set(form_data.application), + code: Set(hashed_password), + public_key: Set("lorem ipsum pub key".to_string()), + private_key: Set("lorem ipsum priv key".to_string()), + created_at: Set(chrono::offset::Local::now().naive_local()), + updated_at: Set(chrono::offset::Local::now().naive_local()), + ..Default::default() + } + .insert(db) + .await } } diff --git a/entity/src/admin.rs b/entity/src/admin.rs index 1dd8420..ccfab5a 100644 --- a/entity/src/admin.rs +++ b/entity/src/admin.rs @@ -1,10 +1,8 @@ //! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 use sea_orm::entity::prelude::*; -use rocket::serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] -#[serde(crate = "rocket::serde")] +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] #[sea_orm(table_name = "admin")] pub struct Model { #[sea_orm(primary_key)] diff --git a/entity/src/candidate.rs b/entity/src/candidate.rs index 7c20178..d9199ce 100644 --- a/entity/src/candidate.rs +++ b/entity/src/candidate.rs @@ -1,7 +1,8 @@ -//! SeaORM Entity. Generated by sea-orm-codegen 0.10.0 -use chrono::{DateTime, NaiveDate, Local}; -use rocket::serde::{Deserialize, Serialize}; +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 + use sea_orm::entity::prelude::*; +use rocket::serde::{Deserialize, Serialize}; + #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] #[serde(crate = "rocket::serde")] @@ -9,26 +10,30 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub application: i32, + #[serde(skip_deserializing, skip_serializing)] pub code: String, pub name: Option, pub surname: Option, pub birth_surname: Option, pub birthplace: Option, - pub birthdate: Option, + pub birthdate: Option, pub address: Option, pub telephone: Option, - #[sea_orm(default_value="Česká republika")] pub citizenship: Option, pub email: Option, pub sex: Option, pub study: Option, pub personal_identification_number: Option, - #[sea_orm(column_type = "Text")] + #[sea_orm(column_type = "Text", nullable)] pub personal_identification_number_hash: Option, + #[serde(skip_deserializing, skip_serializing)] pub public_key: String, + #[serde(skip_deserializing, skip_serializing)] pub private_key: String, - pub created_at: DateTime, - pub updated_at: DateTime, + #[serde(skip_deserializing, skip_serializing)] + pub created_at: DateTime, + #[serde(skip_deserializing, skip_serializing)] + pub updated_at: DateTime, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/entity/src/lib.rs b/entity/src/lib.rs index ffaaadc..5c0177c 100644 --- a/entity/src/lib.rs +++ b/entity/src/lib.rs @@ -1,6 +1,3 @@ -#[macro_use] -extern crate rocket; - pub mod prelude; pub mod admin; diff --git a/entity/src/parent.rs b/entity/src/parent.rs index 48e1f7c..4cf47cc 100644 --- a/entity/src/parent.rs +++ b/entity/src/parent.rs @@ -1,10 +1,8 @@ -//! SeaORM Entity. Generated by sea-orm-codegen 0.10.0 -use sea_orm::entity::prelude::*; -use chrono::{DateTime, Local}; -use rocket::serde::{Deserialize, Serialize}; +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] -#[serde(crate = "rocket::serde")] +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] #[sea_orm(table_name = "parent")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] @@ -13,8 +11,8 @@ pub struct Model { pub surname: Option, pub telephone: Option, pub email: Option, - pub created_at: DateTime, - pub updated_at: DateTime, + pub created_at: DateTime, + pub updated_at: DateTime, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/entity/src/prelude.rs b/entity/src/prelude.rs index eba3b7f..06a7e4b 100644 --- a/entity/src/prelude.rs +++ b/entity/src/prelude.rs @@ -1,3 +1,5 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 + pub use super::admin::Entity as Admin; pub use super::candidate::Entity as Candidate; pub use super::parent::Entity as Parent;