From 90e64967869da1761f03ad70f34a4f3566bdab89 Mon Sep 17 00:00:00 2001 From: shockrah Date: Fri, 7 Aug 2020 21:23:38 -0700 Subject: [PATCH 01/47] Auth wall now uses id for lookups Secret is now checked against bcrypt, default cost now 13 New AuthReason::BadKey Meant to give us more specific auth responses but main isn't dealing w/ it yet --- server/src/auth.rs | 74 ++++++++++++++++++++++++---------------------- server/src/main.rs | 2 +- 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/server/src/auth.rs b/server/src/auth.rs index 8865fe9..69b4ffb 100644 --- a/server/src/auth.rs +++ b/server/src/auth.rs @@ -1,59 +1,60 @@ -use mysql_async::Pool; +use bcrypt; +use mysql_async::{Conn, Pool}; use mysql_async::prelude::{params, Queryable}; -use crate::db_types::{UBigInt, VarChar}; +use crate::db_types::{BigInt, Integer, UBigInt, VarChar}; use crate::routes; -pub const BCRYPT_COST: u32 = 14; +// used when we create a new users for the first time +pub const _BCRYPT_COST: u32 = 14; pub enum AuthReason { - Good, //passed regular check + Good, //passed regular check OpenAuth, // route does not require auth - NoKey, + NoKey, // key missing + BadKey, // key is bad } fn open_route(path: &str) -> bool { return path == routes::INVITE_JOIN } + +fn valid_user(secret: &str, row: &Option<(VarChar, VarChar, BigInt, Integer, UBigInt)>) -> bool { + match row { + Some(row) => { + if let Ok(result) = bcrypt::verify(secret, &row.0) { + return result; + } + return false; + }, + _ => return false + } +} + pub async fn wall_entry(path: &str, pool: &Pool, params: &mut serde_json::Value) -> Result { - use serde_json::json; // Start by Checking if the api key is in our keystore if open_route(path) { Ok(AuthReason::OpenAuth) } else { - if let Some(key) = params.get("secret") { - let key_str = key.as_str(); - let conn = pool.get_conn().await?; - // (id, name, secret) - type RowType = Option<(UBigInt, VarChar)>; - let db_result: Result<(_, RowType), mysql_async::error::Error> = conn - .first_exec(r"SELECT id, name FROM members WHERE secret = :secret ", mysql_async::params!{ "secret" => key_str}) - .await; - - match db_result { - Ok((_, row)) => { - match row{ - Some(user_row) => { - params["userid"] = json!(user_row.0); - params["username"] = json!(user_row.1); - Ok(AuthReason::Good) - }, - None => Ok(AuthReason::NoKey) - } + match (params.get("id"), params.get("secret")) { + (Some(id_v), Some(secret_v)) => { + let id = id_v.as_str().unwrap(); + let secret = secret_v.as_str().unwrap(); + let conn = pool.get_conn().await?; + let db_tup: (Conn, Option<(VarChar, VarChar, BigInt, Integer, UBigInt)>) = conn.first_exec( + "SELECT secret, name, joindate, status, permissions FROM members WHERE id = :id", + mysql_async::params!{"id" => id}).await?; + if valid_user(secret, &db_tup.1) { + Ok(AuthReason::Good) } - Err(e) => { - println!("\tIssue fetching auth data {:?}", e); - Ok(AuthReason::NoKey) + else { + Ok(AuthReason::BadKey) } + }, + _ => { + Ok(AuthReason::NoKey) } - - //let (_con, row): (_, Option<(UBigInt, VarChar)>) = conn - // .first_exec(r"SELECT userid, name FROM keys WHERE secret = :secret ", mysql_async::params!{ "secret" => key}) - // .await; - } - else { - Ok(AuthReason::NoKey) } } } @@ -66,6 +67,7 @@ pub fn generate_secret() -> String { use base64::{encode_config, URL_SAFE}; let mut buf: Vec = vec![0;64]; - getrandom(&mut buf); + getrandom(&mut buf).unwrap(); encode_config(buf,URL_SAFE) } + diff --git a/server/src/main.rs b/server/src/main.rs index c7b478a..c38bd05 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -98,7 +98,7 @@ async fn main_responder(request: Request) -> Result, hyper: // Deal with permissions errors at this point match auth_result { OpenAuth | Good => route_dispatcher(&pool, &mut response, &method, path, params).await, - NoKey => { + NoKey | BadKey => { println!("\tAUTH: NoKey/BadKey"); *response.status_mut() = StatusCode::UNAUTHORIZED }, From b3d603eecc1ea3be59f32efd01287f626ee92573 Mon Sep 17 00:00:00 2001 From: shockrah Date: Sat, 8 Aug 2020 00:30:35 -0700 Subject: [PATCH 02/47] verification of secrets now has clearer fallback --- server/src/auth.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/src/auth.rs b/server/src/auth.rs index 69b4ffb..4a5fd4d 100644 --- a/server/src/auth.rs +++ b/server/src/auth.rs @@ -6,7 +6,7 @@ use crate::db_types::{BigInt, Integer, UBigInt, VarChar}; use crate::routes; // used when we create a new users for the first time -pub const _BCRYPT_COST: u32 = 14; +pub const BCRYPT_COST: u32 = 14; pub enum AuthReason { Good, //passed regular check OpenAuth, // route does not require auth @@ -22,10 +22,10 @@ fn open_route(path: &str) -> bool { fn valid_user(secret: &str, row: &Option<(VarChar, VarChar, BigInt, Integer, UBigInt)>) -> bool { match row { Some(row) => { - if let Ok(result) = bcrypt::verify(secret, &row.0) { - return result; + match bcrypt::verify(secret, &row.0) { + Ok(result) => result, + Err(_) => return false } - return false; }, _ => return false } @@ -39,7 +39,7 @@ pub async fn wall_entry(path: &str, pool: &Pool, params: &mut serde_json::Value) else { match (params.get("id"), params.get("secret")) { (Some(id_v), Some(secret_v)) => { - let id = id_v.as_str().unwrap(); + let id = id_v.as_u64().unwrap(); let secret = secret_v.as_str().unwrap(); let conn = pool.get_conn().await?; let db_tup: (Conn, Option<(VarChar, VarChar, BigInt, Integer, UBigInt)>) = conn.first_exec( From 537ba957e4a3c5f98b29b20676d8b77bd114e98f Mon Sep 17 00:00:00 2001 From: shockrah Date: Sat, 8 Aug 2020 00:30:45 -0700 Subject: [PATCH 03/47] removed dead code --- server/src/channels.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/server/src/channels.rs b/server/src/channels.rs index 81c3a49..c11d3ca 100644 --- a/server/src/channels.rs +++ b/server/src/channels.rs @@ -1,5 +1,3 @@ -use std::borrow::Cow; - use hyper::{StatusCode, Response, Body}; use hyper::header::HeaderValue; @@ -35,20 +33,6 @@ impl ChannelType { } } - - // whole ass function exists because serde_json is a walking pos - pub fn from_i64_opt(x: Option) -> ChannelType { - if let Some(i) = x { - match i { - 1 => ChannelType::Voice, - 2 => ChannelType::Text, - _ => ChannelType::Undefined - } - } - else { - ChannelType::Undefined - } - } } // Primary way of interpretting sql data on our channels table From b4ec7005c7df210f8346d53b248689574330629e Mon Sep 17 00:00:00 2001 From: shockrah Date: Sat, 8 Aug 2020 00:31:12 -0700 Subject: [PATCH 04/47] removed more dead code --- server/src/db_types.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/server/src/db_types.rs b/server/src/db_types.rs index e1d15ee..4969457 100644 --- a/server/src/db_types.rs +++ b/server/src/db_types.rs @@ -1,13 +1,7 @@ pub type Integer = i32; -pub type UInteger = u32; pub type UBigInt = u64; pub type BigInt = i64; pub type VarChar = String; -pub enum DbError { - BadParam(&'static str), - Connection(&'static str), - Internal -} From 0d7e4e15b9ea8b337944b368a9b73c1a7642c6ee Mon Sep 17 00:00:00 2001 From: shockrah Date: Sat, 8 Aug 2020 00:32:46 -0700 Subject: [PATCH 05/47] removed dead code insert_new_member returns the raw secret ont whats stored in the db --- server/src/members.rs | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/server/src/members.rs b/server/src/members.rs index 944e0b5..60671a1 100644 --- a/server/src/members.rs +++ b/server/src/members.rs @@ -7,6 +7,7 @@ use serde_json::Value; use serde::Serialize; use crate::db_types::{UBigInt, BigInt, Integer, VarChar}; +use crate::auth; #[derive(Serialize)] pub struct Member { @@ -18,29 +19,16 @@ pub struct Member { pub permissions: UBigInt, } -struct InsertableMember<'n> { - name: &'n str, - permissions: u64, -} -impl<'n> InsertableMember<'n> { - fn new(name: &'n str) -> InsertableMember<'n> { - use crate::perms::{JOIN_VOICE, SEND_MESSAGES}; - - let now: BigInt = Utc::now().timestamp_millis(); - let default_perms = JOIN_VOICE | SEND_MESSAGES; - InsertableMember { - name: name, - permissions: default_perms, - } - } -} - pub async fn insert_new_member(p: &Pool, name: VarChar, perms: u64) -> Result { use crate::auth::generate_secret; let conn: Conn = p.get_conn().await?; - let secret: String = generate_secret(); + let secret_raw: String = generate_secret(); + let secret = match bcrypt::hash(&secret_raw, auth::BCRYPT_COST) { + Ok(value) => value, + Err(e) => panic!("\tCould not insert member due to bcrypt failure:\n\t\t{}",e) + }; let now: BigInt = Utc::now().timestamp(); let conn = conn.drop_exec( @@ -63,7 +51,7 @@ pub async fn insert_new_member(p: &Pool, name: VarChar, perms: u64) -> Result, params: Value) { .unwrap_or(&default_name) .as_str().unwrap_or("NewUser"); - let pre_mem = InsertableMember::new(name); match insert_new_member(p, name.to_string(), perms::GENERAL_NEW).await { Ok(new_member) => { *resp.status_mut() = StatusCode::OK; @@ -96,3 +83,4 @@ async fn general_new_user(p: &Pool, resp: &mut Response, params: Value) { } } } + From 1d5224242ed1bad8739268d6d5a3d37ae1c59341 Mon Sep 17 00:00:00 2001 From: shockrah Date: Sat, 8 Aug 2020 00:33:31 -0700 Subject: [PATCH 06/47] new sample key using the new encryption to get past the auth barrier --- server/tests/common.sh | 16 +++++++++------- server/tests/verify_basic_cases.sh | 8 ++++---- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/server/tests/common.sh b/server/tests/common.sh index a996878..8bc7813 100644 --- a/server/tests/common.sh +++ b/server/tests/common.sh @@ -1,13 +1,15 @@ #!/bin/sh # Details for our bs user when testing things -export id=1 -export secret=secret -export name=godrah -export joindate=123 -export status=1 -export permissions=69 +# raw from `-c same` flag +# {"id":5,"secret":"JYFKgDOgEAn2vJISwRSkRKn3kNE2EQD0dTFOG24UipQ9kQfBOura06PYf-hYGdFbCNeDJy2LxLu-9N13S8Auaw==","name":"adminsama","joindate":1596865071,"status":0,"permissions":18446744073709551615} +export id=5 +export secret='JYFKgDOgEAn2vJISwRSkRKn3kNE2EQD0dTFOG24UipQ9kQfBOura06PYf-hYGdFbCNeDJy2LxLu-9N13S8Auaw==' +export name='adminsama' +export joindate='1596865071' +export status=0 +export permissions='18446744073709551615' -export simple_key='{"secret":"secret"}' +export simple_key='{"secret":"'"$secret"'","id":'$id'}' export url='localhost:8888' diff --git a/server/tests/verify_basic_cases.sh b/server/tests/verify_basic_cases.sh index b23e796..e25004c 100644 --- a/server/tests/verify_basic_cases.sh +++ b/server/tests/verify_basic_cases.sh @@ -13,14 +13,14 @@ list_all_channels() { # TEST } create_channel() { - kv='{"secret":"secret", "name":"sample", "kind":2, "description":"some bs description"}' + kv="{\"secret\":\"$secret\", \"name\":\"sample\", \"kind\":2, \"description\":\"some bs description\"}" result=$($crl $POST $url/channels/create -d "$kv") code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') log_result good_create_channel 200 $code "$result" } delete_channel() { - kv='{"secret":"secret", "name":"sample"}' + kv="{\"secret\":\"$secret\", \"name\":\"sample\"}" result=$($crl $POST $url/channels/delete -d "$kv") code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') log_result good_delete_channel 200 $code "$result" @@ -28,10 +28,10 @@ delete_channel() { send_message() { # ignoring the reaction to this as its not _completely_ relevant for this test - $crl $POST $url/channels/create -d '{"secret":"secret","name":"msgchannel","kind":2}' > /dev/null + $crl $POST $url/channels/create -d "{\"id\":$id, \"secret\":\"$secret\",\"name\":\"msgchannel\",\"kind\":2}" > /dev/null # now we can try sending the right parameters to send a basic message - kv='{"secret":"secret", "content":"message sample", "channel":"msgchannel"}' + kv="{\"secret\":\"$secret\", \"content\":\"message sample\", \"channel\":\"msgchannel\"}" result=$($crl $POST $url/message/send -d "$kv") code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') # non-existant channel for now but whatever ignore for now From 47cc48575d7e5b2e966a7753c8c9f48fcd8ee024 Mon Sep 17 00:00:00 2001 From: shockrah Date: Sat, 8 Aug 2020 21:30:10 -0700 Subject: [PATCH 07/47] added missing keys in json params to queries --- server/tests/verify_basic_cases.sh | 6 +++--- server/tests/verify_err_cases.sh | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server/tests/verify_basic_cases.sh b/server/tests/verify_basic_cases.sh index e25004c..519435f 100644 --- a/server/tests/verify_basic_cases.sh +++ b/server/tests/verify_basic_cases.sh @@ -13,14 +13,14 @@ list_all_channels() { # TEST } create_channel() { - kv="{\"secret\":\"$secret\", \"name\":\"sample\", \"kind\":2, \"description\":\"some bs description\"}" + kv="{\"id\":$id,\"secret\":\"$secret\", \"name\":\"sample\", \"kind\":2, \"description\":\"some bs description\"}" result=$($crl $POST $url/channels/create -d "$kv") code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') log_result good_create_channel 200 $code "$result" } delete_channel() { - kv="{\"secret\":\"$secret\", \"name\":\"sample\"}" + kv="{\"id\":$id,\"secret\":\"$secret\", \"name\":\"sample\"}" result=$($crl $POST $url/channels/delete -d "$kv") code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') log_result good_delete_channel 200 $code "$result" @@ -31,7 +31,7 @@ send_message() { $crl $POST $url/channels/create -d "{\"id\":$id, \"secret\":\"$secret\",\"name\":\"msgchannel\",\"kind\":2}" > /dev/null # now we can try sending the right parameters to send a basic message - kv="{\"secret\":\"$secret\", \"content\":\"message sample\", \"channel\":\"msgchannel\"}" + kv="{\"id\":$id,\"secret\":\"$secret\", \"content\":\"message sample\", \"channel\":\"msgchannel\"}" result=$($crl $POST $url/message/send -d "$kv") code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') # non-existant channel for now but whatever ignore for now diff --git a/server/tests/verify_err_cases.sh b/server/tests/verify_err_cases.sh index 35bdffa..655af73 100644 --- a/server/tests/verify_err_cases.sh +++ b/server/tests/verify_err_cases.sh @@ -15,7 +15,7 @@ list_channels_bad_key() { } delete_channel_missing_param() { - kv='{"secret":"secret"}' + kv="{\"id\":$id, \"secret\":\"$secret\"}" result=$($crl $POST $url/channels/delete -d "$kv") code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') log_result delete_channel_missing_param 400 $code "$result" @@ -23,7 +23,7 @@ delete_channel_missing_param() { delete_channel_no_channel() { # Should 200 as the api just drops the result - kv='{"secret":"secret", "name":"yes"}' + kv="{\"id\":$id, \"secret\":\"$secret\", \"name\":\"yes\"}" result=$($crl $POST $url/channels/delete -d "$kv") code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') log_result delete_channel_no_channel_found 200 $code "$result" From 776ceb83c9a1eec8213e2ee303a9306f26a48a7e Mon Sep 17 00:00:00 2001 From: shockrah Date: Sat, 8 Aug 2020 21:31:07 -0700 Subject: [PATCH 08/47] send_message was expecting the wrong parameters --- server/src/messages.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/messages.rs b/server/src/messages.rs index 399f3b4..29a43ce 100644 --- a/server/src/messages.rs +++ b/server/src/messages.rs @@ -43,7 +43,7 @@ pub async fn send_message(pool: &Pool, response: &mut Response, params: Va let content_r = params.get("content"); let channel_name_r = params.get("channel"); // auth module guarantees this will be there in the correct form - let author = params.get("userid") + let author = params.get("id") .unwrap().as_u64().unwrap(); match (content_r, channel_name_r) { From bae3e02b489b189dcbba70595d8d2f5dd7ddbd4a Mon Sep 17 00:00:00 2001 From: shockrah Date: Sun, 9 Aug 2020 23:28:04 -0700 Subject: [PATCH 09/47] adding build caching and delaying tests as cargo is slow to start up sometimes --- .gitlab-ci.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index eeeb220..ba50f89 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,10 +1,16 @@ image: shockrah/freechat:0.3 -debug-build: +build-release: stage: build only: - testing + cache: + paths: + - target/ + - $CI_HOME_DIR/.cargo/ + script: + - CARGO_HOME=$CI_PROJECT_DIR/.cargo - cd server/ - cargo build - diesel setup --database-url $DATABASE_URL @@ -13,12 +19,15 @@ debug-build: basic-test: stage: test + needs: "build-release" only: - testing script: - cd server/ - cargo run -- -s& - fc_id=$! + - echo Sleeping to let cargo catch up + - sleep 3 - cd tests/ - bash ./main.sh body - kill ${fc_id} From ed8f15b17c74e89ded5b0b5ca8361017fe27c0d9 Mon Sep 17 00:00:00 2001 From: shockrah Date: Sun, 9 Aug 2020 23:29:02 -0700 Subject: [PATCH 10/47] fixed broken array for needs field in testing stage --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ba50f89..3a0e1b4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,7 +19,7 @@ build-release: basic-test: stage: test - needs: "build-release" + needs: ["build-release"] only: - testing script: From baae2caaf0e3650c9dfc00b0cb0bd576d9f3f9b4 Mon Sep 17 00:00:00 2001 From: shockrah Date: Sun, 9 Aug 2020 23:39:03 -0700 Subject: [PATCH 11/47] testing cargo run as it never really starts up --- .gitlab-ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3a0e1b4..9aa6660 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,10 +24,8 @@ basic-test: - testing script: - cd server/ - - cargo run -- -s& + - cargo run -- -s - fc_id=$! - - echo Sleeping to let cargo catch up - - sleep 3 - cd tests/ - bash ./main.sh body - kill ${fc_id} From e4c6c46061bc98d306d5e5054065491df63d1f97 Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 10 Aug 2020 13:44:18 -0700 Subject: [PATCH 12/47] Proper build caching and updating needs field for testing stage --- .gitlab-ci.yml | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9aa6660..9ceb6a5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,32 +1,33 @@ image: shockrah/freechat:0.3 +variables: + CARGO_HOME: $CI_PROJECT_DIR/.cargo build-release: - stage: build - only: - - testing - cache: + stage: build + only: + - testing + cache: paths: - - target/ - - $CI_HOME_DIR/.cargo/ + - target/ - script: - - CARGO_HOME=$CI_PROJECT_DIR/.cargo - - cd server/ - - cargo build - - diesel setup --database-url $DATABASE_URL + before_script: + - cd server/ + script: + - cargo build + - diesel setup --database-url $DATABASE_URL -basic-test: - stage: test - needs: ["build-release"] - only: - - testing - script: - - cd server/ - - cargo run -- -s - - fc_id=$! - - cd tests/ - - bash ./main.sh body - - kill ${fc_id} +api-test: + stage: test + needs: ["build-release"] + only: + - testing + script: + - cd server/ + - cargo run -- -s > server.log& + - export fc_id=$! + - cd tests/ + - bash ./main.sh body + - kill ${fc_id} From 8986c23dbfa231b6cc261d14e9f43cce7955ec6b Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 10 Aug 2020 13:45:03 -0700 Subject: [PATCH 13/47] bash script now waits for the rust process to create its server response log before starting tests --- server/tests/main.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/server/tests/main.sh b/server/tests/main.sh index c790e9a..fc1f1e3 100644 --- a/server/tests/main.sh +++ b/server/tests/main.sh @@ -39,8 +39,16 @@ if [ "$1" = "body" ];then export _show_body=1 fi +# Waiting until the web server actuall finishes starting up +echo Waiting on server.log +while ! grep "Serving" ../server.log > /dev/null; do + sleep 1 +done + source ./common.sh export -f log_result + +echo ================================ echo TestName ExpectedCode ActualCode bash ./verify_basic_cases.sh @@ -48,3 +56,4 @@ bash ./verify_basic_cases.sh bash ./verify_err_cases.sh bash ./verify_mal_cases.sh + From 7895eca99e7928fceb31a74a445e3811f89ce0fd Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 10 Aug 2020 13:45:47 -0700 Subject: [PATCH 14/47] fixed indentation issue --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9ceb6a5..eebcf54 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,8 +7,8 @@ build-release: only: - testing cache: - paths: - - target/ + paths: + - target/ before_script: - cd server/ From 0cee67ce69efcc666d0285030312760526ae78ef Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 10 Aug 2020 14:04:58 -0700 Subject: [PATCH 15/47] using release builds instead of debug builds to make tests run faster Added /server/target to cache --- .gitlab-ci.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index eebcf54..f0a5339 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,6 +2,13 @@ image: shockrah/freechat:0.3 variables: CARGO_HOME: $CI_PROJECT_DIR/.cargo +cache: + key: ${CI_COMMIT_REF_SLUG} + paths: + - $CI_PROJECT_DIR/.cargo + - $CI_PROJECT_DIR/server/target + + build-release: stage: build only: @@ -13,7 +20,7 @@ build-release: before_script: - cd server/ script: - - cargo build + - cargo build --release - diesel setup --database-url $DATABASE_URL @@ -25,7 +32,7 @@ api-test: - testing script: - cd server/ - - cargo run -- -s > server.log& + - cargo run --release -- -s > server.log& - export fc_id=$! - cd tests/ - bash ./main.sh body From 489fdb69b89165217ea8c49098d4d3358fcdd448 Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 10 Aug 2020 17:15:29 -0700 Subject: [PATCH 16/47] target/ directory was being ignore so we override that in the cache section also changed the key to the ci_job_name diesel setup now happens before we build the release binary --- .gitlab-ci.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f0a5339..e8214d1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,26 +3,25 @@ variables: CARGO_HOME: $CI_PROJECT_DIR/.cargo cache: - key: ${CI_COMMIT_REF_SLUG} + key: "$CI_JOB_NAME" + untracked: true paths: - - $CI_PROJECT_DIR/.cargo + - $CARGO_HOME - $CI_PROJECT_DIR/server/target +before_script: + - export PATH="$CARGO_HOME/bin:$PATH" + build-release: stage: build only: - testing - cache: - paths: - - target/ - before_script: - - cd server/ script: - - cargo build --release + - cd server/ - diesel setup --database-url $DATABASE_URL - + - cargo build --release api-test: From 7131e1acf51e3b18aeb2ce28d4b3d7dea778a8bb Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 10 Aug 2020 17:16:08 -0700 Subject: [PATCH 17/47] new schema which ultimately simplfies things for us --- .../migrations/2020-02-04-083657_invites/up.sql | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/server/migrations/2020-02-04-083657_invites/up.sql b/server/migrations/2020-02-04-083657_invites/up.sql index 28e4c7c..f49c229 100644 --- a/server/migrations/2020-02-04-083657_invites/up.sql +++ b/server/migrations/2020-02-04-083657_invites/up.sql @@ -1,15 +1,12 @@ --- @id : id of the invite - --- @expires : unix timestamp of when that invite expries --- can be set to null which means it never expires +-- @id : id of the invite which is also its kill date -- @uses : can be null which means it doesn't have a use limit --- @max_uses : if this is null uses only ever incremented but we don't care for destroying on that parameter +-- @expires: boolean that tells wether the key expires or not + CREATE TABLE IF NOT EXISTS `invites` ( - `id` bigint UNSIGNED NOT NULL, - `expires` bigint, - `uses` integer, - `max_uses` integer, + `id` BIGINT UNIQUE NOT NULL, + `uses` BIGINT, + `expires` BOOLEAN NOT NULL, PRIMARY KEY( `id` ) ); From f0209217de2ed1538ec94784358069a606720df9 Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 10 Aug 2020 17:16:51 -0700 Subject: [PATCH 18/47] ignoring diesel.toml from diesel as we dont use it at all --- server/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/server/.gitignore b/server/.gitignore index 4f4a797..24e4eed 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -3,3 +3,4 @@ target static/css/ dev-sql/ +diesel.toml From 996a08f58a9e174b2b72a6ad8fe53b05bc9693ac Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 10 Aug 2020 17:17:36 -0700 Subject: [PATCH 19/47] Visual cleanup but also we're now dictating in main how the invites api should work now --- server/src/main.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/server/src/main.rs b/server/src/main.rs index c38bd05..ab3407b 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -45,20 +45,14 @@ async fn route_dispatcher(pool: &Pool, resp: &mut Response, meth: &Method, // At some point we should have some way of hiding this obnoxious complexity use routes::resolve_dynamic_route; match (meth, path) { - (&Method::GET, routes::INVITE_JOIN) => { - if let Err(_) = invites::route_join_invite_code(pool, resp, params).await { - *resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; - } - }, - (&Method::GET, routes::INVITE_CREATE) => { - if let Err(_) = invites::create_invite(pool, resp).await { - *resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; - } - }, + /* INVITES */ + (&Method::GET, routes::INVITE_JOIN) => invites::route_join_invite_code(pool, resp, params).await, + (&Method::GET, routes::INVITE_CREATE) => { invites::create(pool, resp).await, + /* CHANNELS */ (&Method::GET, routes::CHANNELS_LIST) => channels::list_channels(pool, resp).await, (&Method::POST, routes::CHANNELS_CREATE) => channels::create_channel(pool, resp, params).await, (&Method::POST, routes::CHANNELS_DELETE) => channels::delete_channel(pool, resp, params).await, - + /* MESSAGING */ (&Method::POST, routes::MESSAGE_SEND) => messages::send_message(pool, resp, params).await, _ => { // We attempt dynamic routes as fallback for a few reasons From 3e91d42f944339652f525de81831aae5231a4cf2 Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 10 Aug 2020 18:02:01 -0700 Subject: [PATCH 20/47] passing in corret params to invite endpoints removed random '{' that was in the invites::create match arm --- server/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/main.rs b/server/src/main.rs index ab3407b..568262a 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -46,8 +46,8 @@ async fn route_dispatcher(pool: &Pool, resp: &mut Response, meth: &Method, use routes::resolve_dynamic_route; match (meth, path) { /* INVITES */ - (&Method::GET, routes::INVITE_JOIN) => invites::route_join_invite_code(pool, resp, params).await, - (&Method::GET, routes::INVITE_CREATE) => { invites::create(pool, resp).await, + (&Method::GET, routes::INVITE_JOIN) => invites::join(pool, resp, params).await, + (&Method::GET, routes::INVITE_CREATE) => invites::create(pool, resp, params).await, /* CHANNELS */ (&Method::GET, routes::CHANNELS_LIST) => channels::list_channels(pool, resp).await, (&Method::POST, routes::CHANNELS_CREATE) => channels::create_channel(pool, resp, params).await, From 8a91d51dc6823adc9d5616c254ae35ba41f0e27f Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 10 Aug 2020 18:06:27 -0700 Subject: [PATCH 21/47] Invite struct has been simplified Invite::as_json_str/from_tuple changed to reflect new field changes + insert_new_invite: short and sweet error handling by the caller for now * create_invite now named `create` + reduced code complexity in invites::create so its very straight forward to read(imo) --- server/src/invites.rs | 93 ++++++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/server/src/invites.rs b/server/src/invites.rs index 967fec1..dd7a1f5 100644 --- a/server/src/invites.rs +++ b/server/src/invites.rs @@ -9,10 +9,13 @@ use hyper::{Response, Body, StatusCode}; use chrono::Utc; use rand::random; -struct InviteRow { - id: u64, - expires: u64, - uses: i32, + +use crate::db_types::{BigInt, Integer}; + +struct Invite { + id: BigInt, + uses: Option, // optional because some links are permanent + expires: bool, } /* * Error handling: @@ -20,29 +23,22 @@ struct InviteRow { * are of the enum mysql_async::error::Error */ -impl InviteRow { - pub fn new() -> InviteRow { - let dt = Utc::now() + chrono::Duration::minutes(30); - // TODO:[maybe] ensure no collisions by doing a quick database check here - let invite = InviteRow { - id: random::(), // hopefully there won't ever be collision with this size of pool - uses: 1, // default/hardcorded for now - expires: dt.timestamp() as u64 - }; - invite - } - pub fn from_tuple(tup: (u64, u64, i32)) -> InviteRow { - InviteRow { +impl Invite { + pub fn from_tuple(tup: (BigInt, Option, bool)) -> Invite { + /* + * Let's us convert tuples from mysql into convenient invite structs */ + Invite { id: tup.0, - expires: tup.1, - uses: tup.2, + uses: tup.1, + expires: tup.2 } } pub fn as_json_str(&self) -> String { + // TODO: Deprecate let id = format!("\"id\":{}", self.id); let expires = format!("\"expires\":{}", self.expires); - let uses = format!("\"uses\":{}", self.uses); + let uses = format!("\"uses\":{:?}", self.uses); let mut data = String::from("{"); data.push_str(&format!("{},", id)); @@ -52,14 +48,14 @@ impl InviteRow { } } -async fn get_invite_by_code(pool: &Pool, value: Option<&str>) -> Result, Error> { +async fn get_invite_by_code(pool: &Pool, value: Option<&str>) -> Result, Error> { if let Some(val) = value { let conn = pool.get_conn().await?; - let db_row_result: (Conn, Option<(u64, u64, i32)>) = conn + let db_row_result: (Conn, Option<(BigInt, Option, bool)>) = conn .first_exec(r"SELECT * FROM", mysql_async::params!{"code"=>val}) .await?; if let Some(tup) = db_row_result.1 { - Ok(Some(InviteRow::from_tuple(tup))) + Ok(Some(Invite::from_tuple(tup))) } else { // basically nothing was found but nothing bad happened @@ -70,7 +66,7 @@ async fn get_invite_by_code(pool: &Pool, value: Option<&str>) -> Result Result<(), Error>{ +async fn record_invite_usage(pool: &Pool, data: &Invite) -> Result<(), Error>{ /* * By this this is called we really don't care about what happens as we've * already been querying the db and the likely hood of this seriously failing @@ -87,7 +83,7 @@ async fn record_invite_usage(pool: &Pool, data: &InviteRow) -> Result<(), Error> } pub async fn route_join_invite_code(pool: &Pool, response: &mut Response, params: Value) -> Result<(), Error> { - // First check that the code is there + // collect some things first if let Some(code) = params.get("code") { if let Some(row) = get_invite_by_code(pool, code.as_str()).await? { // since we have a row make sure the invite is valid @@ -107,17 +103,42 @@ pub async fn route_join_invite_code(pool: &Pool, response: &mut Response, Ok(()) } -pub async fn create_invite(pool: &Pool, response: &mut Response) -> Result<(), Error> { - let invite = InviteRow::new(); +async fn insert_new_invite(pool: &Pool, invite: &Invite) -> Result<(), Error>{ let conn = pool.get_conn().await?; - conn.prep_exec(r"INSERT INTO invites (id, expires, uses) VALUES (:id, :expires, :uses", - mysql_async::params!{ - "id" => invite.id, - "expires" => invite.expires, - "uses" => invite.uses, - }).await?; + conn.prep_exec( + "INSERT INTO invites (id, uses, expires) + VALUES (:id, :uses, :expires)", params!{ + "id" => invite.id, + "uses" => invite.uses, + "expires" => invite.expires + }).await?; - *response.body_mut() = Body::from(invite.as_json_str()); - *response.status_mut() = StatusCode::OK; Ok(()) -} \ No newline at end of file +} + +pub async fn create(pool: &Pool, response: &mut Response, params: Value) { + /* + * Creates a new invite + */ + + let use_count = match params.get("uses") { + Some(val) => val.as_i64(), + None => None + }; + + let expires = match params.get("expires") { + Some(val) => val.as_bool().unwrap(), + None => true + }; + + let invite = Invite { + id: (Utc::now() + chrono::Duration::minutes(30)).timestamp(), + uses: use_count, + expires: expires + }; + + match insert_new_invite(&pool, &invite).await { + Ok(_) => *response.body_mut() = Body::from("yes"), + Err(mysqle) => *response.status_mut() = StatusCode::BAD_REQUEST + } +} From 2c6cdf92823d211bc3afae15e218a8b78b180bf6 Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 10 Aug 2020 21:48:19 -0700 Subject: [PATCH 22/47] Removed dead code and removed frivilous import alias --- server/src/members.rs | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/server/src/members.rs b/server/src/members.rs index 60671a1..2e0c559 100644 --- a/server/src/members.rs +++ b/server/src/members.rs @@ -1,9 +1,6 @@ use chrono::Utc; -use hyper::{Body, Response, StatusCode}; -use hyper::header::{HeaderName, HeaderValue}; -use mysql_async::{Conn, Pool, error::Error as MySqlError}; +use mysql_async::{Conn, Pool, error::Error}; use mysql_async::prelude::{params, Queryable}; -use serde_json::Value; use serde::Serialize; use crate::db_types::{UBigInt, BigInt, Integer, VarChar}; @@ -20,7 +17,7 @@ pub struct Member { } -pub async fn insert_new_member(p: &Pool, name: VarChar, perms: u64) -> Result { +pub async fn insert_new_member(p: &Pool, name: VarChar, perms: u64) -> Result { use crate::auth::generate_secret; let conn: Conn = p.get_conn().await?; @@ -58,29 +55,3 @@ pub async fn insert_new_member(p: &Pool, name: VarChar, perms: u64) -> Result, params: Value) { - /* - * @name: string => desired default name - */ - use crate::perms; - let default_name = serde_json::json!("NewUser"); - let name = params.get("name") - .unwrap_or(&default_name) - .as_str().unwrap_or("NewUser"); - - match insert_new_member(p, name.to_string(), perms::GENERAL_NEW).await { - Ok(new_member) => { - *resp.status_mut() = StatusCode::OK; - let json_hdr_name = HeaderName::from_static("Content-Type"); - let json_hdr_val = HeaderValue::from_static("application/json"); - resp.headers_mut().insert(json_hdr_name, json_hdr_val); - *resp.body_mut() = Body::from(serde_json::to_string(&new_member).unwrap()); - }, - Err(_) => { - *resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; - *resp.body_mut() = Body::from("Could not process input"); - } - } -} - From 157d1333175024f942bac9f473ee636eb547835a Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 10 Aug 2020 21:48:44 -0700 Subject: [PATCH 23/47] join via invite seems to be scaffolded properly but now requires testing --- server/src/invites.rs | 143 +++++++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 72 deletions(-) diff --git a/server/src/invites.rs b/server/src/invites.rs index dd7a1f5..0e4eae1 100644 --- a/server/src/invites.rs +++ b/server/src/invites.rs @@ -1,4 +1,5 @@ use serde_json::Value; +use serde::Serialize; use mysql_async; use mysql_async::{Conn, Pool}; @@ -8,10 +9,11 @@ use mysql_async::prelude::{params, Queryable}; use hyper::{Response, Body, StatusCode}; use chrono::Utc; -use rand::random; -use crate::db_types::{BigInt, Integer}; +use crate::db_types::BigInt; +use crate::members::{self, Member}; +#[derive(Serialize)] struct Invite { id: BigInt, uses: Option, // optional because some links are permanent @@ -23,84 +25,77 @@ struct Invite { * are of the enum mysql_async::error::Error */ -impl Invite { - pub fn from_tuple(tup: (BigInt, Option, bool)) -> Invite { - /* - * Let's us convert tuples from mysql into convenient invite structs */ - Invite { - id: tup.0, - uses: tup.1, - expires: tup.2 - } - } - - pub fn as_json_str(&self) -> String { - // TODO: Deprecate - let id = format!("\"id\":{}", self.id); - let expires = format!("\"expires\":{}", self.expires); - let uses = format!("\"uses\":{:?}", self.uses); - - let mut data = String::from("{"); - data.push_str(&format!("{},", id)); - data.push_str(&format!("{},", expires)); - data.push_str(&format!("{}}}", uses)); - data - } -} - -async fn get_invite_by_code(pool: &Pool, value: Option<&str>) -> Result, Error> { - if let Some(val) = value { - let conn = pool.get_conn().await?; - let db_row_result: (Conn, Option<(BigInt, Option, bool)>) = conn - .first_exec(r"SELECT * FROM", mysql_async::params!{"code"=>val}) - .await?; - if let Some(tup) = db_row_result.1 { - Ok(Some(Invite::from_tuple(tup))) - } - else { - // basically nothing was found but nothing bad happened - Ok(None) - } - } - // again db didn't throw a fit but we don't have a good input - else {Ok(None)} -} - -async fn record_invite_usage(pool: &Pool, data: &Invite) -> Result<(), Error>{ +async fn valid_invite(pool: &Pool, id: BigInt) -> Result{ /* - * By this this is called we really don't care about what happens as we've - * already been querying the db and the likely hood of this seriously failing - * is low enough to write a wall of text and not a wall of error handling code - */ + * Fetches an invite from the database to check for validity + */ let conn = pool.get_conn().await?; - let _db_result = conn - .prep_exec(r"UPDATE invites SET uses = :uses WHERE id = :id", mysql_async::params!{ - "uses" => data.uses - 1, - "id" => data.id - }).await?; + let db_fetch_result: (Conn, Option<(Option, bool)>) = + conn.first_exec("SELECT uses, expires FROM invites WHERE id = :id", + params!{"id" => id}).await?; + + if let Some(row) = db_fetch_result.1 { + // if expires at all + if row.1 { + let now = Utc::now().timestamp(); + // old? + let mut status = now > id; + // used? + if row.0.is_some() && status == false { + status = row.0.unwrap() <= 0; // safe unwrap since we know its Some(_) + } + return Ok(status) + } + // no expiry date? no problem + return Ok(true); + } + // prolly not a real id + else { + return Ok(false); + } - Ok(()) } -pub async fn route_join_invite_code(pool: &Pool, response: &mut Response, params: Value) -> Result<(), Error> { - // collect some things first - if let Some(code) = params.get("code") { - if let Some(row) = get_invite_by_code(pool, code.as_str()).await? { - // since we have a row make sure the invite is valid - let now = Utc::now().timestamp() as u64; - // usable and expires in the future - if row.uses > 0 && row.expires > now { - record_invite_usage(pool, &row).await?; - // TODO: assign some actual data to the body - *response.status_mut() = StatusCode::OK; +async fn use_invite(pool: &Pool, code: Option) -> Option{ + /* + * Attempts to change the state of the current invite being provided + */ + use crate::perms::GENERAL_NEW; + let id = match code { + Some(id) => id, + None => 0 + }; + + if let Ok(valid) = valid_invite(pool, id).await { + if valid { + match members::insert_new_member(pool, "Anonymous".into(), GENERAL_NEW).await { + Ok(member) => return Some(member), + Err(_) => return None } } + else { + return None; + } } - else{ - *response.status_mut() = StatusCode::BAD_REQUEST; + else { + return None; } +} - Ok(()) +pub async fn join(pool: &Pool, response: &mut Response, params: Value) { + /* + * Main dispatcher for dealing with an attempted join via a given invide code + */ + let code = match params.get("invite-id") { + Some(val) => val.as_i64(), + None => None + }; + + match use_invite(&pool, code).await { + Some(new_account) => *response.body_mut() = Body::from(serde_json::to_string(&new_account).unwrap()), + None => { + } + } } async fn insert_new_invite(pool: &Pool, invite: &Invite) -> Result<(), Error>{ @@ -126,8 +121,9 @@ pub async fn create(pool: &Pool, response: &mut Response, params: Value) { None => None }; + // TODO: remove the unwrap let expires = match params.get("expires") { - Some(val) => val.as_bool().unwrap(), + Some(val) => val.as_bool().unwrap_or(true), None => true }; @@ -139,6 +135,9 @@ pub async fn create(pool: &Pool, response: &mut Response, params: Value) { match insert_new_invite(&pool, &invite).await { Ok(_) => *response.body_mut() = Body::from("yes"), - Err(mysqle) => *response.status_mut() = StatusCode::BAD_REQUEST + Err(mysqle) => { + println!("\tINVITES::CREATE::ERROR: {}", mysqle); + *response.status_mut() = StatusCode::BAD_REQUEST; + } } } From 32ee49ed08d296dd1d4a7a229adab7d54a6e1225 Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 10 Aug 2020 22:34:26 -0700 Subject: [PATCH 24/47] adding some dynamic route basees new feature to check for open routes --- server/src/routes.rs | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/server/src/routes.rs b/server/src/routes.rs index 054c839..b27b106 100644 --- a/server/src/routes.rs +++ b/server/src/routes.rs @@ -1,4 +1,3 @@ -pub const INVITE_JOIN: &'static str = "/invite/join"; // requires @code pub const INVITE_CREATE: &'static str = "/invite/create"; // requires none pub const CHANNELS_LIST: &'static str = "/channels/list"; // requires none @@ -7,8 +6,13 @@ pub const CHANNELS_DELETE: &'static str = "/channels/delete"; // requires @name pub const MESSAGE_SEND: &'static str = "/message/send"; // requires @content -const DYNAMIC_ROUTE_BASES: [&'static str;1] = [ - "/invites", +pub const SERVER_META: &'static str = "/meta"; // open + +// potentially adding more bases later +const DYNAMIC_ROUTE_BASES: [(&'static str, bool);3] = [ + ("/join", true), // open + ("/public", true), // open : valid sections := neighbors|rules|description + ("/user", false), // TODO: valid sections := /meta/|/dm/ ]; pub struct DynRoute { @@ -21,9 +25,9 @@ pub fn resolve_dynamic_route(uri: &str) -> Option { let mut valid = false; let mut base_ref = ""; for base in DYNAMIC_ROUTE_BASES.iter() { - if uri.starts_with(base) { + if uri.starts_with(base.0) { valid = true; - base_ref = base; + base_ref = base.0; break; } } @@ -37,3 +41,15 @@ pub fn resolve_dynamic_route(uri: &str) -> Option { None } } + +pub fn is_open(path: &str) -> bool { + let mut ret = false; + ret = path == SERVER_META; + for route in DYNAMIC_ROUTE_BASES.iter() { + if route.1 == true || ret == true{ + ret = true; + break; + } + } + return ret; +} From 5d1b95bec6521014b288d7b49cf31948893382f9 Mon Sep 17 00:00:00 2001 From: shockrah Date: Tue, 11 Aug 2020 19:43:05 -0700 Subject: [PATCH 25/47] making routes::is_open behavior a lot more clear moved /join to be handled by the dynamic path handler --- server/src/auth.rs | 6 +----- server/src/main.rs | 11 +++++++---- server/src/routes.rs | 11 ++++++++--- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/server/src/auth.rs b/server/src/auth.rs index 4a5fd4d..85fb60a 100644 --- a/server/src/auth.rs +++ b/server/src/auth.rs @@ -15,10 +15,6 @@ pub enum AuthReason { } -fn open_route(path: &str) -> bool { - return path == routes::INVITE_JOIN -} - fn valid_user(secret: &str, row: &Option<(VarChar, VarChar, BigInt, Integer, UBigInt)>) -> bool { match row { Some(row) => { @@ -33,7 +29,7 @@ fn valid_user(secret: &str, row: &Option<(VarChar, VarChar, BigInt, Integer, UBi pub async fn wall_entry(path: &str, pool: &Pool, params: &mut serde_json::Value) -> Result { // Start by Checking if the api key is in our keystore - if open_route(path) { + if routes::is_open(path) { Ok(AuthReason::OpenAuth) } else { diff --git a/server/src/main.rs b/server/src/main.rs index 568262a..3cb548a 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -46,7 +46,6 @@ async fn route_dispatcher(pool: &Pool, resp: &mut Response, meth: &Method, use routes::resolve_dynamic_route; match (meth, path) { /* INVITES */ - (&Method::GET, routes::INVITE_JOIN) => invites::join(pool, resp, params).await, (&Method::GET, routes::INVITE_CREATE) => invites::create(pool, resp, params).await, /* CHANNELS */ (&Method::GET, routes::CHANNELS_LIST) => channels::list_channels(pool, resp).await, @@ -63,9 +62,13 @@ async fn route_dispatcher(pool: &Pool, resp: &mut Response, meth: &Method, // Computatinoal bounds are really of no concern with this api since // we're not doing any heavy calculations at any point if let Some(route) = resolve_dynamic_route(path) { - *resp.status_mut() = StatusCode::OK; - println!("\tStatic part: {}", route.base); - println!("\tDynamic part: {}", route.dynamic); + match (meth, route.base.as_str()) { + (&Method::GET, routes::DYN_JOIN) => invites::join(pool, resp, params).await, + _ => { + println!("\tNOT FOUND: {}: {}", meth, path); + *resp.status_mut() = StatusCode::NOT_FOUND + } + } } else { println!("\tNOT FOUND: {}: {}", meth, path); diff --git a/server/src/routes.rs b/server/src/routes.rs index b27b106..391bffb 100644 --- a/server/src/routes.rs +++ b/server/src/routes.rs @@ -9,11 +9,12 @@ pub const MESSAGE_SEND: &'static str = "/message/send"; // requires @content pub const SERVER_META: &'static str = "/meta"; // open // potentially adding more bases later -const DYNAMIC_ROUTE_BASES: [(&'static str, bool);3] = [ +pub const DYNAMIC_ROUTE_BASES: [(&'static str, bool);3] = [ ("/join", true), // open ("/public", true), // open : valid sections := neighbors|rules|description ("/user", false), // TODO: valid sections := /meta/|/dm/ ]; +pub const DYN_JOIN: &'static str = DYNAMIC_ROUTE_BASES[0].0; pub struct DynRoute { pub base: String, @@ -43,8 +44,11 @@ pub fn resolve_dynamic_route(uri: &str) -> Option { } pub fn is_open(path: &str) -> bool { - let mut ret = false; - ret = path == SERVER_META; + /* + * Simple interface for determining if a route/base is open + * i.e. requires authentication or not + */ + let mut ret = path == SERVER_META; for route in DYNAMIC_ROUTE_BASES.iter() { if route.1 == true || ret == true{ ret = true; @@ -53,3 +57,4 @@ pub fn is_open(path: &str) -> bool { } return ret; } + From da01c13ac56b4bb66df7c9de78d51a6324a35045 Mon Sep 17 00:00:00 2001 From: shockrah Date: Tue, 11 Aug 2020 20:03:42 -0700 Subject: [PATCH 26/47] removed ancient todo --- server/todo | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 server/todo diff --git a/server/todo b/server/todo deleted file mode 100644 index 537a1fd..0000000 --- a/server/todo +++ /dev/null @@ -1,28 +0,0 @@ -for now we'll acheive these things via some tests -users: - - create new users via some tests - - search users - we should be able to ask for the first _n_ users in a server - this _n_ value will be 250 for now since user names should be pretty short and we're only going to care about the usernames+id's - - - update - whenever a user wants to change their display name or something on the server - - - remove users - whenever a user wants to be removed from a server - all we need for this one is the userid for that server then we should remove them - -# todo for later but these schemas are there for sake of brevity and completeness -# they're just not being dealth with atm -channels: - -# mod::invites - -Better random number generation in use_invite function - -# Walls - -Right now there's literally 0 security checks in place and thats because: -1. im lazy with that at the moment -2. if the underlying logic is fucked then the security won't do anything -3. finally the code is built to add things onto it \ No newline at end of file From 14f91e2240ce8b1f8428e102aa67b6eb19f23d3a Mon Sep 17 00:00:00 2001 From: shockrah Date: Wed, 12 Aug 2020 19:48:09 -0700 Subject: [PATCH 27/47] first integrated unit test to start replacing bash test suite --- server/src/invites.rs | 37 ++++++++++++++++++++++++++++++++++++- server/src/main.rs | 1 + 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/server/src/invites.rs b/server/src/invites.rs index 0e4eae1..45077d1 100644 --- a/server/src/invites.rs +++ b/server/src/invites.rs @@ -134,10 +134,45 @@ pub async fn create(pool: &Pool, response: &mut Response, params: Value) { }; match insert_new_invite(&pool, &invite).await { - Ok(_) => *response.body_mut() = Body::from("yes"), + Ok(_) => {}, Err(mysqle) => { println!("\tINVITES::CREATE::ERROR: {}", mysqle); *response.status_mut() = StatusCode::BAD_REQUEST; } } } + + +#[cfg(test)] +mod invites_test { + /* + * INVITE CREATION + * Good - Bad - Malicious + */ + + use crate::testing::{get_pool, hyper_resp}; + use hyper::StatusCode; + use serde_json::Value; + + #[tokio::test] + async fn create_invite_good() { + // Generation of data + let p = get_pool(); + let mut resp = hyper_resp(); + // expected params + let params: Value = serde_json::from_str(r#" + { + "uses": 3, + "expire": null + } + "#).unwrap(); + + // Collection + super::join(&p, &mut resp, params).await; + let _ = p.disconnect().await; + + // Analysis - todo + assert_eq!(StatusCode::OK, resp.status()); + println!("Body contents (should be nothing) => {:?}", resp.body()); + } +} diff --git a/server/src/main.rs b/server/src/main.rs index 3cb548a..a074fc0 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -23,6 +23,7 @@ use mysql_async::Pool; use dotenv::dotenv; use clap::{Arg, App}; +mod testing; mod auth; use auth::AuthReason; From 85acc6a3095274cc75d6cde8b598a349bc4b9baf Mon Sep 17 00:00:00 2001 From: shockrah Date: Wed, 12 Aug 2020 19:48:42 -0700 Subject: [PATCH 28/47] Helper functions for other tests There are kinda random and really just push away seams from the UT's themselves --- server/src/testing/mod.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 server/src/testing/mod.rs diff --git a/server/src/testing/mod.rs b/server/src/testing/mod.rs new file mode 100644 index 0000000..8a751b8 --- /dev/null +++ b/server/src/testing/mod.rs @@ -0,0 +1,19 @@ +// Functions which are only really useful for the unit tests but which show up +// constantly in the tests themselves + +#[cfg(test)] +pub fn get_pool() -> mysql_async::Pool { + use dotenv::dotenv; + use mysql_async::Pool; + + dotenv().ok(); + return Pool::new(&std::env::var("DATABASE_URL").unwrap()) +} + +#[cfg(test)] +pub fn hyper_resp() -> hyper::Response { + use hyper::{Response, Body}; + + Response::new(Body::empty()) +} + From c0200b1711c6e85232c1640655321f7c4cdaca5e Mon Sep 17 00:00:00 2001 From: shockrah Date: Wed, 12 Aug 2020 20:02:18 -0700 Subject: [PATCH 29/47] removed frivolous println/old comment --- server/src/invites.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/src/invites.rs b/server/src/invites.rs index 45077d1..29acb33 100644 --- a/server/src/invites.rs +++ b/server/src/invites.rs @@ -171,8 +171,6 @@ mod invites_test { super::join(&p, &mut resp, params).await; let _ = p.disconnect().await; - // Analysis - todo assert_eq!(StatusCode::OK, resp.status()); - println!("Body contents (should be nothing) => {:?}", resp.body()); } } From 0475c801a0685d2c0a4bb25963ab6a8091e8721e Mon Sep 17 00:00:00 2001 From: shockrah Date: Wed, 12 Aug 2020 20:02:58 -0700 Subject: [PATCH 30/47] move list_all_channels_to equivalent rust code --- server/src/channels.rs | 22 ++++++++++++++++++++++ server/tests/verify_basic_cases.sh | 6 ------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/server/src/channels.rs b/server/src/channels.rs index c11d3ca..11eb2ad 100644 --- a/server/src/channels.rs +++ b/server/src/channels.rs @@ -201,3 +201,25 @@ pub async fn delete_channel(pool: &Pool, response: &mut Response, params: *response.status_mut() = StatusCode::BAD_REQUEST; } } + + +#[cfg(test)] +mod channels_tests { + use crate::testing::{get_pool, hyper_resp}; + use hyper::StatusCode; + + #[tokio::test] + async fn list_all_channels_good() { + // Generation of data + let p = get_pool(); + let mut resp = hyper_resp(); + // @params: none + // Collection of data + super::list_channels(&p, &mut resp).await; + + // Analysis + assert_eq!(StatusCode::OK, resp.status()); + println!("list_all_channels_good : \t{:?}", resp.body()); + } + +} diff --git a/server/tests/verify_basic_cases.sh b/server/tests/verify_basic_cases.sh index 519435f..b128564 100644 --- a/server/tests/verify_basic_cases.sh +++ b/server/tests/verify_basic_cases.sh @@ -6,12 +6,6 @@ active_tests='list_all_channels create_channel delete_channel send_message ' -list_all_channels() { # TEST - result=$(curl --silent -i $GET $url/channels/list -d $simple_key) - code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') - log_result "good_list_all_channels" 200 $code "$result" -} - create_channel() { kv="{\"id\":$id,\"secret\":\"$secret\", \"name\":\"sample\", \"kind\":2, \"description\":\"some bs description\"}" result=$($crl $POST $url/channels/create -d "$kv") From 6e6f48a34ce4e4c1cf05177084455f7c6d8e0315 Mon Sep 17 00:00:00 2001 From: shockrah Date: Wed, 12 Aug 2020 20:46:45 -0700 Subject: [PATCH 31/47] Moved test::create_channel_good to rust unit tests --- server/src/channels.rs | 21 +++++++++++++++++++++ server/tests/verify_basic_cases.sh | 7 ------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/server/src/channels.rs b/server/src/channels.rs index 11eb2ad..14c5f2c 100644 --- a/server/src/channels.rs +++ b/server/src/channels.rs @@ -206,6 +206,7 @@ pub async fn delete_channel(pool: &Pool, response: &mut Response, params: #[cfg(test)] mod channels_tests { use crate::testing::{get_pool, hyper_resp}; + use serde_json::Value; use hyper::StatusCode; #[tokio::test] @@ -220,6 +221,26 @@ mod channels_tests { // Analysis assert_eq!(StatusCode::OK, resp.status()); println!("list_all_channels_good : \t{:?}", resp.body()); + let _ = p.disconnect().await; } + #[tokio::test] + async fn create_channel_good() { + let p = get_pool(); + let mut resp = hyper_resp(); + // @params: name + kind + [description] + let params: Value = serde_json::from_str(r#" + { + "name": "sample channel", + "kind": 2, + "description": "some random bs" + } + "#).unwrap(); + + super::create_channel(&p, &mut resp, params).await; + + // hopefully we 200 + assert_eq!(StatusCode::OK, resp.status()); + let _ = p.disconnect().await; + } } diff --git a/server/tests/verify_basic_cases.sh b/server/tests/verify_basic_cases.sh index b128564..302d941 100644 --- a/server/tests/verify_basic_cases.sh +++ b/server/tests/verify_basic_cases.sh @@ -6,13 +6,6 @@ active_tests='list_all_channels create_channel delete_channel send_message ' -create_channel() { - kv="{\"id\":$id,\"secret\":\"$secret\", \"name\":\"sample\", \"kind\":2, \"description\":\"some bs description\"}" - result=$($crl $POST $url/channels/create -d "$kv") - code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') - log_result good_create_channel 200 $code "$result" -} - delete_channel() { kv="{\"id\":$id,\"secret\":\"$secret\", \"name\":\"sample\"}" result=$($crl $POST $url/channels/delete -d "$kv") From 0280ae09aefe6567a53d8110f846b36117268d76 Mon Sep 17 00:00:00 2001 From: shockrah Date: Thu, 13 Aug 2020 20:47:26 -0700 Subject: [PATCH 32/47] unique trait added to channelss name field --- server/migrations/2020-03-11-005217_channels/up.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/migrations/2020-03-11-005217_channels/up.sql b/server/migrations/2020-03-11-005217_channels/up.sql index 067160f..c5ca788 100644 --- a/server/migrations/2020-03-11-005217_channels/up.sql +++ b/server/migrations/2020-03-11-005217_channels/up.sql @@ -1,6 +1,6 @@ CREATE TABLE IF NOT EXISTS `channels` ( `id` BIGINT UNSIGNED NOT NULL auto_increment, - `name` VARCHAR(255) NOT NULL, + `name` UNIQUE VARCHAR(255) NOT NULL, `description` VARCHAR(1024), `kind` INTEGER NOT NULL, PRIMARY KEY(`id`), UNIQUE KEY(`name`) From ced09650600f92e30a0f0982f27e8e010feb93dc Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 17 Aug 2020 18:51:35 -0700 Subject: [PATCH 33/47] fixed channel creation/deletion test so it behaves properlu, all new tests passing atm --- server/src/channels.rs | 40 +++++++++++++++++++++++++++++----------- server/src/common.rs | 38 ++++++++++++++++++++++++++++++++++++++ server/src/main.rs | 14 +++++++------- 3 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 server/src/common.rs diff --git a/server/src/channels.rs b/server/src/channels.rs index 14c5f2c..aab4c53 100644 --- a/server/src/channels.rs +++ b/server/src/channels.rs @@ -8,6 +8,7 @@ use mysql_async::prelude::{params, Queryable}; use serde_json::Value; use crate::db_types::{UBigInt, VarChar, Integer}; +use crate::common; #[derive(Debug)] pub enum ChannelType { @@ -164,9 +165,10 @@ pub async fn create_channel(pool: &Pool, response: &mut Response, params: match insert_channel(pool, name, desc, kind).await { // Server Errors are generally _ok_ to reveal in body I suppose Err(Error::Server(se)) => { - *response.status_mut() = StatusCode::BAD_REQUEST; - let b = format!("Server code: {}\nServer Message:{}", se.code, se.message); - *response.body_mut() = Body::from(b); + common::db_err_response_body(response, se); + //*response.status_mut() = StatusCode::BAD_REQUEST; + //let b = format!("Server code: {}\nServer Message:{}", se.code, se.message); + //*response.body_mut() = Body::from(b); }, // generic errors get a 500 Err(_) => { @@ -193,7 +195,7 @@ pub async fn delete_channel(pool: &Pool, response: &mut Response, params: match db_delete_channel(pool, name).await { Ok(_) => *response.status_mut() = StatusCode::OK, Err(e) => { - println!("delete_chanel sql error :\n{}", e); + *response.body_mut() = Body::from(format!("delete_chanel sql error :\n{}", e)); } } } @@ -208,6 +210,7 @@ mod channels_tests { use crate::testing::{get_pool, hyper_resp}; use serde_json::Value; use hyper::StatusCode; + const DUMMY_TRANSIENT_CHANNEL: &'static str = "sample channel"; #[tokio::test] async fn list_all_channels_good() { @@ -225,22 +228,37 @@ mod channels_tests { } #[tokio::test] - async fn create_channel_good() { + async fn delete_and_create_channel_good() { + use chrono::Utc; let p = get_pool(); let mut resp = hyper_resp(); // @params: name + kind + [description] - let params: Value = serde_json::from_str(r#" - { - "name": "sample channel", + let cname_id = Utc::now(); + let params: Value = serde_json::from_str(&format!(r#" + {{ + "name": "{}-{}", "kind": 2, "description": "some random bs" - } - "#).unwrap(); + }} + "#, DUMMY_TRANSIENT_CHANNEL, cname_id)).unwrap(); super::create_channel(&p, &mut resp, params).await; - // hopefully we 200 + println!("CREATE CHANNEL: {:?}", resp.body()); assert_eq!(StatusCode::OK, resp.status()); + + + // clean up and hopefully delete the channel properly + let mut resp_delete = hyper_resp(); + let params_delete: Value = serde_json::from_str(&format!(r#" + {{ + "name": "{}-{}" + }} + "#, DUMMY_TRANSIENT_CHANNEL, cname_id)).unwrap(); + println!("Parameters: {}", params_delete); + super::delete_channel(&p, &mut resp_delete, params_delete).await; + + println!("Body: {:?}", resp.body()); let _ = p.disconnect().await; } } diff --git a/server/src/common.rs b/server/src/common.rs new file mode 100644 index 0000000..72f6780 --- /dev/null +++ b/server/src/common.rs @@ -0,0 +1,38 @@ +use mysql_async::error::ServerError; +use hyper::{Body, Response, StatusCode}; +use serde::Serialize; +use serde_json::to_string as json; + +#[derive(Serialize)] +struct GenericErrData { + status: u16, + message: &'static str, + note: &'static str, +} + + +pub fn db_err_response_body(response: &mut Response, err: ServerError) { + *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + match err.code { + // Duplicate unique value was (tried to be) inserted + 1062 => { + + let s = json(&GenericErrData { + status: 1062, + message: "Duplicate key was inserted and failed", + note: "Check parameters given" + }).unwrap(); + *response.body_mut() = Body::from(s); + }, + // Generic errors + _ => { + let s = json(&GenericErrData{ + status: 500, + message: "Generic Backend error", + note:"" + }).unwrap(); + *response.body_mut() = Body::from(s); + } + } +} + diff --git a/server/src/main.rs b/server/src/main.rs index a074fc0..dd9dd5f 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -22,21 +22,21 @@ use mysql_async::Pool; use dotenv::dotenv; use clap::{Arg, App}; - -mod testing; -mod auth; use auth::AuthReason; +mod auth; + mod routes; mod invites; mod channels; - mod members; - -mod messages; -mod http_params; mod perms; +mod messages; + +mod http_params; mod db_types; +mod common; +mod testing; const NO_ERR: u16 = 0; const CONFIG_ERR: u16 = 1; From afb965f4e8e3b854dc58572fc5d1312d164d8b2f Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 17 Aug 2020 18:51:54 -0700 Subject: [PATCH 34/47] removed waiting period --- server/tests/main.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/server/tests/main.sh b/server/tests/main.sh index fc1f1e3..4c42641 100644 --- a/server/tests/main.sh +++ b/server/tests/main.sh @@ -39,12 +39,6 @@ if [ "$1" = "body" ];then export _show_body=1 fi -# Waiting until the web server actuall finishes starting up -echo Waiting on server.log -while ! grep "Serving" ../server.log > /dev/null; do - sleep 1 -done - source ./common.sh export -f log_result From e6273b437be23f6520da419fa08eeb2efd4a7238 Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 17 Aug 2020 19:19:16 -0700 Subject: [PATCH 35/47] adding 400 case for send_message test - all tests passing as expected --- server/src/messages.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/server/src/messages.rs b/server/src/messages.rs index 29a43ce..38f2b6b 100644 --- a/server/src/messages.rs +++ b/server/src/messages.rs @@ -75,3 +75,31 @@ pub async fn send_message(pool: &Pool, response: &mut Response, params: Va } } } + +#[cfg(test)] +mod messaging_tests { + use crate::testing::{get_pool, hyper_resp}; + use serde_json::Value; + use hyper::StatusCode; + + #[tokio::test] + async fn send_message_test_missing_channel() { + /* + * Attempt to send a message i na channel that does not exist + */ + let p = get_pool(); + let mut resp = hyper_resp(); + + let params: Value = serde_json::from_str(r#" + { + "channel": "this does not exist", + "content": "bs message", + "id": 420 + } + "#).unwrap(); + + super::send_message(&p, &mut resp, params).await; + + assert_ne!(StatusCode::OK, resp.status()); + } +} From aa01d0ee9043a12721338eeb1ef2ca59966e54a4 Mon Sep 17 00:00:00 2001 From: shockrah Date: Thu, 20 Aug 2020 19:19:45 -0700 Subject: [PATCH 36/47] Channel (public) now exposes its field as public --- server/src/channels.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/channels.rs b/server/src/channels.rs index aab4c53..71d7d38 100644 --- a/server/src/channels.rs +++ b/server/src/channels.rs @@ -38,10 +38,10 @@ impl ChannelType { // Primary way of interpretting sql data on our channels table pub struct Channel { - id: u64, - name: String, - description: String, - kind: ChannelType + pub id: u64, + pub name: String, + pub description: String, + pub kind: ChannelType } #[derive(Debug)] From e46ea5080d6cb6548edc0607c3ee142d864f9584 Mon Sep 17 00:00:00 2001 From: shockrah Date: Thu, 20 Aug 2020 19:20:04 -0700 Subject: [PATCH 37/47] send_message_good works as intented and passes! --- server/src/messages.rs | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/server/src/messages.rs b/server/src/messages.rs index 38f2b6b..43c7f27 100644 --- a/server/src/messages.rs +++ b/server/src/messages.rs @@ -102,4 +102,47 @@ mod messaging_tests { assert_ne!(StatusCode::OK, resp.status()); } + + #[tokio::test] + async fn send_message_good() { + use crate::members::insert_new_member; + use crate::perms::GENERAL_NEW; + use mysql_async::params; + use mysql_async::prelude::Queryable; + use crate::testing::tmp_channel_params; + + let p = get_pool(); + let mut resp = hyper_resp(); + + let tmp_chan = tmp_channel_params(&p, "sample").await; + + const TMP_NAME: &'static str = "bs user"; + let temp_member = insert_new_member(&p, TMP_NAME.into(), GENERAL_NEW).await.unwrap(); + + + let params: Value = serde_json::from_str(&format!(r#" + {{ + "id": {}, + "channel": "{}", + "content": "bs message" + }} + "#, temp_member.id, tmp_chan.name)).unwrap(); + + super::send_message(&p, &mut resp, params).await; + + if resp.status() == StatusCode::BAD_REQUEST { + panic!("{:?}", resp.body()); + } + + // Destroy the the message and the user that we created + let conn = match p.get_conn().await { + Ok(c) => c, + Err(e) => panic!("Could not get connection to db during send_message_good:\nIssue:\t{}", e) + }; + + let conn = conn.drop_exec("DELETE FROM messages WHERE author_id = :id", params!{"id" => temp_member.id}).await.unwrap(); + let conn = conn.drop_exec("DELETE FROM members WHERE id = :id", params!{"id" => temp_member.id}).await.unwrap(); + let _ = conn.drop_exec("DELETE FROM channels WHERE name = :name", params!{"name" => tmp_chan.name}).await; + } } + From 296fde99544a57081b39f18907abefca2430c30e Mon Sep 17 00:00:00 2001 From: shockrah Date: Thu, 20 Aug 2020 19:20:21 -0700 Subject: [PATCH 38/47] new helper function to generate channel parametesr for generating chanels --- server/src/testing/mod.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/server/src/testing/mod.rs b/server/src/testing/mod.rs index 8a751b8..31cdd81 100644 --- a/server/src/testing/mod.rs +++ b/server/src/testing/mod.rs @@ -17,3 +17,22 @@ pub fn hyper_resp() -> hyper::Response { Response::new(Body::empty()) } + + +#[cfg(test)] +pub async fn tmp_channel_params(p: &mysql_async::Pool, chan_name: &'static str) -> crate::channels::Channel { + use crate::channels::{Channel, ChannelType}; + use mysql_async::{params, prelude::Queryable}; + + let conn = p.get_conn().await.unwrap(); + let _ = conn.prep_exec( + "INSERT INTO channels (name, description, kind) VALUES (:name, :description, :kind)", + params!{"name" => chan_name, "kind" => 0, "description" => "no description for testing"}).await; + + Channel { + id: 0, + name: chan_name.into(), + kind: ChannelType::Text, + description: "no description for testing".into() + } +} From 0d146f5dc1a6e2182b263440c025b6fe696e20f4 Mon Sep 17 00:00:00 2001 From: shockrah Date: Thu, 20 Aug 2020 19:22:04 -0700 Subject: [PATCH 39/47] basic tests are now covered no need for this file --- server/tests/verify_basic_cases.sh | 38 ------------------------------ 1 file changed, 38 deletions(-) delete mode 100644 server/tests/verify_basic_cases.sh diff --git a/server/tests/verify_basic_cases.sh b/server/tests/verify_basic_cases.sh deleted file mode 100644 index 302d941..0000000 --- a/server/tests/verify_basic_cases.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -# Available tests marked with `TEST` - ez grep usage - -active_tests='list_all_channels create_channel delete_channel -send_message -' - -delete_channel() { - kv="{\"id\":$id,\"secret\":\"$secret\", \"name\":\"sample\"}" - result=$($crl $POST $url/channels/delete -d "$kv") - code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') - log_result good_delete_channel 200 $code "$result" -} - -send_message() { - # ignoring the reaction to this as its not _completely_ relevant for this test - $crl $POST $url/channels/create -d "{\"id\":$id, \"secret\":\"$secret\",\"name\":\"msgchannel\",\"kind\":2}" > /dev/null - - # now we can try sending the right parameters to send a basic message - kv="{\"id\":$id,\"secret\":\"$secret\", \"content\":\"message sample\", \"channel\":\"msgchannel\"}" - result=$($crl $POST $url/message/send -d "$kv") - code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') - # non-existant channel for now but whatever ignore for now - log_result good_send_message 200 $code "$result" -} - -# Dispatcher to run our tests -if [ -z $1 ];then - for cmd in $active_tests;do - $cmd - done -else - for cmd in $@;do - $cmd - echo '\n'$? - done -fi From 6dfc6ed687b1eb3d2a46f5c46e8ac3f7f8e8bbac Mon Sep 17 00:00:00 2001 From: shockrah Date: Thu, 20 Aug 2020 20:39:26 -0700 Subject: [PATCH 40/47] adding test for missing secret key --- server/src/auth.rs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/server/src/auth.rs b/server/src/auth.rs index 85fb60a..afdf428 100644 --- a/server/src/auth.rs +++ b/server/src/auth.rs @@ -27,7 +27,7 @@ fn valid_user(secret: &str, row: &Option<(VarChar, VarChar, BigInt, Integer, UBi } } -pub async fn wall_entry(path: &str, pool: &Pool, params: &mut serde_json::Value) -> Result { +pub async fn wall_entry(path: &str, pool: &Pool, params: &serde_json::Value) -> Result { // Start by Checking if the api key is in our keystore if routes::is_open(path) { Ok(AuthReason::OpenAuth) @@ -67,3 +67,33 @@ pub fn generate_secret() -> String { encode_config(buf,URL_SAFE) } + + +#[cfg(test)] +mod auth_tests { + use crate::testing::get_pool; + use serde_json::Value; + use mysql_async::prelude::Queryable; + + #[tokio::test] + async fn missing_key() { + let pool = get_pool(); + let conn = pool.get_conn().await.unwrap(); + let conn = conn.drop_exec( + r#"INSERT INTO members (id, secret, name, joindate, status,permissions) + VALUES(1, "abc", "bsname", 1,0,0) + "#, + ()).await.unwrap(); + + let params: Value = serde_json::from_str(r#" + { + "id": 1 + } + "#).unwrap(); + + let result = super::wall_entry("/channels/list", &pool, ¶ms).await; + let _ = conn.drop_exec(r#"DELETE FROM members WHERE secret = "abc""#,()).await; + assert_eq!(true, result.is_ok()); + } + +} From 200602c69835ca21645458ffeebc937a4c5d8f77 Mon Sep 17 00:00:00 2001 From: shockrah Date: Thu, 20 Aug 2020 20:43:05 -0700 Subject: [PATCH 41/47] ignoring sen_message_good as its really expensive on debug builds --- server/src/messages.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/messages.rs b/server/src/messages.rs index 43c7f27..ffe0f6f 100644 --- a/server/src/messages.rs +++ b/server/src/messages.rs @@ -103,7 +103,7 @@ mod messaging_tests { assert_ne!(StatusCode::OK, resp.status()); } - #[tokio::test] + #[tokio::test]#[ignore] async fn send_message_good() { use crate::members::insert_new_member; use crate::perms::GENERAL_NEW; From 85f39a358650aa6427484857e6e7951104222c6e Mon Sep 17 00:00:00 2001 From: shockrah Date: Thu, 20 Aug 2020 20:51:03 -0700 Subject: [PATCH 42/47] added case for missing parameter in delete_channel endpoint" --- server/src/channels.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/server/src/channels.rs b/server/src/channels.rs index 71d7d38..77d39cb 100644 --- a/server/src/channels.rs +++ b/server/src/channels.rs @@ -261,4 +261,17 @@ mod channels_tests { println!("Body: {:?}", resp.body()); let _ = p.disconnect().await; } + + #[tokio::test] + async fn delete_channel_missing_name() { + let p = get_pool(); + let mut resp = hyper_resp(); + // this endpoint is super lenient for some reason btw + let param: Value = serde_json::from_str("{}").expect("JSON is not written correctly"); + + super::delete_channel(&p, &mut resp, param).await; + + assert_eq!(StatusCode::BAD_REQUEST, resp.status()); + } + } From 80112b8c59dc2f0419c90b4845cbe2df84aaa8cc Mon Sep 17 00:00:00 2001 From: shockrah Date: Thu, 20 Aug 2020 20:53:24 -0700 Subject: [PATCH 43/47] added endpoint test for channel deletion based on non existant cname --- server/src/channels.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/server/src/channels.rs b/server/src/channels.rs index 77d39cb..fdb537d 100644 --- a/server/src/channels.rs +++ b/server/src/channels.rs @@ -274,4 +274,17 @@ mod channels_tests { assert_eq!(StatusCode::BAD_REQUEST, resp.status()); } + #[tokio::test] + async fn delet_channel_non_real_channel() { + let p = get_pool(); + let mut resp = hyper_resp(); + // this endpoint is super lenient for some reason btw + let param: Value = serde_json::from_str(r#"{ + "name": "this channel doesn't exist" + }"#).expect("JSON is not written correctly"); + + super::delete_channel(&p, &mut resp, param).await; + + assert_eq!(StatusCode::OK, resp.status()); + } } From c76cbf24bfb80aff885df569213b47613d7adb00 Mon Sep 17 00:00:00 2001 From: shockrah Date: Thu, 20 Aug 2020 20:54:00 -0700 Subject: [PATCH 44/47] more curl tests have been successfully moved to cargo --- server/tests/verify_err_cases.sh | 41 -------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 server/tests/verify_err_cases.sh diff --git a/server/tests/verify_err_cases.sh b/server/tests/verify_err_cases.sh deleted file mode 100644 index 655af73..0000000 --- a/server/tests/verify_err_cases.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -active_tests='list_channels_no_key list_channels_bad_key delete_channel_missing_param delete_channel_no_channel' - -list_channels_no_key() { - result=$($crl $GET $url/channels/list) - code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') - log_result list_channels_no_key 401 $code "$result" -} - -list_channels_bad_key() { - result=$($crl $GET $url/channels/list -d '{"secret":"something else"}') - code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') - log_result list_channels_bad_key 401 $code "$result" -} - -delete_channel_missing_param() { - kv="{\"id\":$id, \"secret\":\"$secret\"}" - result=$($crl $POST $url/channels/delete -d "$kv") - code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') - log_result delete_channel_missing_param 400 $code "$result" -} - -delete_channel_no_channel() { - # Should 200 as the api just drops the result - kv="{\"id\":$id, \"secret\":\"$secret\", \"name\":\"yes\"}" - result=$($crl $POST $url/channels/delete -d "$kv") - code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') - log_result delete_channel_no_channel_found 200 $code "$result" -} - -# Dispatcher to run our tests -if [ -z $1 ];then - for cmd in $active_tests;do - $cmd - done -else - for cmd in $@;do - $cmd - done -fi From 8a0b2cb19058473757ea33a60bd203102eed1748 Mon Sep 17 00:00:00 2001 From: shockrah Date: Sat, 22 Aug 2020 15:43:19 -0700 Subject: [PATCH 45/47] switching to new testing scheme in gitlab-ci.yml --- .gitlab-ci.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e8214d1..5d2ef29 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -31,9 +31,5 @@ api-test: - testing script: - cd server/ - - cargo run --release -- -s > server.log& - - export fc_id=$! - - cd tests/ - - bash ./main.sh body - - kill ${fc_id} + - cargo test --release From 302cac2b519bbb490705004fc8d3b4c213e399a3 Mon Sep 17 00:00:00 2001 From: shockrah Date: Sat, 22 Aug 2020 15:44:06 -0700 Subject: [PATCH 46/47] removing deprecated test scripts --- server/tests/common.sh | 22 ------------- server/tests/main.sh | 53 -------------------------------- server/tests/verify_mal_cases.sh | 23 -------------- 3 files changed, 98 deletions(-) delete mode 100644 server/tests/common.sh delete mode 100644 server/tests/main.sh delete mode 100644 server/tests/verify_mal_cases.sh diff --git a/server/tests/common.sh b/server/tests/common.sh deleted file mode 100644 index 8bc7813..0000000 --- a/server/tests/common.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh -# Details for our bs user when testing things -# raw from `-c same` flag -# {"id":5,"secret":"JYFKgDOgEAn2vJISwRSkRKn3kNE2EQD0dTFOG24UipQ9kQfBOura06PYf-hYGdFbCNeDJy2LxLu-9N13S8Auaw==","name":"adminsama","joindate":1596865071,"status":0,"permissions":18446744073709551615} -export id=5 -export secret='JYFKgDOgEAn2vJISwRSkRKn3kNE2EQD0dTFOG24UipQ9kQfBOura06PYf-hYGdFbCNeDJy2LxLu-9N13S8Auaw==' -export name='adminsama' -export joindate='1596865071' -export status=0 -export permissions='18446744073709551615' - -export simple_key='{"secret":"'"$secret"'","id":'$id'}' - -export url='localhost:8888' - -export GET='-X GET' -export POST='-X POST' - -export arrows='>>>>>' -export line='=============' - -export crl='curl --silent -i' diff --git a/server/tests/main.sh b/server/tests/main.sh deleted file mode 100644 index 4c42641..0000000 --- a/server/tests/main.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -# This script is basically just a convenient launch pad script for running all -# the tests at once -# Most tests should be runnable by doing ./script.sh name_of_test - - -# First the 'good' input tests -# This is to say that we get input that: -# 1. is properly formatted -# 2. has all the info we need & none we don't -# 3. has basically nothing malicious about it - -log_result() { - name=$1 - expect=$2 - actual=$3 - result=$4 - - green='\033[1;32m' - red='\033[1;91m' - nc='\033[0m' - if [ $expect != $actual ];then - echo -e ${red}${name}${nc} ${green}$expect ${red}$actual${nc} - echo -e ${red}==========${nc} - echo "$result" | sed 's/^/\t/g' - echo -e ${red}==========${nc} - else - echo -e ${green}${name}${nc} $expect $actual - if [ ! -z "$_show_body" ];then - echo ========== - echo "$result" | sed 's/^/\t/g' - echo ========== - fi - fi -} - -if [ "$1" = "body" ];then - export _show_body=1 -fi - -source ./common.sh -export -f log_result - -echo ================================ -echo TestName ExpectedCode ActualCode - -bash ./verify_basic_cases.sh - -bash ./verify_err_cases.sh - -bash ./verify_mal_cases.sh - diff --git a/server/tests/verify_mal_cases.sh b/server/tests/verify_mal_cases.sh deleted file mode 100644 index 7c97afd..0000000 --- a/server/tests/verify_mal_cases.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -active_tests='malicious_list_channels' - -malicious_list_channels() { - key='{"secret": ";-- select * from members;"}' - result=$(curl --silent -i -X GET localhost:8888/channels/list -d '{"secret": "-- select * from members;"}') - code=$(echo "$result" | grep HTTP\/1.1 | awk '{print $2}') - log_result malicious_list_channels 401 $code "$result" -} - - -# Dispatcher to run our tests -if [ -z $1 ];then - for cmd in $active_tests;do - $cmd - done -else - for cmd in $@;do - $cmd - echo '\n'$? - done -fi From 742623db04b49a7d63a5f9e90af7f0e3cc38ee76 Mon Sep 17 00:00:00 2001 From: shockrah Date: Sat, 22 Aug 2020 15:44:56 -0700 Subject: [PATCH 47/47] removing last bit of unused tests directory --- server/tests/status.md | 19 ------------------- server/tests/todo.md | 25 ------------------------- 2 files changed, 44 deletions(-) delete mode 100644 server/tests/status.md delete mode 100644 server/tests/todo.md diff --git a/server/tests/status.md b/server/tests/status.md deleted file mode 100644 index 8211e23..0000000 --- a/server/tests/status.md +++ /dev/null @@ -1,19 +0,0 @@ -# State of Tests - -Here is a description of what is passing and what is failing where - -## Full passes - -_Nothing for now_ - -## Basic Passes - -_Nothing for now_ - -## Err Passes - -_Nothing for now_ - -## Mal Passes - -_Nothing for now_ diff --git a/server/tests/todo.md b/server/tests/todo.md deleted file mode 100644 index af47f70..0000000 --- a/server/tests/todo.md +++ /dev/null @@ -1,25 +0,0 @@ -Testing happens on a per-modules basis - -# Messages - -All required, none finished - -# Channels - -* list\_all\_channels - - Good and bad users done - - Malicious users not done - -* create\_channel - sql driver is totally fucked m80 - -* delete\_channel - not ready for testing - -* set\_channel\_attribute - not ready for testing - -# Invites - -* create - not tested - -* use - not tested