aboutsummaryrefslogtreecommitdiff
path: root/crates/secd/src/auth/n.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--crates/secd/src/auth/n.rs166
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?;