aboutsummaryrefslogtreecommitdiff
path: root/backend/src/db
diff options
context:
space:
mode:
authorIlion Beyst <ilion.beyst@gmail.com>2021-12-29 19:56:31 +0100
committerIlion Beyst <ilion.beyst@gmail.com>2021-12-29 19:56:31 +0100
commit3eeaab6cec70e7a06a99a1ac2662974f71064bee (patch)
tree9d5a2665ed32df41b2be131d5e27e8b321ce78a8 /backend/src/db
parentee5af8d07625bfc7ad11b842b3941bb095aa6a6e (diff)
parent1fb4a5151bd8cfe6de4d8c19e2066a9281a0b61a (diff)
downloadplanetwars.dev-3eeaab6cec70e7a06a99a1ac2662974f71064bee.tar.xz
planetwars.dev-3eeaab6cec70e7a06a99a1ac2662974f71064bee.zip
Merge branch 'backend-server'
Diffstat (limited to 'backend/src/db')
-rw-r--r--backend/src/db/bots.rs53
-rw-r--r--backend/src/db/mod.rs3
-rw-r--r--backend/src/db/sessions.rs46
-rw-r--r--backend/src/db/users.rs108
4 files changed, 210 insertions, 0 deletions
diff --git a/backend/src/db/bots.rs b/backend/src/db/bots.rs
new file mode 100644
index 0000000..bc9cb11
--- /dev/null
+++ b/backend/src/db/bots.rs
@@ -0,0 +1,53 @@
+use diesel::prelude::*;
+use serde::{Deserialize, Serialize};
+
+use crate::schema::{bots, code_bundles};
+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<Bot> {
+ diesel::insert_into(bots::table)
+ .values(new_bot)
+ .get_result(conn)
+}
+
+pub fn find_bot(id: i32, conn: &PgConnection) -> QueryResult<Bot> {
+ 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<CodeBundle> {
+ 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
new file mode 100644
index 0000000..947b789
--- /dev/null
+++ b/backend/src/db/mod.rs
@@ -0,0 +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
new file mode 100644
index 0000000..96f3926
--- /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.id,
+ };
+ let session = insert_into(sessions::table)
+ .values(&new_session)
+ .get_result::<Session>(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..663f173
--- /dev/null
+++ b/backend/src/db/users.rs
@@ -0,0 +1,108 @@
+use crate::schema::users;
+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 id: i32,
+ pub username: String,
+ pub password_salt: Vec<u8>,
+ pub password_hash: Vec<u8>,
+}
+
+// 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<User> {
+ 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::<User>(conn)
+}
+
+pub fn authenticate_user(credentials: &Credentials, db_conn: &PgConnection) -> Option<User> {
+ users::table
+ .filter(users::username.eq(&credentials.username))
+ .first::<User>(db_conn)
+ .optional()
+ .unwrap()
+ .and_then(|user| {
+ 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);
+}