aboutsummaryrefslogtreecommitdiff
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
parent5bd4ddc7627ae1114d15f6f0485b2cef571be1b7 (diff)
downloadpoko_server-8c68b01bb9a28ada388187c17fa9b28a115e7cc7.tar.gz
poko_server-8c68b01bb9a28ada388187c17fa9b28a115e7cc7.tar.bz2
poko_server-8c68b01bb9a28ada388187c17fa9b28a115e7cc7.zip
user login/logout
-rw-r--r--Cargo.lock73
-rw-r--r--Cargo.toml2
-rw-r--r--TODO4
-rw-r--r--src/db.rs253
-rw-r--r--src/main.rs195
-rw-r--r--src/uid.rs2
6 files changed, 396 insertions, 133 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2beae38..c5faea0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -96,6 +96,12 @@ dependencies = [
]
[[package]]
+name = "base16ct"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
+
+[[package]]
name = "binascii"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -108,6 +114,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
name = "bytemuck"
version = "1.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -143,6 +158,25 @@ dependencies = [
]
[[package]]
+name = "cpufeatures"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
name = "deranged"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -185,6 +219,16 @@ dependencies = [
]
[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -320,6 +364,16 @@ dependencies = [
]
[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -706,11 +760,13 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
name = "poko-rocket-server"
version = "0.1.0"
dependencies = [
+ "base16ct",
"hex",
"rand",
"rocket",
"serde",
"serde_json",
+ "sha2",
"tokio",
]
@@ -1026,6 +1082,17 @@ dependencies = [
]
[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1317,6 +1384,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
name = "ubyte"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 116d4fa..d9939bb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,9 +6,11 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+base16ct = { version = "0.2.0", features = ["alloc"] }
hex = "0.4.3"
rand = "0.8.5"
rocket = { version = "0.5.1", features = ["json"] }
serde = { version = "1.0.203", features = ["derive"] }
serde_json = "1.0.120"
+sha2 = "0.10.8"
tokio = "1.38.0"
diff --git a/TODO b/TODO
index 5920364..95a564f 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,6 @@
Need to make a proper http status code thingy
Deleting of users
-Cookies
Transfers
Token Values
-Token Stock Prices (Values) \ No newline at end of file
+Token Stock Prices (Values)
+Docs \ No newline at end of file
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> {