freechat/server-api/src/auth.rs
shockrah 9eff4284a9 + checkin mod::auth for valid permissions
+ helper function for getting a permission mask from permissions module
2020-08-25 23:27:41 -07:00

117 lines
3.5 KiB
Rust

use bcrypt;
use mysql_async::{Conn, Pool};
use mysql_async::prelude::{params, Queryable};
use crate::db_types::{BigInt, Integer, UBigInt, VarChar};
use crate::routes;
// used when we create a new users for the first time
pub const BCRYPT_COST: u32 = 14;
pub enum AuthReason {
Good, //passed regular check
OpenAuth, // route does not require auth
NoKey, // key missing
BadKey, // key is bad
}
fn valid_user(secret: &str, row: &Option<(VarChar, VarChar, BigInt, Integer, UBigInt)>) -> bool {
match row {
Some(row) => {
match bcrypt::verify(secret, &row.0) {
Ok(result) => result,
Err(_) => return false
}
},
_ => return false
}
}
fn valid_perms(user_opt: &Option<(VarChar, VarChar, BigInt, Integer, UBigInt)>, path: &str) -> bool {
use crate::perms;
if let Some(user) = user_opt {
if let Some(p) = perms::get_perm_mask(path) {
return (p & user.4) == p;
}
return true; // no perms required
}
return false;
}
pub async fn wall_entry(path: &str, pool: &Pool, params: &serde_json::Value) -> Result<AuthReason, mysql_async::error::Error> {
// Start by Checking if the api key is in our keystore
if routes::is_open(path) {
Ok(AuthReason::OpenAuth)
}
else {
match (params.get("id"), params.get("secret")) {
/*
* If we apparantly have user data then check for validity in credentials
*/
(Some(id_v), Some(secret_v)) => {
/* unwrapping because i couldn't care less about poorly formatted request data */
let id = id_v.as_u64().unwrap();
let secret = secret_v.as_str().unwrap();
let conn = pool.get_conn().await?;
let db_tup: (Conn, Option<(VarChar, VarChar, BigInt, Integer, UBigInt)>) = conn.first_exec(
"SELECT secret, name, joindate, status, permissions FROM members WHERE id = :id",
mysql_async::params!{"id" => id}).await?;
let user_data = &db_tup.1;
if valid_user(secret, user_data) && valid_perms(user_data, path) {
Ok(AuthReason::Good)
}
else {
Ok(AuthReason::BadKey)
}
},
_ => {
Ok(AuthReason::NoKey)
}
}
}
}
pub fn generate_secret() -> String {
/*
* Generates a url-safe-plaintext secret for our db
* */
use getrandom::getrandom;
use base64::{encode_config, URL_SAFE};
let mut buf: Vec<u8> = vec![0;64];
getrandom(&mut buf).unwrap();
encode_config(buf,URL_SAFE)
}
#[cfg(test)]
mod auth_tests {
use crate::testing::get_pool;
use serde_json::Value;
use mysql_async::prelude::Queryable;
#[tokio::test]
async fn missing_key() {
let pool = get_pool();
let conn = pool.get_conn().await.unwrap();
let conn = conn.drop_exec(
r#"INSERT INTO members (id, secret, name, joindate, status,permissions)
VALUES(1, "abc", "bsname", 1,0,0)
"#,
()).await.unwrap();
let params: Value = serde_json::from_str(r#"
{
"id": 1
}
"#).unwrap();
let result = super::wall_entry("/channels/list", &pool, &params).await;
let _ = conn.drop_exec(r#"DELETE FROM members WHERE secret = "abc""#,()).await;
assert_eq!(true, result.is_ok());
}
}