diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/db.rs | 253 | ||||
-rw-r--r-- | src/main.rs | 195 | ||||
-rw-r--r-- | src/uid.rs | 2 |
3 files changed, 319 insertions, 131 deletions
diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..43a2cc7 --- /dev/null +++ b/src/db.rs @@ -0,0 +1,253 @@ +use serde::{Serialize, Deserialize}; +use sha2::{Digest, Sha256}; +use std::collections::HashMap; +use std::path::PathBuf; +use std::{fs::File, io::Write}; + +use crate::uid::{self, UID}; + +#[derive(Serialize, Deserialize)] +pub struct Config { + dbsave: PathBuf, + realm: String, +} +impl Config { + pub fn new() -> Config { + Config { dbsave: "db.json".into(), realm: "localhost".into() } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone, Copy)] +enum Color { + White, + Red, + Blue, + Green, + Yellow, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +struct Token { + color: Color, + amount: usize, +} +impl Token { + pub fn new(color: Color, amount: usize) -> Token { + Token { color, amount } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct User { + name: String, + hashed_password: String, + id: UID, + sessions: HashMap<String, String>, + tokovec: Vec<Token>, +} +impl User { + pub fn new(name: String, password: String, id: UID) -> User { + let base_tokens = vec![ + Token::new(Color::White, 2), + Token::new(Color::Red, 2), + Token::new(Color::Blue, 2), + Token::new(Color::Green, 2), + Token::new(Color::Yellow, 2), + ]; + + let hashed_password = User::hash(&password); + + User { name, hashed_password, id, tokovec: base_tokens, sessions: HashMap::new() } + } + + fn hash(t: &String) -> String { + let hashed = Sha256::digest(&t); + let hashed = base16ct::lower::encode_string(&hashed); + + hashed + } + + fn new_session(&mut self, client: String) -> String { + let hash: [u8; 32] = rand::random(); + let hash = User::hash(&hash.iter().map(|v| {format!("{}", v)}).collect()); + self.sessions.insert(hash.clone(), client); + hash + } + + fn rm_session(&mut self, session: &String) -> Result<(), String> { + match self.sessions.remove(session) { + Some(_) => Ok(()), + None => Err("Could not find sessoin".into()), + } + } + + fn clear_sessions(&mut self) { + self.sessions.clear() + } + + fn get_sessions(&mut self) -> Vec<(String, String)> { + self.sessions.iter().map(|(k, v)| { + (k.clone(), v.clone()) + }).collect() + } + + fn login(&mut self, password: &String, clientid: &String) -> Result<String, String> { + if self.hashed_password == User::hash(password) { + Ok(self.new_session(clientid.clone())) + } else { + Err("Could not login".into()) + } + } + + fn authenticate(&self, session: &String) -> bool { + match self.sessions.get(session) { + // Session authenticated/found + Some(_) => true, + // Session not found + None => false, + } + + } + + fn logout(&mut self, session: &String) -> Result<(), String> { + if self.authenticate(session) { + self.rm_session(session) + } else { + Err("Not Authenticated".into()) + } + } + + pub fn get_name(&self) -> String { + self.name.clone() + } + pub fn hex_id(&self) -> String { + self.id.to_hex() + } +} + +#[derive(Serialize, Deserialize)] +pub struct DB { + pub uid_generator: uid::Generator, + users: Vec<User>, + config: Config, +} +impl DB { + async fn save(&self) -> Result<(), String> { + let mut f = match File::create(&self.config.dbsave) { + Ok(n) => n, + Err(n) => { + match n.kind() { + std::io::ErrorKind::PermissionDenied => panic!("{:?}: Permission Denied", &self.config.dbsave), + n => return Err(n.to_string()), + } + }, + }; + let s = match serde_json::to_string(self) { + Ok(n) => n, + Err(n) => return Err(n.to_string()), + }; + match f.write(s.as_bytes()) { + Ok(_) => return Ok(()), + Err(n) => return Err(n.to_string()), + }; + } + pub fn load(c: Config) -> Self { + match File::open(&c.dbsave) { + Ok(n) => { + match serde_json::from_reader(n) { + Ok(n) => n, + Err(n) => panic!("{}", n), + } + }, + Err(n) => match n.kind() { + std::io::ErrorKind::PermissionDenied => panic!("{:?}: Permission Denied", &c.dbsave), + _ => DB::new(c), + }, + } + } + pub fn new(config: Config) -> Self { + DB { uid_generator: uid::Generator::new(), users: vec![], config } + } + + pub async fn get_user(&mut self, id: &str) -> Result<&User, String> { + match UID::from(id.to_string()) { + Ok(n) => { + for u in self.users.iter_mut() { + if u.id == n { + return Ok(u) + } + } + }, + Err(_) => (), + } + + Err("Not Found".into()) + } + pub async fn get_mut_user(&mut self, id: &str) -> Result<&mut User, String> { + match UID::from(id.to_string()) { + Ok(n) => { + for u in self.users.iter_mut() { + if u.id == n { + return Ok(u) + } + } + }, + Err(_) => (), + } + + Err("Not Found".into()) + } + + pub async fn get_user_by_name(&self, name: &str) -> Result<Vec<User>, String> { + let mut vec = vec![]; + for u in self.users.clone() { + if u.name == name { + vec.push(u) + } + } + + if vec.len() == 0 { + Err("Not Found".into()) + } else { + return Ok(vec) + } + + } + + pub async fn get_all_users(&self) -> Result<Vec<User>, String> { + if self.users.len() == 0 { + Err("No Users".into()) + } else { + Ok(self.users.clone()) + } + } + + pub async fn new_user(&mut self, name: String, password: String, id: UID) -> Result<(), String> { + let user = User::new(name, password, id); + self.users.push(user); + self.save().await + } + + pub async fn login(&mut self, id: &String, password: &String, clientid: &String) -> Result<String, String> { + let r = match self.get_mut_user(id).await { + Ok(n) => { + n.login(password, clientid) + }, + Err(n) => Err(n) + }; + + let _ = self.save().await; + r + } + pub async fn logout(&mut self, id: &String, session: &String) -> Result<(), String> { + let r = match self.get_mut_user(id).await { + Ok(n) => { + n.logout(session) + }, + Err(n) => Err(n) + }; + + let _ = self.save().await; + r + } +} diff --git a/src/main.rs b/src/main.rs index 603d714..281b63b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,5 @@ #[macro_use] extern crate rocket; -use std::{fs::File, io::Write, vec}; - use rocket::{http::Status, serde::json::Json, State}; use serde::{Deserialize, Serialize}; @@ -9,148 +7,53 @@ mod uid; use tokio::sync::Mutex; use uid::UID; -#[derive(Debug, Serialize, Deserialize, Clone, Copy)] -enum Color { - White, - Red, - Blue, - Green, - Yellow, -} +mod db; +use db::DB; +use db::User; +use db::Config; -#[derive(Debug, Serialize, Deserialize, Clone)] -struct Token { - color: Color, - amount: usize, -} -impl Token { - pub fn new(color: Color, amount: usize) -> Token { - Token { color, amount } - } +#[get("/")] +async fn index() -> &'static str { + "This is a POKO server" } -#[derive(Debug, Serialize, Deserialize, Clone)] -struct User { + +#[derive(Serialize)] +struct UserOut { name: String, - id: UID, - tokovec: Vec<Token> + id: String, } -impl User { - pub fn new(name: String, id: UID) -> User { - let base_tokens = vec![ - Token::new(Color::White, 2), - Token::new(Color::Red, 2), - Token::new(Color::Blue, 2), - Token::new(Color::Green, 2), - Token::new(Color::Yellow, 2), - ]; - User { name: name, id: id, tokovec: base_tokens } +impl Into<UserOut> for User { + fn into(self) -> UserOut { + UserOut { name: self.get_name(), id: self.hex_id() } } } -const DBSAVE: &str = "db.json"; - -#[derive(Serialize, Deserialize)] -struct DB { - uid_generator: uid::Generator, - users: Vec<User>, -} -impl DB { - async fn save(&self) -> Result<(), String> { - let mut f = match File::create(DBSAVE) { - Ok(n) => n, - Err(n) => { - match n.kind() { - std::io::ErrorKind::PermissionDenied => panic!("{}: Permission Denied", DBSAVE), - n => return Err(n.to_string()), - } - }, - }; - let s = match serde_json::to_string(self) { - Ok(n) => n, - Err(n) => return Err(n.to_string()), - }; - match f.write(s.as_bytes()) { - Ok(_) => return Ok(()), - Err(n) => return Err(n.to_string()), - }; - } - pub fn load() -> Self { - match File::open(DBSAVE) { - Ok(n) => { - match serde_json::from_reader(n) { - Ok(n) => n, - Err(n) => panic!("{}", n), - } - }, - Err(n) => match n.kind() { - std::io::ErrorKind::PermissionDenied => panic!("{}: Permission Denied", DBSAVE), - _ => DB::new(), - }, - } - } - pub fn new() -> Self { - DB { uid_generator: uid::Generator::new(), users: vec![] } - } - - async fn get_user(&self, id: &str) -> Result<User, String> { - match UID::from(id.to_string()) { - Ok(n) => { - for u in self.users.clone() { - if u.id == n { - return Ok(u) - } - } - }, - Err(_) => (), - } - - Err("Not Found".into()) - } - async fn get_user_by_name(&self, name: &str) -> Result<Vec<User>, String> { - let mut vec = vec![]; - for u in self.users.clone() { - if u.name == name { - vec.push(u) - } - } - - if vec.len() == 0 { - Err("Not Found".into()) - } else { - return Ok(vec) - } - - } - - async fn new_user(&mut self, name: String, id: UID) -> Result<(), String> { - let user = User::new(name, id); - self.users.push(user); - self.save().await - } -} - -#[get("/")] -async fn index() -> &'static str { - "This is a POKO server" -} - -#[get("/user/<user>")] -async fn get_user(user: &str, db: &State<Mutex<DB>>) -> (Status, Result<Json<User>, Json<String>>) { - let db = db.lock().await; +#[get("/<user>")] +async fn get_user(user: &str, db: &State<Mutex<DB>>) -> (Status, Result<Json<UserOut>, Json<String>>) { + let mut db = db.lock().await; match db.get_user(user).await { - Ok(n) => (Status::Ok, Ok(n.into())), + Ok(n) => (Status::Ok, Ok(Json(n.clone().into()))), Err(n) => (Status::NotFound, Err(n.into())) } } -#[get("/user/by_name/<user>")] -async fn get_users_by_name(user: &str, db: &State<Mutex<DB>>) -> (Status, Result<Json<Vec<User>>, Json<String>>) { +#[get("/by_name/<user>")] +async fn get_users_by_name(user: &str, db: &State<Mutex<DB>>) -> (Status, Result<Json<Vec<UserOut>>, Json<String>>) { let db = db.lock().await; match db.get_user_by_name(user).await { - Ok(n) => (Status::Ok, Ok(n.into())), + Ok(n) => (Status::Ok, Ok(Json(n.into_iter().map(|v| v.into()).collect()))), Err(n) => (Status::NotFound, Err(n.into())) } } +#[get("/all")] +async fn get_all_users(db: &State<Mutex<DB>>) -> (Status, Result<Json<Vec<UserOut>>, Json<String>>) { + let db = db.lock().await; + match db.get_all_users().await { + Ok(n) => (Status::Ok, Ok(Json(n.into_iter().map(|v| v.into()).collect()))), + //Nothing found + Err(n) => (Status::NotFound, Err(n.into())), + } +} fn default_id() -> String { "random".into() @@ -158,10 +61,11 @@ fn default_id() -> String { #[derive(Deserialize)] struct UserForm { name: String, + password: String, #[serde(default = "default_id")] id: String, } -#[post("/user", data="<data>", format="json")] +#[post("/register", data="<data>", format="json")] async fn new_user(data: Json<UserForm>, db: &State<Mutex<DB>>) -> (Status, Result<(), Json<String>>) { let mut db = db.lock().await; let id; @@ -198,7 +102,7 @@ async fn new_user(data: Json<UserForm>, db: &State<Mutex<DB>>) -> (Status, Resul match id { None => return (Status::InternalServerError, Err("".to_string().into())), Some(n) => { - match db.new_user(data.name.clone(), n).await { + match db.new_user(data.name.clone(), data.password.clone(), n).await { // User has been created Ok(_) => return (Status::Created, Ok(())), // Could not create user @@ -208,7 +112,38 @@ async fn new_user(data: Json<UserForm>, db: &State<Mutex<DB>>) -> (Status, Resul } } +#[derive(Deserialize)] +struct LoginForm { + id: String, + password: String, + clientid: String, +} +#[post("/", data="<data>", format="json")] +async fn login(data: Json<LoginForm>, db: &State<Mutex<DB>>) -> (Status, Json<String>) { + let mut db = db.lock().await; + match db.login(&data.id, &data.password, &data.clientid).await { + Ok(n) => (Status::Ok, n.into()), + Err(n) => (Status::Unauthorized, n.into()), + } +} + +#[derive(Deserialize)] +struct LogoutForm { + id: String, + sessionhash: String, +} +#[post("/logout", data="<data>", format="json")] +async fn logout(data: Json<LogoutForm>, db: &State<Mutex<DB>>) -> (Status, Result<(), String>) { + let mut db = db.lock().await; + match db.logout(&data.id, &data.sessionhash).await { + Ok(_) => (Status::Ok, Ok(())), + Err(n) => (Status::Unauthorized, Err(n)), + } +} + #[launch] fn rocket() -> _ { - rocket::build().manage(Mutex::new(DB::load())).mount("/", routes![index, new_user, get_user, get_users_by_name]) + rocket::build().manage(Mutex::new(DB::load(Config::new()))) + .mount("/", routes![index]) + .mount("/user", routes![login, get_users_by_name, get_user, new_user, get_all_users, logout]) }
\ No newline at end of file @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] pub struct UID(u8, u8); impl UID { - fn to_hex(&self) -> String { + pub fn to_hex(&self) -> String { hex::encode_upper(vec![self.0, self.1]) } pub fn from(s: String) -> Result<Self, String> { |