diff --git a/.env b/.env index baf06ba..eabc9e2 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -DATABASE_URL="mysql://nerdcult_api:test@localhost/NC_Accounts" \ No newline at end of file +DATABASE_URL="postgres://postgres:postgres@localhost/test-db" \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 1c4c33a..c6bc59c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ env_logger = "0.10" log = "0.4" clap = { version = "4.3.21", features = ["derive"] } actix-web-httpauth = "0.8.0" -sqlx = { version = "0.7.1", features = ["runtime-tokio", "mysql", "chrono"] } +sqlx = { version = "0.7.1", features = ["runtime-tokio", "postgres", "chrono"] } uuid = { version = "1.4.1", features = ["v4"] } chrono = "0.4" mail-send = "0.4.0" diff --git a/src/accounts.rs b/src/accounts.rs index f6f2d39..cda26f7 100644 --- a/src/accounts.rs +++ b/src/accounts.rs @@ -3,11 +3,11 @@ use pbkdf2::{ password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, Pbkdf2, }; -use sqlx::{mysql::MySqlPool, types::chrono as sqlx_chrono}; +use sqlx::{postgres::PgPool, types::chrono as sqlx_chrono}; #[derive(Debug)] pub struct Account { - pub id: u64, + pub id: i64, pub username: String, pub email: String, pub salt: String, @@ -19,7 +19,7 @@ pub struct Account { impl Account { /// This doesn't check if an account with that name is already existing! pub async fn new( - pool: &MySqlPool, + pool: &PgPool, username: &String, email: &String, password: &String, @@ -33,7 +33,7 @@ impl Account { let joined = sqlx_chrono::Utc::now().naive_utc(); sqlx::query!( - r#"INSERT INTO Accounts (username, email, salt, password, joined, verified) VALUES (?, ?, ?, ?, ?, false);"#, + r#"INSERT INTO Accounts (username, email, salt, password, joined, verified) VALUES ($1, $2, $3, $4, $5, false);"#, username, email, salt.to_string(), @@ -51,10 +51,10 @@ impl Account { } } - pub async fn from_username(pool: &MySqlPool, username: &String) -> Result> { + pub async fn from_username(pool: &PgPool, username: &String) -> Result> { match sqlx::query_as!( Self, - r#"SELECT id, username, email, salt, password, joined, verified as `verified: bool` FROM Accounts WHERE username = ?;"#, + r#"SELECT id, username, email, salt, password, joined, verified as "verified!: bool" FROM Accounts WHERE username = $1;"#, username ) .fetch_one(pool) @@ -66,10 +66,10 @@ impl Account { } } - pub async fn from_id(pool: &MySqlPool, id: u64) -> Result> { + pub async fn from_id(pool: &PgPool, id: i64) -> Result> { match sqlx::query_as!( Self, - r#"SELECT id, username, email, salt, password, joined, verified as `verified: bool` FROM Accounts WHERE id = ?;"#, + r#"SELECT id, username, email, salt, password, joined, verified as "verified!: bool" FROM Accounts WHERE id = $1;"#, id ) .fetch_one(pool) @@ -81,10 +81,10 @@ impl Account { } } - pub async fn from_email(pool: &MySqlPool, email: &String) -> Result> { + pub async fn from_email(pool: &PgPool, email: &String) -> Result> { match sqlx::query_as!( Self, - r#"SELECT id, username, email, salt, password, joined, verified as `verified: bool` FROM Accounts WHERE email = ?;"#, + r#"SELECT id, username, email, salt, password, joined, verified as "verified!: bool" FROM Accounts WHERE email = $1;"#, email ) .fetch_one(pool) diff --git a/src/api/account/handlers.rs b/src/api/account/handlers.rs index 74ab92c..4809625 100644 --- a/src/api/account/handlers.rs +++ b/src/api/account/handlers.rs @@ -6,7 +6,7 @@ use crate::{ use anyhow::Result; use log::info; use mail_send::{mail_builder::MessageBuilder, SmtpClientBuilder}; -use sqlx::MySqlPool; +use sqlx::PgPool; fn is_sql_injection(string: &String) -> bool { match libinjection::sqli(string) { @@ -26,7 +26,7 @@ impl AlphaExt for String { } pub async fn register( - pool: &MySqlPool, + pool: &PgPool, request: data::RegisterRequest, ) -> Result { if is_sql_injection(&request.username) || is_sql_injection(&request.email) { @@ -84,10 +84,7 @@ pub async fn register( Ok(data::RegisterResponse::Success) } -pub async fn verify( - pool: &MySqlPool, - request: data::VerifyRequest, -) -> Result { +pub async fn verify(pool: &PgPool, request: data::VerifyRequest) -> Result { if !request.token.is_alpha() { return Ok(data::VerifyResponse::Blocked); } @@ -103,7 +100,7 @@ pub async fn verify( } pub async fn authenticate( - pool: &MySqlPool, + pool: &PgPool, request: data::AuthenticateRequest, ) -> Result { if is_sql_injection(&request.username) { @@ -130,7 +127,7 @@ pub async fn authenticate( )) } -pub async fn delete(pool: &MySqlPool, auth: String) -> Result { +pub async fn delete(pool: &PgPool, auth: String) -> Result { if !auth.is_alpha() { return Ok(data::DeleteResponse::Blocked); } @@ -141,13 +138,13 @@ pub async fn delete(pool: &MySqlPool, auth: String) -> Result Result Result { @@ -184,7 +181,7 @@ pub async fn token_delete( Ok(data::TokenDeleteResponse::Success) } -pub async fn token_list(pool: &MySqlPool, auth: String) -> Result { +pub async fn token_list(pool: &PgPool, auth: String) -> Result { if !auth.is_alpha() { return Ok(data::TokenListResponse::Blocked); } @@ -196,7 +193,7 @@ pub async fn token_list(pool: &MySqlPool, auth: String) -> Result = sqlx::query_as!( AuthToken, - r#"SELECT * FROM AuthTokens WHERE account = ?;"#, + r#"SELECT * FROM AuthTokens WHERE account = $1;"#, token.account ) .fetch_all(pool) @@ -205,5 +202,7 @@ pub async fn token_list(pool: &MySqlPool, auth: String) -> Result Result<()> { +pub async fn start(port: u16, pool: PgPool) -> Result<()> { info!("HTTP server starting on port {} ...", port); sqlx::query!( r#" CREATE TABLE IF NOT EXISTS Accounts ( - id INT8 UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, - username VARCHAR(32) NOT NULL, - email TEXT NOT NULL, - salt VARCHAR(22) NOT NULL, - password VARCHAR(96) NOT NULL, - joined DATETIME NOT NULL, - verified BOOLEAN NOT NULL, + id SERIAL8 NOT NULL, + username VARCHAR(32) NOT NULL, + email TEXT NOT NULL, + salt VARCHAR(22) NOT NULL, + password VARCHAR(96) NOT NULL, + joined TIMESTAMP NOT NULL, + verified BOOLEAN NOT NULL, PRIMARY KEY(id) ); "# @@ -32,10 +32,10 @@ pub async fn start(port: u16, pool: MySqlPool) -> Result<()> { sqlx::query!( r#" CREATE TABLE IF NOT EXISTS AuthTokens ( - token VARCHAR(32) NOT NULL, - account INT8 UNSIGNED NOT NULL, - expire DATETIME NOT NULL - ); + token VARCHAR(32) NOT NULL, + account SERIAL8 NOT NULL, + expire TIMESTAMP NOT NULL + ); "# ) .execute(&pool) @@ -44,10 +44,10 @@ pub async fn start(port: u16, pool: MySqlPool) -> Result<()> { sqlx::query!( r#" CREATE TABLE IF NOT EXISTS VerificationTokens ( - token VARCHAR(32) NOT NULL, - account INT8 UNSIGNED NOT NULL, - expire DATETIME NOT NULL - ); + token VARCHAR(32) NOT NULL, + account SERIAL8 NOT NULL, + expire TIMESTAMP NOT NULL + ); "# ) .execute(&pool) diff --git a/src/main.rs b/src/main.rs index 5b1d372..63dcebb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ mod tokens; use anyhow::Result; use clap::Parser; use log::info; -use sqlx::mysql::MySqlPool; +use sqlx::postgres::PgPool; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -28,7 +28,7 @@ async fn main() -> Result<()> { port = p; } - let pool = MySqlPool::connect(&std::env::var("DATABASE_URL").map_err(|_| { + let pool = PgPool::connect(&std::env::var("DATABASE_URL").map_err(|_| { anyhow::Error::msg("Environment variable DATABASE_URL needs to be specified!") })?) .await?; diff --git a/src/tokens.rs b/src/tokens.rs index d2ec5b5..5fba91f 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -1,17 +1,17 @@ use anyhow::{Error, Result}; -use sqlx::{mysql::MySqlPool, types::chrono as sqlx_chrono}; +use sqlx::{postgres::PgPool, types::chrono as sqlx_chrono}; #[derive(Debug)] pub struct AuthToken { pub token: String, - pub account: u64, + pub account: i64, pub expire: sqlx_chrono::NaiveDateTime, } impl AuthToken { pub async fn new( - pool: &MySqlPool, - account_id: u64, + pool: &PgPool, + account_id: i64, lifetime: chrono::Duration, ) -> Result { let expire = match sqlx_chrono::Utc::now() @@ -28,7 +28,7 @@ impl AuthToken { let mut token = Self { token: uuid::Uuid::new_v4().simple().to_string(), - account: account_id as u64, + account: account_id, expire, }; @@ -38,7 +38,7 @@ impl AuthToken { sqlx::query!( r#" - INSERT INTO AuthTokens (token, account, expire) VALUES (?, ?, ?); + INSERT INTO AuthTokens (token, account, expire) VALUES ($1, $2, $3); "#, token.token, token.account, @@ -50,10 +50,10 @@ impl AuthToken { Ok(token) } - pub async fn check(pool: &MySqlPool, alphanumeric_token: &String) -> Result> { + pub async fn check(pool: &PgPool, alphanumeric_token: &String) -> Result> { let query_result = sqlx::query_as!( Self, - r#"SELECT * FROM AuthTokens WHERE token = ?;"#, + r#"SELECT * FROM AuthTokens WHERE token = $1;"#, alphanumeric_token ) .fetch_one(pool) @@ -73,8 +73,8 @@ impl AuthToken { } } - pub async fn delete(&self, pool: &MySqlPool) -> Result<()> { - sqlx::query!(r#"DELETE FROM AuthTokens WHERE token = ?;"#, self.token) + pub async fn delete(&self, pool: &PgPool) -> Result<()> { + sqlx::query!(r#"DELETE FROM AuthTokens WHERE token = $1;"#, self.token) .execute(pool) .await?; Ok(()) @@ -84,14 +84,14 @@ impl AuthToken { #[derive(Debug)] pub struct VerificationToken { pub token: String, - pub account: u64, + pub account: i64, pub expire: sqlx_chrono::NaiveDateTime, } impl VerificationToken { pub async fn new( - pool: &MySqlPool, - account_id: u64, + pool: &PgPool, + account_id: i64, lifetime: chrono::Duration, ) -> Result { let expire = match sqlx_chrono::Utc::now() @@ -108,7 +108,7 @@ impl VerificationToken { let mut token = Self { token: uuid::Uuid::new_v4().simple().to_string(), - account: account_id as u64, + account: account_id, expire, }; @@ -121,8 +121,8 @@ impl VerificationToken { sqlx::query!( r#" - INSERT INTO VerificationTokens (token, account, expire) VALUES (?, ?, ?); - "#, + INSERT INTO VerificationTokens (token, account, expire) VALUES ($1, $2, $3); + "#, token.token, token.account, token.expire, @@ -133,10 +133,10 @@ impl VerificationToken { Ok(token) } - pub async fn check(pool: &MySqlPool, alphanumeric_token: &String) -> Result> { + pub async fn check(pool: &PgPool, alphanumeric_token: &String) -> Result> { let query_result = sqlx::query_as!( Self, - r#"SELECT * FROM VerificationTokens WHERE token = ?;"#, + r#"SELECT * FROM VerificationTokens WHERE token = $1;"#, alphanumeric_token ) .fetch_one(pool) @@ -156,9 +156,9 @@ impl VerificationToken { } } - pub async fn delete(&self, pool: &MySqlPool) -> Result<()> { + pub async fn delete(&self, pool: &PgPool) -> Result<()> { sqlx::query!( - r#"DELETE FROM VerificationTokens WHERE token = ?;"#, + r#"DELETE FROM VerificationTokens WHERE token = $1;"#, self.token ) .execute(pool) @@ -166,11 +166,11 @@ impl VerificationToken { Ok(()) } - pub async fn apply(&self, pool: &MySqlPool) -> Result<()> { + pub async fn apply(&self, pool: &PgPool) -> Result<()> { self.delete(pool).await?; sqlx::query!( - r#"UPDATE Accounts SET verified=true WHERE id = ?;"#, + r#"UPDATE Accounts SET verified=true WHERE id = $1;"#, self.account ) .execute(pool)