aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCurly Bryce <curlybryce@protonmail.com>2024-07-03 14:33:18 -0600
committerCurly Bryce <curlybryce@protonmail.com>2024-07-03 14:33:18 -0600
commit8c68b01bb9a28ada388187c17fa9b28a115e7cc7 (patch)
tree154296044638473e53d6d00141fb2d216fd619a3 /src
parent5bd4ddc7627ae1114d15f6f0485b2cef571be1b7 (diff)
downloadpoko_server-8c68b01bb9a28ada388187c17fa9b28a115e7cc7.tar.gz
poko_server-8c68b01bb9a28ada388187c17fa9b28a115e7cc7.tar.bz2
poko_server-8c68b01bb9a28ada388187c17fa9b28a115e7cc7.zip
user login/logout
Diffstat (limited to 'src')
-rw-r--r--src/db.rs253
-rw-r--r--src/main.rs195
-rw-r--r--src/uid.rs2
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
diff --git a/src/uid.rs b/src/uid.rs
index b15f65b..fed0af0 100644
--- a/src/uid.rs
+++ b/src/uid.rs
@@ -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> {