|
|
@ -8,6 +8,10 @@ use std::{ |
|
|
|
#[cfg(target_family = "unix")] |
|
|
|
#[cfg(target_family = "unix")] |
|
|
|
use std::os::unix::fs::DirBuilderExt; |
|
|
|
use std::os::unix::fs::DirBuilderExt; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use argon2::{ |
|
|
|
|
|
|
|
password_hash::{rand_core::OsRng, SaltString}, |
|
|
|
|
|
|
|
Argon2, PasswordHash, PasswordHasher, PasswordVerifier, |
|
|
|
|
|
|
|
}; |
|
|
|
use dashmap::DashMap; |
|
|
|
use dashmap::DashMap; |
|
|
|
use lazy_static::lazy_static; |
|
|
|
use lazy_static::lazy_static; |
|
|
|
|
|
|
|
|
|
|
@ -32,6 +36,12 @@ pub enum Error { |
|
|
|
FS, |
|
|
|
FS, |
|
|
|
#[error("id collision wtf")] |
|
|
|
#[error("id collision wtf")] |
|
|
|
IDCollision, |
|
|
|
IDCollision, |
|
|
|
|
|
|
|
#[error("thing exists")] |
|
|
|
|
|
|
|
Exists, |
|
|
|
|
|
|
|
#[error("argon2 error")] |
|
|
|
|
|
|
|
Argon2(#[from] argon2::password_hash::Error), |
|
|
|
|
|
|
|
#[error("bad password")] |
|
|
|
|
|
|
|
BadPassword, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn meta_str_to_map(from: &str) -> Result<HashMap<&str, String>, Error> { |
|
|
|
fn meta_str_to_map(from: &str) -> Result<HashMap<&str, String>, Error> { |
|
|
@ -49,6 +59,15 @@ fn meta_str_to_map(from: &str) -> Result<HashMap<&str, String>, Error> { |
|
|
|
Ok(map) |
|
|
|
Ok(map) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn hash_password(password: String) -> Result<String, Error> { |
|
|
|
|
|
|
|
let salt = SaltString::generate(&mut OsRng); |
|
|
|
|
|
|
|
let argon2 = Argon2::default(); |
|
|
|
|
|
|
|
let hash = argon2 |
|
|
|
|
|
|
|
.hash_password(&password.into_bytes(), &salt)? |
|
|
|
|
|
|
|
.to_string(); |
|
|
|
|
|
|
|
Ok(hash) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
struct TextPaste { |
|
|
|
struct TextPaste { |
|
|
|
pub text: String, |
|
|
|
pub text: String, |
|
|
|
pub meta: TextMeta, |
|
|
|
pub meta: TextMeta, |
|
|
@ -110,6 +129,38 @@ impl UserData { |
|
|
|
is_admin, |
|
|
|
is_admin, |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn new(username: String, password: String, is_admin: bool) -> Result<Self, Error> { |
|
|
|
|
|
|
|
let pw_hash = hash_password(password)?; |
|
|
|
|
|
|
|
Ok(Self { |
|
|
|
|
|
|
|
username, |
|
|
|
|
|
|
|
pw_hash, |
|
|
|
|
|
|
|
is_admin, |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn set_password(&mut self, password: String) -> Result<(), Error> { |
|
|
|
|
|
|
|
self.pw_hash = hash_password(password)?; |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn set_password_checked(&mut self, old: String, new: String) -> Result<(), Error> { |
|
|
|
|
|
|
|
self.validate_password(old)?; |
|
|
|
|
|
|
|
self.set_password(new); |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn validate_password(&self, password: String) -> Result<(), Error> { |
|
|
|
|
|
|
|
let parsed_hash = PasswordHash::new(&self.pw_hash)?; |
|
|
|
|
|
|
|
Argon2::default().verify_password(&password.into_bytes(), &parsed_hash)?; |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl std::fmt::Display for UserData { |
|
|
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
|
|
|
|
|
|
|
write!(f, "{}: {}", self.username, self.pw_hash) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub struct DataStorage { |
|
|
|
pub struct DataStorage { |
|
|
@ -225,6 +276,47 @@ impl DataStorage { |
|
|
|
Ok(()) |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn create_user( |
|
|
|
|
|
|
|
&mut self, |
|
|
|
|
|
|
|
username: String, |
|
|
|
|
|
|
|
password: String, |
|
|
|
|
|
|
|
is_admin: bool, |
|
|
|
|
|
|
|
) -> Result<(), Error> { |
|
|
|
|
|
|
|
if self.users.contains_key(&username) { |
|
|
|
|
|
|
|
return Err(Error::Exists); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.users |
|
|
|
|
|
|
|
.insert(username, UserData::new(username, password, is_admin)?); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.write_users()?; |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn user_change_password( |
|
|
|
|
|
|
|
&mut self, |
|
|
|
|
|
|
|
username: String, |
|
|
|
|
|
|
|
old_password: String, |
|
|
|
|
|
|
|
new_password: String, |
|
|
|
|
|
|
|
) -> Result<(), Error> { |
|
|
|
|
|
|
|
let mut user = *self |
|
|
|
|
|
|
|
.users |
|
|
|
|
|
|
|
.get_mut(&username) |
|
|
|
|
|
|
|
.ok_or(Error::ShitMissing(username))?; |
|
|
|
|
|
|
|
user.set_password_checked(old_password, new_password)?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.write_users()?; |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn delete_user(&mut self, username: String) -> Result<(), Error> { |
|
|
|
|
|
|
|
self.users |
|
|
|
|
|
|
|
.remove(&username) |
|
|
|
|
|
|
|
.ok_or(Error::ShitMissing(username))?; |
|
|
|
|
|
|
|
self.write_users()?; |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn ensure_folder_structure(&self) -> Result<(), Error> { |
|
|
|
fn ensure_folder_structure(&self) -> Result<(), Error> { |
|
|
|
let dir_builder = DirBuilder::new().recursive(true); |
|
|
|
let dir_builder = DirBuilder::new().recursive(true); |
|
|
|
if cfg!(target_family = "unix") { |
|
|
|
if cfg!(target_family = "unix") { |
|
|
@ -244,14 +336,11 @@ impl DataStorage { |
|
|
|
.read(true) |
|
|
|
.read(true) |
|
|
|
.open(self.base_dir.join(*REDIRECTS_FILE))?; |
|
|
|
.open(self.base_dir.join(*REDIRECTS_FILE))?; |
|
|
|
|
|
|
|
|
|
|
|
// TODO: replace with meta_str_to_map and then self.redirects.extend
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for line in io::BufReader::new(f).lines() { |
|
|
|
for line in io::BufReader::new(f).lines() { |
|
|
|
line?; |
|
|
|
line?; |
|
|
|
let (id, url) = line?.split_once(": ").ok_or(Error::Metadata)?; |
|
|
|
let (id, url) = line?.split_once(": ").ok_or(Error::Metadata)?; |
|
|
|
self.redirects |
|
|
|
self.redirects |
|
|
|
.insert(id.trim().to_string(), url.trim().to_string()); |
|
|
|
.insert(id.trim().to_string(), url.trim().to_string()); |
|
|
|
// if there's a better way to do this let me know
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
Ok(()) |
|
|
@ -329,6 +418,21 @@ impl DataStorage { |
|
|
|
Ok(()) |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn write_users(&self) -> Result<(), Error> { |
|
|
|
|
|
|
|
let fuckery = String::from("---"); |
|
|
|
|
|
|
|
self.users.into_iter().map(|(_, user)| { |
|
|
|
|
|
|
|
if user.is_admin { |
|
|
|
|
|
|
|
fuckery = format!("{}\n{}", user.to_string(), fuckery) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
fuckery += format!("\n{}", user.to_string()).as_str() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::fs::write(self.base_dir.join(*USERS_FILE), fuckery)?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn write_text(&self, id: String) -> Result<(), Error> { |
|
|
|
fn write_text(&self, id: String) -> Result<(), Error> { |
|
|
|
let text_paste = self.text.get(&id).ok_or(Error::ShitMissing(id))?; |
|
|
|
let text_paste = self.text.get(&id).ok_or(Error::ShitMissing(id))?; |
|
|
|
let fuckery = text_paste.meta.to_string(); |
|
|
|
let fuckery = text_paste.meta.to_string(); |
|
|
|