pub mod email; pub mod sqldb; use std::collections::HashMap; use super::Identity; use crate::{EmailValidation, Session, SessionSecret}; use lazy_static::lazy_static; use thiserror::Error; use uuid::Uuid; pub enum EmailType { Login, Signup, } #[derive(Error, Debug, derive_more::Display)] pub enum EmailMessengerError { InvalidEmailAddress, FailedToSendEmail, Unknown, } #[async_trait::async_trait] pub trait EmailMessenger { async fn send_email( &self, email_address: &str, validation_id: &str, secret_code: &str, t: EmailType, ) -> Result<(), EmailMessengerError>; } #[derive(Error, Debug, derive_more::Display)] pub enum StoreError { SqlxError(#[from] sqlx::Error), EmailAlreadyExists, CodeAppearsMoreThanOnce, CodeDoesNotExist(String), IdentityIdMustExistInvariant, TooManyEmailValidations, NoEmailValidationFound, Unknown, } const EMAIL_TEMPLATE_DEFAULT_LOGIN: &str = "You requested a login link. Please click the following link %secd_code% to login as %secd_email_address%"; const EMAIL_TEMPLATE_DEFAULT_SIGNUP: &str = "You requested a sign up. Please click the following link %secd_code% to complete your sign up and validate %secd_email_address%"; const ERR_MSG_MIGRATION_FAILED: &str = "Failed to execute migrations. This appears to be a secd issue. File a bug at https://www.github.com/secd-lib"; const SQLITE: &str = "sqlite"; const PGSQL: &str = "pgsql"; const WRITE_IDENTITY: &str = "write_identity"; const WRITE_EMAIL_VALIDATION: &str = "write_email_validation"; const FIND_EMAIL_VALIDATION: &str = "find_email_validation"; const WRITE_EMAIL: &str = "write_email"; const READ_IDENTITY: &str = "read_identity"; const FIND_IDENTITY: &str = "find_identity"; const FIND_IDENTITY_BY_CODE: &str = "find_identity_by_code"; const READ_IDENTITY_RAW_ID: &str = "read_identity_raw_id"; const READ_EMAIL_RAW_ID: &str = "read_email_raw_id"; const WRITE_SESSION: &str = "write_session"; const READ_SESSION: &str = "read_session"; lazy_static! { static ref SQLS: HashMap<&'static str, HashMap<&'static str, &'static str>> = { let sqlite_sqls: HashMap<&'static str, &'static str> = [ ( WRITE_IDENTITY, include_str!("../../store/sqlite/sql/write_identity.sql"), ), ( WRITE_EMAIL_VALIDATION, include_str!("../../store/sqlite/sql/write_email_validation.sql"), ), ( WRITE_EMAIL, include_str!("../../store/sqlite/sql/write_email.sql"), ), ( READ_IDENTITY, include_str!("../../store/sqlite/sql/read_identity.sql"), ), ( FIND_IDENTITY, include_str!("../../store/sqlite/sql/find_identity.sql"), ), ( FIND_IDENTITY_BY_CODE, include_str!("../../store/sqlite/sql/find_identity_by_code.sql"), ), ( READ_IDENTITY_RAW_ID, include_str!("../../store/sqlite/sql/read_identity_raw_id.sql"), ), ( READ_EMAIL_RAW_ID, include_str!("../../store/sqlite/sql/read_email_raw_id.sql"), ), ( WRITE_SESSION, include_str!("../../store/sqlite/sql/write_session.sql"), ), ( READ_SESSION, include_str!("../../store/sqlite/sql/read_session.sql"), ), ( FIND_EMAIL_VALIDATION, include_str!("../../store/sqlite/sql/find_email_validation.sql"), ), ] .iter() .cloned() .collect(); let pg_sqls: HashMap<&'static str, &'static str> = [ ( WRITE_IDENTITY, include_str!("../../store/pg/sql/write_identity.sql"), ), ( WRITE_EMAIL_VALIDATION, include_str!("../../store/pg/sql/write_email_validation.sql"), ), ( WRITE_EMAIL, include_str!("../../store/pg/sql/write_email.sql"), ), ( READ_IDENTITY, include_str!("../../store/pg/sql/read_identity.sql"), ), ( FIND_IDENTITY, include_str!("../../store/pg/sql/find_identity.sql"), ), ( FIND_IDENTITY_BY_CODE, include_str!("../../store/pg/sql/find_identity_by_code.sql"), ), ( READ_IDENTITY_RAW_ID, include_str!("../../store/pg/sql/read_identity_raw_id.sql"), ), ( READ_EMAIL_RAW_ID, include_str!("../../store/pg/sql/read_email_raw_id.sql"), ), ( WRITE_SESSION, include_str!("../../store/pg/sql/write_session.sql"), ), ( READ_SESSION, include_str!("../../store/pg/sql/read_session.sql"), ), ( FIND_EMAIL_VALIDATION, include_str!("../../store/pg/sql/find_email_validation.sql"), ), ] .iter() .cloned() .collect(); let sqls: HashMap<&'static str, HashMap<&'static str, &'static str>> = [(SQLITE, sqlite_sqls), (PGSQL, pg_sqls)] .iter() .cloned() .collect(); sqls }; } #[async_trait::async_trait] pub trait Store { async fn write_email(&self, identity_id: Uuid, email_address: &str) -> Result<(), StoreError>; async fn find_email_validation( &self, validation_id: Option<&Uuid>, code: Option<&str>, ) -> Result; async fn write_email_validation( &self, ev: &EmailValidation, // TODO: Make this write an EmailValidation ) -> Result; async fn find_identity( &self, identity_id: Option<&Uuid>, email: Option<&str>, ) -> Result, StoreError>; async fn find_identity_by_code(&self, code: &str) -> Result; async fn write_identity(&self, i: &Identity) -> Result<(), StoreError>; async fn read_identity(&self, identity_id: &Uuid) -> Result; async fn write_session(&self, session: &Session) -> Result<(), StoreError>; async fn read_session(&self, secret: &SessionSecret) -> Result; }