diff options
Diffstat (limited to '')
| -rw-r--r-- | crates/secd/src/client/mod.rs | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/crates/secd/src/client/mod.rs b/crates/secd/src/client/mod.rs new file mode 100644 index 0000000..3925657 --- /dev/null +++ b/crates/secd/src/client/mod.rs @@ -0,0 +1,209 @@ +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<EmailValidation, StoreError>; + async fn write_email_validation( + &self, + ev: &EmailValidation, + // TODO: Make this write an EmailValidation + ) -> Result<Uuid, StoreError>; + + async fn find_identity( + &self, + identity_id: Option<&Uuid>, + email: Option<&str>, + ) -> Result<Option<Identity>, StoreError>; + async fn find_identity_by_code(&self, code: &str) -> Result<Identity, StoreError>; + async fn write_identity(&self, i: &Identity) -> Result<(), StoreError>; + async fn read_identity(&self, identity_id: &Uuid) -> Result<Identity, StoreError>; + + async fn write_session(&self, session: &Session) -> Result<(), StoreError>; + async fn read_session(&self, secret: &SessionSecret) -> Result<Session, StoreError>; +} |
