use std::{
collections::BTreeMap,
fmt::{self, Display},
sync::{Arc, RwLock},
};
use serde::{Deserialize, Serialize};
use crate::{db_structure::KeyString, networking_utilities::{blake3_hash, decode_hex, decode_hex_to_arr32, encode_hex, ServerError}};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Permission {
Read,
Write,
Upload,
}
impl Permission {
pub fn from_str(s: &str) -> Option<Self> {
match s {
"Read" => Some(Permission::Read),
"Write" => Some(Permission::Write),
"Upload" => Some(Permission::Upload),
_ => None,
}
}
pub fn to_str(&self) -> String {
match self {
Permission::Write => "Write".to_owned(),
Permission::Read => "Read".to_owned(),
Permission::Upload => "Upload".to_owned(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct User {
pub username: String,
pub password: [u8; 32],
pub admin: bool,
pub can_upload: bool,
pub can_read: Vec<String>,
pub can_write: Vec<String>,
}
impl Display for User {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut can_read = String::new();
for item in &self.can_read {
can_read.push('\t');
can_read.push_str(&item);
can_read.push('\n');
}
if can_read.len() > 0 {can_read.pop();}
let mut can_write = String::new();
for item in &self.can_write {
can_write.push('\t');
can_write.push_str(&item);
can_write.push('\n');
}
if can_write.len() > 0 {can_write.pop();}
let printer = format!("username\n\t{}\npassword\n\t{}\nadmin\n\t{}\ncan_upload\n\t{}\ncan_read\n{}\ncan_write\n{}",
self.username, encode_hex(&self.password), self.admin.to_string(), self.can_upload.to_string(), can_read, can_write
);
write!(f, "{}", printer)
}
}
impl User {
pub fn new(username: &str, password: &str) -> User {
User {
username: String::from(username),
password: blake3_hash(password.as_bytes()),
admin: false,
can_upload: false,
can_read: Vec::new(),
can_write: Vec::new(),
}
}
pub fn admin(username: &str, password: &str) -> User {
User {
username: String::from(username),
password: blake3_hash(password.as_bytes()),
admin: true,
can_upload: true,
can_read: Vec::new(),
can_write: Vec::new(),
}
}
}
#[inline]
pub fn user_has_permission(
table_name: &str,
permission: Permission,
username: &str,
users: Arc<RwLock<BTreeMap<KeyString, RwLock<User>>>>,
) -> bool {
let user = users.read().unwrap();
let user = match user.get(&KeyString::from(username)) {
Some(u) => u.read().unwrap(),
None => return false,
};
if user.admin {
return true;
}
match permission {
Permission::Upload => user.can_upload,
Permission::Read => user.can_read.contains(&table_name.to_owned()),
Permission::Write => user.can_write.contains(&table_name.to_owned()),
}
}
#[derive(Debug, Clone)]
pub enum AuthenticationError {
WrongUser(String),
WrongPassword,
TooLong,
Permission,
WrongStringFormat,
}
impl fmt::Display for AuthenticationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AuthenticationError::WrongUser(_) => write!(f, "IU"),
AuthenticationError::WrongPassword => write!(f, "IP"),
AuthenticationError::TooLong => write!(f, "LA"),
AuthenticationError::Permission => write!(f, "NP"),
AuthenticationError::WrongStringFormat => write!(f, "WF"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_user_string_parsing() {
let temp = String::from(
r#"(username:"admin",password:(210,137,178,218,155,112,81,243,107,78,57,110,10,243,224,105,231,140,241,25,167,253,203,100,55,182,133,196,135,94,159,158),admin:true,can_upload:true,can_read:[],can_write:[])"#,
);
let test_user: User = ron::from_str(&temp).unwrap();
dbg!(test_user);
let user_string = ron::to_string(&User::admin("admin", "admin")).unwrap();
println!("{}", user_string);
let user: User = ron::from_str(&user_string).unwrap();
assert_eq!(user, User::admin("admin", "admin"));
}
}