From 272db2a72cb8861024a4d6c9065e7c358adce37a Mon Sep 17 00:00:00 2001 From: Paulo Bressan Date: Mon, 22 Jul 2024 15:51:59 -0300 Subject: [PATCH] Added secret "pepper" (#52) * feat: added secret pepper * chore: updated secret to use playtext * chore: removed dbg macro * chore: updated test manifest --- ...a015938e43a82ede8b2190657efcf5fcea89b.json | 12 ----- ...ffa107af65ba7e806c3b4dcd6db8004b48ef9.json | 12 +++++ src/bin/rpc.rs | 2 + src/domain/event.rs | 1 + src/domain/project.rs | 52 +++++++++++++++---- .../cache/migrations/20240606_tables.sql | 1 + src/driven/cache/project.rs | 8 +-- src/drivers/grpc/middlewares/auth.rs | 2 +- src/drivers/grpc/mod.rs | 8 ++- src/drivers/grpc/project.rs | 16 ++++-- test/manifest.yaml | 1 + 11 files changed, 85 insertions(+), 30 deletions(-) delete mode 100644 .sqlx/query-cd5e37a3ab751331e22af968089a015938e43a82ede8b2190657efcf5fcea89b.json create mode 100644 .sqlx/query-f00e79c9e725e895a778bc5f398ffa107af65ba7e806c3b4dcd6db8004b48ef9.json diff --git a/.sqlx/query-cd5e37a3ab751331e22af968089a015938e43a82ede8b2190657efcf5fcea89b.json b/.sqlx/query-cd5e37a3ab751331e22af968089a015938e43a82ede8b2190657efcf5fcea89b.json deleted file mode 100644 index deda991..0000000 --- a/.sqlx/query-cd5e37a3ab751331e22af968089a015938e43a82ede8b2190657efcf5fcea89b.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "SQLite", - "query": "\n INSERT INTO project_secret (id, project_id, name, phc)\n VALUES ($1, $2, $3, $4)\n ", - "describe": { - "columns": [], - "parameters": { - "Right": 4 - }, - "nullable": [] - }, - "hash": "cd5e37a3ab751331e22af968089a015938e43a82ede8b2190657efcf5fcea89b" -} diff --git a/.sqlx/query-f00e79c9e725e895a778bc5f398ffa107af65ba7e806c3b4dcd6db8004b48ef9.json b/.sqlx/query-f00e79c9e725e895a778bc5f398ffa107af65ba7e806c3b4dcd6db8004b48ef9.json new file mode 100644 index 0000000..592123f --- /dev/null +++ b/.sqlx/query-f00e79c9e725e895a778bc5f398ffa107af65ba7e806c3b4dcd6db8004b48ef9.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n INSERT INTO project_secret (id, project_id, name, phc, secret)\n VALUES ($1, $2, $3, $4, $5)\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 5 + }, + "nullable": [] + }, + "hash": "f00e79c9e725e895a778bc5f398ffa107af65ba7e806c3b4dcd6db8004b48ef9" +} diff --git a/src/bin/rpc.rs b/src/bin/rpc.rs index 335035d..a2dfcc5 100644 --- a/src/bin/rpc.rs +++ b/src/bin/rpc.rs @@ -41,6 +41,7 @@ struct Config { addr: String, db_path: String, auth: Auth, + secret: String, kafka_producer: HashMap, kafka_consumer: HashMap, } @@ -65,6 +66,7 @@ impl From for GrpcConfig { addr: value.addr, db_path: value.db_path, auth_url: value.auth.url, + secret: value.secret, kafka: value.kafka_producer, } } diff --git a/src/domain/event.rs b/src/domain/event.rs index b5ddc92..e41ab44 100644 --- a/src/domain/event.rs +++ b/src/domain/event.rs @@ -36,6 +36,7 @@ pub struct ProjectSecretCreated { pub project_id: String, pub name: String, pub phc: String, + pub secret: Vec, } into_event!(ProjectSecretCreated); diff --git a/src/domain/project.rs b/src/domain/project.rs index 256806b..31a5e3e 100644 --- a/src/domain/project.rs +++ b/src/domain/project.rs @@ -85,7 +85,21 @@ pub async fn create_secret( let key = Alphanumeric.sample_string(&mut rand::thread_rng(), 16); let salt_string = SaltString::generate(&mut OsRng); - let argon2 = Argon2::default(); + let secret = cmd.secret.to_bytes(); + + let argon2 = match Argon2::new_with_secret( + secret, + Default::default(), + Default::default(), + Default::default(), + ) { + Ok(argon2) => argon2.clone(), + Err(error) => { + error!(?error, "error to configure argon2 with secret"); + bail!("internal error") + } + }; + let password_hash = argon2 .hash_password(key.to_bytes(), salt_string.as_salt()) .map_err(|err| Error::msg(err.to_string()))?; @@ -98,6 +112,7 @@ pub async fn create_secret( project_id: project.id, name: cmd.name, phc: password_hash.to_string(), + secret: secret.to_vec(), }; event.dispatch(evt.into()).await?; @@ -130,10 +145,21 @@ pub async fn verify_secret( let secrets = cache.find_secret_by_project_id(project_id).await?; - let argon2 = Argon2::default(); - let secret = secrets.into_iter().find(|secret| { - let Ok(password_hash) = PasswordHash::new(&secret.phc) else { - error!(project_id, secret_id = secret.id, "error to decode phc"); + let secret = secrets.into_iter().find(|project_secret| { + let argon2 = Argon2::new_with_secret( + &project_secret.secret, + Default::default(), + Default::default(), + Default::default(), + ) + .unwrap(); + + let Ok(password_hash) = PasswordHash::new(&project_secret.phc) else { + error!( + project_id, + secret_id = project_secret.id, + "error to decode phc" + ); return false; }; @@ -200,16 +226,18 @@ impl From for ProjectCache { #[derive(Debug, Clone)] pub struct CreateProjectSecretCmd { pub credential: Credential, + pub secret: String, pub id: String, pub project_id: String, pub name: String, } impl CreateProjectSecretCmd { - pub fn new(credential: Credential, project_id: String, name: String) -> Self { + pub fn new(credential: Credential, secret: String, project_id: String, name: String) -> Self { let id = Uuid::new_v4().to_string(); Self { credential, + secret, id, project_id, name, @@ -223,6 +251,7 @@ pub struct ProjectSecretCache { pub project_id: String, pub name: String, pub phc: String, + pub secret: Vec, } impl From for ProjectSecretCache { fn from(value: ProjectSecretCreated) -> Self { @@ -231,6 +260,7 @@ impl From for ProjectSecretCache { project_id: value.project_id, name: value.name, phc: value.phc, + secret: value.secret, } } } @@ -334,10 +364,11 @@ mod tests { } } - const KEY: &str = "dmtr_apikey12e8hvc63gfp8jutwv3t4z6jy2gr8lswa"; - const PHC: &str = "$argon2id$v=19$m=19456,t=2,p=1$L3YdRbmEOXUg66MZF9McXQ$h4LohO/+Zvo6xRhcomO7KuIjrM9pAlHeQU9ZwEYMwnM"; + const KEY: &str = "dmtr_apikey1g9gyswtcf3zxwd26v4x5jj3jw5wx3sn2"; + const PHC: &str = "$argon2id$v=19$m=19456,t=2,p=1$xVIt6Wr/bm1FewVhTr6zgA$nTO6EgGeOYZe7thACrHmFUWND40U4GEQCXKyvqzvRvs"; + const SECRET: &str = "fabric@txpipe"; const INVALID_KEY: &str = "dmtr_apikey1xe6xzcjxv9nhycnz2ffnq6m02y7nat9e"; - const INVALID_HRP_KEY: &str = "mykey1x9zk7c60wfj8xv6929ykynmnwseehq78"; + const INVALID_HRP_KEY: &str = "dmtr_test18pp5vkjzfuuyzwpeg9gk2a2zvsylc5wg"; impl Default for CreateProjectSecretCmd { fn default() -> Self { @@ -346,6 +377,7 @@ mod tests { id: Uuid::new_v4().to_string(), project_id: Uuid::new_v4().to_string(), name: "Key 1".into(), + secret: SECRET.into(), } } } @@ -356,6 +388,7 @@ mod tests { project_id: Uuid::new_v4().to_string(), name: "Key 1".into(), phc: PHC.into(), + secret: SECRET.to_bytes().to_vec(), } } } @@ -366,6 +399,7 @@ mod tests { project_id: Uuid::new_v4().to_string(), name: "Key 1".into(), phc: PHC.into(), + secret: SECRET.to_bytes().to_vec(), } } } diff --git a/src/driven/cache/migrations/20240606_tables.sql b/src/driven/cache/migrations/20240606_tables.sql index 7150631..b98d4b0 100644 --- a/src/driven/cache/migrations/20240606_tables.sql +++ b/src/driven/cache/migrations/20240606_tables.sql @@ -25,5 +25,6 @@ CREATE TABLE IF NOT EXISTS project_secret ( project_id TEXT NOT NULL, name TEXT NOT NULL, phc TEXT NOT NULL, + secret BLOB NOT NULL, FOREIGN KEY(project_id) REFERENCES project(id) ); diff --git a/src/driven/cache/project.rs b/src/driven/cache/project.rs index b6647c8..86d5667 100644 --- a/src/driven/cache/project.rs +++ b/src/driven/cache/project.rs @@ -79,13 +79,14 @@ impl ProjectDrivenCache for SqliteProjectDrivenCache { async fn create_secret(&self, secret: &ProjectSecretCache) -> Result<()> { sqlx::query!( r#" - INSERT INTO project_secret (id, project_id, name, phc) - VALUES ($1, $2, $3, $4) + INSERT INTO project_secret (id, project_id, name, phc, secret) + VALUES ($1, $2, $3, $4, $5) "#, secret.id, secret.project_id, secret.name, secret.phc, + secret.secret ) .execute(&self.sqlite.db) .await?; @@ -95,7 +96,7 @@ impl ProjectDrivenCache for SqliteProjectDrivenCache { async fn find_secret_by_project_id(&self, project_id: &str) -> Result> { let secrets = sqlx::query_as::<_, ProjectSecretCache>( r#" - SELECT id, project_id, name, phc + SELECT id, project_id, name, phc, secret FROM project_secret WHERE project_id = $1; "#, ) @@ -143,6 +144,7 @@ impl FromRow<'_, SqliteRow> for ProjectSecretCache { project_id: row.try_get("project_id")?, name: row.try_get("name")?, phc: row.try_get("phc")?, + secret: row.try_get("secret")?, }) } } diff --git a/src/drivers/grpc/middlewares/auth.rs b/src/drivers/grpc/middlewares/auth.rs index 4df4f19..86e4f43 100644 --- a/src/drivers/grpc/middlewares/auth.rs +++ b/src/drivers/grpc/middlewares/auth.rs @@ -53,7 +53,7 @@ impl tonic::service::Interceptor for AuthenticatorImpl { return tokio::runtime::Runtime::new().unwrap().block_on(async { match project::verify_secret(self.cache.clone(), &project_id, &token).await { Ok(secret) => { - let credential = Credential::ApiKey(secret.id); + let credential = Credential::ApiKey(secret.project_id); request.extensions_mut().insert(credential); Ok(request) } diff --git a/src/drivers/grpc/mod.rs b/src/drivers/grpc/mod.rs index 6c443ee..5aae43c 100644 --- a/src/drivers/grpc/mod.rs +++ b/src/drivers/grpc/mod.rs @@ -36,8 +36,11 @@ pub async fn server(config: GrpcConfig) -> Result<()> { project_cache.clone(), ); - let project_inner = - project::ProjectServiceImpl::new(project_cache.clone(), event_bridge.clone()); + let project_inner = project::ProjectServiceImpl::new( + project_cache.clone(), + event_bridge.clone(), + config.secret.clone(), + ); let project_service = ProjectServiceServer::with_interceptor(project_inner, auth.clone()); let resource_inner = @@ -62,5 +65,6 @@ pub struct GrpcConfig { pub addr: String, pub db_path: String, pub auth_url: String, + pub secret: String, pub kafka: HashMap, } diff --git a/src/drivers/grpc/project.rs b/src/drivers/grpc/project.rs index 45a2235..a492665 100644 --- a/src/drivers/grpc/project.rs +++ b/src/drivers/grpc/project.rs @@ -11,11 +11,20 @@ use crate::domain::{ pub struct ProjectServiceImpl { pub cache: Arc, pub event: Arc, + pub secret: String, } impl ProjectServiceImpl { - pub fn new(cache: Arc, event: Arc) -> Self { - Self { cache, event } + pub fn new( + cache: Arc, + event: Arc, + secret: String, + ) -> Self { + Self { + cache, + event, + secret, + } } } @@ -59,7 +68,8 @@ impl proto::project_service_server::ProjectService for ProjectServiceImpl { let req = request.into_inner(); - let cmd = CreateProjectSecretCmd::new(credential, req.project_id, req.name); + let cmd = + CreateProjectSecretCmd::new(credential, self.secret.clone(), req.project_id, req.name); let result = project::create_secret(self.cache.clone(), self.event.clone(), cmd.clone()).await; diff --git a/test/manifest.yaml b/test/manifest.yaml index 9056b5e..55956eb 100644 --- a/test/manifest.yaml +++ b/test/manifest.yaml @@ -153,6 +153,7 @@ data: rpc.toml: | addr="0.0.0.0:80" db_path="test.db" + secret="fabric@txpipe" [kafka_producer] "bootstrap.servers" = "redpanda.demeter-kafka.svc.cluster.local:19092"