aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authorbenj <benj@rse8.com>2022-11-25 16:42:16 -0800
committerbenj <benj@rse8.com>2022-11-25 16:42:16 -0800
commitaa8c20d501b58001a5e1b24964c62363e2112ff8 (patch)
tree82e53aa5efd6e0a96e8c436655c083de617a6131 /src/client
parentfcd972fd9ae7579724b0ba9b401ceb729e6e0108 (diff)
downloadsecdiam-aa8c20d501b58001a5e1b24964c62363e2112ff8.tar
secdiam-aa8c20d501b58001a5e1b24964c62363e2112ff8.tar.gz
secdiam-aa8c20d501b58001a5e1b24964c62363e2112ff8.tar.bz2
secdiam-aa8c20d501b58001a5e1b24964c62363e2112ff8.tar.lz
secdiam-aa8c20d501b58001a5e1b24964c62363e2112ff8.tar.xz
secdiam-aa8c20d501b58001a5e1b24964c62363e2112ff8.tar.zst
secdiam-aa8c20d501b58001a5e1b24964c62363e2112ff8.zip
some shell is coming together and a rough API
Diffstat (limited to '')
-rw-r--r--src/client/mod.rs81
-rw-r--r--src/client/sqldb.rs172
2 files changed, 253 insertions, 0 deletions
diff --git a/src/client/mod.rs b/src/client/mod.rs
new file mode 100644
index 0000000..bb32e2c
--- /dev/null
+++ b/src/client/mod.rs
@@ -0,0 +1,81 @@
+pub mod sqldb;
+
+use thiserror::Error;
+use uuid::Uuid;
+
+use super::Identity;
+
+#[derive(Error, Debug)]
+pub enum StoreError {
+ #[error("sqlx client error")]
+ SqlxError(#[from] sqlx::Error),
+ #[error(
+ "More than one oauth provider identified, but no client_id was provided for disambiguation"
+ )]
+ TooManyOauthProviders,
+ #[error("Oath provider not registered. First register the Oauth provider before executing")]
+ OauthProviderNotRegistered,
+ #[error("An unknown error occurred")]
+ Unknown,
+}
+
+#[async_trait::async_trait]
+pub trait Store {
+ // async fn read_oauth_authorization_location(
+ // &self,
+ // provider: OauthProvider,
+ // client_id: Option<OauthClientId>,
+ // ) -> Result<String, StoreError>;
+
+ // async fn write_oauth_authorization_request(
+ // &self,
+ // identity_id: Uuid,
+ // provider: OauthProvider,
+ // raw: String,
+ // state: String,
+ // ) -> Result<(), StoreError>;
+
+ // async fn write_oauth_provider(
+ // &self,
+ // provider: OauthProvider,
+ // consent_uri: OauthConsentUri,
+ // client_id: OauthClientId,
+ // client_secret: OauthClientSecretEncrypted,
+ // redirect_uri: String,
+ // ) -> Result<(), StoreError>;
+
+ // fn read_email_challenge(&self) -> Result<T, StoreError>;
+ // fn write_email_challenge(&self) -> Result<T, StoreError>;
+
+ async fn write_email(&self, id: Uuid, email_address: &str) -> Result<(), StoreError>;
+ async fn write_email_validation_request(
+ &self,
+ id: Uuid,
+ email_address: &str,
+ ) -> Result<Uuid, StoreError>;
+
+ async fn find_identity(
+ &self,
+ id: Option<&Uuid>,
+ email: Option<&str>,
+ ) -> Result<Option<Identity>, StoreError>;
+ async fn write_identity(&self, i: &Identity) -> Result<(), StoreError>;
+ async fn read_identity(&self, id: &Uuid) -> Result<Identity, StoreError>;
+
+ // fn read_sms_challenge(&self) -> Result<T, StoreError>;
+ // fn write_sms_challenge(&self) -> Result<T, StoreError>;
+}
+
+// #[derive(sqlx::FromRow, Debug)]
+// struct Identity {
+// #[sqlx(rename = "identity_public_id")]
+// id: Uuid,
+// }
+
+// #[derive(sqlx::FromRow, Debug)]
+// struct OauthProviderRecord {
+// consent_uri: String,
+// client_id: OauthClientId,
+// client_secret_encrypted: OauthClientSecretEncrypted,
+// redirect_uri: String,
+// }
diff --git a/src/client/sqldb.rs b/src/client/sqldb.rs
new file mode 100644
index 0000000..6ad0cc1
--- /dev/null
+++ b/src/client/sqldb.rs
@@ -0,0 +1,172 @@
+use log::{debug, error};
+use uuid::Uuid;
+
+use crate::{util, Identity};
+
+use super::{Store, StoreError};
+
+pub struct SqliteClient {
+ pool: sqlx::Pool<sqlx::Sqlite>,
+}
+
+impl SqliteClient {
+ pub async fn new(pool: sqlx::Pool<sqlx::Sqlite>) -> Self {
+ sqlx::migrate!("store/sqlite/migrations")
+ .run(&pool)
+ .await
+ .expect(
+ "Failed to execute migrations. This appears to be a secd issue. File a bug at https://www.github.com/secd-lib"
+ );
+
+ sqlx::query("pragma foreign_keys = on")
+ .execute(&pool)
+ .await
+ .expect(
+ "Failed to initialize FK pragma. File a bug at https://www.github.com/secd-lib",
+ );
+
+ SqliteClient { pool }
+ }
+}
+
+impl SqliteClient {
+ async fn read_identity_raw_id(&self, id: &Uuid) -> Result<i64, StoreError> {
+ Ok(sqlx::query_as::<_, (i64,)>(
+ "
+select identity_id from identity where identity_public_id = ?",
+ )
+ .bind(id)
+ .fetch_one(&self.pool)
+ .await
+ .map_err(util::log_err)?
+ .0)
+ }
+
+ async fn read_email_raw_id(&self, address: &str) -> Result<i64, StoreError> {
+ Ok(sqlx::query_as::<_, (i64,)>(
+ "
+select email_id from email where address = ?",
+ )
+ .bind(address)
+ .fetch_one(&self.pool)
+ .await
+ .map_err(util::log_err)?
+ .0)
+ }
+}
+
+#[async_trait::async_trait]
+impl Store for SqliteClient {
+ async fn write_email(&self, identity_id: Uuid, email_address: &str) -> Result<(), StoreError> {
+ let mut tx = self.pool.begin().await?;
+
+ let identity_id = self.read_identity_raw_id(&identity_id).await?;
+
+ let email_id: (i64,) = sqlx::query_as(
+ "
+insert into email (address) values (?) returning email_id",
+ )
+ .bind(email_address)
+ .fetch_one(&mut tx)
+ .await
+ .map_err(util::log_err)?;
+
+ debug!("identity: {}, email: {}", identity_id, email_id.0);
+
+ sqlx::query(
+ "
+insert into identity_email (identity_id, email_id) values (?,?);",
+ )
+ .bind(identity_id)
+ .bind(email_id.0)
+ .execute(&mut tx)
+ .await
+ .map_err(util::log_err)?;
+
+ tx.commit().await?;
+
+ Ok(())
+ }
+
+ async fn write_email_validation_request(
+ &self,
+ identity_id: Uuid,
+ email_address: &str,
+ ) -> Result<Uuid, StoreError> {
+ let identity_id = self.read_identity_raw_id(&identity_id).await?;
+ let email_id = self.read_email_raw_id(email_address).await?;
+
+ let request_id = Uuid::new_v4();
+ sqlx::query("
+insert into email_validation_request (email_validation_request_public_id, identity_email_id, is_validated)
+values (?, (select identity_email_id from identity_email where identity_id = ? and email_id =?), ?)",
+ )
+ .bind(request_id)
+ .bind(identity_id)
+ .bind(email_id)
+ .bind(false)
+ .execute(&self.pool)
+ .await
+ .map_err(util::log_err)?;
+
+ Ok(request_id)
+ }
+
+ async fn write_identity(&self, i: &Identity) -> Result<(), StoreError> {
+ sqlx::query(
+ "
+insert into identity (identity_public_id, data, created_at) values (?, ?, ?)",
+ )
+ .bind(i.id)
+ .bind(i.data.clone())
+ .bind(i.created_at)
+ .execute(&self.pool)
+ .await
+ .map_err(|e| {
+ error!("{:?}", e);
+ e
+ })?;
+
+ Ok(())
+ }
+
+ async fn read_identity(&self, id: &Uuid) -> Result<Identity, StoreError> {
+ Ok(sqlx::query_as::<_, Identity>(
+ "
+select identity_public_id, data, created_at from identity where identity_public_id = ?",
+ )
+ .bind(id)
+ .fetch_one(&self.pool)
+ .await
+ .map_err(util::log_err)?)
+ }
+
+ async fn find_identity(
+ &self,
+ id: Option<&Uuid>,
+ email: Option<&str>,
+ ) -> Result<Option<Identity>, StoreError> {
+ Ok(
+ match sqlx::query_as::<_, Identity>(
+ "
+select identity_public_id, data, i.created_at
+from identity i
+join identity_email ie using (identity_id)
+join email e using (email_id)
+where ((? is null) or (i.identity_public_id = ?))
+and ((? is null) or (e.address = ?));",
+ )
+ .bind(id)
+ .bind(id)
+ .bind(email)
+ .bind(email)
+ .fetch_one(&self.pool)
+ .await
+ {
+ Ok(i) => Some(i),
+ Err(sqlx::Error::RowNotFound) => None,
+ Err(e) => return Err(StoreError::SqlxError(e)),
+ },
+ )
+ }
+}