added code for creating new owner accounts, and for dealing with some commandline arguments

This commit is contained in:
shockrah 2020-08-05 23:14:00 -07:00
parent 34ac3709f7
commit 99ccb14bf2

View File

@ -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<Body>, 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<Member>) {
use std::env::set_var;
let mut potential_owner: Option<Member> = 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<u8> = 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}}");}