aboutsummaryrefslogtreecommitdiff
path: root/crates/secd/src/client/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/secd/src/client/mod.rs')
-rw-r--r--crates/secd/src/client/mod.rs209
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>;
+}