use reqwest; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] pub struct AuthInput { pub username: String, pub password: String, } #[derive(Serialize, Deserialize)] pub struct RegisterInput { pub username: String, pub email: String, pub password: String, } #[derive(Serialize, Deserialize)] pub struct AuthResponse { pub success: bool, pub message: String, } impl Default for AuthResponse { fn default() -> Self { AuthResponse { success: false, message: String::new(), } } } #[derive(Serialize, Deserialize)] pub struct HourEntryInsertInput { pub email: String, pub hours: i32, pub date_worked: String, } #[derive(Serialize, Deserialize)] pub struct HourEntryDeleteInput { pub email: String, pub id: i32, } #[derive(Serialize, Deserialize, Clone)] pub struct HourEntry { pub id: i32, pub user_id: i32, pub hours: i32, pub date_worked: String, pub date_entered: String, } impl Default for HourEntry { fn default() -> Self { HourEntry { id: 0, user_id: 0, hours: 0, date_worked: String::new(), date_entered: String::new(), } } } #[derive(Serialize, Deserialize, Clone)] pub struct UserInfoInput { pub email: String, } #[derive(Serialize, Deserialize, Clone)] pub struct UserInfoResponse { pub success: bool, pub message: String, } impl Default for UserInfoResponse { fn default() -> Self { UserInfoResponse { success: false, message: String::new(), } } } pub async fn auth(username: String, password: String, api_address: String) -> AuthResponse { // Connect to env!("WEB_API_ADDRESS")/api/auth/login and return AuthResponse let client = reqwest::Client::new(); let response = client .post(&format!("{}/api/auth/login", api_address)) .json(&AuthInput { username: username.to_string(), password: password.to_string(), }) .send() .await .unwrap(); response.json().await.unwrap() } pub async fn register( username: String, email: String, password: String, api_address: String, ) -> AuthResponse { // Connect to env!("WEB_API_ADDRESS")/api/auth/register and return AuthResponse let client = reqwest::Client::new(); let response = client .post(&format!("{}/api/auth/register", api_address)) .json(&RegisterInput { username: username.to_string(), email: email.to_string(), password: password.to_string(), }) .send() .await .unwrap(); response.json().await.unwrap() } pub async fn get_user_info(api_key: String, api_address: String) -> UserInfoResponse { // Connect to env!("WEB_API_ADDRESS")/api/user/info and return UserInfo let client = reqwest::Client::new(); let response = client .get(&format!("{}/api/user/info", api_address)) .header("Authentication", api_key) .send() .await .unwrap(); response.json().await.unwrap() } pub async fn get_hour_entries(api_key: String, api_address: String) -> Vec { // Connect to env!("WEB_API_ADDRESS")/api/hours/all and return Vec let email = get_user_info(api_key.clone(), api_address.clone()) .await .message; let client = reqwest::Client::new(); let response = client .post(&format!("{}/api/hours/all", api_address)) .header("Authentication", api_key) .json(&UserInfoInput { email: email.to_string(), }) .send() .await .unwrap(); response.json().await.unwrap() } pub async fn insert_hour_entry( hours: i32, date_worked: String, api_key: String, api_address: String, ) -> HourEntry { // Connect to env!("WEB_API_ADDRESS")/api/hours/insert and return HourEntry let email = get_user_info(api_key.clone(), api_address.clone()) .await .message; let client = reqwest::Client::new(); let response = client .post(&format!("{}/api/hours/insert", api_address)) .header("Authentication", api_key) .json(&HourEntryInsertInput { email: email.to_string(), hours: hours, date_worked: date_worked.to_string(), }) .send() .await .unwrap(); response.json().await.unwrap() } pub async fn delete_hour_entry(id: i32, api_key: String, api_address: String) { // Connect to env!("WEB_API_ADDRESS")/api/hours/delete and return () let email = get_user_info(api_key.clone(), api_address.clone()) .await .message; let client = reqwest::Client::new(); client .post(&format!("{}/api/hours/delete", api_address)) .header("Authentication", api_key) .json(&HourEntryDeleteInput { email: email.to_string(), id: id, }) .send() .await .unwrap(); } /* All the nice old things! use mysql::prelude::*; use mysql::*; use argon2::{ password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, Argon2, }; // Different exit contituons for verify_user #[derive(Debug, PartialEq, Eq)] pub enum VerifyExit { EmptyArg, Success, WrongPassword, UserNotFound, } #[derive(Debug, PartialEq, Eq, Clone)] pub struct User { pub id: i32, pub username: String, pub password: String, } impl User { pub fn new() -> User { User { id: 0, username: "".to_owned(), password: "".to_owned(), } } } // Time entry struct #[derive(Debug, PartialEq, Eq, Clone)] pub struct TimeEntry { pub id: i32, pub user_id: i32, pub hours: i32, pub date_worked: String, pub date_entered: String, } // Role struct #[derive(Debug, PartialEq, Eq, Clone)] pub struct Role { pub id: i32, pub name: String, } #[derive(Debug, PartialEq, Eq)] pub enum Roles { Member, Leader, } // UserRole struct #[derive(Debug, PartialEq, Eq, Clone)] pub struct UserRole { pub user_id: i32, pub role_id: i32, } pub struct DatabaseManager { pub pool: Pool, } impl DatabaseManager { pub fn new() -> DatabaseManager { DatabaseManager { pool: Pool::new(Opts::from_url("mysql://root:QXSrgXhclUKrC6jl37qQ5ytRVbthtyJSCvFR23ZhYvlbAGwysvqecSsg1eeLPkLP3J9YrSinM192JmcgjrIPdLAhDc5wpU1im1ocNV7oU01CWL4GYgfNdMFx9mMlKZOc@172.104.253.237:3306/tuntikirjanpito").unwrap()).unwrap(), } } pub fn init_connection(&mut self) { println!("Connected to mysql"); } pub fn register_user(&mut self, username: &str, password: &str) -> bool { if username.is_empty() || password.is_empty() { println!("Username or password is empty"); return false; } let password = password.as_bytes(); let salt = SaltString::generate(&mut OsRng); // Argon2 with default params (Argon2id v19) let argon2 = Argon2::default(); // Hash password to PHC string let password_hash = argon2.hash_password(password, &salt).unwrap().to_string(); self.pool .get_conn() .ok() .unwrap() .exec_drop( "INSERT INTO users (username, password) values (:username, :password)", params! { "username" => username, "password" => password_hash, }, ) .unwrap(); println!("User {} registered", username); return true; } pub fn verify_user(&mut self, username: &str, password: &str) -> VerifyExit { if username.is_empty() || password.is_empty() { println!("Username or password is empty"); return VerifyExit::EmptyArg; } let res = self .pool .get_conn() .ok() .unwrap() .query_first(format!( "SELECT id, username, password FROM users WHERE username='{un}'", un = username )) //Unpack Result .map(|row| { //Unpack Option row.map(|(id, username, password)| User { id: id, username: username, password: password, }) }); match res.unwrap() { Some(user) => { let parsed_hash = PasswordHash::new(&user.password); if Argon2::default() .verify_password(password.as_bytes(), &parsed_hash.unwrap()) .is_ok() { println!("Password verified"); return VerifyExit::Success; } else { println!("Password not verified"); return VerifyExit::WrongPassword; } } None => return VerifyExit::UserNotFound, } } pub fn get_user(&mut self, username: &str) -> User { let res = self .pool .get_conn() .ok() .unwrap() .query_first(format!( "SELECT id, username, password FROM users WHERE username='{un}'", un = username )) //Unpack Result .map(|row| { //Unpack Option row.map(|(id, username, password)| User { id: id, username: username, password: password, }) }); match res.unwrap() { Some(mut user) => { println!("User {} found", user.username); user.password = "".to_owned(); return user.clone(); } None => { return User { id: -1, username: "".to_owned(), password: "".to_owned(), } } } } pub fn add_time_entry(&mut self, user_id: i32, hours: i32, date_worked: &str) { self.pool .get_conn() .ok() .unwrap() .exec_drop( "INSERT INTO entries (user_id, hours, date_worked, date_entered) values (:user_id, :hours, :date_worked, CURDATE())", params! { "user_id" => user_id, "hours" => hours, "date_worked" => date_worked, }, ) .unwrap(); } pub fn delete_time_entry(&mut self, id: i32) { self.pool .get_conn() .ok() .unwrap() .exec_drop( "DELETE FROM entries WHERE id=:id", params! { "id" => id, }, ) .unwrap(); } pub fn get_time_entries(&mut self, user_id: i32) -> Vec { self.pool .get_conn() .ok() .unwrap() .query_map( format!( "SELECT id, user_id, hours, date_worked, date_entered FROM entries WHERE user_id={uid}", uid = user_id ), |(id, user_id, hours, date_worked, date_entered)| TimeEntry { id, user_id, hours, date_worked, date_entered, }, ) .unwrap() } pub fn get_roles(&mut self) -> Vec { self.pool .get_conn() .ok() .unwrap() .query_map("SELECT id, name FROM roles", |(id, name)| Role { id, name }) .unwrap() } pub fn get_user_role_pairs(&mut self) -> Vec { self.pool .get_conn() .ok() .unwrap() .query_map( "SELECT user_id, role_id FROM users_roles", |(user_id, role_id)| UserRole { user_id, role_id }, ) .unwrap() } } */