diff options
Diffstat (limited to '')
| -rw-r--r-- | crates/secd/src/client/email/mod.rs | 18 | ||||
| -rw-r--r-- | crates/secd/src/client/spice/mod.rs | 21 | ||||
| -rw-r--r-- | crates/secd/src/client/store/mod.rs | 84 | ||||
| -rw-r--r-- | crates/secd/src/client/store/sql_db.rs | 102 |
4 files changed, 47 insertions, 178 deletions
diff --git a/crates/secd/src/client/email/mod.rs b/crates/secd/src/client/email/mod.rs index 7c7b233..9e591ba 100644 --- a/crates/secd/src/client/email/mod.rs +++ b/crates/secd/src/client/email/mod.rs @@ -42,7 +42,7 @@ pub enum MessengerType { pub(crate) struct LocalMailer {} impl LocalMailer { - pub fn new() -> Arc<dyn EmailMessenger + Send + Sync + 'static> { + pub fn new_ref() -> Arc<dyn EmailMessenger + Send + Sync + 'static> { warn!("You are using the local mailer, which will not work in production!"); Arc::new(LocalMailer {}) } @@ -59,7 +59,7 @@ pub(crate) struct Sendgrid { pub api_key: String, } impl Sendgrid { - pub fn new(api_key: String) -> Arc<dyn EmailMessenger + Send + Sync + 'static> { + pub fn new_ref(api_key: String) -> Arc<dyn EmailMessenger + Send + Sync + 'static> { Arc::new(Sendgrid { api_key }) } } @@ -89,7 +89,7 @@ impl Sendable for EmailValidationMessage { .subject(self.subject.clone()) .multipart(MultiPart::alternative_plain_html( "".to_string(), - String::from(self.body.clone()), + self.body.clone(), ))?; let mailer = lettre::SmtpTransport::unencrypted_localhost(); @@ -135,7 +135,7 @@ pub(crate) fn parse_email_template( validation_secret: Option<String>, validation_code: Option<String>, ) -> Result<String, EmailMessengerError> { - let mut t = template.clone().to_string(); + let mut t = String::from(template); // We do not allow substutions for a variety of reasons, but mainly security ones. // The only things we want to share are those which secd allows. In this case, that // means we only send an email with static content as provided by the filter, except @@ -143,8 +143,14 @@ pub(crate) fn parse_email_template( // present in the email. t = t.replace("{{secd::validation_id}}", &validation_id.to_string()); - validation_secret.map(|secret| t = t.replace("{{secd::validation_secret}}", &secret)); - validation_code.map(|code| t = t.replace("{{secd::validation_code}}", &code)); + + if let Some(secret) = validation_secret { + t = t.replace("{{secd::validation_secret}}", &secret); + } + + if let Some(code) = validation_code { + t = t.replace("{{secd::validation_code}}", &code); + } Ok(t) } diff --git a/crates/secd/src/client/spice/mod.rs b/crates/secd/src/client/spice/mod.rs index d3ca30d..67965d7 100644 --- a/crates/secd/src/client/spice/mod.rs +++ b/crates/secd/src/client/spice/mod.rs @@ -3,6 +3,7 @@ // favor of a light weight solution that leverages the Zanzibar API but disregards the // scaling part. +#[allow(clippy::module_inception)] pub mod spice { tonic::include_proto!("authzed.api.v1"); } @@ -10,7 +11,7 @@ pub mod spice { use spice::permissions_service_client::PermissionsServiceClient; use spice::schema_service_client::SchemaServiceClient; use spice::WriteSchemaRequest; -use std::env::var; +use std::matches; use tonic::metadata::MetadataValue; use tonic::transport::Channel; use tonic::{Request, Status}; @@ -19,7 +20,6 @@ use crate::auth::z::{self, Subject}; use crate::client::spice::spice::{ relationship_update, ObjectReference, Relationship, RelationshipUpdate, SubjectReference, }; -use crate::{ENV_SPICE_SECRET, ENV_SPICE_SERVER}; use self::spice::check_permission_response::Permissionship; use self::spice::{consistency, CheckPermissionRequest, Consistency, WriteRelationshipsRequest}; @@ -36,12 +36,7 @@ pub(crate) struct Spice { } impl Spice { - pub async fn new() -> Self { - let secret = - var(ENV_SPICE_SECRET).expect("initialization error: Failed to find SPICE_SECRET"); - let server = - var(ENV_SPICE_SERVER).expect("initialization error: Failed to find SPICE_SERVER"); - + pub async fn new(secret: String, server: String) -> Self { let channel = Channel::from_shared(server) .expect("invalid SPICE_SERVER uri") .connect() @@ -69,10 +64,10 @@ impl Spice { let response = client.check_permission(request).await?.into_inner(); - Ok(match Permissionship::from_i32(response.permissionship) { - Some(Permissionship::HasPermission) => true, - _ => false, - }) + Ok(matches!( + Permissionship::from_i32(response.permissionship), + Some(Permissionship::HasPermission) + )) } pub async fn write_relationship(&self, rs: &[z::Relationship]) -> Result<(), SpiceError> { @@ -83,7 +78,7 @@ impl Spice { let request = tonic::Request::new(WriteRelationshipsRequest { updates: rs - .into_iter() + .iter() .map(|t| RelationshipUpdate { operation: (relationship_update::Operation::Touch as i32), relationship: Some(Relationship { diff --git a/crates/secd/src/client/store/mod.rs b/crates/secd/src/client/store/mod.rs index 7bf01d5..6c42dba 100644 --- a/crates/secd/src/client/store/mod.rs +++ b/crates/secd/src/client/store/mod.rs @@ -6,8 +6,8 @@ use std::sync::Arc; use uuid::Uuid; use crate::{ - util, Address, AddressType, AddressValidation, Credential, CredentialId, CredentialType, - Identity, IdentityId, Session, + Address, AddressType, AddressValidation, Credential, CredentialId, CredentialType, Identity, + IdentityId, }; use self::sql_db::SqlClient; @@ -60,21 +60,13 @@ pub(crate) struct IdentityLens<'a> { pub id: Option<&'a Uuid>, pub address_type: Option<&'a AddressType>, pub validated_address: Option<bool>, - pub session_token_hash: Option<Vec<u8>>, } impl<'a> Lens for IdentityLens<'a> {} -pub(crate) struct SessionLens<'a> { - pub token_hash: Option<&'a Vec<u8>>, - pub identity_id: Option<&'a IdentityId>, -} -impl<'a> Lens for SessionLens<'a> {} - pub(crate) struct CredentialLens<'a> { pub id: Option<CredentialId>, pub identity_id: Option<IdentityId>, pub t: Option<&'a CredentialType>, - pub restrict_by_key: Option<bool>, } impl<'a> Lens for CredentialLens<'a> {} @@ -94,7 +86,7 @@ impl<'a> Storable<'a> for Address { store: Arc<dyn Store>, lens: &'a Self::Lens, ) -> Result<Vec<Self::Item>, StoreError> { - let typ = lens.t.map(|at| at.to_string().clone()); + let typ = lens.t.map(|at| at.to_string()); let typ = typ.as_deref(); let val = lens.t.and_then(|at| at.get_value()); @@ -151,54 +143,18 @@ impl<'a> Storable<'a> for Identity { Ok(match store.get_type() { StoreType::Postgres { c } => { - c.find_identity( - lens.id, - val, - lens.validated_address, - &lens.session_token_hash, - ) - .await? + c.find_identity(lens.id, val, lens.validated_address) + .await? } StoreType::Sqlite { c } => { - c.find_identity( - lens.id, - val, - lens.validated_address, - &lens.session_token_hash, - ) - .await? + c.find_identity(lens.id, val, lens.validated_address) + .await? } }) } } #[async_trait] -impl<'a> Storable<'a> for Session { - type Item = Session; - type Lens = SessionLens<'a>; - - async fn write(&self, store: Arc<dyn Store>) -> Result<(), StoreError> { - let token_hash = util::hash(&self.token); - match store.get_type() { - StoreType::Postgres { c } => c.write_session(self, &token_hash).await?, - StoreType::Sqlite { c } => c.write_session(self, &token_hash).await?, - } - Ok(()) - } - async fn find( - store: Arc<dyn Store>, - lens: &'a Self::Lens, - ) -> Result<Vec<Self::Item>, StoreError> { - let token = lens.token_hash.map(|t| t.clone()).unwrap_or(vec![]); - - Ok(match store.get_type() { - StoreType::Postgres { c } => c.find_session(token, lens.identity_id).await?, - StoreType::Sqlite { c } => c.find_session(token, lens.identity_id).await?, - }) - } -} - -#[async_trait] impl<'a> Storable<'a> for Credential { type Item = Credential; type Lens = CredentialLens<'a>; @@ -217,31 +173,9 @@ impl<'a> Storable<'a> for Credential { ) -> Result<Vec<Self::Item>, StoreError> { Ok(match store.get_type() { StoreType::Postgres { c } => { - c.find_credential( - lens.id, - lens.identity_id, - lens.t, - if let Some(true) = lens.restrict_by_key { - true - } else { - false - }, - ) - .await? - } - StoreType::Sqlite { c } => { - c.find_credential( - lens.id, - lens.identity_id, - lens.t, - if let Some(true) = lens.restrict_by_key { - true - } else { - false - }, - ) - .await? + c.find_credential(lens.id, lens.identity_id, lens.t).await? } + StoreType::Sqlite { c } => c.find_credential(lens.id, lens.identity_id, lens.t).await?, }) } } diff --git a/crates/secd/src/client/store/sql_db.rs b/crates/secd/src/client/store/sql_db.rs index 3e72fe8..7b3a68e 100644 --- a/crates/secd/src/client/store/sql_db.rs +++ b/crates/secd/src/client/store/sql_db.rs @@ -1,7 +1,7 @@ use super::{Store, StoreError, StoreType}; use crate::{ - Address, AddressType, AddressValidation, AddressValidationMethod, Credential, CredentialId, - CredentialType, Identity, IdentityId, Session, + util::ErrorContext, Address, AddressType, AddressValidation, AddressValidationMethod, + Credential, CredentialId, CredentialType, Identity, IdentityId, }; use email_address::EmailAddress; use lazy_static::lazy_static; @@ -26,8 +26,6 @@ const WRITE_CREDENTIAL: &str = "write_credential"; const FIND_CREDENTIAL: &str = "find_credential"; const WRITE_IDENTITY: &str = "write_identity"; const FIND_IDENTITY: &str = "find_identity"; -const WRITE_SESSION: &str = "write_session"; -const FIND_SESSION: &str = "find_session"; const ERR_MSG_MIGRATION_FAILED: &str = "Failed to apply secd migrations to a sql db. File a bug at https://www.github.com/branchcontrol/secdiam"; @@ -59,14 +57,6 @@ lazy_static! { include_str!("../../../store/sqlite/sql/find_identity.sql"), ), ( - WRITE_SESSION, - include_str!("../../../store/sqlite/sql/write_session.sql"), - ), - ( - FIND_SESSION, - include_str!("../../../store/sqlite/sql/find_session.sql"), - ), - ( WRITE_CREDENTIAL, include_str!("../../../store/sqlite/sql/write_credential.sql"), ), @@ -105,14 +95,6 @@ lazy_static! { include_str!("../../../store/pg/sql/find_identity.sql"), ), ( - WRITE_SESSION, - include_str!("../../../store/pg/sql/write_session.sql"), - ), - ( - FIND_SESSION, - include_str!("../../../store/pg/sql/find_session.sql"), - ), - ( WRITE_CREDENTIAL, include_str!("../../../store/pg/sql/write_credential.sql"), ), @@ -145,7 +127,7 @@ impl<T> SqlxResultExt<T> for Result<T, sqlx::Error> { return Err(StoreError::IdempotentCheckAlreadyExists); } } - self.map_err(|e| StoreError::SqlClientError(e)) + self.map_err(StoreError::SqlClientError) } } @@ -169,7 +151,7 @@ impl Store for PgClient { } impl PgClient { - pub async fn new(pool: sqlx::Pool<Postgres>) -> Arc<dyn Store + Send + Sync + 'static> { + pub async fn new_ref(pool: sqlx::Pool<Postgres>) -> Arc<dyn Store + Send + Sync + 'static> { sqlx::migrate!("store/pg/migrations", "secd") .run(&pool) .await @@ -196,7 +178,7 @@ impl Store for SqliteClient { } impl SqliteClient { - pub async fn new(pool: sqlx::Pool<Sqlite>) -> Arc<dyn Store + Send + Sync + 'static> { + pub async fn new_ref(pool: sqlx::Pool<Sqlite>) -> Arc<dyn Store + Send + Sync + 'static> { sqlx::migrate!("store/sqlite/migrations", "secd") .run(&pool) .await @@ -436,7 +418,6 @@ where id: Option<&Uuid>, address_value: Option<&str>, address_is_validated: Option<bool>, - session_token_hash: &Option<Vec<u8>>, ) -> Result<Vec<Identity>, StoreError> { let sqls = get_sqls(&self.sqls_root, FIND_IDENTITY); let rs = sqlx::query_as::< @@ -452,7 +433,6 @@ where .bind(id) .bind(address_value) .bind(address_is_validated) - .bind(session_token_hash) .fetch_all(&self.pool) .await .extend_err()?; @@ -462,7 +442,8 @@ where res.push(Identity { id, address_validations: vec![], - credentials: vec![], + credentials: self.find_credential(None, Some(id), None).await?, + new_credentials: vec![], rules: vec![], metadata, created_at, @@ -473,57 +454,12 @@ where Ok(res) } - pub async fn write_session(&self, s: &Session, token_hash: &[u8]) -> Result<(), StoreError> { - let sqls = get_sqls(&self.sqls_root, WRITE_SESSION); - sqlx::query(&sqls[0]) - .bind(s.identity_id) - .bind(token_hash) - .bind(s.created_at) - .bind(s.expired_at) - .bind(s.revoked_at) - .execute(&self.pool) - .await - .extend_err()?; - - Ok(()) - } - - pub async fn find_session( - &self, - token: Vec<u8>, - identity_id: Option<&Uuid>, - ) -> Result<Vec<Session>, StoreError> { - let sqls = get_sqls(&self.sqls_root, FIND_SESSION); - let rs = - sqlx::query_as::<_, (Uuid, OffsetDateTime, OffsetDateTime, Option<OffsetDateTime>)>( - &sqls[0], - ) - .bind(token) - .bind(identity_id) - .bind(OffsetDateTime::now_utc()) - .bind(OffsetDateTime::now_utc()) - .fetch_all(&self.pool) - .await - .extend_err()?; - - let mut res = vec![]; - for (identity_id, created_at, expired_at, revoked_at) in rs.into_iter() { - res.push(Session { - identity_id, - token: vec![], - created_at, - expired_at, - revoked_at, - }); - } - Ok(res) - } - pub async fn write_credential(&self, c: &Credential) -> Result<(), StoreError> { let sqls = get_sqls(&self.sqls_root, WRITE_CREDENTIAL); let partial_key = match &c.t { - crate::CredentialType::Passphrase { key, value: _ } => Some(key.clone()), - _ => None, + CredentialType::Passphrase { key, .. } => Some(key.clone()), + CredentialType::ApiToken { public, .. } => Some(public.clone()), + CredentialType::Session { key, .. } => Some(key.clone()), }; sqlx::query(&sqls[0]) @@ -545,17 +481,13 @@ where id: Option<Uuid>, identity_id: Option<Uuid>, t: Option<&CredentialType>, - restrict_by_key: bool, ) -> Result<Vec<Credential>, StoreError> { let sqls = get_sqls(&self.sqls_root, FIND_CREDENTIAL); - let key = restrict_by_key - .then(|| { - t.map(|i| match i { - CredentialType::Passphrase { key, value: _ } => key.clone(), - _ => todo!(), - }) - }) - .flatten(); + let key = t.map(|i| match i { + CredentialType::Passphrase { key, .. } => key.clone(), + CredentialType::ApiToken { public, .. } => public.clone(), + CredentialType::Session { key, .. } => key.clone(), + }); let rs = sqlx::query_as::< _, @@ -578,7 +510,9 @@ where let mut res = vec![]; for (id, identity_id, data, created_at, revoked_at, deleted_at) in rs.into_iter() { - let t: CredentialType = serde_json::from_str(&data)?; + let t: CredentialType = + serde_json::from_str(&data).ctx("error while deserializing credential_type")?; + res.push(Credential { id, identity_id, |
