Browse Source

oh right user management is needed

xenua 2 years ago
parent
commit
240e8bce1d
  1. 45
      Cargo.lock
  2. 1
      Cargo.toml
  3. 110
      src/data.rs

45
Cargo.lock generated

@ -26,6 +26,17 @@ version = "1.0.68" @@ -26,6 +26,17 @@ version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
[[package]]
name = "argon2"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73"
dependencies = [
"base64ct",
"blake2",
"password-hash",
]
[[package]]
name = "async-trait"
version = "0.1.60"
@ -93,12 +104,27 @@ dependencies = [ @@ -93,12 +104,27 @@ dependencies = [
"tower-service",
]
[[package]]
name = "base64ct"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "blake2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest",
]
[[package]]
name = "block-buffer"
version = "0.10.3"
@ -290,6 +316,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" @@ -290,6 +316,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
@ -672,6 +699,17 @@ dependencies = [ @@ -672,6 +699,17 @@ dependencies = [
"regex",
]
[[package]]
name = "password-hash"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700"
dependencies = [
"base64ct",
"rand_core",
"subtle",
]
[[package]]
name = "percent-encoding"
version = "2.2.0"
@ -1009,6 +1047,12 @@ dependencies = [ @@ -1009,6 +1047,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "subtle"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
version = "1.0.107"
@ -1279,6 +1323,7 @@ name = "v" @@ -1279,6 +1323,7 @@ name = "v"
version = "0.1.0"
dependencies = [
"anyhow",
"argon2",
"axum",
"dashmap",
"lazy_static",

1
Cargo.toml

@ -7,6 +7,7 @@ edition = "2021" @@ -7,6 +7,7 @@ edition = "2021"
[dependencies]
anyhow = "1.0.68"
argon2 = { version = "0.4.1", features = ["std"] }
axum = "0.6.1"
dashmap = "5.4.0"
lazy_static = "1.4.0"

110
src/data.rs

@ -8,6 +8,10 @@ use std::{ @@ -8,6 +8,10 @@ use std::{
#[cfg(target_family = "unix")]
use std::os::unix::fs::DirBuilderExt;
use argon2::{
password_hash::{rand_core::OsRng, SaltString},
Argon2, PasswordHash, PasswordHasher, PasswordVerifier,
};
use dashmap::DashMap;
use lazy_static::lazy_static;
@ -32,6 +36,12 @@ pub enum Error { @@ -32,6 +36,12 @@ pub enum Error {
FS,
#[error("id collision wtf")]
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> {
@ -49,6 +59,15 @@ 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)
}
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 {
pub text: String,
pub meta: TextMeta,
@ -110,6 +129,38 @@ impl UserData { @@ -110,6 +129,38 @@ impl UserData {
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 {
@ -225,6 +276,47 @@ impl DataStorage { @@ -225,6 +276,47 @@ impl DataStorage {
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> {
let dir_builder = DirBuilder::new().recursive(true);
if cfg!(target_family = "unix") {
@ -244,14 +336,11 @@ impl DataStorage { @@ -244,14 +336,11 @@ impl DataStorage {
.read(true)
.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() {
line?;
let (id, url) = line?.split_once(": ").ok_or(Error::Metadata)?;
self.redirects
.insert(id.trim().to_string(), url.trim().to_string());
// if there's a better way to do this let me know
}
Ok(())
@ -329,6 +418,21 @@ impl DataStorage { @@ -329,6 +418,21 @@ impl DataStorage {
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> {
let text_paste = self.text.get(&id).ok_or(Error::ShitMissing(id))?;
let fuckery = text_paste.meta.to_string();

Loading…
Cancel
Save