From eabeb7ed7b641dea0b8e71ab33ab97b4ed7a4cda Mon Sep 17 00:00:00 2001 From: Ilion Beyst Date: Mon, 13 Dec 2021 22:41:20 +0100 Subject: start implementing basic login functionality --- backend/src/db/mod.rs | 2 + backend/src/db/sessions.rs | 46 ++++++++++++++++++++ backend/src/db/users.rs | 106 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 backend/src/db/mod.rs create mode 100644 backend/src/db/sessions.rs create mode 100644 backend/src/db/users.rs (limited to 'backend/src/db') diff --git a/backend/src/db/mod.rs b/backend/src/db/mod.rs new file mode 100644 index 0000000..b6e3efc --- /dev/null +++ b/backend/src/db/mod.rs @@ -0,0 +1,2 @@ +pub mod sessions; +pub mod users; diff --git a/backend/src/db/sessions.rs b/backend/src/db/sessions.rs new file mode 100644 index 0000000..0cc3f1a --- /dev/null +++ b/backend/src/db/sessions.rs @@ -0,0 +1,46 @@ +use super::users::User; +use crate::schema::{sessions, users}; +use base64; +use diesel::PgConnection; +use diesel::{insert_into, prelude::*, Insertable, RunQueryDsl}; +use rand::{self, Rng}; + +#[derive(Insertable)] +#[table_name = "sessions"] +struct NewSession { + token: String, + user_id: i32, +} + +#[derive(Queryable, Debug, PartialEq)] +pub struct Session { + pub id: i32, + pub user_id: i32, + pub token: String, +} + +pub fn create_session(user: &User, conn: &PgConnection) -> Session { + let new_session = NewSession { + token: gen_session_token(), + user_id: user.user_id, + }; + let session = insert_into(sessions::table) + .values(&new_session) + .get_result::(conn) + .unwrap(); + + return session; +} + +pub fn find_user_by_session(token: &str, conn: &PgConnection) -> QueryResult<(Session, User)> { + sessions::table + .inner_join(users::table) + .filter(sessions::token.eq(&token)) + .first::<(Session, User)>(conn) +} + +pub fn gen_session_token() -> String { + let mut rng = rand::thread_rng(); + let token: [u8; 32] = rng.gen(); + return base64::encode(&token); +} diff --git a/backend/src/db/users.rs b/backend/src/db/users.rs new file mode 100644 index 0000000..c06e5b3 --- /dev/null +++ b/backend/src/db/users.rs @@ -0,0 +1,106 @@ +use crate::{schema::users, DbConn}; +use argon2; +use diesel::{prelude::*, PgConnection}; +use rand::Rng; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize)] +pub struct Credentials<'a> { + pub username: &'a str, + pub password: &'a str, +} + +#[derive(Insertable)] +#[table_name = "users"] +pub struct NewUser<'a> { + pub username: &'a str, + pub password_hash: &'a [u8], + pub password_salt: &'a [u8], +} + +#[derive(Queryable, Debug)] +pub struct User { + pub user_id: i32, + pub username: String, + pub password_salt: Vec, + pub password_hash: Vec, +} + +// TODO: make this configurable somewhere +fn argon2_config() -> argon2::Config<'static> { + argon2::Config { + variant: argon2::Variant::Argon2i, + version: argon2::Version::Version13, + mem_cost: 4096, + time_cost: 3, + lanes: 1, + thread_mode: argon2::ThreadMode::Sequential, + // TODO: set a secret + secret: &[], + ad: &[], + hash_length: 32, + } +} + +pub fn create_user(credentials: &Credentials, conn: &PgConnection) -> QueryResult { + let argon_config = argon2_config(); + + let salt: [u8; 32] = rand::thread_rng().gen(); + let hash = argon2::hash_raw(credentials.password.as_bytes(), &salt, &argon_config).unwrap(); + let new_user = NewUser { + username: &credentials.username, + password_salt: &salt, + password_hash: &hash, + }; + diesel::insert_into(users::table) + .values(&new_user) + .get_result::(conn) +} + +pub fn authenticate_user(credentials: &Credentials, db_conn: &PgConnection) -> Option { + let user = users::table + .filter(users::username.eq(&credentials.username)) + .first::(db_conn) + .unwrap(); + + let password_matches = argon2::verify_raw( + credentials.password.as_bytes(), + &user.password_salt, + &user.password_hash, + &argon2_config(), + ) + .unwrap(); + + if password_matches { + return Some(user); + } else { + return None; + } +} + +#[test] +fn test_argon() { + let credentials = Credentials { + username: "piepkonijn", + password: "geheim123", + }; + let argon_config = argon2_config(); + + let salt: [u8; 32] = rand::thread_rng().gen(); + let hash = argon2::hash_raw(credentials.password.as_bytes(), &salt, &argon_config).unwrap(); + let new_user = NewUser { + username: &credentials.username, + password_hash: &hash, + password_salt: &salt, + }; + + let password_matches = argon2::verify_raw( + credentials.password.as_bytes(), + &new_user.password_salt, + &new_user.password_hash, + &argon2_config(), + ) + .unwrap(); + + assert!(password_matches); +} -- cgit v1.2.3 From 6aa72b3c8717f32e62c772aeed327d3cd9a6fa65 Mon Sep 17 00:00:00 2001 From: Ilion Beyst Date: Wed, 15 Dec 2021 22:40:55 +0100 Subject: gracefully handle invalid login credentials --- backend/src/db/users.rs | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'backend/src/db') diff --git a/backend/src/db/users.rs b/backend/src/db/users.rs index c06e5b3..29cee88 100644 --- a/backend/src/db/users.rs +++ b/backend/src/db/users.rs @@ -58,24 +58,26 @@ pub fn create_user(credentials: &Credentials, conn: &PgConnection) -> QueryResul } pub fn authenticate_user(credentials: &Credentials, db_conn: &PgConnection) -> Option { - let user = users::table + users::table .filter(users::username.eq(&credentials.username)) .first::(db_conn) - .unwrap(); + .optional() + .unwrap() + .and_then(|user| { + let password_matches = argon2::verify_raw( + credentials.password.as_bytes(), + &user.password_salt, + &user.password_hash, + &argon2_config(), + ) + .unwrap(); - let password_matches = argon2::verify_raw( - credentials.password.as_bytes(), - &user.password_salt, - &user.password_hash, - &argon2_config(), - ) - .unwrap(); - - if password_matches { - return Some(user); - } else { - return None; - } + if password_matches { + return Some(user); + } else { + return None; + } + }) } #[test] -- cgit v1.2.3 From 52242b03f1af7f73e73592c2e5ee2bc54813a64d Mon Sep 17 00:00:00 2001 From: Ilion Beyst Date: Sun, 19 Dec 2021 00:16:46 +0100 Subject: simple bot uploads --- backend/src/db/bots.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++ backend/src/db/mod.rs | 1 + backend/src/db/sessions.rs | 2 +- backend/src/db/users.rs | 2 +- 4 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 backend/src/db/bots.rs (limited to 'backend/src/db') diff --git a/backend/src/db/bots.rs b/backend/src/db/bots.rs new file mode 100644 index 0000000..d359e28 --- /dev/null +++ b/backend/src/db/bots.rs @@ -0,0 +1,54 @@ +use diesel::prelude::*; +use serde::{Deserialize, Serialize}; + +use crate::schema::{bots, code_bundles}; +use crate::DbConn; +use chrono; + +#[derive(Insertable)] +#[table_name = "bots"] +pub struct NewBot<'a> { + pub owner_id: i32, + pub name: &'a str, +} + +#[derive(Queryable, Debug, PartialEq, Serialize, Deserialize)] +pub struct Bot { + pub id: i32, + pub owner_id: i32, + pub name: String, +} + +pub fn create_bot(new_bot: &NewBot, conn: &PgConnection) -> QueryResult { + diesel::insert_into(bots::table) + .values(new_bot) + .get_result(conn) +} + +pub fn find_bot(id: i32, conn: &PgConnection) -> QueryResult { + bots::table.find(id).first(conn) +} + +#[derive(Insertable)] +#[table_name = "code_bundles"] +pub struct NewCodeBundle<'a> { + pub bot_id: i32, + pub path: &'a str, +} + +#[derive(Queryable, Serialize, Deserialize, Debug)] +pub struct CodeBundle { + pub id: i32, + pub bot_id: i32, + pub path: String, + pub created_at: chrono::NaiveDateTime, +} + +pub fn create_code_bundle( + new_code_bundle: &NewCodeBundle, + conn: &PgConnection, +) -> QueryResult { + diesel::insert_into(code_bundles::table) + .values(new_code_bundle) + .get_result(conn) +} diff --git a/backend/src/db/mod.rs b/backend/src/db/mod.rs index b6e3efc..947b789 100644 --- a/backend/src/db/mod.rs +++ b/backend/src/db/mod.rs @@ -1,2 +1,3 @@ +pub mod bots; pub mod sessions; pub mod users; diff --git a/backend/src/db/sessions.rs b/backend/src/db/sessions.rs index 0cc3f1a..96f3926 100644 --- a/backend/src/db/sessions.rs +++ b/backend/src/db/sessions.rs @@ -22,7 +22,7 @@ pub struct Session { pub fn create_session(user: &User, conn: &PgConnection) -> Session { let new_session = NewSession { token: gen_session_token(), - user_id: user.user_id, + user_id: user.id, }; let session = insert_into(sessions::table) .values(&new_session) diff --git a/backend/src/db/users.rs b/backend/src/db/users.rs index 29cee88..0817766 100644 --- a/backend/src/db/users.rs +++ b/backend/src/db/users.rs @@ -20,7 +20,7 @@ pub struct NewUser<'a> { #[derive(Queryable, Debug)] pub struct User { - pub user_id: i32, + pub id: i32, pub username: String, pub password_salt: Vec, pub password_hash: Vec, -- cgit v1.2.3 From 1fb4a5151bd8cfe6de4d8c19e2066a9281a0b61a Mon Sep 17 00:00:00 2001 From: Ilion Beyst Date: Wed, 29 Dec 2021 16:11:27 +0100 Subject: migrate to axum --- backend/src/db/bots.rs | 1 - backend/src/db/users.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'backend/src/db') diff --git a/backend/src/db/bots.rs b/backend/src/db/bots.rs index d359e28..bc9cb11 100644 --- a/backend/src/db/bots.rs +++ b/backend/src/db/bots.rs @@ -2,7 +2,6 @@ use diesel::prelude::*; use serde::{Deserialize, Serialize}; use crate::schema::{bots, code_bundles}; -use crate::DbConn; use chrono; #[derive(Insertable)] diff --git a/backend/src/db/users.rs b/backend/src/db/users.rs index 0817766..663f173 100644 --- a/backend/src/db/users.rs +++ b/backend/src/db/users.rs @@ -1,4 +1,4 @@ -use crate::{schema::users, DbConn}; +use crate::schema::users; use argon2; use diesel::{prelude::*, PgConnection}; use rand::Rng; -- cgit v1.2.3