diff options
Diffstat (limited to '')
| -rw-r--r-- | crates/secd/src/auth/n.rs | 166 |
1 files changed, 143 insertions, 23 deletions
diff --git a/crates/secd/src/auth/n.rs b/crates/secd/src/auth/n.rs index 1d3b2d5..1f32fd6 100644 --- a/crates/secd/src/auth/n.rs +++ b/crates/secd/src/auth/n.rs @@ -1,20 +1,23 @@ -use std::str::FromStr; - use crate::{ client::{ - email::{EmailValidationMessage, Sendable}, + email::{ + parse_email_template, EmailValidationMessage, Sendable, DEFAULT_SIGNIN_EMAIL, + DEFAULT_SIGNUP_EMAIL, + }, store::{ - AddressLens, AddressValidationLens, IdentityLens, SessionLens, Storable, StoreError, + AddressLens, AddressValidationLens, CredentialLens, IdentityLens, SessionLens, + Storable, StoreError, }, }, util, Address, AddressType, AddressValidation, AddressValidationId, AddressValidationMethod, - Credential, CredentialType, Identity, PhoneNumber, Secd, SecdError, Session, SessionToken, + Credential, CredentialType, Identity, IdentityId, Secd, SecdError, Session, SessionToken, ADDRESSS_VALIDATION_CODE_SIZE, ADDRESS_VALIDATION_ALLOWS_ATTEMPTS, ADDRESS_VALIDATION_IDENTITY_SURJECTION, EMAIL_VALIDATION_DURATION, }; use email_address::EmailAddress; use log::warn; use rand::Rng; +use std::str::FromStr; use time::{Duration, OffsetDateTime}; use uuid::Uuid; @@ -22,10 +25,15 @@ impl Secd { pub async fn validate_email( &self, email_address: &str, - identity_id: Option<Uuid>, + identity_id: Option<IdentityId>, ) -> Result<AddressValidation, SecdError> { let email_address = EmailAddress::from_str(email_address)?; - // record address (idempotent operation) + let mut email_template = self + .cfg + .email_signup_message + .clone() + .unwrap_or(DEFAULT_SIGNUP_EMAIL.into()); + let mut address = Address { id: Uuid::new_v4(), t: AddressType::Email { @@ -50,6 +58,12 @@ impl Secd { .into_iter() .next() .ok_or(SecdError::AddressValidationFailed)?; + + email_template = self + .cfg + .email_signin_message + .clone() + .unwrap_or(DEFAULT_SIGNIN_EMAIL.into()); } let secret = hex::encode(rand::thread_rng().gen::<[u8; 32]>()); @@ -76,13 +90,23 @@ impl Secd { validation.write(self.store.clone()).await?; - let msg =EmailValidationMessage { + let msg = EmailValidationMessage { + from_address: self + .cfg + .email_address_from + .clone() + .unwrap_or("SecD <noreply@secd.com>".parse().unwrap()), + replyto_address: self + .cfg + .email_address_replyto + .clone() + .unwrap_or("SecD <noreply@secd.com>".parse().unwrap()), recipient: email_address.clone(), - subject: "Confirm Your Email".into(), - body: format!("This is an email validation message. Click this link [{:?}?s={}] or use the code [{}]", validation.id, secret, code), + subject: "Login Request".into(), + body: parse_email_template(&email_template, validation.id, Some(secret), Some(code))?, }; - match msg.send().await { + match msg.send(self.email_messenger.clone()).await { Ok(_) => { /* TODO: Write down the message*/ } Err(e) => { validation.revoked_at = Some(OffsetDateTime::now_utc()); @@ -95,10 +119,11 @@ impl Secd { } pub async fn validate_sms( &self, - phone_number: &PhoneNumber, + // phone_number: &PhoneNumber, ) -> Result<AddressValidation, SecdError> { todo!() } + pub async fn complete_address_validation( &self, validation_id: &AddressValidationId, @@ -215,21 +240,83 @@ impl Secd { Ok(session) } + pub async fn create_credential( &self, t: CredentialType, - key: String, - value: Option<String>, - ) -> Result<Credential, SecdError> { - todo!() + identity_id: Option<IdentityId>, + ) -> Result<Identity, SecdError> { + let identity = match identity_id { + Some(id) => Identity::find( + self.store.clone(), + &IdentityLens { + id: Some(&id), + address_type: None, + validated_address: None, + session_token_hash: None, + }, + ) + .await? + .into_iter() + .nth(0) + .ok_or(SecdError::IdentityNotFound)?, + + None => { + let id = Identity { + id: Uuid::new_v4(), + address_validations: vec![], + credentials: vec![], + rules: vec![], + metadata: None, + created_at: OffsetDateTime::now_utc(), + deleted_at: None, + }; + id.write(self.store.clone()).await?; + id + } + }; + + let mut credential = match &Credential::find( + self.store.clone(), + &CredentialLens { + id: None, + identity_id: Some(identity.id), + t: Some(&t), + restrict_by_key: Some(false), + }, + ) + .await?[..] + { + [] => Credential { + id: Uuid::new_v4(), + identity_id: identity.id, + t, + created_at: OffsetDateTime::now_utc(), + revoked_at: None, + deleted_at: None, + }, + _ => return Err(SecdError::CredentialAlreadyExists), + }; + + credential.hash(&self.crypter)?; + credential + .write(self.store.clone()) + .await + .map_err(|err| match err { + StoreError::IdempotentCheckAlreadyExists => SecdError::CredentialAlreadyExists, + err => SecdError::StoreError(err), + })?; + + Ok(identity) } pub async fn validate_credential( &self, - t: CredentialType, - key: String, - value: Option<String>, + // t: CredentialType, + // key: String, + // value: Option<String>, ) -> Result<Session, SecdError> { + // Credential::find(store, lens) use key here as unique index todo!() } @@ -254,15 +341,23 @@ impl Secd { } } - pub async fn get_identity(&self, i: &SessionToken) -> Result<Identity, SecdError> { - let token_hash = util::hash(&hex::decode(i)?); + pub async fn get_identity( + &self, + i: Option<IdentityId>, + t: Option<SessionToken>, + ) -> Result<Identity, SecdError> { + let token_hash = match t { + Some(tok) => Some(util::hash(&hex::decode(&tok)?)), + None => None, + }; + let mut i = Identity::find( self.store.clone(), &IdentityLens { - id: None, + id: i.as_ref(), address_type: None, validated_address: None, - session_token_hash: Some(token_hash), + session_token_hash: token_hash, }, ) .await?; @@ -279,6 +374,31 @@ impl Secd { } } + pub async fn update_identity_metadata( + &self, + i: IdentityId, + md: String, + ) -> Result<Identity, SecdError> { + let mut identity = Identity::find( + self.store.clone(), + &IdentityLens { + id: Some(&i), + address_type: None, + validated_address: None, + session_token_hash: None, + }, + ) + .await? + .into_iter() + .nth(0) + .ok_or(SecdError::IdentityNotFound)?; + + identity.metadata = Some(md); + identity.write(self.store.clone()).await?; + + Ok(identity) + } + pub async fn revoke_session(&self, session: &mut Session) -> Result<(), SecdError> { session.revoked_at = Some(OffsetDateTime::now_utc()); session.write(self.store.clone()).await?; |
