diff --git a/.sqlx/query-3edf94a78114819085b573ff51702faee1c444c24bbf6d33ec1b4b245dd9f675.json b/.sqlx/query-3edf94a78114819085b573ff51702faee1c444c24bbf6d33ec1b4b245dd9f675.json new file mode 100644 index 0000000..b8455bd --- /dev/null +++ b/.sqlx/query-3edf94a78114819085b573ff51702faee1c444c24bbf6d33ec1b4b245dd9f675.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT EXISTS (\n SELECT 1\n FROM verification_otp\n WHERE\n username = $1\n AND\n purpose = $2\n AND\n secret = $3\n );", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "3edf94a78114819085b573ff51702faee1c444c24bbf6d33ec1b4b245dd9f675" +} diff --git a/.sqlx/query-c54d1b89ee39ceb11326e82962eef3d8f588f6252ba4bbac99fb73cdbbcd2204.json b/.sqlx/query-c54d1b89ee39ceb11326e82962eef3d8f588f6252ba4bbac99fb73cdbbcd2204.json new file mode 100644 index 0000000..f56645c --- /dev/null +++ b/.sqlx/query-c54d1b89ee39ceb11326e82962eef3d8f588f6252ba4bbac99fb73cdbbcd2204.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM\n verification_otp\n WHERE\n username = $1\n AND\n purpose = $2\n AND\n secret = $3;", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "c54d1b89ee39ceb11326e82962eef3d8f588f6252ba4bbac99fb73cdbbcd2204" +} diff --git a/src/identity/adapters/output/db/postgres/create_verification_secret.rs b/src/identity/adapters/output/db/postgres/create_verification_secret.rs index 21aa8d2..c825589 100644 --- a/src/identity/adapters/output/db/postgres/create_verification_secret.rs +++ b/src/identity/adapters/output/db/postgres/create_verification_secret.rs @@ -15,7 +15,7 @@ impl CreateVerificationSecretOutDBPort for DBOutPostgresAdapter { VALUES ($1, $2, $3, $4);", &msg.secret, OffsetDateTime::now_utc(), - &msg.purpose, + REGISTRATION_SECRET_PURPOSE, &msg.username, ) .execute(&self.pool) @@ -39,7 +39,6 @@ mod tests { let msg = CreateSecretMsgBuilder::default() .secret("secret".into()) - .purpose("purpose".into()) .username("username".into()) .build() .unwrap(); diff --git a/src/identity/adapters/output/db/postgres/mod.rs b/src/identity/adapters/output/db/postgres/mod.rs index c6f79ca..9a1e7dd 100644 --- a/src/identity/adapters/output/db/postgres/mod.rs +++ b/src/identity/adapters/output/db/postgres/mod.rs @@ -10,9 +10,11 @@ use sqlx::postgres::PgPool; use crate::db::{migrate::RunMigrations, sqlx_postgres::Postgres}; pub mod create_verification_secret; +pub mod delete_verification_secret; pub mod email_exists; mod errors; pub mod username_exists; +pub mod verification_secret_exists; #[derive(Clone)] pub struct DBOutPostgresAdapter { diff --git a/src/identity/adapters/output/db/postgres/verification_secret_exists.rs b/src/identity/adapters/output/db/postgres/verification_secret_exists.rs new file mode 100644 index 0000000..4a43d81 --- /dev/null +++ b/src/identity/adapters/output/db/postgres/verification_secret_exists.rs @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: 2024 Aravinth Manivannan +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use super::DBOutPostgresAdapter; +use crate::identity::application::port::output::db::{errors::*, verification_secret_exists::*}; + +#[async_trait::async_trait] +impl VerificationSecretExistsOutDBPort for DBOutPostgresAdapter { + async fn verification_secret_exists( + &self, + msg: &VerifySecretExistsMsg, + ) -> OutDBPortResult { + let res = sqlx::query!( + "SELECT EXISTS ( + SELECT 1 + FROM verification_otp + WHERE + username = $1 + AND + purpose = $2 + AND + secret = $3 + );", + msg.username, + REGISTRATION_SECRET_PURPOSE, + msg.secret, + ) + .fetch_one(&self.pool) + .await?; + if let Some(x) = res.exists { + Ok(x) + } else { + Ok(false) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::identity::application::port::output::db::create_verification_secret::*; + + #[actix_rt::test] + async fn test_postgres_verification_secret_exists() { + let username = "batman"; + let secret = "bsdasdf"; + let settings = crate::settings::tests::get_settings().await; + let db = super::DBOutPostgresAdapter::new( + sqlx::postgres::PgPool::connect(&settings.database.url) + .await + .unwrap(), + ); + let msg = VerifySecretExistsMsgBuilder::default() + .username(username.into()) + .secret(secret.into()) + .build() + .unwrap(); + + // state doesn't exist + assert!(!db.verification_secret_exists(&msg).await.unwrap()); + + let create_msg = CreateSecretMsgBuilder::default() + .secret(secret.into()) + .username(username.into()) + .build() + .unwrap(); + db.create_verification_secret(create_msg).await.unwrap(); + + // state exists + assert!(db.verification_secret_exists(&msg).await.unwrap()); + + settings.drop_db().await; + } +} diff --git a/src/identity/application/port/output/db/create_verification_secret.rs b/src/identity/application/port/output/db/create_verification_secret.rs index e7f2b34..612fbad 100644 --- a/src/identity/application/port/output/db/create_verification_secret.rs +++ b/src/identity/application/port/output/db/create_verification_secret.rs @@ -15,9 +15,9 @@ pub use tests::*; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Builder)] pub struct CreateSecretMsg { pub secret: String, - pub purpose: String, pub username: String, } +pub const REGISTRATION_SECRET_PURPOSE: &str = "account_validation"; #[automock] #[async_trait::async_trait] diff --git a/src/identity/application/port/output/db/mod.rs b/src/identity/application/port/output/db/mod.rs index b3e3eeb..67f9db9 100644 --- a/src/identity/application/port/output/db/mod.rs +++ b/src/identity/application/port/output/db/mod.rs @@ -3,8 +3,8 @@ // SPDX-License-Identifier: AGPL-3.0-or-later pub mod create_verification_secret; +pub mod delete_verification_secret; pub mod email_exists; pub mod errors; pub mod username_exists; -//pub mod get_verification_secret; -//pub mod delete_verification_secret; +pub mod verification_secret_exists; diff --git a/src/identity/application/port/output/db/verification_secret_exists.rs b/src/identity/application/port/output/db/verification_secret_exists.rs new file mode 100644 index 0000000..8feb3bf --- /dev/null +++ b/src/identity/application/port/output/db/verification_secret_exists.rs @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2024 Aravinth Manivannan +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use derive_builder::Builder; +use mockall::predicate::*; +use mockall::*; +use serde::{Deserialize, Serialize}; + +pub use super::create_verification_secret::REGISTRATION_SECRET_PURPOSE; +use super::errors::*; +#[cfg(test)] +#[allow(unused_imports)] +pub use tests::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Builder)] +pub struct VerifySecretExistsMsg { + pub secret: String, + pub username: String, +} + +#[automock] +#[async_trait::async_trait] +pub trait VerificationSecretExistsOutDBPort: Send + Sync { + async fn verification_secret_exists( + &self, + msg: &VerifySecretExistsMsg, + ) -> OutDBPortResult; +} + +pub type VerificationSecretExistsOutDBPortObj = + std::sync::Arc; + +#[cfg(test)] +pub mod tests { + use super::*; + + use std::sync::Arc; + + pub fn mock_verification_secret_exists_db_port( + times: Option, + returning: bool, + ) -> VerificationSecretExistsOutDBPortObj { + let mut m = MockVerificationSecretExistsOutDBPort::new(); + if let Some(times) = times { + m.expect_verification_secret_exists() + .times(times) + .returning(move |_| Ok(returning)); + } else { + m.expect_verification_secret_exists() + .returning(move |_| Ok(returning)); + } + + Arc::new(m) + } +} diff --git a/src/identity/application/services/register_user/service.rs b/src/identity/application/services/register_user/service.rs index 24d2240..6fd1f64 100644 --- a/src/identity/application/services/register_user/service.rs +++ b/src/identity/application/services/register_user/service.rs @@ -13,7 +13,6 @@ use crate::identity::application::port::output::{ use crate::utils::random_string::*; pub const SECRET_LEN: usize = 20; -pub const REGISTRATION_SECRET_PURPOSE: &str = "account_validation"; #[derive(Builder)] pub struct RegisterUserService { @@ -54,7 +53,6 @@ impl RegisterUserUseCase for RegisterUserService { .create_verification_secret( CreateSecretMsgBuilder::default() .secret(secret.clone()) - .purpose(REGISTRATION_SECRET_PURPOSE.into()) .username(cmd.username().into()) .build() .unwrap(),