diff --git a/Cargo.lock b/Cargo.lock index 3eefe94..0cade06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,6 +64,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "anyhow" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" + [[package]] name = "arrayref" version = "0.3.6" @@ -124,6 +130,18 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e223af0dc48c96d4f8342ec01a4974f139df863896b316681efd36742f22cc67" +[[package]] +name = "bcrypt" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b70db86f3c560199b0dada79a22b9a924622384abb2a756a9707ffcce077f2" +dependencies = [ + "base64 0.12.2", + "blowfish", + "byteorder", + "getrandom", +] + [[package]] name = "bitflags" version = "1.2.1" @@ -171,6 +189,17 @@ dependencies = [ "byte-tools", ] +[[package]] +name = "blowfish" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aeb80d00f2688459b8542068abd974cfb101e7a82182414a99b5026c0d85cc3" +dependencies = [ + "block-cipher-trait", + "byteorder", + "opaque-debug", +] + [[package]] name = "bumpalo" version = "3.4.0" @@ -601,6 +630,8 @@ dependencies = [ name = "katbin" version = "0.1.0" dependencies = [ + "anyhow", + "bcrypt", "chrono", "diesel", "diesel_migrations", diff --git a/Cargo.toml b/Cargo.toml index 8c549b8..2bbb99c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,8 @@ dotenv = "0.15.0" jsonwebtoken = "7.1.2" slog = "2.5.2" slog-term = "2.6.0" +anyhow = "1.0" slog-async = "2.5.0" +bcrypt = "0.8" chrono = "0.4.11" uuid = { version = "0.8", features = ["serde", "v4"] } diff --git a/diesel.toml b/diesel.toml index 92267c8..560f723 100644 --- a/diesel.toml +++ b/diesel.toml @@ -1,5 +1,5 @@ # For documentation on how to configure this file, -# see diesel.rs/guides/configuring-diesel-cli +# see postgres/guides/configuring-diesel-cli [print_schema] file = "src/schema.rs" diff --git a/src/api/routes/paste.rs b/src/api/routes/paste.rs index bd45081..0e51e3b 100644 --- a/src/api/routes/paste.rs +++ b/src/api/routes/paste.rs @@ -1,17 +1,19 @@ -use rocket::{Rocket, response::status, http::{Status, Cookies, Cookie}}; -use rocket_contrib::json::Json; -use crate::core::paste::{entity::Paste, diesel::create_paste}; -use serde_json::Value; -use crate::utils::{db, phonetic_key}; -use std::ops::{DerefMut}; -use rocket::response::status::Custom; -use crate::utils::phonetic_key::get_random_id; +use std::ops::DerefMut; +use rocket::{http::{Cookie, Cookies, Status}, response::status, Rocket}; +use rocket::response::status::Custom; +use rocket_contrib::json::Json; +use serde_json::Value; + +use crate::core::paste::{entity::Paste, service::create_paste}; +use crate::core::users::{service::create_or_fetch_user}; +use crate::utils::{db, phonetic_key}; +use crate::utils::phonetic_key::get_random_id; #[post("/", data = "")] fn create(mut paste: Json, conn: db::DbConn, mut ck: Cookies) -> Custom> { // Check if frontend sent a session cookie - let session = match ck.get_private("session") { + let user_id = match ck.get_private("session") { Some(c) => c.value().to_string(), None => { let user_id = get_random_id(); @@ -20,11 +22,22 @@ fn create(mut paste: Json, conn: db::DbConn, mut ck: Cookies) -> Custom user, + Err(e) => return status::Custom(Status::InternalServerError, Json(json!({ + "err": e.to_string(), + "msg": "Failed to create or fetch user" + }))) + }; + let new_paste = paste.deref_mut(); if new_paste.id.is_none() { new_paste.id = Some(phonetic_key::get_random_id()); } + new_paste.belongs_to = Some(user.id); + match create_paste(new_paste, &conn) { Ok(_) => { status::Custom(Status::Created, Json(json!({ diff --git a/src/core/paste/entity.rs b/src/core/paste/entity.rs index d542b2f..82d8c3a 100644 --- a/src/core/paste/entity.rs +++ b/src/core/paste/entity.rs @@ -1,10 +1,10 @@ use crate::schema::pastes; -#[table_name="pastes"] +#[table_name = "pastes"] #[derive(AsChangeset, Serialize, Deserialize, Queryable, Insertable)] pub struct Paste { pub id: Option, - pub belongs_to: String, + pub belongs_to: Option, pub is_url: bool, - pub content: String + pub content: String, } \ No newline at end of file diff --git a/src/core/paste/mod.rs b/src/core/paste/mod.rs index 17f177d..7807cca 100644 --- a/src/core/paste/mod.rs +++ b/src/core/paste/mod.rs @@ -1,4 +1,3 @@ pub mod entity; -pub mod repository; -pub mod diesel; +pub mod postgres; pub mod service; \ No newline at end of file diff --git a/src/core/paste/diesel.rs b/src/core/paste/postgres.rs similarity index 54% rename from src/core/paste/diesel.rs rename to src/core/paste/postgres.rs index 90744f0..45cab94 100644 --- a/src/core/paste/diesel.rs +++ b/src/core/paste/postgres.rs @@ -1,13 +1,14 @@ -use diesel::{RunQueryDsl}; +use anyhow::Result; use diesel::pg::PgConnection; -use diesel::result::Error; +use diesel::RunQueryDsl; use crate::schema::pastes; use super::entity::Paste; -pub fn create_paste(paste: &Paste, conn: &PgConnection) -> Result { - diesel::insert_into(pastes::table) +pub fn create_paste(paste: &Paste, conn: &PgConnection) -> Result { + let rows = diesel::insert_into(pastes::table) .values(paste) - .execute(conn) + .execute(conn)?; + Ok(rows) } \ No newline at end of file diff --git a/src/core/paste/repository.rs b/src/core/paste/repository.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/core/paste/service.rs b/src/core/paste/service.rs index e69de29..5ad7f2c 100644 --- a/src/core/paste/service.rs +++ b/src/core/paste/service.rs @@ -0,0 +1,9 @@ +use anyhow::Result; +use diesel::pg::PgConnection; + +use super::entity::Paste; +use super::postgres; + +pub fn create_paste(paste: &Paste, conn: &PgConnection) -> Result { + postgres::create_paste(paste, conn) +} \ No newline at end of file diff --git a/src/core/users/diesel.rs b/src/core/users/diesel.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/core/users/entity.rs b/src/core/users/entity.rs index e69de29..ff9ba1d 100644 --- a/src/core/users/entity.rs +++ b/src/core/users/entity.rs @@ -0,0 +1,10 @@ +use crate::schema::users; + +#[table_name = "users"] +#[derive(AsChangeset, Serialize, Deserialize, Queryable, Insertable)] +pub struct User { + pub id: String, + pub username: Option, + pub password: Option, + pub activated: Option, +} \ No newline at end of file diff --git a/src/core/users/mod.rs b/src/core/users/mod.rs index 2c2c2d0..b2a1bcb 100644 --- a/src/core/users/mod.rs +++ b/src/core/users/mod.rs @@ -1,4 +1,3 @@ -pub mod diesel; +pub mod postgres; pub mod entity; -pub mod repository; pub mod service; \ No newline at end of file diff --git a/src/core/users/postgres.rs b/src/core/users/postgres.rs new file mode 100644 index 0000000..ac1e201 --- /dev/null +++ b/src/core/users/postgres.rs @@ -0,0 +1,19 @@ +use diesel::prelude::*; +use diesel::pg::PgConnection; +use anyhow::Result; + +use crate::core::users::entity::User; +use crate::schema::users; + +pub fn create_user(user: &User, conn: &PgConnection) -> Result { + let records = diesel::insert_into(users::table) + .values(user) + .on_conflict_do_nothing() + .execute(conn)?; + Ok(records) +} + +pub fn find_user(id: String, conn: &PgConnection) -> Result { + let user = users::table.find(id).get_result::(conn)?; + Ok(user) +} \ No newline at end of file diff --git a/src/core/users/repository.rs b/src/core/users/repository.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/core/users/service.rs b/src/core/users/service.rs index e69de29..c71703a 100644 --- a/src/core/users/service.rs +++ b/src/core/users/service.rs @@ -0,0 +1,36 @@ +use anyhow::Result; +use bcrypt::{DEFAULT_COST, hash}; +use diesel::pg::PgConnection; +use diesel::result::Error; + +use crate::core::users::entity::User; + +use super::postgres; + +pub fn create_user(user: &mut User, conn: &PgConnection) -> Result { + let hashed_pass = hash(user.password.as_ref().unwrap().as_bytes(), DEFAULT_COST)?; + user.password = Some(hashed_pass); + postgres::create_user(user, conn) +} + +pub fn create_or_fetch_user(id: String, conn: &PgConnection) -> Result { + let user = match postgres::find_user(id.clone(), conn) { + Ok(user) => user, + Err(err) => { + match err.downcast_ref::() { + Some(Error::NotFound) => { + let new_user = User { + id: id.clone(), + username: None, + password: None, + activated: Some(false), + }; + postgres::create_user(&new_user, conn)?; + new_user + } + _ => return Err(err) + } + } + }; + Ok(user) +} \ No newline at end of file