aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/mod.rs81
-rw-r--r--src/client/sqldb.rs172
-rw-r--r--src/ipl/authn.rs1
-rw-r--r--src/ipl/authz.rs1
-rw-r--r--src/ipl/mod.rs2
-rw-r--r--src/lib.rs171
-rw-r--r--src/main.old112
-rw-r--r--src/util/mod.rs15
8 files changed, 0 insertions, 555 deletions
diff --git a/src/client/mod.rs b/src/client/mod.rs
deleted file mode 100644
index bb32e2c..0000000
--- a/src/client/mod.rs
+++ /dev/null
@@ -1,81 +0,0 @@
-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
deleted file mode 100644
index 6ad0cc1..0000000
--- a/src/client/sqldb.rs
+++ /dev/null
@@ -1,172 +0,0 @@
-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)),
- },
- )
- }
-}
diff --git a/src/ipl/authn.rs b/src/ipl/authn.rs
deleted file mode 100644
index 8b13789..0000000
--- a/src/ipl/authn.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/ipl/authz.rs b/src/ipl/authz.rs
deleted file mode 100644
index f2f23e4..0000000
--- a/src/ipl/authz.rs
+++ /dev/null
@@ -1 +0,0 @@
-// TODO: Authorization suite
diff --git a/src/ipl/mod.rs b/src/ipl/mod.rs
deleted file mode 100644
index 1946995..0000000
--- a/src/ipl/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod authn;
-pub mod authz;
diff --git a/src/lib.rs b/src/lib.rs
deleted file mode 100644
index 7856d5c..0000000
--- a/src/lib.rs
+++ /dev/null
@@ -1,171 +0,0 @@
-mod client;
-mod util;
-
-use std::sync::Arc;
-
-use client::{sqldb::SqliteClient, Store, StoreError};
-use derive_more::Display;
-use email_address::EmailAddress;
-use log::{error, info};
-use serde::Serialize;
-use time::OffsetDateTime;
-use uuid::Uuid;
-
-#[derive(Copy, Display, Clone, Debug)]
-pub enum OauthProvider {
- Amazon,
- Apple,
- Dropbox,
- Facebook,
- Github,
- Gitlab,
- Google,
- Instagram,
- LinkedIn,
- Microsoft,
- Paypal,
- Reddit,
- Spotify,
- Strava,
- Stripe,
- Twitch,
- Twitter,
- WeChat,
-}
-
-#[derive(Display, Debug)]
-pub enum AuthStore {
- Sqlite,
- Postgres,
- MySql,
- Mongo,
- Dynamo,
- Redis,
-}
-
-pub type OauthClientId = String;
-pub type OauthClientSecretEncrypted = String;
-pub type OauthConsentUri = String;
-
-pub type IdentityId = Uuid;
-
-//////////////////////////////////////////////////
-// Resources
-#[derive(sqlx::FromRow, Debug, Serialize)]
-pub struct Identity {
- #[sqlx(rename = "identity_public_id")]
- id: Uuid,
- created_at: sqlx::types::time::OffsetDateTime,
- #[serde(skip_serializing_if = "Option::is_none")]
- data: Option<String>,
-}
-
-pub struct ApiKey {}
-
-pub struct Session {}
-
-pub struct ValidationRequest {}
-//////////////////////////////////////////////////
-#[derive(Debug, derive_more::Display, thiserror::Error)]
-pub enum SecdError {
- InvalidEmailAddress,
- SqliteInitializationFailure(sqlx::Error),
- StoreError(#[from] StoreError),
- EmailValidationRequestError,
- Unknown,
-}
-
-pub struct Secd {
- store: Arc<dyn Store + Send + Sync + 'static>,
-}
-
-impl Secd {
- pub async fn init(
- auth_store: AuthStore,
- conn_string: Option<String>,
- // TODO: Turn Secd into a trait and impl separately.
- // TODO: initialize email and SMS templates with secd
- ) -> Result<Self, SecdError> {
- let store = match auth_store {
- AuthStore::Sqlite => SqliteClient::new(
- sqlx::sqlite::SqlitePoolOptions::new()
- .connect(conn_string.unwrap_or("sqlite::memory:".into()).as_str())
- .await
- .map_err(|e| SecdError::SqliteInitializationFailure(e))?,
- ),
- // TODO: if AuthStore is provided, then configure the client.
- _ => return Err(SecdError::Unknown),
- }
- .await;
-
- Ok(Secd {
- store: Arc::new(store),
- })
- }
-
- pub async fn create_identity(&self) -> Result<Uuid, SecdError> {
- let id = Uuid::new_v4();
- self.store
- .write_identity(&Identity {
- id,
- created_at: OffsetDateTime::now_utc(),
- data: None,
- })
- .await?;
-
- Ok(id)
- }
-
- pub async fn create_validation_request(&self, email: Option<&str>) -> Result<(), SecdError> {
- // TODO: refactor based on email, phone, or some other template? Or break up the API?
- let email = match email {
- Some(ea) => {
- if EmailAddress::is_valid(ea) {
- ea
- } else {
- return Err(SecdError::InvalidEmailAddress);
- }
- }
- None => return Err(SecdError::InvalidEmailAddress),
- };
-
- match self.store.find_identity(None, Some(email)).await? {
- Some(identity) => {
- error!("TODO: implement email send with LOGIN template");
- error!("TODO: send to: {}", email);
- let req_id = self
- .store
- .write_email_validation_request(identity.id, email)
- .await?;
-
- // TODO: provide some dummy email handlers that are used when testing locally...
- error!("TODO: when the request comes back, it needs to hit something like /iam/identity/1234/email-validation/1234?code=2345");
- error!("TODO: consequently, we may want to shorten the url by providing a quick access code and/or /iam/email-validation/1234/validate");
- }
- None => {
- let identity = Identity {
- id: Uuid::new_v4(),
- created_at: OffsetDateTime::now_utc(),
- data: None,
- };
- self.store.write_identity(&identity).await?;
- self.store.write_email(identity.id, email).await?;
- error!("TODO: implement email send with SIGN_UP template");
- self.store
- .write_email_validation_request(identity.id, email)
- .await?;
- }
- }
-
- error!("TODO: think about returning the identity id for which this validation request was created");
- Ok(())
- }
-
- pub async fn get_identity(&self, id: IdentityId) -> Result<Identity, SecdError> {
- Ok(self.store.read_identity(&id).await?)
- }
-
- pub async fn create_email_validation(email: String) -> Result<(), SecdError> {
- Ok(())
- }
-}
diff --git a/src/main.old b/src/main.old
deleted file mode 100644
index e9d36c5..0000000
--- a/src/main.old
+++ /dev/null
@@ -1,112 +0,0 @@
-mod api;
-mod client;
-mod service;
-mod util;
-
-use std::error::Error;
-
-use client::sqldb::PostgresClient;
-use env_logger::Env;
-use service::authn::Authn;
-use sqlx::postgres::PgPoolOptions;
-
-#[async_std::main]
-async fn main() -> Result<(), Box<dyn Error>> {
- env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init();
-
- // Load configuration
- // which DB do you want to use?
- // what is the connection string (e.g. location, pass, etc...)?
-
- let pool = PgPoolOptions::new()
- .max_connections(5)
- .connect("postgres://secduser:p4ssw0rd@localhost:5419/secd")
- .await?;
-
- sqlx::migrate!("store/sql/migrations").run(&pool).await?;
-
- // there are a few routes
- // the service itself just provides some local functions which may be wrapped in a server.
- // if you want to use the server, then you start the java/python/rust/ruby/go/etc... server
- // otherwise, you just bring in the java/python/rust/ruby/go/etc... client
- // also...maybe a terraform template to launch a _minimal_ auth server
- // with your choice of RDS, dynamo, bigquery, or even local sqlite...
-
- // obviously need to configure terraform things...
-
- // if using the server, then you need to configure a few things:
- // oauth endpoint with response_type, client_id
-
- // scratch
- let pg_client = Box::new(PostgresClient::new(pool));
- let authn = Authn { store: pg_client };
-
- //////////////////////////////////////////////////
- // CREATE NEW IDENTITY // which would be saved by the client
- let identity = authn.register_identity().await?;
-
- //////////////////////////////////////////////////
- // Register a new oauth provider with some secrets, redirect, ids, etc...
- authn
- .register_oauth_provider(
- api::OauthProvider::Google,
- format!("client_id_{}", "CLIENT_SECRET_123"),
- format!("client_secret_{}", util::generate_random_url_safe(4)),
- "https://iam.SOMESITE.com/goauth...provided by default or customized".to_string(),
- )
- .await?;
-
- //////////////////////////////////////////////////
- // Start oauth challenge and return the appropriate location.
- let loc = authn
- .initiate_oauth_challenge(identity, api::OauthProvider::Google)
- .await?;
-
- //////////////////////////////////////////////////
- // Complete oauth challenge and return a session token
- // let session = authn
- // .complete_oauth_challenge(identity, api::OauthProvider::Google, state, access_token, expires_at, raw);
-
- //////////////////////////////////////////////////
- // Start email challenge
- // authn.initiate_email_challenge(identity, email_address);
-
- //////////////////////////////////////////////////
- // Complete email challenge
- // let session = authn.complete_email_challenge(email_address, code);
-
- //////////////////////////////////////////////////
- // Start SMS challenge
- // authn.initiate_sms_challenge(identity, phone_number);
-
- //////////////////////////////////////////////////
- // Complete SMS challenge
- // let session = authn.complete_sms_challenge(phone_number, code);
-
- //////////////////////////////////////////////////
- // Validate credentials
- // let session = authn.validate(username, passphrase);
-
- //////////////////////////////////////////////////
- // Revoke session
- // authn.revoke_session(token);
-
- //////////////////////////////////////////////////
- // Create API key
- // let pub, priv = authn.generate_api_key(identity, Some(expires_at));
-
- //////////////////////////////////////////////////
- // Revoke API key
- // authn.revoke_api_key(pub, priv);
-
- //////////////////////////////////////////////////
- // Revoke identity
- // authn.revoke_identity(identity);
-
- println!("Oauth2.0 URL: {}", loc);
-
- Ok(())
-}
-
-// TODO: oauth flow
-// TODO: email flow
diff --git a/src/util/mod.rs b/src/util/mod.rs
deleted file mode 100644
index c939b95..0000000
--- a/src/util/mod.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-use log::error;
-use rand::distributions::Alphanumeric;
-use rand::{thread_rng, Rng};
-
-pub fn log_err(e: sqlx::Error) -> sqlx::Error {
- error!("{:?}", e);
- e
-}
-pub fn generate_random_url_safe(n: usize) -> String {
- thread_rng()
- .sample_iter(&Alphanumeric)
- .take(n)
- .map(char::from)
- .collect()
-}