137 lines
4.0 KiB
Rust
137 lines
4.0 KiB
Rust
use bcrypt::{self, BcryptResult};
|
|
use mysql_async::{Pool};
|
|
use mysql_async::error::Error as SqlError;
|
|
|
|
|
|
use crate::routes;
|
|
|
|
use db::{member::Member, common::FromDB};
|
|
use db::Response;
|
|
|
|
// used when we create a new users for the first time
|
|
#[derive(Debug)]
|
|
pub enum AuthReason {
|
|
Good, //passed regular check
|
|
OpenAuth, // route does not require auth
|
|
NoKey, // key missing
|
|
BadKey, // key is bad
|
|
}
|
|
|
|
|
|
fn valid_secret(given_pass: &str, hash: &str) -> bool {
|
|
let result = bcrypt::verify(given_pass, hash);
|
|
return match result {
|
|
Ok(result) => result,
|
|
Err(e) => {
|
|
eprintln!("{}", e);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn valid_perms(member: Member, path: &str) -> bool {
|
|
use crate::perms;
|
|
// if there are perms on the current path make sure the user has them
|
|
if let Some(p) = perms::get_perm_mask(path) {
|
|
return (p & member.permissions) == p;
|
|
}
|
|
// if no perms then we don't care
|
|
return true;
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
pub fn encrypt_secret(raw: &str) -> BcryptResult<String> {
|
|
const BCRYPT_COST: u32 = 14;
|
|
return bcrypt::hash(raw, BCRYPT_COST);
|
|
}
|
|
|
|
pub async fn wall_entry(path: &str, pool: &Pool, params: &serde_json::Value) -> Result<AuthReason, SqlError> {
|
|
use std::borrow::Cow;
|
|
|
|
// Dont need to auth if it's not required
|
|
if routes::is_open(path) {
|
|
Ok(AuthReason::OpenAuth)
|
|
}
|
|
else {
|
|
// make sure we have some legit parameter to use
|
|
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_or(0); // basically nobody is allowed to have 0 as its supposed to be reserved
|
|
let secret = secret_v.as_str().unwrap_or("");
|
|
return match Member::get(pool, id).await {
|
|
Response::Row(user) => {
|
|
if valid_secret(secret, &user.secret) && valid_perms(user, path){
|
|
Ok(AuthReason::Good)
|
|
}
|
|
else {
|
|
Ok(AuthReason::BadKey)
|
|
}
|
|
},
|
|
Response::Empty => Ok(AuthReason::BadKey),
|
|
Response::Other(err) => Err(SqlError::Other(Cow::from(err))),
|
|
_ => Err(SqlError::Other(Cow::from("Undefined result")))
|
|
}
|
|
},
|
|
_ => {
|
|
Ok(AuthReason::NoKey)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
#[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());
|
|
}
|
|
|
|
#[test]
|
|
fn validity_check() {
|
|
use bcrypt::{hash, DEFAULT_COST};
|
|
let plain = super::generate_secret();
|
|
match hash(&plain, DEFAULT_COST) {
|
|
Ok(hash) => assert_eq!(super::valid_secret(&plain, &hash), true),
|
|
Err(err) => panic!("{}", err)
|
|
}
|
|
}
|
|
}
|