aboutsummaryrefslogtreecommitdiff
path: root/crates/secd/src/lib.rs
diff options
context:
space:
mode:
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,
})
}
}