From 3b61aa924a1350c1eabf105692df084993d36cb4 Mon Sep 17 00:00:00 2001 From: Joseph Montanaro Date: Sun, 21 Jul 2024 06:38:25 -0400 Subject: [PATCH] test CLI credentials against main app --- src-tauri/creddy_cli/src/lib.rs | 2 +- src-tauri/creddy_cli/src/proto.rs | 4 +- src-tauri/src/credentials/aws.rs | 100 ++++++++++++++++++ .../credentials/fixtures/ssh_credentials.sql | 8 ++ src-tauri/src/credentials/ssh.rs | 30 ++++-- 5 files changed, 131 insertions(+), 13 deletions(-) diff --git a/src-tauri/creddy_cli/src/lib.rs b/src-tauri/creddy_cli/src/lib.rs index 8738e04..dd93db6 100644 --- a/src-tauri/creddy_cli/src/lib.rs +++ b/src-tauri/creddy_cli/src/lib.rs @@ -10,7 +10,7 @@ pub use cli::{ pub(crate) use platform::connect; pub use platform::server_addr; -mod proto; +pub mod proto; #[cfg(unix)] diff --git a/src-tauri/creddy_cli/src/proto.rs b/src-tauri/creddy_cli/src/proto.rs index 52459ce..e40defc 100644 --- a/src-tauri/creddy_cli/src/proto.rs +++ b/src-tauri/creddy_cli/src/proto.rs @@ -49,7 +49,7 @@ pub enum CliCredential { } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct AwsBaseCredential { #[serde(default = "default_aws_version")] @@ -59,7 +59,7 @@ pub struct AwsBaseCredential { } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct AwsSessionCredential { #[serde(default = "default_aws_version")] diff --git a/src-tauri/src/credentials/aws.rs b/src-tauri/src/credentials/aws.rs index e6e7523..f84dff1 100644 --- a/src-tauri/src/credentials/aws.rs +++ b/src-tauri/src/credentials/aws.rs @@ -185,10 +185,16 @@ where S: Serializer #[cfg(test)] mod tests { use super::*; + use aws_sdk_sts::primitives::DateTimeFormat; + use creddy_cli::proto::{ + AwsBaseCredential as CliBase, + AwsSessionCredential as CliSession, + }; use sqlx::SqlitePool; use sqlx::types::uuid::uuid; + fn creds() -> AwsBaseCredential { AwsBaseCredential::new( "AKIAIOSFODNN7EXAMPLE".into(), @@ -242,4 +248,98 @@ mod tests { assert_eq!(&creds().into_credential(), &list[0]); assert_eq!(&creds_2().into_credential(), &list[1]); } + + + // In order to avoid the CLI depending on the main app (and thus defeating the purpose + // of having a separate CLI at all) it re-defines the credentials that need to be sent + // back and forth. To prevent the separate definitions from drifting aprt, we test + // serializing/deserializing in both directions. + + #[test] + fn test_cli_to_app_base() { + let cli_base = CliBase { + version: 1, + access_key_id: "AKIAIOSFODNN7EXAMPLE".into(), + secret_access_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY".into(), + }; + + let json = serde_json::to_string(&cli_base).unwrap(); + let computed: AwsBaseCredential = serde_json::from_str(&json) + .expect("Failed to deserialize base credentials from CLI -> main app"); + + assert_eq!(creds(), computed); + } + + #[test] + fn test_app_to_cli_base() { + let base = creds(); + let json = serde_json::to_string(&base).unwrap(); + + let computed: CliBase = serde_json::from_str(&json) + .expect("Failed to deserialize base credentials from main app -> CLI"); + + let expected = CliBase { + version: 1, + access_key_id: "AKIAIOSFODNN7EXAMPLE".into(), + secret_access_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY".into(), + }; + + assert_eq!(expected, computed); + } + + #[test] + fn test_cli_to_app_session() { + let cli_session = CliSession { + version: 1, + access_key_id: "ASIAIOSFODNN7EXAMPLE".into(), + secret_access_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY".into(), + session_token: "JQ70sxbqnOGKu7+krevstYCLCaX2+alUAT60ARTBBnQ=ETC.".into(), + expiration: "2024-07-21T00:00:00Z".into(), + }; + + let json = serde_json::to_string(&cli_session).unwrap(); + let computed: AwsSessionCredential = serde_json::from_str(&json) + .expect("Failed to deserialize session credentials from CLI -> main app"); + + let expected = AwsSessionCredential { + version: 1, + access_key_id: "ASIAIOSFODNN7EXAMPLE".into(), + secret_access_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY".into(), + session_token: "JQ70sxbqnOGKu7+krevstYCLCaX2+alUAT60ARTBBnQ=ETC.".into(), + expiration: DateTime::from_str( + "2024-07-21T00:00:00Z", + DateTimeFormat::DateTimeWithOffset + ).unwrap(), + }; + + assert_eq!(expected, computed); + } + + #[test] + fn test_app_to_cli_session() { + let session = AwsSessionCredential { + version: 1, + access_key_id: "ASIAIOSFODNN7EXAMPLE".into(), + secret_access_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY".into(), + session_token: "JQ70sxbqnOGKu7+krevstYCLCaX2+alUAT60ARTBBnQ=ETC.".into(), + expiration: DateTime::from_str( + "2024-07-21T00:00:00Z", + DateTimeFormat::DateTimeWithOffset + ).unwrap(), + }; + + let json = serde_json::to_string(&session).unwrap(); + let computed: CliSession = serde_json::from_str(&json) + .expect("Failed to deserialize session credentials from main app -> CLI"); + + let expected = CliSession { + version: 1, + access_key_id: "ASIAIOSFODNN7EXAMPLE".into(), + secret_access_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY".into(), + session_token: "JQ70sxbqnOGKu7+krevstYCLCaX2+alUAT60ARTBBnQ=ETC.".into(), + expiration: "2024-07-21T00:00:00Z".into(), + }; + + assert_eq!(expected, computed); + } } diff --git a/src-tauri/src/credentials/fixtures/ssh_credentials.sql b/src-tauri/src/credentials/fixtures/ssh_credentials.sql index 9196d25..2cb0348 100644 --- a/src-tauri/src/credentials/fixtures/ssh_credentials.sql +++ b/src-tauri/src/credentials/fixtures/ssh_credentials.sql @@ -1,3 +1,11 @@ +INSERT INTO credentials (id, name, credential_type, is_default, created_at) +VALUES + (X'11111111111111111111111111111111', 'ssh-plain', 'ssh', 1, 1721557273), + (X'22222222222222222222222222222222', 'ssh-enc', 'ssh', 0, 1721557274), + (X'33333333333333333333333333333333', 'ed25519-plain', 'ssh', 0, 1721557275), + (X'44444444444444444444444444444444', 'ed25519-enc', 'ssh', 0, 1721557276); + + INSERT INTO ssh_credentials (id, algorithm, comment, public_key, private_key_enc, nonce) VALUES ( diff --git a/src-tauri/src/credentials/ssh.rs b/src-tauri/src/credentials/ssh.rs index 02738f6..ea2a620 100644 --- a/src-tauri/src/credentials/ssh.rs +++ b/src-tauri/src/credentials/ssh.rs @@ -299,6 +299,8 @@ fn deserialize_algorithm<'de, D>(deserializer: D) -> Result mod tests { use std::fs::{self, File}; use sqlx::types::uuid::uuid; + use crate::credentials::CredentialRecord; + use super::*; fn path(name: &str) -> String { @@ -434,11 +436,14 @@ mod tests { #[sqlx::test] async fn test_save_db(pool: SqlitePool) { let crypto = Crypto::random(); - let k = rsa_plain(); - let mut txn = pool.begin().await.unwrap(); - k.save_details(&random_uuid(), &crypto, &mut txn).await - .expect("Failed to save SSH key to database"); - txn.commit().await.expect("Failed to finalize transaction"); + let record = CredentialRecord { + id: random_uuid(), + name: "save_test".into(), + is_default: false, + credential: Credential::Ssh(rsa_plain()), + }; + record.save(&crypto, &pool).await + .expect("Failed to save SSH key CredentialRecord to database"); } @@ -454,13 +459,18 @@ mod tests { #[sqlx::test] async fn test_save_load_db(pool: SqlitePool) { let crypto = Crypto::random(); - let id = uuid!("7bc994dd-113a-4841-bcf7-b47c2fffdd25"); - let known = ed25519_plain(); - let mut txn = pool.begin().await.unwrap(); - known.save_details(&id, &crypto, &mut txn).await.unwrap(); - txn.commit().await.unwrap(); + let id = random_uuid(); + let record = CredentialRecord { + id, + name: "save_load_test".into(), + is_default: false, + credential: Credential::Ssh(ed25519_plain()), + }; + + record.save(&crypto, &pool).await.unwrap(); let loaded = SshKey::load(&id, &crypto, &pool).await.unwrap(); + let known = ed25519_plain(); assert_eq!(known.algorithm, loaded.algorithm); assert_eq!(known.comment, loaded.comment);