aboutsummaryrefslogtreecommitdiff
path: root/crates/secd/src/lib.rs
diff options
context:
space:
mode:
authorbenj <benj@rse8.com>2023-04-24 13:24:45 -0700
committerbenj <benj@rse8.com>2023-04-24 13:24:45 -0700
commiteb92f823c31a5e702af7005231f0d6915aad3342 (patch)
treebb624786a47accb2dfcfe95d20c00c9624c28a9c /crates/secd/src/lib.rs
parent176aae037400b43cb3971cd968afe59c73b3097a (diff)
downloadsecdiam-eb92f823c31a5e702af7005231f0d6915aad3342.tar
secdiam-eb92f823c31a5e702af7005231f0d6915aad3342.tar.gz
secdiam-eb92f823c31a5e702af7005231f0d6915aad3342.tar.bz2
secdiam-eb92f823c31a5e702af7005231f0d6915aad3342.tar.lz
secdiam-eb92f823c31a5e702af7005231f0d6915aad3342.tar.xz
secdiam-eb92f823c31a5e702af7005231f0d6915aad3342.tar.zst
secdiam-eb92f823c31a5e702af7005231f0d6915aad3342.zip
email templates, sendgrid, creds, and some experimental things
Started playing with namespace configs and integrating with zanzibar impls. Still lot's of experimenting and dead code going on.
Diffstat (limited to 'crates/secd/src/lib.rs')
-rw-r--r--crates/secd/src/lib.rs183
1 files changed, 150 insertions, 33 deletions
diff --git a/crates/secd/src/lib.rs b/crates/secd/src/lib.rs
index 15615b2..54759a5 100644
--- a/crates/secd/src/lib.rs
+++ b/crates/secd/src/lib.rs
@@ -2,8 +2,10 @@ pub mod auth;
mod client;
mod util;
+use async_trait::async_trait;
+use auth::z::Relationship;
use client::{
- email::{EmailMessenger, EmailMessengerError, LocalMailer},
+ email::{EmailMessenger, EmailMessengerError, LocalMailer, Sendgrid},
spice::{Spice, SpiceError},
store::{
sql_db::{PgClient, SqliteClient},
@@ -11,28 +13,40 @@ use client::{
},
};
use email_address::EmailAddress;
-use log::{error, info};
+use lettre::message::Mailbox;
+use log::{error, info, warn};
use serde::{Deserialize, Serialize};
-use serde_with::{serde_as, DisplayFromStr};
-use std::{env::var, str::FromStr, sync::Arc};
+use serde_with::serde_as;
+use std::{
+ env::{set_var, var},
+ str::FromStr,
+ sync::Arc,
+};
use strum_macros::{Display, EnumString, EnumVariantNames};
use time::OffsetDateTime;
-use url::Url;
+use util::crypter::{Crypter, CrypterError};
use uuid::Uuid;
pub const ENV_AUTH_STORE_CONN_STRING: &str = "SECD_AUTH_STORE_CONN_STRING";
+pub const ENV_CRYPTER_SECRET_KEY: &str = "SECD_CRYPTER_SECRET_KEY";
+pub const ENV_EMAIL_ADDRESS_FROM: &str = "SECD_EMAIL_ADDRESS_FROM";
+pub const ENV_EMAIL_ADDRESS_REPLYTO: &str = "SECD_EMAIL_ADDRESS_REPLYTO";
pub const ENV_EMAIL_MESSENGER: &str = "SECD_EMAIL_MESSENGER";
pub const ENV_EMAIL_MESSENGER_CLIENT_ID: &str = "SECD_EMAIL_MESSENGER_CLIENT_ID";
pub const ENV_EMAIL_MESSENGER_CLIENT_SECRET: &str = "SECD_EMAIL_MESSENGER_CLIENT_SECRET";
+pub const ENV_EMAIL_SENDGRID_API_KEY: &str = "SECD_EMAIL_SENDGRID_API_KEY";
+pub const ENV_EMAIL_SIGNIN_MESSAGE: &str = "SECD_EMAIL_SIGNIN_MESSAGE";
+pub const ENV_EMAIL_SIGNUP_MESSAGE: &str = "SECD_EMAIL_SIGNUP_MESSAGE";
pub const ENV_SPICE_SECRET: &str = "SECD_SPICE_SECRET";
pub const ENV_SPICE_SERVER: &str = "SECD_SPICE_SERVER";
-const SESSION_SIZE_BYTES: usize = 32;
-const SESSION_DURATION: i64 = 60 /* seconds*/ * 60 /* minutes */ * 24 /* hours */ * 360 /* days */;
-const EMAIL_VALIDATION_DURATION: i64 = 60 /* seconds*/ * 15 /* minutes */;
const ADDRESSS_VALIDATION_CODE_SIZE: u8 = 6;
const ADDRESS_VALIDATION_ALLOWS_ATTEMPTS: u8 = 5;
const ADDRESS_VALIDATION_IDENTITY_SURJECTION: bool = true;
+const CRYPTER_SECRET_KEY_DEFAULT: &str = "sup3rs3cr3t";
+const EMAIL_VALIDATION_DURATION: i64 = 60 /* seconds*/ * 15 /* minutes */;
+const SESSION_DURATION: i64 = 60 /* seconds*/ * 60 /* minutes */ * 24 /* hours */ * 360 /* days */;
+const SESSION_SIZE_BYTES: usize = 32;
pub type AddressId = Uuid;
pub type AddressValidationId = Uuid;
@@ -45,10 +59,17 @@ pub type SessionToken = String;
#[derive(Debug, derive_more::Display, thiserror::Error)]
pub enum SecdError {
+ // AuthenticationError(AuthnError);
+ // AuthorizationError(AuthzError);
+ // // InitializationError(...)
AddressValidationFailed,
AddressValidationSessionExchangeFailed,
AddressValidationExpiredOrConsumed,
+ CredentialAlreadyExists,
+
+ CrypterError(#[from] CrypterError),
+
TooManyIdentities,
IdentityNotFound,
@@ -69,9 +90,62 @@ pub enum SecdError {
}
pub struct Secd {
- store: Arc<dyn Store + Send + Sync + 'static>,
+ crypter: Crypter,
email_messenger: Arc<dyn EmailMessenger + Send + Sync + 'static>,
spice: Option<Arc<Spice>>,
+ store: Arc<dyn Store + Send + Sync + 'static>,
+ cfg: Cfg,
+}
+
+struct Cfg {
+ email_address_from: Option<Mailbox>,
+ email_address_replyto: Option<Mailbox>,
+ email_signup_message: Option<String>,
+ email_signin_message: Option<String>,
+}
+
+#[async_trait]
+pub trait Authentication {
+ async fn validate_address(
+ &self,
+ address_type: AddressType,
+ identity_id: Option<IdentityId>,
+ ) -> Result<AddressValidation, SecdError>;
+
+ async fn complete_address_validation(
+ &self,
+ validation_id: &AddressValidationId,
+ plaintext_token: Option<String>,
+ plaintext_code: Option<String>,
+ ) -> Result<AddressValidation, SecdError>;
+
+ async fn create_credential(
+ &self,
+ t: &CredentialType,
+ identity_id: Option<IdentityId>,
+ ) -> Result<IdentityId, SecdError>;
+ // async fn update_credential(&self, t: &CredentialType) -> Result<(), SecdError>;
+ async fn reset_credential(
+ &self,
+ t: &CredentialType,
+ address: &AddressType,
+ ) -> Result<Credential, SecdError>;
+ async fn validate_credential(&self, t: &CredentialType) -> Result<Credential, SecdError>;
+
+ // async fn expire_session_chain(&self, t: &SessionToken) -> Result<(), SecdError>;
+ // async fn expire_sessions(&self, i: &IdentityId) -> Result<(), SecdError>;
+
+ // async fn get_identity(&self, t: &SessionToken) -> Result<Identity, SecdError>;
+ // async fn get_session(&self, t: &SessionToken) -> Result<Session, SecdError>;
+}
+
+#[async_trait]
+pub trait Authorization {
+ async fn check(&self, r: &Relationship) -> Result<bool, SecdError>;
+ async fn expand(&self) -> Result<(), SecdError>;
+ async fn read(&self) -> Result<(), SecdError>;
+ async fn watch(&self) -> Result<(), SecdError>;
+ async fn write(&self, relationships: &[Relationship]) -> Result<(), SecdError>;
}
#[derive(Display, Debug, Serialize, Deserialize, EnumString, EnumVariantNames)]
@@ -136,34 +210,53 @@ pub enum AddressType {
Sms { phone_number: Option<PhoneNumber> },
}
+#[serde_as]
#[derive(Debug, Serialize)]
pub struct Credential {
pub id: CredentialId,
pub identity_id: IdentityId,
pub t: CredentialType,
+ #[serde(with = "time::serde::timestamp")]
+ pub created_at: OffsetDateTime,
+ #[serde(with = "time::serde::timestamp::option")]
+ pub revoked_at: Option<OffsetDateTime>,
+ #[serde(with = "time::serde::timestamp::option")]
+ pub deleted_at: Option<OffsetDateTime>,
}
#[serde_as]
-#[derive(Debug, Serialize)]
+#[derive(Debug, Display, Serialize, Deserialize, EnumString)]
pub enum CredentialType {
- Passphrase {
- key: String,
- value: String,
- },
- Oicd {
- value: String,
- },
- OneTimeCodes {
- codes: Vec<String>,
- },
- Totp {
- #[serde_as(as = "DisplayFromStr")]
- url: Url,
- code: String,
- },
- WebAuthn {
- value: String,
- },
+ Passphrase { key: String, value: String },
+ Oidc { value: String },
+ OneTimeCodes { codes: Vec<String> },
+ // Totp {
+ // #[serde_as(as = "DisplayFromStr")]
+ // url: Url,
+ // code: String,
+ // },
+ WebAuthn { value: String },
+}
+
+struct SecuredCredential {
+ pub id: CredentialId,
+ pub identity_id: IdentityId,
+ pub t: CredentialType,
+ pub created_at: OffsetDateTime,
+ pub revoked_at: Option<OffsetDateTime>,
+ pub deleted_at: Option<OffsetDateTime>,
+}
+
+enum SecuredCredentialType {
+ Passphrase { key: String, value: String },
+ Oidc { value: String },
+ OneTimeCodes { codes: Vec<String> },
+ // Totp {
+ // #[serde_as(as = "DisplayFromStr")]
+ // url: Url,
+ // code: String,
+ // },
+ WebAuthn { value: String },
}
#[serde_with::skip_serializing_none]
@@ -207,12 +300,29 @@ impl Secd {
&var(ENV_EMAIL_MESSENGER).unwrap_or(AuthEmailMessenger::Local.to_string()),
)
.expect("unreachable f4ad0f48-0812-427f-b477-0f9c67bb69c5");
- let email_messenger_client_id = var(ENV_EMAIL_MESSENGER_CLIENT_ID).ok();
- let email_messenger_client_secret = var(ENV_EMAIL_MESSENGER_CLIENT_SECRET).ok();
+ let crypter_secret_key = var(ENV_CRYPTER_SECRET_KEY).unwrap_or_else(|_| {
+ warn!(
+ "NO CRYPTER KEY PROVIDED, USING DEFAULT KEY. DO NOT USE THIS KEY IN PRODUCTION. PROVIDE A UNIQUE SECRET KEY BY SETTING THE ENVIORNMENT VARIABLE {}. THE DEFAULT KEY IS: {}",
+ ENV_CRYPTER_SECRET_KEY,
+ CRYPTER_SECRET_KEY_DEFAULT,
+ );
+ CRYPTER_SECRET_KEY_DEFAULT.to_string()
+ });
info!("starting client with auth_store: {:?}", auth_store);
info!("starting client with email_messenger: {:?}", auth_store);
+ let cfg = Cfg {
+ email_address_from: var(ENV_EMAIL_ADDRESS_FROM)
+ .ok()
+ .and_then(|s| s.parse().ok()),
+ email_address_replyto: var(ENV_EMAIL_ADDRESS_REPLYTO)
+ .ok()
+ .and_then(|s| s.parse().ok()),
+ email_signup_message: var(ENV_EMAIL_SIGNUP_MESSAGE).ok(),
+ email_signin_message: var(ENV_EMAIL_SIGNIN_MESSAGE).ok(),
+ };
+
let store = match auth_store {
AuthStore::Sqlite { conn } => {
if z_schema.is_some() {
@@ -252,7 +362,10 @@ impl Secd {
};
let email_sender = match email_messenger {
- AuthEmailMessenger::Local => LocalMailer {},
+ AuthEmailMessenger::Local => LocalMailer::new(),
+ AuthEmailMessenger::Sendgrid => Sendgrid::new(
+ var(ENV_EMAIL_SENDGRID_API_KEY).expect("No SENDGRID_API_KEY provided"),
+ ),
_ => unimplemented!(),
};
@@ -267,10 +380,14 @@ impl Secd {
None => None,
};
+ let crypter = Crypter::new(crypter_secret_key.as_bytes());
+
Ok(Secd {
- store,
- email_messenger: Arc::new(email_sender),
+ crypter,
+ email_messenger: email_sender,
spice,
+ store,
+ cfg,
})
}
}