Moved new member for admins logic to members module

More generally members now has a proper looking insert new member function

Added new combinational permision for basic users

Reworked DATBASE_URL initialization for the API's environment variable
Nearly there to conditionally running the server, only need to add one more guard
This commit is contained in:
shockrah 2020-08-06 19:09:59 -07:00
parent c1b50bd36a
commit 1b8e52e3e0
3 changed files with 61 additions and 97 deletions

View File

@ -7,7 +7,7 @@ extern crate base64;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::convert::Infallible; // our main dispatcher basically never fails hence why we use this 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 tokio;
use hyper::{ use hyper::{
@ -17,12 +17,10 @@ use hyper::{
Method, StatusCode, Method, StatusCode,
service::{make_service_fn, service_fn} service::{make_service_fn, service_fn}
}; };
use mysql_async::{params, Pool}; use mysql_async::Pool;
use mysql_async::prelude::Queryable;
use dotenv::dotenv; use dotenv::dotenv;
use clap::{Arg, App}; use clap::{Arg, App};
use chrono::Utc;
mod auth; mod auth;
use auth::AuthReason; use auth::AuthReason;
@ -32,7 +30,6 @@ mod invites;
mod channels; mod channels;
mod members; mod members;
use members::Member;
mod messages; mod messages;
mod http_params; 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<Member>) { #[tokio::main]
use std::env::set_var; async fn main() -> Result<(), u16>{
let mut potential_owner: Option<Member> = None;
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") let args = App::new("Freechat Server")
.version("0.1") .version("0.1")
@ -172,60 +178,11 @@ fn parse_options(eflags: u16) -> (u16, Option<Member>) {
set_var("DATABASE_URL", db_url); set_var("DATABASE_URL", db_url);
} }
if let Some(name) = args.value_of("create-owner") { if let Some(owner_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>{
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 {
let p = Pool::new(&env::var("DATABASE_URL").unwrap()); let p = Pool::new(&env::var("DATABASE_URL").unwrap());
// TODO: move this logic over to members.rs println!("Creating owner {{ {} }}...", owner_name);
if let Ok(conn) = p.get_conn().await { members::insert_new_member(&p, owner_name.to_string(), std::u64::MAX);
let _db_res = conn.prep_exec( p.disconnect();
"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;
}
} }
if main_ret == NO_ERR { if main_ret == NO_ERR {

View File

@ -1,9 +1,8 @@
use chrono::Utc; use chrono::Utc;
use hyper::{Body, Response, StatusCode}; use hyper::{Body, Response, StatusCode};
use hyper::header::{HeaderName, HeaderValue}; 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::prelude::{params, Queryable};
use mysql_async::error::Error as SQLError;
use serde_json::Value; use serde_json::Value;
use crate::db_types::{UBigInt, BigInt, Integer, VarChar}; 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<Member, SQLError> { pub async fn insert_new_member(p: &Pool, name: VarChar, perms: u64) -> Result<Member, MySqlError> {
// standard insertion with no checks, those should be done before we get here use crate::auth::generate_secret;
// in theory only 500's should happen here assuming we did everything else right
let conn = conn.batch_exec( let conn: Conn = p.get_conn().await?;
"INSERT INTO `members` () ", 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!{ mysql_async::params!{
"name" => mem.name, "secret" => secret.as_ref(),
"permissions" => mem.permissions, "name" => name.as_ref(),
/*"badges" => mem.permissions*/ // not used yet as we have no serializer "joindate" => now,
"status" => 0,
"permissions" => perms
}).await?; }).await?;
type MemberRow = (UBigInt, VarChar, VarChar, BigInt, Integer, UBigInt); // now pull back the user from our db and return that row
let (conn, row): (Conn, Option<MemberRow>) = conn.first_exec( let db_row_result: (Conn, Option<UBigInt>) = conn.first_exec(
"SELECT * from `members` WHERE `secret` = :secret ", "SELECT id FROM members WHERE secret = :secret",
params!{ params!{
"secret" => mem.name "secret" => secret.as_ref()
}).await?; }).await?;
Ok(Member { Ok(Member {
id: 0, id: db_row_result.1.unwrap(), // if we made it this far this shouldn't fail (i hope)
secret: "secret".into(), secret: secret,
name: "String".into(), name: name,
joindate: 123, joindate: now,
status: 69, status: 0,
permissions: 0, permissions: perms
}) })
} }
@ -73,25 +79,24 @@ async fn general_new_user(p: &Pool, resp: &mut Response<Body>, params: Value) {
/* /*
* @name: string => desired default name * @name: string => desired default name
*/ */
use crate::perms;
let default_name = serde_json::json!("NewUser"); let default_name = serde_json::json!("NewUser");
let name = params.get("name") let name = params.get("name")
.unwrap_or(&default_name) .unwrap_or(&default_name)
.as_str().unwrap_or("NewUser"); .as_str().unwrap_or("NewUser");
let pre_mem = InsertableMember::new(name); let pre_mem = InsertableMember::new(name);
if let Ok(conn) = p.get_conn().await { match insert_new_member(p, name.to_string(), perms::GENERAL_NEW).await {
match insert_new_member(conn, pre_mem).await { Ok(new_member) => {
Ok(new_member) => { *resp.status_mut() = StatusCode::OK;
*resp.status_mut() = StatusCode::OK; let json_hdr_name = HeaderName::from_static("Content-Type");
let json_hdr_name = HeaderName::from_static("Content-Type"); let json_hdr_val = HeaderValue::from_static("application/json");
let json_hdr_val = HeaderValue::from_static("application/json"); resp.headers_mut().insert(json_hdr_name, json_hdr_val);
resp.headers_mut().insert(json_hdr_name, json_hdr_val); *resp.body_mut() = Body::from(new_member.to_json());
*resp.body_mut() = Body::from(new_member.to_json()); },
}, Err(_) => {
Err(_) => { *resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
*resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; *resp.body_mut() = Body::from("Could not process input");
*resp.body_mut() = Body::from("Could not process input");
}
} }
} }
} }

View File

@ -1,2 +1,4 @@
pub const JOIN_VOICE:u64 = 1; pub const JOIN_VOICE:u64 = 1;
pub const SEND_MESSAGES:u64 = 2; pub const SEND_MESSAGES:u64 = 2;
pub const GENERAL_NEW: u64 = JOIN_VOICE | SEND_MESSAGES;