From 99ccb14bf281393a9400d1ebd60ddf9369b74e86 Mon Sep 17 00:00:00 2001 From: shockrah Date: Wed, 5 Aug 2020 23:14:00 -0700 Subject: [PATCH] added code for creating new owner accounts, and for dealing with some commandline arguments --- server/src/main.rs | 123 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 108 insertions(+), 15 deletions(-) diff --git a/server/src/main.rs b/server/src/main.rs index e692829..ac9636f 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,6 +1,8 @@ extern crate chrono; +extern crate clap; extern crate dotenv; extern crate getrandom; +extern crate bcrypt; extern crate base64; use std::net::SocketAddr; @@ -16,7 +18,11 @@ use hyper::{ service::{make_service_fn, service_fn} }; use mysql_async::Pool; +use mysql_async::prelude::Queryable; + use dotenv::dotenv; +use clap::{Arg, App}; +use chrono::Utc; mod auth; use auth::AuthReason; @@ -24,12 +30,19 @@ use auth::AuthReason; mod routes; mod invites; mod channels; + mod members; +use members::Member; + mod messages; mod http_params; mod perms; mod db_types; +const NO_ERR: u16 = 0; +const CONFIG_ERR: u16 = 1; +const SHUTDOWN_ERR: u16 = 2; + async fn route_dispatcher(pool: &Pool, resp: &mut Response, meth: &Method, path: &str, params: serde_json::Value) { // At some point we should have some way of hiding this obnoxious complexity use routes::resolve_dynamic_route; @@ -109,11 +122,82 @@ async fn shutdown_signal() { .await .expect("Failed to capture ctrl-c signal"); } + +async fn start_server(ecode: u16) -> u16 { + println!("Servering on localhost:8888"); + let addr = SocketAddr::from(([127,0,0,1], 8888)); + let service = make_service_fn(|_conn| async { + Ok::<_, Infallible>(service_fn(main_responder)) + }); + let server = Server::bind(&addr).serve(service); + let graceful_shutdown = server.with_graceful_shutdown(shutdown_signal()); + + if let Err(e) = graceful_shutdown.await { + eprintln!("Server shutdown error: {}", e); + return ecode | SHUTDOWN_ERR; + } + else { + return ecode + } + +} + +// 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; + + let args = App::new("Freechat Server") + .version("0.1") + .author("shockrah") + .about("Decentralized chat system") + .arg(Arg::with_name("db-url") + .short("d") + .long("db-url") + .value_name("DATABASE URL") + .help("Sets the DATABASE URL via an environment variable") + .takes_value(true)) + .arg(Arg::with_name("create-owner") + .short("c") + .long("create-owner") + .value_name("Owner") + .help("Creates an account with full permissions in the SQL database.")) + .arg(Arg::with_name("server") + .short("s") + .long("server") + .help("Starts the API server")) + .get_matches(); + + if let Some(db_url) = args.value_of("db-url") { + 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>{ - const NO_ERR: u16 = 0; - const CONFIG_ERR: u16 = 1; - const SHUTDOWN_ERR: u16 = 2; let mut main_ret: u16 = 0; // setting up environment variables @@ -124,21 +208,30 @@ async fn main() -> Result<(), u16>{ main_ret |= CONFIG_ERR; } } - if main_ret == NO_ERR { - println!("Servering on localhost:8888"); - let addr = SocketAddr::from(([127,0,0,1], 8888)); - let service = make_service_fn(|_conn| async { - Ok::<_, Infallible>(service_fn(main_responder)) - }); - let server = Server::bind(&addr).serve(service); - let graceful_shutdown = server.with_graceful_shutdown(shutdown_signal()); - - if let Err(e) = graceful_shutdown.await { - main_ret |= SHUTDOWN_ERR; - eprintln!("Server shutdown error: {}", e); + 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 { + 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)", + mysql_async::params!{ + "secret" => owner.secret, + "name" => owner.name, + "joindate" => owner.joindate, + "status" => owner.status, + "permissions" => owner.permissions + }).await; } } + if main_ret == NO_ERR { + main_ret = start_server(main_ret).await; + } + if main_ret != 0 { // dumb as heck loggin method here if main_ret & CONFIG_ERR != 0 {println!("ERROR: Config was not setup properly => Missing {{DATABASE_URL}}");}