101 lines
2.5 KiB
Rust
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(())
|
|
}
|