From 54dd75ca70d32b82f818c9403ea2299123384a7d Mon Sep 17 00:00:00 2001 From: Jupeyy Date: Wed, 9 Oct 2024 17:45:26 +0200 Subject: [PATCH] Add sqlite support (to game server & base-sql lib). Also add a very basic test case for sqlite in the game server. Improve rename test for game server --- .github/workflows/build.yml | 11 +- Cargo.lock | 36 +++- Cargo.toml | 7 +- lib/ddnet-account-game-server/Cargo.toml | 16 +- .../src/auto_login.rs | 2 +- .../src/auto_login/queries.rs | 23 ++- .../src/auto_login/sqlite/try_insert_user.sql | 12 ++ lib/ddnet-account-game-server/src/lib.rs | 6 + lib/ddnet-account-game-server/src/rename.rs | 2 +- .../src/rename/queries.rs | 23 ++- .../src/rename/sqlite/try_rename.sql | 6 + lib/ddnet-account-game-server/src/setup.rs | 87 +++++++--- .../src/setup/sqlite/delete/user.sql | 1 + .../src/setup/sqlite/user.sql | 8 + .../src/tests/mod.rs | 2 + .../src/tests/sqlite.rs | 81 +++++++++ lib/ddnet-account-sql/Cargo.toml | 10 +- lib/ddnet-account-sql/src/lib.rs | 3 + lib/ddnet-account-sql/src/query.rs | 28 ++- lib/ddnet-account-sql/src/version.rs | 164 ++++++++++++++---- .../src/version/sqlite/delete/version.sql | 1 + .../src/version/sqlite/get_version.sql | 6 + .../src/version/sqlite/set_version.sql | 16 ++ .../src/version/sqlite/version.sql | 10 ++ src/account_info.rs | 4 +- src/account_token.rs | 6 +- src/certs.rs | 8 +- src/credential_auth_token.rs | 6 +- src/delete.rs | 14 +- src/link_credential.rs | 14 +- src/login.rs | 36 ++-- src/logout.rs | 4 +- src/logout_all.rs | 8 +- src/sign.rs | 4 +- src/tests/game_server/rename.rs | 26 ++- src/unlink_credential.rs | 6 +- src/update.rs | 15 +- 37 files changed, 564 insertions(+), 148 deletions(-) create mode 100644 lib/ddnet-account-game-server/src/auto_login/sqlite/try_insert_user.sql create mode 100644 lib/ddnet-account-game-server/src/rename/sqlite/try_rename.sql create mode 100644 lib/ddnet-account-game-server/src/setup/sqlite/delete/user.sql create mode 100644 lib/ddnet-account-game-server/src/setup/sqlite/user.sql create mode 100644 lib/ddnet-account-game-server/src/tests/mod.rs create mode 100644 lib/ddnet-account-game-server/src/tests/sqlite.rs create mode 100644 lib/ddnet-account-sql/src/version/sqlite/delete/version.sql create mode 100644 lib/ddnet-account-sql/src/version/sqlite/get_version.sql create mode 100644 lib/ddnet-account-sql/src/version/sqlite/set_version.sql create mode 100644 lib/ddnet-account-sql/src/version/sqlite/version.sql diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 93aab47..abd5af5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,8 +48,12 @@ jobs: run: | cargo build -p ddnet-account-client cargo build -p ddnet-account-game-server + cargo build -p ddnet-account-game-server --no-default-features --features sqlite + cargo build -p ddnet-account-game-server --no-default-features --features sqlite,mysql cargo build -p ddnet-accounts-shared cargo build -p ddnet-account-sql + cargo build -p ddnet-account-sql --no-default-features --features sqlite + cargo build -p ddnet-account-sql --no-default-features --features sqlite,mysql cargo build -p ddnet-accounts-types cargo build -p ddnet-account-client-http-fs cargo build -p ddnet-account-client-reqwest @@ -63,8 +67,12 @@ jobs: run: | cargo build -p ddnet-account-client --release cargo build -p ddnet-account-game-server --release + cargo build -p ddnet-account-game-server --release --no-default-features --features sqlite + cargo build -p ddnet-account-game-server --release --no-default-features --features sqlite,mysql cargo build -p ddnet-accounts-shared --release cargo build -p ddnet-account-sql --release + cargo build -p ddnet-account-sql --release --no-default-features --features sqlite + cargo build -p ddnet-account-sql --release --no-default-features --features sqlite,mysql cargo build -p ddnet-accounts-types --release cargo build -p ddnet-account-client-http-fs --release cargo build -p ddnet-account-client-reqwest --release @@ -72,4 +80,5 @@ jobs: - name: Test release run: | - cargo test --release -- --test-threads=1 + cargo test --release -p ddnet-accounts -- --test-threads=1 + cargo test --release -p ddnet-account-game-server --features sqlite -- --test-threads=1 diff --git a/Cargo.lock b/Cargo.lock index 06d3d42..0c63dbe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -637,7 +637,7 @@ dependencies = [ [[package]] name = "ddnet-account-game-server" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "async-trait", @@ -646,11 +646,12 @@ dependencies = [ "ddnet-accounts-types", "sqlx", "thiserror", + "tokio", ] [[package]] name = "ddnet-account-sql" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "async-trait", @@ -659,7 +660,7 @@ dependencies = [ [[package]] name = "ddnet-accounts" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "argon2", @@ -1002,6 +1003,18 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec" +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "pin-project", + "spin 0.9.8", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1758,6 +1771,17 @@ dependencies = [ "redox_syscall 0.5.6", ] +[[package]] +name = "libsqlite3-sys" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -2828,6 +2852,9 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "spinning_top" @@ -2897,8 +2924,10 @@ dependencies = [ "dotenvy", "either", "event-listener", + "flume", "futures-channel", "futures-core", + "futures-executor", "futures-intrusive", "futures-util", "generic-array 0.14.7", @@ -2907,6 +2936,7 @@ dependencies = [ "indexmap 1.9.3", "itoa", "libc", + "libsqlite3-sys", "log", "memchr", "num-bigint", diff --git a/Cargo.toml b/Cargo.toml index 65bde3e..2c20e4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ members = [ [package] name = "ddnet-accounts" -version = "0.1.0" +version = "0.1.1" edition = "2021" authors = ["Jupeyy"] license = "MIT OR Apache-2.0" @@ -25,7 +25,7 @@ name = "account-server" [dependencies] ddnet-accounts-types = { version = "0.1.0", path = "lib/ddnet-accounts-types" } ddnet-accounts-shared = { version = "0.1.0", path = "lib/ddnet-accounts-shared" } -ddnet-account-sql = { version = "0.1.0", path = "lib/ddnet-account-sql" } +ddnet-account-sql = { version = "0.2.0", path = "lib/ddnet-account-sql", features = ["mysql"] } tokio = { version = "1.39.3", features = ["rt-multi-thread", "sync", "fs", "time", "macros"] } axum = "0.7.5" @@ -64,10 +64,9 @@ notify = { version = "6.1.1", default-features = false, features = ["macos_kqueu [dev-dependencies] ddnet-account-client = { version = "0.1.0", path = "lib/ddnet-account-client" } -ddnet-account-game-server = { version = "0.1.0", path = "lib/ddnet-account-game-server" } +ddnet-account-game-server = { version = "0.2.0", path = "lib/ddnet-account-game-server" } ddnet-account-client-http-fs = { version = "0.1.0", path = "lib/ddnet-account-client-http-fs" } ddnet-account-client-reqwest = { version = "0.1.0", path = "lib/ddnet-account-client-reqwest" } regex = "1.10.6" tempfile = "3.12.0" - diff --git a/lib/ddnet-account-game-server/Cargo.toml b/lib/ddnet-account-game-server/Cargo.toml index 6f2a6f8..a8d2fe3 100644 --- a/lib/ddnet-account-game-server/Cargo.toml +++ b/lib/ddnet-account-game-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ddnet-account-game-server" -version = "0.1.0" +version = "0.2.0" edition = "2021" authors = ["Jupeyy"] license = "MIT OR Apache-2.0" @@ -10,10 +10,20 @@ repository = "https://github.com/ddnet/ddnet-accounts" [dependencies] ddnet-accounts-types = { version = "0.1.0", path = "../ddnet-accounts-types" } ddnet-accounts-shared = { version = "0.1.0", path = "../ddnet-accounts-shared" } -ddnet-account-sql = { version = "0.1.0", path = "../ddnet-account-sql" } +ddnet-account-sql = { version = "0.2.0", path = "../ddnet-account-sql", default-features = false } # https://github.com/launchbadge/sqlx/issues/2636 -sqlx = { version = "=0.6.3", features = ["mysql", "any", "runtime-tokio-rustls", "chrono"] } +sqlx = { version = "=0.6.3", features = ["any", "runtime-tokio-rustls", "chrono"] } anyhow = { version = "1.0.86", features = ["backtrace"] } async-trait = "0.1.81" thiserror = "1.0.63" + +[dev-dependencies] +tokio = { version = "1.39.3", features = ["rt-multi-thread", "sync", "fs", "time", "macros"] } +anyhow = { version = "1.0.86", features = ["backtrace"] } + +[features] +mysql = ["ddnet-account-sql/mysql", "sqlx/mysql"] +sqlite = ["ddnet-account-sql/sqlite", "sqlx/sqlite"] + +default = ["mysql"] diff --git a/lib/ddnet-account-game-server/src/auto_login.rs b/lib/ddnet-account-game-server/src/auto_login.rs index 6bbd56b..7801337 100644 --- a/lib/ddnet-account-game-server/src/auto_login.rs +++ b/lib/ddnet-account-game-server/src/auto_login.rs @@ -62,7 +62,7 @@ pub async fn auto_login( }; let res = qry - .query(&shared.db.register_user_statement) + .query(con, &shared.db.register_user_statement) .execute(&mut *con) .await .map_err(|err| AutoLoginError::Database(err.into()))?; diff --git a/lib/ddnet-account-game-server/src/auto_login/queries.rs b/lib/ddnet-account-game-server/src/auto_login/queries.rs index e749b48..bf4234f 100644 --- a/lib/ddnet-account-game-server/src/auto_login/queries.rs +++ b/lib/ddnet-account-game-server/src/auto_login/queries.rs @@ -1,7 +1,7 @@ -use ddnet_account_sql::query::Query; -use ddnet_accounts_types::account_id::AccountId; use anyhow::anyhow; use async_trait::async_trait; +use ddnet_account_sql::query::Query; +use ddnet_accounts_types::account_id::AccountId; use sqlx::any::AnyRow; use sqlx::Executor; use sqlx::Statement; @@ -18,6 +18,7 @@ pub struct RegisterUser<'a> { #[async_trait] impl<'a> Query<()> for RegisterUser<'a> { + #[cfg(feature = "mysql")] async fn prepare_mysql( connection: &mut sqlx::AnyConnection, ) -> anyhow::Result> { @@ -25,6 +26,15 @@ impl<'a> Query<()> for RegisterUser<'a> { .prepare(include_str!("mysql/try_insert_user.sql")) .await?) } + #[cfg(feature = "sqlite")] + async fn prepare_sqlite( + connection: &mut sqlx::AnyConnection, + ) -> anyhow::Result> { + Ok(connection + .prepare(include_str!("sqlite/try_insert_user.sql")) + .await?) + } + #[cfg(feature = "mysql")] fn query_mysql<'b>( &'b self, statement: &'b sqlx::any::AnyStatement<'static>, @@ -33,6 +43,15 @@ impl<'a> Query<()> for RegisterUser<'a> { statement.query().bind(self.default_name).bind(account_id) } + #[cfg(feature = "sqlite")] + fn query_sqlite<'b>( + &'b self, + statement: &'b sqlx::any::AnyStatement<'static>, + ) -> sqlx::query::Query<'b, sqlx::Any, sqlx::any::AnyArguments<'b>> { + let account_id = self.account_id; + + statement.query().bind(self.default_name).bind(account_id) + } fn row_data(_row: &AnyRow) -> anyhow::Result<()> { Err(anyhow!( "Data rows are not supported for this query. diff --git a/lib/ddnet-account-game-server/src/auto_login/sqlite/try_insert_user.sql b/lib/ddnet-account-game-server/src/auto_login/sqlite/try_insert_user.sql new file mode 100644 index 0000000..f479977 --- /dev/null +++ b/lib/ddnet-account-game-server/src/auto_login/sqlite/try_insert_user.sql @@ -0,0 +1,12 @@ +INSERT + OR IGNORE INTO user ( + name, + account_id, + create_time + ) +VALUES + ( + ?, + ?, + datetime('now') + ); diff --git a/lib/ddnet-account-game-server/src/lib.rs b/lib/ddnet-account-game-server/src/lib.rs index d8ede43..0efde85 100644 --- a/lib/ddnet-account-game-server/src/lib.rs +++ b/lib/ddnet-account-game-server/src/lib.rs @@ -11,6 +11,9 @@ #![deny(clippy::nursery)] #![deny(clippy::all)] +#[cfg(not(any(feature = "mysql", feature = "sqlite")))] +std::compile_error!("at least the mysql or sqlite feature must be used."); + /// Data types and operations related to /// logging in a user to the game server. pub mod auto_login; @@ -27,3 +30,6 @@ pub mod setup; /// Shared data that is used in the game /// server implementation. pub mod shared; + +#[cfg(test)] +mod tests; diff --git a/lib/ddnet-account-game-server/src/rename.rs b/lib/ddnet-account-game-server/src/rename.rs index 12de151..18668de 100644 --- a/lib/ddnet-account-game-server/src/rename.rs +++ b/lib/ddnet-account-game-server/src/rename.rs @@ -73,7 +73,7 @@ pub async fn rename( let qry = RenameUser { account_id, name }; let res = qry - .query(&shared.db.try_rename_statement) + .query(con, &shared.db.try_rename_statement) .execute(&mut *con) .await; diff --git a/lib/ddnet-account-game-server/src/rename/queries.rs b/lib/ddnet-account-game-server/src/rename/queries.rs index fa58ce0..c012ef4 100644 --- a/lib/ddnet-account-game-server/src/rename/queries.rs +++ b/lib/ddnet-account-game-server/src/rename/queries.rs @@ -1,7 +1,7 @@ -use ddnet_account_sql::query::Query; -use ddnet_accounts_types::account_id::AccountId; use anyhow::anyhow; use async_trait::async_trait; +use ddnet_account_sql::query::Query; +use ddnet_accounts_types::account_id::AccountId; use sqlx::any::AnyRow; use sqlx::Executor; use sqlx::Statement; @@ -18,6 +18,7 @@ pub struct RenameUser<'a> { #[async_trait] impl<'a> Query<()> for RenameUser<'a> { + #[cfg(feature = "mysql")] async fn prepare_mysql( connection: &mut sqlx::AnyConnection, ) -> anyhow::Result> { @@ -25,6 +26,15 @@ impl<'a> Query<()> for RenameUser<'a> { .prepare(include_str!("mysql/try_rename.sql")) .await?) } + #[cfg(feature = "sqlite")] + async fn prepare_sqlite( + connection: &mut sqlx::AnyConnection, + ) -> anyhow::Result> { + Ok(connection + .prepare(include_str!("sqlite/try_rename.sql")) + .await?) + } + #[cfg(feature = "mysql")] fn query_mysql<'b>( &'b self, statement: &'b sqlx::any::AnyStatement<'static>, @@ -33,6 +43,15 @@ impl<'a> Query<()> for RenameUser<'a> { statement.query().bind(self.name).bind(account_id) } + #[cfg(feature = "sqlite")] + fn query_sqlite<'b>( + &'b self, + statement: &'b sqlx::any::AnyStatement<'static>, + ) -> sqlx::query::Query<'b, sqlx::Any, sqlx::any::AnyArguments<'b>> { + let account_id = self.account_id; + + statement.query().bind(self.name).bind(account_id) + } fn row_data(_row: &AnyRow) -> anyhow::Result<()> { Err(anyhow!( "Data rows are not supported for this query. diff --git a/lib/ddnet-account-game-server/src/rename/sqlite/try_rename.sql b/lib/ddnet-account-game-server/src/rename/sqlite/try_rename.sql new file mode 100644 index 0000000..22e33bc --- /dev/null +++ b/lib/ddnet-account-game-server/src/rename/sqlite/try_rename.sql @@ -0,0 +1,6 @@ +UPDATE + user +SET + name = ? +WHERE + user.account_id = ?; diff --git a/lib/ddnet-account-game-server/src/setup.rs b/lib/ddnet-account-game-server/src/setup.rs index 64de3ea..8cb0cb1 100644 --- a/lib/ddnet-account-game-server/src/setup.rs +++ b/lib/ddnet-account-game-server/src/setup.rs @@ -8,9 +8,12 @@ use sqlx::Statement; const VERSION_NAME: &str = "account-game-server"; -async fn setup_version1_mysql(con: &mut AnyConnection) -> anyhow::Result<()> { +async fn setup_version1_generic( + con: &mut AnyConnection, + user_sql: &'static str, +) -> anyhow::Result<()> { // first create all statements (syntax check) - let user = con.prepare(include_str!("setup/mysql/user.sql")).await?; + let user = con.prepare(user_sql).await?; // afterwards actually create tables user.query().execute(&mut *con).await?; @@ -20,13 +23,61 @@ async fn setup_version1_mysql(con: &mut AnyConnection) -> anyhow::Result<()> { Ok(()) } +async fn delete_generic(pool: &sqlx::AnyPool, delete_sql: &'static str) -> anyhow::Result<()> { + let mut pool_con = pool.acquire().await?; + let con = pool_con.acquire().await?; + + // first create all statements (syntax check) + // delete in reverse order to creating + let user = con.prepare(delete_sql).await?; + + // afterwards actually drop tables + let user = user.query().execute(&mut *con).await; + + let _ = set_version(con, VERSION_NAME, 0).await; + + // handle errors at once + user?; + + Ok(()) +} + +#[cfg(feature = "mysql")] +mod mysql { + use sqlx::AnyConnection; + + pub(super) async fn setup_version1(con: &mut AnyConnection) -> anyhow::Result<()> { + super::setup_version1_generic(con, include_str!("setup/mysql/user.sql")).await + } + + pub(super) async fn delete(pool: &sqlx::AnyPool) -> anyhow::Result<()> { + super::delete_generic(pool, include_str!("setup/mysql/delete/user.sql")).await + } +} + +#[cfg(feature = "sqlite")] +mod sqlite { + use sqlx::AnyConnection; + + pub(super) async fn setup_version1(con: &mut AnyConnection) -> anyhow::Result<()> { + super::setup_version1_generic(con, include_str!("setup/sqlite/user.sql")).await + } + + pub(super) async fn delete(pool: &sqlx::AnyPool) -> anyhow::Result<()> { + super::delete_generic(pool, include_str!("setup/sqlite/delete/user.sql")).await + } +} + async fn setup_version1(con: &mut AnyConnection) -> anyhow::Result<()> { match con.kind() { - sqlx::any::AnyKind::MySql => setup_version1_mysql(con).await, + #[cfg(feature = "mysql")] + sqlx::any::AnyKind::MySql => mysql::setup_version1(con).await, + #[cfg(feature = "sqlite")] + sqlx::any::AnyKind::Sqlite => sqlite::setup_version1(con).await, } } -/// Sets up all mysql tables required for a game server user +/// Sets up all tables required for a game server user pub async fn setup(pool: &sqlx::AnyPool) -> anyhow::Result<()> { let mut pool_con = pool.acquire().await?; let con = pool_con.acquire().await?; @@ -44,30 +95,12 @@ pub async fn setup(pool: &sqlx::AnyPool) -> anyhow::Result<()> { .await } -async fn delete_mysql(pool: &sqlx::AnyPool) -> anyhow::Result<()> { - let mut pool_con = pool.acquire().await?; - let con = pool_con.acquire().await?; - - // first create all statements (syntax check) - // delete in reverse order to creating - let user = con - .prepare(include_str!("setup/mysql/delete/user.sql")) - .await?; - - // afterwards actually drop tables - let user = user.query().execute(&mut *con).await; - - let _ = set_version(con, VERSION_NAME, 0).await; - - // handle errors at once - user?; - - Ok(()) -} - -/// Drop all tables related to a game server mysql setup +/// Drop all tables related to a game server database setup pub async fn delete(pool: &sqlx::AnyPool) -> anyhow::Result<()> { match pool.any_kind() { - sqlx::any::AnyKind::MySql => delete_mysql(pool).await, + #[cfg(feature = "mysql")] + sqlx::any::AnyKind::MySql => mysql::delete(pool).await, + #[cfg(feature = "sqlite")] + sqlx::any::AnyKind::Sqlite => sqlite::delete(pool).await, } } diff --git a/lib/ddnet-account-game-server/src/setup/sqlite/delete/user.sql b/lib/ddnet-account-game-server/src/setup/sqlite/delete/user.sql new file mode 100644 index 0000000..94dc7a2 --- /dev/null +++ b/lib/ddnet-account-game-server/src/setup/sqlite/delete/user.sql @@ -0,0 +1 @@ +DROP TABLE user; diff --git a/lib/ddnet-account-game-server/src/setup/sqlite/user.sql b/lib/ddnet-account-game-server/src/setup/sqlite/user.sql new file mode 100644 index 0000000..e4d6d21 --- /dev/null +++ b/lib/ddnet-account-game-server/src/setup/sqlite/user.sql @@ -0,0 +1,8 @@ +CREATE TABLE user ( + id INTEGER AUTO_INCREMENT, + name VARCHAR(32) NOT NULL COLLATE BINARY UNIQUE, + account_id BIGINT NOT NULL UNIQUE, + -- UTC timestamp! (datetime('now')) + create_time DATETIME NOT NULL, + PRIMARY KEY(id) +); diff --git a/lib/ddnet-account-game-server/src/tests/mod.rs b/lib/ddnet-account-game-server/src/tests/mod.rs new file mode 100644 index 0000000..e6abbb3 --- /dev/null +++ b/lib/ddnet-account-game-server/src/tests/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "sqlite")] +pub mod sqlite; diff --git a/lib/ddnet-account-game-server/src/tests/sqlite.rs b/lib/ddnet-account-game-server/src/tests/sqlite.rs new file mode 100644 index 0000000..bae0d51 --- /dev/null +++ b/lib/ddnet-account-game-server/src/tests/sqlite.rs @@ -0,0 +1,81 @@ +use ddnet_accounts_shared::game_server::user_id::UserId; +use sqlx::{any::AnyPoolOptions, sqlite::SqliteConnectOptions}; + +#[tokio::test] +pub async fn sqlite() -> anyhow::Result<()> { + // ignore old test runs + let _ = tokio::fs::remove_file(DB_FILE).await; + const DB_FILE: &str = "test-db.sqlite"; + + let pool = AnyPoolOptions::new() + .max_connections(10) + .connect_with( + SqliteConnectOptions::new() + .filename(DB_FILE) + .create_if_missing(true) + .into(), + ) + .await?; + + // setup + crate::setup::setup(&pool).await?; + + let shared = crate::prepare::prepare(&pool).await?; + + // acc created + assert!( + crate::auto_login::auto_login( + shared.clone(), + &pool, + &UserId { + account_id: Some(0), + public_key: Default::default(), + }, + ) + .await? + ); + + // no acc created + assert!( + !crate::auto_login::auto_login( + shared.clone(), + &pool, + &UserId { + account_id: Some(0), + public_key: Default::default(), + }, + ) + .await? + ); + + // rename working + crate::rename::rename( + shared.clone(), + &pool, + &UserId { + account_id: Some(0), + public_key: Default::default(), + }, + "my_new_name", + ) + .await?; + + // rename exactly 32 characters + crate::rename::rename( + shared.clone(), + &pool, + &UserId { + account_id: Some(0), + public_key: Default::default(), + }, + "01234567890123456789012345678901", + ) + .await?; + + // delete + crate::setup::delete(&pool).await?; + + tokio::fs::remove_file(DB_FILE).await?; + + Ok(()) +} diff --git a/lib/ddnet-account-sql/Cargo.toml b/lib/ddnet-account-sql/Cargo.toml index c118840..1b5b476 100644 --- a/lib/ddnet-account-sql/Cargo.toml +++ b/lib/ddnet-account-sql/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ddnet-account-sql" -version = "0.1.0" +version = "0.2.0" edition = "2021" authors = ["Jupeyy"] license = "MIT OR Apache-2.0" @@ -9,6 +9,12 @@ repository = "https://github.com/ddnet/ddnet-accounts" [dependencies] # https://github.com/launchbadge/sqlx/issues/2636 -sqlx = { version = "=0.6.3", features = ["mysql", "any", "runtime-tokio-rustls", "chrono"] } +sqlx = { version = "=0.6.3", features = ["any", "runtime-tokio-rustls", "chrono"] } async-trait = "0.1.81" anyhow = { version = "1.0.86", features = ["backtrace"] } + +[features] +mysql = ["sqlx/mysql"] +sqlite = ["sqlx/sqlite"] + +default = ["mysql"] diff --git a/lib/ddnet-account-sql/src/lib.rs b/lib/ddnet-account-sql/src/lib.rs index 2604bac..58e1b31 100644 --- a/lib/ddnet-account-sql/src/lib.rs +++ b/lib/ddnet-account-sql/src/lib.rs @@ -6,6 +6,9 @@ #![deny(clippy::nursery)] #![deny(clippy::all)] +#[cfg(not(any(feature = "mysql", feature = "sqlite")))] +std::compile_error!("at least the mysql or sqlite feature must be used."); + use sqlx::{any::AnyQueryResult, Error}; /// Everything related to queries diff --git a/lib/ddnet-account-sql/src/query.rs b/lib/ddnet-account-sql/src/query.rs index 7904c02..b0ade2a 100644 --- a/lib/ddnet-account-sql/src/query.rs +++ b/lib/ddnet-account-sql/src/query.rs @@ -5,32 +5,56 @@ use sqlx::any::{AnyKind, AnyRow}; #[async_trait] pub trait Query { /// MySQL version of [`Query::prepare`]. + #[cfg(feature = "mysql")] async fn prepare_mysql( connection: &mut sqlx::AnyConnection, ) -> anyhow::Result>; + /// Sqlite version of [`Query::prepare`]. + #[cfg(feature = "sqlite")] + async fn prepare_sqlite( + connection: &mut sqlx::AnyConnection, + ) -> anyhow::Result>; + /// Prepare a statement to be later used by [`Query::query`]. async fn prepare( connection: &mut sqlx::AnyConnection, ) -> anyhow::Result> { match connection.kind() { + #[cfg(feature = "mysql")] AnyKind::MySql => Self::prepare_mysql(connection).await, + #[cfg(feature = "sqlite")] + AnyKind::Sqlite => Self::prepare_sqlite(connection).await, //_ => Err(anyhow!("database backend not implemented.")), } } - /// Get a query with all arguments bound already, ready to be fetched. + /// MySQL version of [`Query::query`]. + #[cfg(feature = "mysql")] fn query_mysql<'a>( &'a self, statement: &'a sqlx::any::AnyStatement<'static>, ) -> sqlx::query::Query<'a, sqlx::Any, sqlx::any::AnyArguments<'a>>; + /// Sqlite version of [`Query::query`]. + #[cfg(feature = "sqlite")] + fn query_sqlite<'a>( + &'a self, + statement: &'a sqlx::any::AnyStatement<'static>, + ) -> sqlx::query::Query<'a, sqlx::Any, sqlx::any::AnyArguments<'a>>; + /// Get a query with all arguments bound already, ready to be fetched. fn query<'a>( &'a self, + connection: &sqlx::AnyConnection, statement: &'a sqlx::any::AnyStatement<'static>, ) -> sqlx::query::Query<'a, sqlx::Any, sqlx::any::AnyArguments<'a>> { - self.query_mysql(statement) + match connection.kind() { + #[cfg(feature = "mysql")] + AnyKind::MySql => self.query_mysql(statement), + #[cfg(feature = "sqlite")] + AnyKind::Sqlite => self.query_sqlite(statement), + } } /// Gets the row data for a result row of this query diff --git a/lib/ddnet-account-sql/src/version.rs b/lib/ddnet-account-sql/src/version.rs index 994231b..6fdb65b 100644 --- a/lib/ddnet-account-sql/src/version.rs +++ b/lib/ddnet-account-sql/src/version.rs @@ -4,11 +4,9 @@ use sqlx::Executor; use sqlx::Row; use sqlx::Statement; -async fn try_setup_mysql(con: &mut AnyConnection) -> anyhow::Result<()> { +async fn try_setup_generic(con: &mut AnyConnection, sql: &'static str) -> anyhow::Result<()> { // first create all statements (syntax check) - let version = con - .prepare(include_str!("version/mysql/version.sql")) - .await?; + let version = con.prepare(sql).await?; // afterwards actually create tables version.query().execute(&mut *con).await?; @@ -16,14 +14,15 @@ async fn try_setup_mysql(con: &mut AnyConnection) -> anyhow::Result<()> { Ok(()) } -async fn get_or_set_version_mysql(con: &mut AnyConnection, name: &str) -> anyhow::Result { +async fn get_or_set_version_generic( + con: &mut AnyConnection, + name: &str, + get_version_sql: &'static str, + set_version_sql: &'static str, +) -> anyhow::Result { // first create all statements (syntax check) - let get_version = con - .prepare(include_str!("version/mysql/get_version.sql")) - .await?; - let set_version = con - .prepare(include_str!("version/mysql/set_version.sql")) - .await?; + let get_version = con.prepare(get_version_sql).await?; + let set_version = con.prepare(set_version_sql).await?; let name = name.to_string(); @@ -48,15 +47,14 @@ async fn get_or_set_version_mysql(con: &mut AnyConnection, name: &str) -> anyhow } } -async fn set_version_mysql( +async fn set_version_generic( con: &mut AnyConnection, name: &str, version: i64, + set_version_sql: &'static str, ) -> anyhow::Result<()> { // first create all statements (syntax check) - let set_version = con - .prepare(include_str!("version/mysql/set_version.sql")) - .await?; + let set_version = con.prepare(set_version_sql).await?; Ok(set_version .query() @@ -68,6 +66,102 @@ async fn set_version_mysql( .map(|_| ())?) } +async fn delete_genric(pool: &sqlx::AnyPool, delete_sql: &'static str) -> anyhow::Result<()> { + let mut pool_con = pool.acquire().await?; + let con = pool_con.acquire().await?; + + // first create all statements (syntax check) + // delete in reverse order to creating + let version = con.prepare(delete_sql).await?; + + // afterwards actually drop tables + let version = version.query().execute(&mut *con).await; + + // handle errors at once + version?; + + Ok(()) +} + +#[cfg(feature = "mysql")] +mod mysql { + use sqlx::AnyConnection; + + pub(super) async fn try_setup(con: &mut AnyConnection) -> anyhow::Result<()> { + super::try_setup_generic(con, include_str!("version/mysql/version.sql")).await + } + + pub(super) async fn get_or_set_version( + con: &mut AnyConnection, + name: &str, + ) -> anyhow::Result { + super::get_or_set_version_generic( + con, + name, + include_str!("version/mysql/get_version.sql"), + include_str!("version/mysql/set_version.sql"), + ) + .await + } + + pub(super) async fn set_version( + con: &mut AnyConnection, + name: &str, + version: i64, + ) -> anyhow::Result<()> { + super::set_version_generic( + con, + name, + version, + include_str!("version/mysql/set_version.sql"), + ) + .await + } + + pub(super) async fn delete(pool: &sqlx::AnyPool) -> anyhow::Result<()> { + super::delete_genric(pool, include_str!("version/mysql/delete/version.sql")).await + } +} + +#[cfg(feature = "sqlite")] +mod sqlite { + use sqlx::AnyConnection; + pub(super) async fn try_setup(con: &mut AnyConnection) -> anyhow::Result<()> { + super::try_setup_generic(con, include_str!("version/sqlite/version.sql")).await + } + + pub(super) async fn get_or_set_version( + con: &mut AnyConnection, + name: &str, + ) -> anyhow::Result { + super::get_or_set_version_generic( + con, + name, + include_str!("version/sqlite/get_version.sql"), + include_str!("version/sqlite/set_version.sql"), + ) + .await + } + + pub(super) async fn set_versione( + con: &mut AnyConnection, + name: &str, + version: i64, + ) -> anyhow::Result<()> { + super::set_version_generic( + con, + name, + version, + include_str!("version/sqlite/set_version.sql"), + ) + .await + } + + pub(super) async fn delete(pool: &sqlx::AnyPool) -> anyhow::Result<()> { + super::delete_genric(pool, include_str!("version/sqlite/delete/version.sql")).await + } +} + /// Use this function to obtain the current version number. /// /// If the version table does not exist, sets up the version table. @@ -75,10 +169,17 @@ async fn set_version_mysql( /// version, without manually doing it by hand. pub async fn get_version(con: &mut AnyConnection, name: &str) -> anyhow::Result { match con.kind() { + #[cfg(feature = "mysql")] sqlx::any::AnyKind::MySql => { // try setup - try_setup_mysql(con).await?; - get_or_set_version_mysql(con, name).await + mysql::try_setup(con).await?; + mysql::get_or_set_version(con, name).await + } + #[cfg(feature = "sqlite")] + sqlx::any::AnyKind::Sqlite => { + // try setup + sqlite::try_setup(con).await?; + sqlite::get_or_set_version(con, name).await } } } @@ -87,33 +188,20 @@ pub async fn get_version(con: &mut AnyConnection, name: &str) -> anyhow::Result< /// This can (and should) be called inside a transaction pub async fn set_version(con: &mut AnyConnection, name: &str, version: i64) -> anyhow::Result<()> { match con.kind() { - sqlx::any::AnyKind::MySql => set_version_mysql(con, name, version).await, + #[cfg(feature = "mysql")] + sqlx::any::AnyKind::MySql => mysql::set_version(con, name, version).await, + #[cfg(feature = "sqlite")] + sqlx::any::AnyKind::Sqlite => sqlite::set_versione(con, name, version).await, } } -async fn delete_mysql(pool: &sqlx::AnyPool) -> anyhow::Result<()> { - let mut pool_con = pool.acquire().await?; - let con = pool_con.acquire().await?; - - // first create all statements (syntax check) - // delete in reverse order to creating - let version = con - .prepare(include_str!("version/mysql/delete/version.sql")) - .await?; - - // afterwards actually drop tables - let version = version.query().execute(&mut *con).await; - - // handle errors at once - version?; - - Ok(()) -} - /// Drop the version table... /// Warning: This is usually not recommended. pub async fn delete(pool: &sqlx::AnyPool) -> anyhow::Result<()> { match pool.any_kind() { - sqlx::any::AnyKind::MySql => delete_mysql(pool).await, + #[cfg(feature = "mysql")] + sqlx::any::AnyKind::MySql => mysql::delete(pool).await, + #[cfg(feature = "sqlite")] + sqlx::any::AnyKind::Sqlite => sqlite::delete(pool).await, } } diff --git a/lib/ddnet-account-sql/src/version/sqlite/delete/version.sql b/lib/ddnet-account-sql/src/version/sqlite/delete/version.sql new file mode 100644 index 0000000..ad81657 --- /dev/null +++ b/lib/ddnet-account-sql/src/version/sqlite/delete/version.sql @@ -0,0 +1 @@ +DROP TABLE table_infra_version; diff --git a/lib/ddnet-account-sql/src/version/sqlite/get_version.sql b/lib/ddnet-account-sql/src/version/sqlite/get_version.sql new file mode 100644 index 0000000..b424583 --- /dev/null +++ b/lib/ddnet-account-sql/src/version/sqlite/get_version.sql @@ -0,0 +1,6 @@ +SELECT + table_infra_version.version +FROM + table_infra_version +WHERE + table_infra_version.name = ?; diff --git a/lib/ddnet-account-sql/src/version/sqlite/set_version.sql b/lib/ddnet-account-sql/src/version/sqlite/set_version.sql new file mode 100644 index 0000000..ae7e645 --- /dev/null +++ b/lib/ddnet-account-sql/src/version/sqlite/set_version.sql @@ -0,0 +1,16 @@ +INSERT INTO + table_infra_version ( + name, + version, + last_update_time + ) +VALUES + ( + ?, + ?, + datetime('now') + ) ON CONFLICT(name) DO +UPDATE +SET + version = ?, + last_update_time = datetime('now'); diff --git a/lib/ddnet-account-sql/src/version/sqlite/version.sql b/lib/ddnet-account-sql/src/version/sqlite/version.sql new file mode 100644 index 0000000..dd55dae --- /dev/null +++ b/lib/ddnet-account-sql/src/version/sqlite/version.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS table_infra_version ( + id INTEGER AUTO_INCREMENT, + -- the group name of the tables to be created + name VARCHAR(64) NOT NULL COLLATE BINARY UNIQUE, + version INTEGER NOT NULL, + -- UTC timestamp! (datetime('now')) of the + -- last time this table was update + last_update_time DATETIME NOT NULL, + PRIMARY KEY(id) +); diff --git a/src/account_info.rs b/src/account_info.rs index 1344be3..8fa6215 100644 --- a/src/account_info.rs +++ b/src/account_info.rs @@ -2,6 +2,7 @@ pub mod queries; use std::{str::FromStr, sync::Arc}; +use axum::Json; use ddnet_account_sql::query::Query; use ddnet_accounts_shared::{ account_server::{ @@ -11,7 +12,6 @@ use ddnet_accounts_shared::{ }, client::account_info::AccountInfoRequest, }; -use axum::Json; use queries::AccountInfo; use sqlx::{Acquire, AnyPool}; @@ -56,7 +56,7 @@ pub async fn account_info( }; let row = qry - .query(&shared.db.account_info) + .query(connection, &shared.db.account_info) .fetch_one(connection) .await?; diff --git a/src/account_token.rs b/src/account_token.rs index 19ff643..98e949e 100644 --- a/src/account_token.rs +++ b/src/account_token.rs @@ -2,6 +2,7 @@ pub mod queries; use std::sync::Arc; +use axum::Json; use ddnet_account_sql::query::Query; use ddnet_accounts_shared::{ account_server::{ @@ -12,7 +13,6 @@ use ddnet_accounts_shared::{ AccountTokenEmailRequest, AccountTokenOperation, AccountTokenSteamRequest, }, }; -use axum::Json; use queries::{AddAccountTokenEmail, AddAccountTokenSteam}; use sqlx::{Acquire, AnyPool}; @@ -78,7 +78,7 @@ pub async fn account_token_email_impl( let con = connection.acquire().await?; let account_token_res = query_add_account_token - .query(&shared.db.account_token_email_statement) + .query(con, &shared.db.account_token_email_statement) .execute(&mut *con) .await?; anyhow::ensure!( @@ -151,7 +151,7 @@ pub async fn account_token_steam_impl( let con = connection.acquire().await?; let account_token_res = query_add_account_token - .query(&shared.db.account_token_steam_statement) + .query(con, &shared.db.account_token_steam_statement) .execute(&mut *con) .await?; anyhow::ensure!( diff --git a/src/certs.rs b/src/certs.rs index 7526395..76cf91d 100644 --- a/src/certs.rs +++ b/src/certs.rs @@ -2,13 +2,13 @@ pub mod queries; use std::{str::FromStr, sync::Arc, time::Duration}; +use anyhow::anyhow; +use axum::Json; use ddnet_account_sql::query::Query; use ddnet_accounts_shared::account_server::{ errors::{AccountServerRequestError, Empty}, result::AccountServerReqResult, }; -use anyhow::anyhow; -use axum::Json; use der::{Decode, Encode}; use p256::ecdsa::{DerSignature, SigningKey}; use queries::{AddCert, GetCerts}; @@ -148,7 +148,7 @@ pub async fn store_cert( let connection = connection.acquire().await?; let res = connection - .execute(qry.query(&db.add_cert_statement)) + .execute(qry.query(connection, &db.add_cert_statement)) .await?; anyhow::ensure!(res.rows_affected() >= 1); @@ -165,7 +165,7 @@ pub async fn get_certs( let connection = connection.acquire().await?; let cert_rows = connection - .fetch_all(qry.query(&db.get_certs_statement)) + .fetch_all(qry.query(connection, &db.get_certs_statement)) .await?; cert_rows diff --git a/src/credential_auth_token.rs b/src/credential_auth_token.rs index 0e49748..bfdd315 100644 --- a/src/credential_auth_token.rs +++ b/src/credential_auth_token.rs @@ -2,6 +2,7 @@ pub mod queries; use std::sync::Arc; +use axum::Json; use ddnet_account_sql::query::Query; use ddnet_accounts_shared::{ account_server::{ @@ -13,7 +14,6 @@ use ddnet_accounts_shared::{ CredentialAuthTokenSteamRequest, }, }; -use axum::Json; use sqlx::{Acquire, AnyPool}; use crate::{ @@ -99,7 +99,7 @@ pub async fn credential_auth_token_email_impl( let con = connection.acquire().await?; let credential_auth_token_res = query_add_credential_auth_token - .query(&shared.db.credential_auth_token_statement) + .query(con, &shared.db.credential_auth_token_statement) .execute(&mut *con) .await?; anyhow::ensure!( @@ -176,7 +176,7 @@ pub async fn credential_auth_token_steam_impl( let con = connection.acquire().await?; let credential_auth_token_res = query_add_credential_auth_token - .query(&shared.db.credential_auth_token_statement) + .query(con, &shared.db.credential_auth_token_statement) .execute(&mut *con) .await?; anyhow::ensure!( diff --git a/src/delete.rs b/src/delete.rs index 20c3f8e..3d5416a 100644 --- a/src/delete.rs +++ b/src/delete.rs @@ -2,6 +2,7 @@ pub mod queries; use std::sync::Arc; +use axum::Json; use ddnet_account_sql::query::Query; use ddnet_accounts_shared::{ account_server::{ @@ -10,7 +11,6 @@ use ddnet_accounts_shared::{ }, client::delete::DeleteRequest, }; -use axum::Json; use sqlx::{Acquire, AnyPool, Connection}; use crate::{ @@ -52,7 +52,7 @@ pub async fn delete(shared: Arc, pool: AnyPool, data: DeleteRequest) -> }; let row = acc_token_qry - .query(&shared.db.account_token_qry_statement) + .query(connection, &shared.db.account_token_qry_statement) .fetch_one(&mut **connection) .await?; @@ -62,7 +62,7 @@ pub async fn delete(shared: Arc, pool: AnyPool, data: DeleteRequest) -> let qry = InvalidateAccountToken { token: &data.account_token, }; - qry.query(&shared.db.invalidate_account_token_statement) + qry.query(connection, &shared.db.invalidate_account_token_statement) .execute(&mut **connection) .await?; @@ -78,7 +78,7 @@ pub async fn delete(shared: Arc, pool: AnyPool, data: DeleteRequest) -> session_data: &None, }; - qry.query(&shared.db.remove_sessions_except_statement) + qry.query(connection, &shared.db.remove_sessions_except_statement) .execute(&mut **connection) .await?; @@ -86,14 +86,14 @@ pub async fn delete(shared: Arc, pool: AnyPool, data: DeleteRequest) -> let qry = UnlinkCredentialEmail { account_id: &account_id, }; - qry.query(&shared.db.unlink_credential_email_statement) + qry.query(connection, &shared.db.unlink_credential_email_statement) .execute(&mut **connection) .await?; let qry = UnlinkCredentialSteam { account_id: &account_id, }; - qry.query(&shared.db.unlink_credential_steam_statement) + qry.query(connection, &shared.db.unlink_credential_steam_statement) .execute(&mut **connection) .await?; @@ -102,7 +102,7 @@ pub async fn delete(shared: Arc, pool: AnyPool, data: DeleteRequest) -> account_id: &account_id, }; - qry.query(&shared.db.remove_account_statement) + qry.query(connection, &shared.db.remove_account_statement) .execute(&mut **connection) .await?; diff --git a/src/link_credential.rs b/src/link_credential.rs index 5fab69c..5e67e8c 100644 --- a/src/link_credential.rs +++ b/src/link_credential.rs @@ -2,6 +2,7 @@ pub mod queries; use std::{str::FromStr, sync::Arc}; +use axum::Json; use ddnet_account_sql::{is_duplicate_entry, query::Query}; use ddnet_accounts_shared::{ account_server::{ @@ -12,7 +13,6 @@ use ddnet_accounts_shared::{ credential_auth_token::CredentialAuthTokenOperation, link_credential::LinkCredentialRequest, }, }; -use axum::Json; use queries::{UnlinkCredentialEmail, UnlinkCredentialSteam}; use sqlx::{Acquire, AnyPool, Connection}; @@ -57,7 +57,7 @@ pub async fn link_credential( }; let row = acc_token_qry - .query(&shared.db.account_token_qry_statement) + .query(connection, &shared.db.account_token_qry_statement) .fetch_one(&mut **connection) .await?; @@ -67,7 +67,7 @@ pub async fn link_credential( let qry = InvalidateAccountToken { token: &data.account_token, }; - qry.query(&shared.db.invalidate_account_token_statement) + qry.query(connection, &shared.db.invalidate_account_token_statement) .execute(&mut **connection) .await?; @@ -97,7 +97,7 @@ pub async fn link_credential( account_id: &account_id, }; - qry.query(&shared.db.unlink_credential_email_statement) + qry.query(connection, &shared.db.unlink_credential_email_statement) .execute(&mut **connection) .await?; @@ -108,7 +108,7 @@ pub async fn link_credential( }; let res = qry - .query(&shared.db.link_credentials_email_qry_statement) + .query(connection, &shared.db.link_credentials_email_qry_statement) .execute(&mut **connection) .await; @@ -125,7 +125,7 @@ pub async fn link_credential( account_id: &account_id, }; - qry.query(&shared.db.unlink_credential_steam_statement) + qry.query(connection, &shared.db.unlink_credential_steam_statement) .execute(&mut **connection) .await?; @@ -136,7 +136,7 @@ pub async fn link_credential( }; let res = qry - .query(&shared.db.link_credentials_steam_qry_statement) + .query(connection, &shared.db.link_credentials_steam_qry_statement) .execute(&mut **connection) .await; diff --git a/src/login.rs b/src/login.rs index b9aa583..2eae471 100644 --- a/src/login.rs +++ b/src/login.rs @@ -2,6 +2,7 @@ pub mod queries; use std::{str::FromStr, sync::Arc}; +use axum::Json; use ddnet_account_sql::query::Query; use ddnet_accounts_shared::{ account_server::{ @@ -10,7 +11,6 @@ use ddnet_accounts_shared::{ client::login::{CredentialAuthToken, LoginRequest}, }; use ddnet_accounts_types::account_id::AccountId; -use axum::Json; use queries::{ AccountIdFromEmail, AccountIdFromLastInsert, AccountIdFromSteam, CredentialAuthTokenData, LinkAccountCredentialEmail, LinkAccountCredentialSteam, @@ -53,7 +53,7 @@ pub async fn get_and_invalidate_credential_auth_token( }; let row = credential_auth_token_qry - .query(&shared.db.credential_auth_token_qry_statement) + .query(connection, &shared.db.credential_auth_token_qry_statement) .fetch_optional(connection) .await?; @@ -114,9 +114,12 @@ pub async fn login( let qry = InvalidateCredentialAuthToken { token: &data.credential_auth_token, }; - qry.query(&shared.db.invalidate_credential_auth_token_statement) - .execute(&mut **connection) - .await?; + qry.query( + connection, + &shared.db.invalidate_credential_auth_token_statement, + ) + .execute(&mut **connection) + .await?; // create account (if not exists) let account_id = match &identifier { @@ -125,7 +128,7 @@ pub async fn login( let qry = AccountIdFromEmail { email }; let row = qry - .query(&shared.db.account_id_from_email_qry_statement) + .query(connection, &shared.db.account_id_from_email_qry_statement) .fetch_optional(&mut **connection) .await?; @@ -138,7 +141,7 @@ pub async fn login( let qry = AccountIdFromSteam { steamid64 }; let row = qry - .query(&shared.db.account_id_from_steam_qry_statement) + .query(connection, &shared.db.account_id_from_steam_qry_statement) .fetch_optional(&mut **connection) .await?; @@ -154,7 +157,7 @@ pub async fn login( let qry = TryCreateAccount {}; let res = qry - .query(&shared.db.try_create_account_statement) + .query(connection, &shared.db.try_create_account_statement) .execute(&mut **connection) .await?; @@ -163,7 +166,10 @@ pub async fn login( // query account data let login_qry = AccountIdFromLastInsert {}; let row = login_qry - .query(&shared.db.account_id_from_last_insert_qry_statement) + .query( + connection, + &shared.db.account_id_from_last_insert_qry_statement, + ) .fetch_one(&mut **connection) .await?; @@ -177,7 +183,10 @@ pub async fn login( }; let res = qry - .query(&shared.db.link_credentials_email_qry_statement) + .query( + connection, + &shared.db.link_credentials_email_qry_statement, + ) .execute(&mut **connection) .await?; @@ -193,7 +202,10 @@ pub async fn login( }; let res = qry - .query(&shared.db.link_credentials_steam_qry_statement) + .query( + connection, + &shared.db.link_credentials_steam_qry_statement, + ) .execute(&mut **connection) .await?; @@ -213,7 +225,7 @@ pub async fn login( pub_key: data.account_data.public_key.as_bytes(), }; - qry.query(&shared.db.create_session_statement) + qry.query(connection, &shared.db.create_session_statement) .execute(&mut **connection) .await?; diff --git a/src/logout.rs b/src/logout.rs index 0ded99a..1da3034 100644 --- a/src/logout.rs +++ b/src/logout.rs @@ -2,6 +2,7 @@ pub mod queries; use std::sync::Arc; +use axum::Json; use ddnet_account_sql::query::Query; use ddnet_accounts_shared::{ account_server::{ @@ -10,7 +11,6 @@ use ddnet_accounts_shared::{ }, client::logout::LogoutRequest, }; -use axum::Json; use sqlx::{Acquire, AnyPool}; use crate::shared::{Shared, CERT_MAX_AGE_DELTA, CERT_MIN_AGE_DELTA}; @@ -53,7 +53,7 @@ pub async fn logout(shared: Arc, pool: AnyPool, data: LogoutRequest) -> hw_id: &data.account_data.hw_id, }; - qry.query(&shared.db.logout_statement) + qry.query(connection, &shared.db.logout_statement) .execute(connection) .await?; diff --git a/src/logout_all.rs b/src/logout_all.rs index 8c8fb59..c5b4f21 100644 --- a/src/logout_all.rs +++ b/src/logout_all.rs @@ -2,6 +2,7 @@ pub mod queries; use std::sync::Arc; +use axum::Json; use ddnet_account_sql::query::Query; use ddnet_accounts_shared::{ account_server::{ @@ -10,7 +11,6 @@ use ddnet_accounts_shared::{ }, client::logout_all::{IgnoreSession, LogoutAllRequest}, }; -use axum::Json; use sqlx::{Acquire, AnyPool, Connection}; use crate::{ @@ -52,7 +52,7 @@ pub async fn logout_all( }; let row = acc_token_qry - .query(&shared.db.account_token_qry_statement) + .query(connection, &shared.db.account_token_qry_statement) .fetch_one(&mut **connection) .await?; @@ -62,7 +62,7 @@ pub async fn logout_all( let qry = InvalidateAccountToken { token: &data.account_token, }; - qry.query(&shared.db.invalidate_account_token_statement) + qry.query(connection, &shared.db.invalidate_account_token_statement) .execute(&mut **connection) .await?; @@ -96,7 +96,7 @@ pub async fn logout_all( session_data: &session_data, }; - qry.query(&shared.db.remove_sessions_except_statement) + qry.query(connection, &shared.db.remove_sessions_except_statement) .execute(&mut **connection) .await?; diff --git a/src/sign.rs b/src/sign.rs index ecf56ff..dabec6e 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -2,6 +2,7 @@ pub mod queries; use std::{str::FromStr, sync::Arc, time::Duration}; +use axum::Json; use ddnet_account_sql::query::Query; use ddnet_accounts_shared::{ account_server::{ @@ -12,7 +13,6 @@ use ddnet_accounts_shared::{ }, client::sign::SignRequest, }; -use axum::Json; use p256::ecdsa::DerSignature; use sqlx::{Acquire, AnyPool}; use x509_cert::builder::Builder; @@ -62,7 +62,7 @@ pub async fn sign( let qry = AuthAttempt { data: &data }; let row = qry - .query(&shared.db.auth_attempt_statement) + .query(connection, &shared.db.auth_attempt_statement) .fetch_one(connection) .await?; let auth_data = AuthAttempt::row_data(&row)?; diff --git a/src/tests/game_server/rename.rs b/src/tests/game_server/rename.rs index 2ffda4c..b5d2a83 100644 --- a/src/tests/game_server/rename.rs +++ b/src/tests/game_server/rename.rs @@ -84,13 +84,25 @@ async fn rename_hardening() { .await; assert!(auto_login_res.is_ok_and(|v| v)); - ddnet_account_game_server::rename::rename( - game_server_data.clone(), - &pool, - &user_id, - "nameless_tee", - ) - .await?; + // Make sure 32 character long names work + assert!( + ddnet_account_game_server::rename::rename( + game_server_data.clone(), + &pool, + &user_id, + "01234567890123456789012345678901", + ) + .await? + ); + assert!( + ddnet_account_game_server::rename::rename( + game_server_data.clone(), + &pool, + &user_id, + "nameless_tee", + ) + .await? + ); let res = ddnet_account_game_server::rename::rename( game_server_data.clone(), &pool, diff --git a/src/unlink_credential.rs b/src/unlink_credential.rs index e78223a..dc64e06 100644 --- a/src/unlink_credential.rs +++ b/src/unlink_credential.rs @@ -2,6 +2,7 @@ pub mod queries; use std::{str::FromStr, sync::Arc}; +use axum::Json; use ddnet_account_sql::query::Query; use ddnet_accounts_shared::{ account_server::{ @@ -10,7 +11,6 @@ use ddnet_accounts_shared::{ }, client::unlink_credential::UnlinkCredentialRequest, }; -use axum::Json; use queries::{UnlinkCredentialByEmail, UnlinkCredentialBySteam}; use sqlx::{Acquire, AnyPool, Connection}; @@ -64,7 +64,7 @@ pub async fn unlink_credential( // remove the current email, if exists. let qry = UnlinkCredentialByEmail { email: &email }; - qry.query(&shared.db.unlink_credential_by_email_statement) + qry.query(connection, &shared.db.unlink_credential_by_email_statement) .execute(&mut **connection) .await? .rows_affected() @@ -76,7 +76,7 @@ pub async fn unlink_credential( steamid64: &steamid64, }; - qry.query(&shared.db.unlink_credential_by_steam_statement) + qry.query(connection, &shared.db.unlink_credential_by_steam_statement) .execute(&mut **connection) .await? .rows_affected() diff --git a/src/update.rs b/src/update.rs index 4f77e15..3553bc3 100644 --- a/src/update.rs +++ b/src/update.rs @@ -13,20 +13,23 @@ pub async fn update_impl(pool: &AnyPool, shared: &Arc) { if let Ok(connection) = connection.acquire().await { // cleanup credential auth tokens let _ = connection - .execute( - CleanupCredentialAuthTokens {} - .query(&shared.db.cleanup_credential_auth_tokens_statement), - ) + .execute(CleanupCredentialAuthTokens {}.query( + connection, + &shared.db.cleanup_credential_auth_tokens_statement, + )) .await; // cleanup account tokens let _ = connection - .execute(CleanupAccountTokens {}.query(&shared.db.cleanup_account_tokens_statement)) + .execute( + CleanupAccountTokens {} + .query(connection, &shared.db.cleanup_account_tokens_statement), + ) .await; // cleanup certs let _ = connection - .execute(CleanupCerts {}.query(&shared.db.cleanup_certs_statement)) + .execute(CleanupCerts {}.query(connection, &shared.db.cleanup_certs_statement)) .await; } }