freechat/json-api/src/invites.rs
shockrah c9658ad5b4 + Adding command line flag [-H/--hmac] to specify hmac file path
This is really just for testing purposes since the files tend to be in
awkward to reach path

- Removing if args.len == 0 check
Basically cannon-fodder tbh

* Auth now reads hmac path from environment var

! All of the above is added for the wss hmac as well

+ Adding command line flag [-W/--wss-hmac] to specify wss-hmac file path
2021-04-26 01:58:18 -07:00

187 lines
5.3 KiB
Rust

use std::time::{SystemTime, UNIX_EPOCH};
use std::collections::HashMap;
use serde_json::json;
use mysql_async::{self, Pool};
use hyper::{Response, Body, StatusCode};
use db::{BigInt, Member, Invite};
use crate::{http, meta, qs_param};
/*
* Error handling:
* All errors raisable from this module come from mysql_async and thus
* are of the enum mysql_async::error::Error
*/
async fn valid_invite(pool: &Pool, id: BigInt) -> bool {
/*
* Fetches an invite from the database to check for validity
*/
let query = if let Ok(inv) = Invite::get(pool, id).await {
match inv {
db::Response::Row(data) => Some(data),
_ => None
}
} else {
None
};
if let Some(invite) = query {
// if expires at all
if invite.expires {
let now: i64 = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Systemtime fetch failed")
.as_millis() as i64;
// old?
let mut valid_status = now < invite.id;
// used?
if invite.uses.is_some() && valid_status == false {
valid_status = invite.uses.unwrap() <= 0; // safe unwrap since we know its Some(_)
}
return valid_status
}
// no expiry date? no problem
return true
}
// prolly not a real id
return false
}
async fn use_invite(pool: &Pool, code: Option<BigInt>) -> Option<Member>{
/*
* Attempts to change the state of the current invite being provided
*/
use crate::auth;
use crate::perms::GENERAL_NEW;
let id = match code {
Some(id) => id,
None => 0
};
// some random comment
if valid_invite(pool, id).await {
let raw_secret = auth::generate_secret();
let secret = auth::encrypt_secret(&raw_secret).unwrap();
return match Member::add(pool, "Anonymous".into(), &secret, GENERAL_NEW).await {
Ok(dbresponse) => {
match dbresponse {
db::Response::Row(mut member) => {
member.secret = raw_secret;
Some(member)
}
_ => None
}
},
// TODO: logggin or something idk
Err(_) => None
}
}
// The invite itself was not valid
// Returning None because we couldn't actually create a proper secret to store
return None;
}
pub async fn join(pool: &Pool, response: &mut Response<Body>, params: HashMap<String, String>) {
/*
* Main dispatcher for dealing with an attempted join via a given invide code
*
*/
let code = match params.get("code") {
Some(val) => {
// i64 because we're using unix timestamps and basically the database is dumb
if let Ok(num) = (*val).to_string().parse::<i64>() {
Some(num)
} else {
None
}
},
None => None
};
// Returns a new member if the code is used successfully
match use_invite(&pool, code).await {
Some(new_account) => {
http::set_json_body(response, json!({
"user": new_account,
"server": meta::get_config()
}))
},
None => {
*response.status_mut() = StatusCode::NOT_FOUND;
}
}
}
async fn allowed_perm_invite(pool: &Pool, uid: u64) -> bool {
use crate::perms;
return match Member::get(pool, uid).await {
Ok(response) => match response {
db::Response::Row(user) => perms::has_perm(user.permissions, perms::CREATE_PERM_INVITES),
_ => false
},
Err(e) => {
eprintln!("[HTTP-DB-ERROR] {}", e);
false
}
};
}
pub async fn create(pool: &Pool, response: &mut Response<Body>, params: HashMap<String, String>) {
/*
* Creates a new invite
* Parameters required asked of the user to provide
* uses : Option<i64>
* expires: Option<bool>
*/
let id = qs_param!(params, "id", u64).unwrap();
let use_count = match params.get("uses") {
Some(val) => {
match (*val).to_string().parse::<i64>() {
Ok(count) => Some(count),
Err(_) => None
}
},
None => None
};
let expirey_request = match params.get("expires") {
Some(exp_val) => {
match exp_val.to_string().parse::<bool>() {
Ok(exp) => {
match exp {
true => allowed_perm_invite(pool, id).await,
false => false
}
},
_ => false
}
}
None => true
};
// TODO: prolly add some kind option to set an expire time
let invite = Invite::new(use_count, expirey_request);
match invite.add(pool).await {
Ok(_) => {
// return the id of the invite
// Link format from here is basically hostname.io:4536/join?code=<some-code>
http::set_json_body(response, serde_json::json!({"invite":invite}))
},
Err(mysqle) => {
println!("\tINVITES::CREATE::ERROR: {}", mysqle);
*response.status_mut() = StatusCode::BAD_REQUEST;
}
}
}