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.rs203
1 files changed, 151 insertions, 52 deletions
diff --git a/crates/secd/src/auth/n.rs b/crates/secd/src/auth/n.rs
index 1f32fd6..dde6e7d 100644
--- a/crates/secd/src/auth/n.rs
+++ b/crates/secd/src/auth/n.rs
@@ -5,12 +5,12 @@ use crate::{
DEFAULT_SIGNUP_EMAIL,
},
store::{
- AddressLens, AddressValidationLens, CredentialLens, IdentityLens, SessionLens,
- Storable, StoreError,
+ AddressLens, AddressValidationLens, CredentialLens, IdentityLens, Storable, StoreError,
},
},
- util, Address, AddressType, AddressValidation, AddressValidationId, AddressValidationMethod,
- Credential, CredentialType, Identity, IdentityId, Secd, SecdError, Session, SessionToken,
+ util::{self, ErrorContext},
+ Address, AddressType, AddressValidation, AddressValidationId, AddressValidationMethod,
+ Credential, CredentialId, CredentialType, Identity, IdentityId, Secd, SecdError,
ADDRESSS_VALIDATION_CODE_SIZE, ADDRESS_VALIDATION_ALLOWS_ATTEMPTS,
ADDRESS_VALIDATION_IDENTITY_SURJECTION, EMAIL_VALIDATION_DURATION,
};
@@ -84,8 +84,8 @@ impl Secd {
revoked_at: None,
validated_at: None,
attempts: 0,
- hashed_token: util::hash(&secret.as_bytes()),
- hashed_code: util::hash(&code.as_bytes()),
+ hashed_token: util::hash(secret.as_bytes()),
+ hashed_code: util::hash(code.as_bytes()),
};
validation.write(self.store.clone()).await?;
@@ -95,11 +95,13 @@ impl Secd {
.cfg
.email_address_from
.clone()
+ .and_then(|s| s.parse().ok())
.unwrap_or("SecD <noreply@secd.com>".parse().unwrap()),
replyto_address: self
.cfg
.email_address_replyto
.clone()
+ .and_then(|s| s.parse().ok())
.unwrap_or("SecD <noreply@secd.com>".parse().unwrap()),
recipient: email_address.clone(),
subject: "Login Request".into(),
@@ -129,7 +131,7 @@ impl Secd {
validation_id: &AddressValidationId,
plaintext_token: Option<String>,
plaintext_code: Option<String>,
- ) -> Result<Session, SecdError> {
+ ) -> Result<Credential, SecdError> {
let mut validation = AddressValidation::find(
self.store.clone(),
&AddressValidationLens {
@@ -193,7 +195,6 @@ impl Secd {
id: None,
address_type: Some(&validation.address.t),
validated_address: Some(true),
- session_token_hash: None,
},
)
.await?;
@@ -210,6 +211,7 @@ impl Secd {
id: Uuid::new_v4(),
address_validations: vec![],
credentials: vec![],
+ new_credentials: vec![],
rules: vec![],
metadata: None,
created_at: OffsetDateTime::now_utc(),
@@ -235,30 +237,72 @@ impl Secd {
validation.validated_at = Some(OffsetDateTime::now_utc());
validation.write(self.store.clone()).await?;
- let session = Session::new(validation.identity_id.expect("unreachable d3ded289-72eb-4a42-a37d-f5c9c697cc61 [assert(identity.is_some()) prevents this]"))?;
+ let mut session = Credential::new_session(validation.identity_id.expect("unreachable d3ded289-72eb-4a42-a37d-f5c9c697cc61 [assert(identity.is_some()) prevents this]"))?;
+ let plaintext_type = session.t.clone();
+
+ session.hash(&self.crypter)?;
session.write(self.store.clone()).await?;
+ session.t = plaintext_type;
+
Ok(session)
}
+ pub async fn create_identity_with_credential(
+ &self,
+ t: CredentialType,
+ identity_id: IdentityId,
+ metadata: Option<String>,
+ ) -> Result<Identity, SecdError> {
+ let identity = Identity::find(
+ self.store.clone(),
+ &IdentityLens {
+ id: Some(&identity_id),
+ address_type: None,
+ validated_address: None,
+ },
+ )
+ .await?;
+
+ if !identity.is_empty() {
+ log::error!("identity was found while creating a new identity with a credential");
+ return Err(SecdError::IdentityAlreadyExists);
+ }
+
+ Identity {
+ id: identity_id,
+ address_validations: vec![],
+ credentials: vec![],
+ new_credentials: vec![],
+ rules: vec![],
+ metadata,
+ created_at: OffsetDateTime::now_utc(),
+ deleted_at: None,
+ }
+ .write(self.store.clone())
+ .await?;
+
+ self.create_credential(t, Some(identity_id), None).await
+ }
+
pub async fn create_credential(
&self,
t: CredentialType,
identity_id: Option<IdentityId>,
+ expires_at: Option<OffsetDateTime>,
) -> Result<Identity, SecdError> {
- let identity = match identity_id {
+ let mut 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)
+ .next()
.ok_or(SecdError::IdentityNotFound)?,
None => {
@@ -266,6 +310,7 @@ impl Secd {
id: Uuid::new_v4(),
address_validations: vec![],
credentials: vec![],
+ new_credentials: vec![],
rules: vec![],
metadata: None,
created_at: OffsetDateTime::now_utc(),
@@ -282,7 +327,6 @@ impl Secd {
id: None,
identity_id: Some(identity.id),
t: Some(&t),
- restrict_by_key: Some(false),
},
)
.await?[..]
@@ -290,9 +334,9 @@ impl Secd {
[] => Credential {
id: Uuid::new_v4(),
identity_id: identity.id,
- t,
+ t: t.clone(),
created_at: OffsetDateTime::now_utc(),
- revoked_at: None,
+ revoked_at: expires_at,
deleted_at: None,
},
_ => return Err(SecdError::CredentialAlreadyExists),
@@ -307,57 +351,100 @@ impl Secd {
err => SecdError::StoreError(err),
})?;
- Ok(identity)
- }
+ identity.new_credentials.push(Credential {
+ id: credential.id,
+ identity_id: credential.identity_id,
+ t,
+ created_at: credential.created_at,
+ revoked_at: credential.revoked_at,
+ deleted_at: credential.deleted_at,
+ });
- pub async fn validate_credential(
- &self,
- // t: CredentialType,
- // key: String,
- // value: Option<String>,
- ) -> Result<Session, SecdError> {
- // Credential::find(store, lens) use key here as unique index
- todo!()
+ Ok(identity)
}
- pub async fn get_session(&self, t: &SessionToken) -> Result<Session, SecdError> {
- let token = hex::decode(t)?;
- let mut session = Session::find(
+ pub async fn validate_credential(&self, t: CredentialType) -> Result<Credential, SecdError> {
+ let mut retrieved = Credential::find(
self.store.clone(),
- &SessionLens {
- token_hash: Some(&util::hash(&token)),
+ &CredentialLens {
+ id: None,
identity_id: None,
+ t: Some(&t),
},
)
- .await?;
- assert!(session.len() <= 1, "get session failed: multiple sessions found for a single token. This is very _very_ bad.");
+ .await?
+ .into_iter()
+ .next()
+ .ok_or(SecdError::InvalidCredential)?;
- if session.is_empty() {
- return Err(SecdError::InvalidSession);
- } else {
- let mut session = session.swap_remove(0);
- session.token = token;
- Ok(session)
- }
+ match retrieved.revoked_at {
+ Some(t) if t <= OffsetDateTime::now_utc() => {
+ log::debug!("credential was revoked");
+ Err(SecdError::InvalidCredential)
+ }
+ _ => Ok(()),
+ }?;
+
+ match retrieved.deleted_at {
+ Some(t) if t <= OffsetDateTime::now_utc() => {
+ log::debug!("credential was deleted");
+ Err(SecdError::InvalidCredential)
+ }
+ _ => Ok(()),
+ }?;
+
+ retrieved.hash_compare(&t, &self.crypter)?;
+
+ // Return the initially provided plaintext credential since it's valid
+ retrieved.t = t;
+
+ Ok(retrieved)
}
pub async fn get_identity(
&self,
i: Option<IdentityId>,
- t: Option<SessionToken>,
+ t: Option<CredentialType>,
) -> Result<Identity, SecdError> {
- let token_hash = match t {
- Some(tok) => Some(util::hash(&hex::decode(&tok)?)),
- None => None,
- };
+ if i.is_none() && t.is_none() {
+ log::error!("get_identity expects that at least one of IdentityId or CredentialType is provided. None were found.");
+ return Err(SecdError::IdentityNotFound);
+ }
+
+ let c = Credential::find(
+ self.store.clone(),
+ &CredentialLens {
+ id: None,
+ identity_id: i,
+ t: t.as_ref(),
+ },
+ )
+ .await?;
+
+ assert!(
+ c.len() <= 1,
+ "The provided credential refers to more than one identity. This is very _very_ bad."
+ );
+ let identity_id = c
+ .into_iter()
+ .next()
+ .ok_or(SecdError::InvalidCredential)
+ .ctx("No identities were found for the provided identity_id and credential_type")?
+ .identity_id;
+
+ if i.is_some() && i != Some(identity_id) {
+ log::error!(
+ "The provided identity does not match the identity associated with this credential"
+ );
+ return Err(SecdError::InvalidCredential);
+ }
let mut i = Identity::find(
self.store.clone(),
&IdentityLens {
- id: i.as_ref(),
+ id: Some(&identity_id),
address_type: None,
validated_address: None,
- session_token_hash: token_hash,
},
)
.await?;
@@ -368,7 +455,7 @@ impl Secd {
);
if i.is_empty() {
- return Err(SecdError::IdentityNotFound);
+ Err(SecdError::IdentityNotFound)
} else {
Ok(i.swap_remove(0))
}
@@ -385,12 +472,11 @@ impl Secd {
id: Some(&i),
address_type: None,
validated_address: None,
- session_token_hash: None,
},
)
.await?
.into_iter()
- .nth(0)
+ .next()
.ok_or(SecdError::IdentityNotFound)?;
identity.metadata = Some(md);
@@ -399,9 +485,22 @@ impl Secd {
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?;
+ pub async fn revoke_credential(&self, credential_id: CredentialId) -> Result<(), SecdError> {
+ let mut credential = Credential::find(
+ self.store.clone(),
+ &CredentialLens {
+ id: Some(credential_id),
+ identity_id: None,
+ t: None,
+ },
+ )
+ .await?
+ .into_iter()
+ .next()
+ .ok_or(SecdError::InvalidCredential)?;
+
+ credential.revoked_at = Some(OffsetDateTime::now_utc());
+ credential.write(self.store.clone()).await?;
Ok(())
}
}