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 } } pub async fn wall_entry(path: &str, pool: &Pool, params: &serde_json::Value) -> Result { // 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")) { (Some(id_v), Some(secret_v)) => { 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?; if valid_user(secret, &db_tup.1) { 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 = 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, ¶ms).await; let _ = conn.drop_exec(r#"DELETE FROM members WHERE secret = "abc""#,()).await; assert_eq!(true, result.is_ok()); } }