diff --git a/server/src/main.rs b/server/src/main.rs index 160e208..6905999 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -7,7 +7,7 @@ extern crate base64; use std::net::SocketAddr; use std::convert::Infallible; // our main dispatcher basically never fails hence why we use this -use std::env; +use std::env::{self, set_var}; use tokio; use hyper::{ @@ -17,12 +17,10 @@ use hyper::{ Method, StatusCode, service::{make_service_fn, service_fn} }; -use mysql_async::{params, Pool}; -use mysql_async::prelude::Queryable; +use mysql_async::Pool; use dotenv::dotenv; use clap::{Arg, App}; -use chrono::Utc; mod auth; use auth::AuthReason; @@ -32,7 +30,6 @@ mod invites; mod channels; mod members; -use members::Member; mod messages; mod http_params; @@ -142,10 +139,19 @@ async fn start_server(ecode: u16) -> u16 { } -// we're only marking this as async because -fn parse_options(eflags: u16) -> (u16, Option) { - use std::env::set_var; - let mut potential_owner: Option = None; + +#[tokio::main] +async fn main() -> Result<(), u16>{ + + let mut main_ret: u16 = 0; + let d_result = dotenv(); + + // check for a database_url before the override we get from the cmd line + if let Err(_d) = d_result { + if let Err(_e) = env::var("DATABASE_URL") { + main_ret |= CONFIG_ERR; + } + } let args = App::new("Freechat Server") .version("0.1") @@ -172,60 +178,11 @@ fn parse_options(eflags: u16) -> (u16, Option) { set_var("DATABASE_URL", db_url); } - if let Some(name) = args.value_of("create-owner") { - use getrandom::getrandom; - - let mut raw: Vec = vec![0;64]; - getrandom(&mut raw).unwrap(); - let raw_key = base64::encode_config(raw, base64::URL_SAFE); - // this we can store in our db as a full string on its own - let secret = bcrypt::hash(raw_key, auth::BCRYPT_COST).unwrap(); - - potential_owner = Some(Member { - // id field is rather meaninglyless since the db takes care of this for us - id: 0, - secret: secret.to_string(), - name: name.to_string(), - joindate: Utc::now().timestamp(), - status: 0, - permissions: std::u64::MAX - }); - } - return (eflags, potential_owner); -} - - - -#[tokio::main] -async fn main() -> Result<(), u16>{ - - let mut main_ret: u16 = 0; - // setting up environment variables - let d_result = dotenv(); - if let Err(_d) = d_result { - // we may be on a pipeline/prod environment so .env may not be there - if let Err(_e) = env::var("DATABASE_URL") { - main_ret |= CONFIG_ERR; - } - } - let (opt_res, potential_owner) = parse_options(main_ret); - main_ret = opt_res; // pull out the return value from the option parsing stage - // create a new owner account if we need to - if let Some(owner) = potential_owner { + if let Some(owner_name) = args.value_of("create-owner") { let p = Pool::new(&env::var("DATABASE_URL").unwrap()); - // TODO: move this logic over to members.rs - if let Ok(conn) = p.get_conn().await { - let _db_res = conn.prep_exec( - "INSERT INTO members (secret, name, joindate, status, permissions) - VALUES(:secret, :name, :joindate, :status, :permissions)", - params!{ - "secret" => owner.secret, - "name" => owner.name, - "joindate" => owner.joindate, - "status" => owner.status, - "permissions" => owner.permissions - }).await; - } + println!("Creating owner {{ {} }}...", owner_name); + members::insert_new_member(&p, owner_name.to_string(), std::u64::MAX); + p.disconnect(); } if main_ret == NO_ERR { diff --git a/server/src/members.rs b/server/src/members.rs index 8ff9a49..151236b 100644 --- a/server/src/members.rs +++ b/server/src/members.rs @@ -1,9 +1,8 @@ use chrono::Utc; use hyper::{Body, Response, StatusCode}; use hyper::header::{HeaderName, HeaderValue}; -use mysql_async::{Conn, Pool}; +use mysql_async::{Conn, Pool, error::Error as MySqlError}; use mysql_async::prelude::{params, Queryable}; -use mysql_async::error::Error as SQLError; use serde_json::Value; use crate::db_types::{UBigInt, BigInt, Integer, VarChar}; @@ -40,32 +39,39 @@ impl<'n> InsertableMember<'n> { } } -async fn insert_new_member<'nm>(conn: Conn, mem: InsertableMember<'nm>) - -> Result { - // standard insertion with no checks, those should be done before we get here - // in theory only 500's should happen here assuming we did everything else right - let conn = conn.batch_exec( - "INSERT INTO `members` () ", + +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 now: BigInt = Utc::now().timestamp(); + + let conn = conn.drop_exec( + "INSERT INTO members(secret, name, joindate, status, permissions) + VALUES(:secret, :name, :joindate, :status, :permissions)", mysql_async::params!{ - "name" => mem.name, - "permissions" => mem.permissions, - /*"badges" => mem.permissions*/ // not used yet as we have no serializer + "secret" => secret.as_ref(), + "name" => name.as_ref(), + "joindate" => now, + "status" => 0, + "permissions" => perms }).await?; - type MemberRow = (UBigInt, VarChar, VarChar, BigInt, Integer, UBigInt); - let (conn, row): (Conn, Option) = conn.first_exec( - "SELECT * from `members` WHERE `secret` = :secret ", + // now pull back the user from our db and return that row + let db_row_result: (Conn, Option) = conn.first_exec( + "SELECT id FROM members WHERE secret = :secret", params!{ - "secret" => mem.name + "secret" => secret.as_ref() }).await?; Ok(Member { - id: 0, - secret: "secret".into(), - name: "String".into(), - joindate: 123, - status: 69, - permissions: 0, + id: db_row_result.1.unwrap(), // if we made it this far this shouldn't fail (i hope) + secret: secret, + name: name, + joindate: now, + status: 0, + permissions: perms }) } @@ -73,25 +79,24 @@ async fn general_new_user(p: &Pool, resp: &mut Response, 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"); let pre_mem = InsertableMember::new(name); - if let Ok(conn) = p.get_conn().await { - match insert_new_member(conn, pre_mem).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(new_member.to_json()); - }, - Err(_) => { - *resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; - *resp.body_mut() = Body::from("Could not process input"); - } + 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(new_member.to_json()); + }, + Err(_) => { + *resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + *resp.body_mut() = Body::from("Could not process input"); } } } diff --git a/server/src/perms.rs b/server/src/perms.rs index c2d9007..dc0a8b7 100644 --- a/server/src/perms.rs +++ b/server/src/perms.rs @@ -1,2 +1,4 @@ pub const JOIN_VOICE:u64 = 1; -pub const SEND_MESSAGES:u64 = 2; \ No newline at end of file +pub const SEND_MESSAGES:u64 = 2; + +pub const GENERAL_NEW: u64 = JOIN_VOICE | SEND_MESSAGES;