Add basic database methods

This commit is contained in:
Raphaël Thériault 2020-09-28 22:44:39 -04:00
parent 81c9246634
commit cc75c189a6
2 changed files with 96 additions and 24 deletions

View File

@ -1,7 +1,7 @@
use crate::{
config::{Config, PasswordConfig},
db::{self, User},
reject::{self, TryExt},
db::User,
reject::TryExt,
};
use anyhow::Result;
use rand::Rng;
@ -34,10 +34,10 @@ pub fn auth_required(
#[tracing::instrument(level = "debug")]
async fn user(header: String, db: &Db, config: &Config) -> Result<User, Rejection> {
if &header[..5] != "Basic" {
return Err(reject::unauthorized());
return Err(crate::reject::unauthorized());
}
let decoded = task::block_in_place(move || base64::decode(&header[6..])).or_401()?;
let decoded = base64::decode(&header[6..]).or_401()?;
let (user, password) = {
let mut split = None;
@ -52,16 +52,27 @@ async fn user(header: String, db: &Db, config: &Config) -> Result<User, Rejectio
(std::str::from_utf8(u).or_401()?, p)
};
let user = db::user(user, db).or_500()?.or_401()?;
let user = crate::db::user(user, db).or_500()?.or_401()?;
if !verify(&user.password_hash, password, &config.password).or_500()? {
return Err(reject::unauthorized());
return Err(crate::reject::unauthorized());
}
Ok(user)
}
#[tracing::instrument(level = "debug", skip(encoded, password))]
fn verify(encoded: &str, password: &[u8], config: &PasswordConfig) -> Result<bool> {
let res = match &config.secret {
Some(s) => task::block_in_place(move || {
argon2::verify_encoded_ext(encoded, password, s.as_bytes(), &[])
})?,
None => task::block_in_place(move || argon2::verify_encoded(encoded, password))?,
};
Ok(res)
}
#[tracing::instrument(level = "debug", skip(password))]
fn hash(password: &[u8], config: &PasswordConfig) -> Result<String> {
pub fn hash(password: &[u8], config: &PasswordConfig) -> Result<String> {
let mut cfg = argon2::Config::default();
if let Some(hl) = config.hash_length {
cfg.hash_length = hl;
@ -79,22 +90,9 @@ fn hash(password: &[u8], config: &PasswordConfig) -> Result<String> {
cfg.secret = s;
}
let hashed = task::block_in_place(move || {
let mut salt = vec![0; config.salt_length.unwrap_or(16)];
rand::thread_rng().fill(&mut salt[..]);
let mut salt = vec![0; config.salt_length.unwrap_or(16)];
rand::thread_rng().fill(&mut salt[..]);
argon2::hash_encoded(password, &salt[..], &cfg)
})?;
let hashed = argon2::hash_encoded(password, &salt[..], &cfg)?;
Ok(hashed)
}
#[tracing::instrument(level = "debug", skip(encoded, password))]
fn verify(encoded: &str, password: &[u8], config: &PasswordConfig) -> Result<bool> {
let res = match &config.secret {
Some(s) => task::block_in_place(move || {
argon2::verify_encoded_ext(encoded, password, s.as_bytes(), &[])
})?,
None => task::block_in_place(move || argon2::verify_encoded(encoded, password))?,
};
Ok(res)
}

View File

@ -1,5 +1,6 @@
use crate::config::DatabaseConfig;
use crate::config::{Config, DatabaseConfig};
use anyhow::Result;
use rand::{distributions::Alphanumeric, Rng};
use serde::{Deserialize, Serialize};
use sled::Db;
use tokio::task;
@ -14,6 +15,53 @@ pub fn connect(config: &DatabaseConfig) -> Result<&'static Db> {
Ok(Box::leak(Box::new(db)))
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum Filite {
File { data: Vec<u8>, mime: String },
Link { location: String },
Text { data: String },
}
#[tracing::instrument(level = "debug", skip(db))]
pub fn filite(id: &str, db: &Db) -> Result<Option<Filite>> {
task::block_in_place(move || {
let bytes = match db.get(id)? {
Some(b) => b,
None => return Ok(None),
};
let filite = bincode::deserialize(&bytes)?;
Ok(Some(filite))
})
}
#[tracing::instrument(level = "debug", skip(db))]
pub fn create_filite(id: &str, filite: Filite, db: &Db) -> Result<bool> {
task::block_in_place(move || {
if db.contains_key(id)? {
return Ok(false);
}
let bytes = bincode::serialize(&filite)?;
db.insert(id, bytes)?;
Ok(true)
})
}
#[tracing::instrument(level = "debug", skip(db))]
pub fn random_id(length: usize, db: &Db) -> Result<String> {
task::block_in_place(move || {
let mut id;
loop {
id = rand::thread_rng()
.sample_iter(Alphanumeric)
.take(length)
.collect();
if !db.contains_key(&id)? {
break Ok(id);
}
}
})
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct User {
pub admin: bool,
@ -32,3 +80,29 @@ pub fn user(id: &str, db: &Db) -> Result<Option<User>> {
Ok(Some(user))
})
}
#[tracing::instrument(level = "debug", skip(password, db))]
pub fn create_user(
id: &str,
password: &str,
admin: bool,
db: &Db,
config: &Config,
) -> Result<bool> {
task::block_in_place(move || {
let users = db.open_tree("users")?;
if users.contains_key(id)? {
return Ok(false);
}
let password_hash = crate::auth::hash(password.as_bytes(), &config.password)?;
let user = User {
admin,
password_hash,
};
let bytes = bincode::serialize(&user)?;
users.insert(id, bytes)?;
Ok(true)
})
}