router/src/session.rs
2021-02-07 17:10:36 -06:00

101 lines
2.5 KiB
Rust

use once_cell::sync::OnceCell;
use sled::{Db, Tree};
static USER_TREE: OnceCell<Tree> = OnceCell::new();
fn user_tree(db: &Db) -> &'static Tree {
USER_TREE.get_or_init(|| db.open_tree("users").unwrap())
}
pub(crate) fn login(db: &Db, username: String, password: String) -> Result<(), anyhow::Error> {
let tree = user_tree(db);
let hash = tree
.get(username.as_bytes())?
.ok_or_else(|| anyhow::anyhow!("Missing user {}", username))?;
let password_hash = String::from_utf8_lossy(&hash);
if bcrypt::verify(password, &password_hash)? {
return Ok(());
}
Err(anyhow::anyhow!("Invalid password"))
}
async fn add_user(db: &Db, username: String, password: String) -> Result<(), anyhow::Error> {
let tree = user_tree(db);
let hash = bcrypt::hash(&password, bcrypt::DEFAULT_COST)?;
if tree
.compare_and_swap(username, None as Option<&[u8]>, Some(hash.as_bytes()))?
.is_err()
{
return Err(anyhow::anyhow!("User already exists"));
}
tree.flush_async().await?;
Ok(())
}
pub(crate) async fn update_password(
db: &Db,
username: String,
new_password: String,
) -> Result<(), anyhow::Error> {
let tree = user_tree(db);
let hash = bcrypt::hash(&new_password, bcrypt::DEFAULT_COST)?;
let updated = tree
.update_and_fetch(username.as_bytes(), move |opt| {
if opt.is_some() {
Some(hash.clone().into_bytes())
} else {
None
}
})?
.is_some();
if updated {
return Ok(());
}
Err(anyhow::anyhow!("User doesn't exist"))
}
pub(crate) async fn verify(db: &Db, username: &str, password: String) -> Result<(), anyhow::Error> {
let tree = user_tree(db);
let hash = tree
.get(username.as_bytes())?
.ok_or_else(|| anyhow::anyhow!("Missing user {}", username))?;
let password_hash = String::from_utf8_lossy(&hash);
if bcrypt::verify(password, &password_hash)? {
return Ok(());
}
Err(anyhow::anyhow!("Invalid password"))
}
pub(crate) async fn create_admin(db: &Db) -> Result<(), anyhow::Error> {
use rand::Rng;
let password = rand::thread_rng()
.sample_iter(rand::distributions::Alphanumeric)
.take(16)
.map(char::from)
.collect::<String>();
if add_user(db, String::from("admin"), password.clone())
.await
.is_ok()
{
println!("Admin password is '{}'", password);
}
Ok(())
}