[wip] cli capability parity
Some checks failed
CI / Rustfmt (push) Successful in 23s
CI / Cargo Audit & Deny (push) Successful in 30s
CI / Web Blocking Checks (push) Successful in 48s
CI / Security Blocking Checks (push) Successful in 8s
CI / Clippy (push) Failing after 1m55s
CI / Web Advisory Checks (push) Successful in 35s
CI / Security Advisory Checks (push) Successful in 37s
CI / Tests (push) Successful in 8m5s
Some checks failed
CI / Rustfmt (push) Successful in 23s
CI / Cargo Audit & Deny (push) Successful in 30s
CI / Web Blocking Checks (push) Successful in 48s
CI / Security Blocking Checks (push) Successful in 8s
CI / Clippy (push) Failing after 1m55s
CI / Web Advisory Checks (push) Successful in 35s
CI / Security Advisory Checks (push) Successful in 37s
CI / Tests (push) Successful in 8m5s
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value as JsonValue;
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
use validator::Validate;
|
||||
|
||||
@@ -61,9 +62,9 @@ pub struct KeyResponse {
|
||||
#[schema(example = true)]
|
||||
pub encrypted: bool,
|
||||
|
||||
/// The secret value (decrypted if encrypted)
|
||||
#[schema(example = "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")]
|
||||
pub value: String,
|
||||
/// The secret value (decrypted if encrypted). Can be a string, object, array, number, or boolean.
|
||||
#[schema(value_type = Value, example = json!("ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"))]
|
||||
pub value: JsonValue,
|
||||
|
||||
/// Creation timestamp
|
||||
#[schema(example = "2024-01-13T10:30:00Z")]
|
||||
@@ -194,21 +195,16 @@ pub struct CreateKeyRequest {
|
||||
#[schema(example = "GitHub API Token")]
|
||||
pub name: String,
|
||||
|
||||
/// The secret value to store
|
||||
#[validate(length(min = 1, max = 10000))]
|
||||
#[schema(example = "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")]
|
||||
pub value: String,
|
||||
/// The secret value to store. Can be a string, object, array, number, or boolean.
|
||||
#[schema(value_type = Value, example = json!("ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"))]
|
||||
pub value: JsonValue,
|
||||
|
||||
/// Whether to encrypt the value (recommended: true)
|
||||
#[serde(default = "default_encrypted")]
|
||||
#[schema(example = true)]
|
||||
/// Whether to encrypt the value at rest (default: false; use --encrypt / -e from CLI)
|
||||
#[serde(default)]
|
||||
#[schema(example = false)]
|
||||
pub encrypted: bool,
|
||||
}
|
||||
|
||||
fn default_encrypted() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Request to update an existing key/secret
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
|
||||
pub struct UpdateKeyRequest {
|
||||
@@ -217,10 +213,9 @@ pub struct UpdateKeyRequest {
|
||||
#[schema(example = "GitHub API Token (Updated)")]
|
||||
pub name: Option<String>,
|
||||
|
||||
/// Update the secret value
|
||||
#[validate(length(min = 1, max = 10000))]
|
||||
#[schema(example = "ghp_new_token_xxxxxxxxxxxxxxxxxxxxxxxx")]
|
||||
pub value: Option<String>,
|
||||
/// Update the secret value. Can be a string, object, array, number, or boolean.
|
||||
#[schema(value_type = Option<Value>, example = json!("ghp_new_token_xxxxxxxxxxxxxxxxxxxxxxxx"))]
|
||||
pub value: Option<JsonValue>,
|
||||
|
||||
/// Update encryption status (re-encrypts if changing from false to true)
|
||||
#[schema(example = true)]
|
||||
|
||||
@@ -115,6 +115,9 @@ async fn mq_reconnect_loop(state: Arc<AppState>, mq_url: String) {
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
// Install HMAC-only JWT crypto provider (must be before any token operations)
|
||||
attune_common::auth::install_crypto_provider();
|
||||
|
||||
// Initialize tracing subscriber
|
||||
tracing_subscriber::fmt()
|
||||
.with_target(false)
|
||||
|
||||
@@ -102,8 +102,8 @@ pub async fn get_key(
|
||||
ApiError::InternalServerError("Encryption key not configured on server".to_string())
|
||||
})?;
|
||||
|
||||
let decrypted_value =
|
||||
attune_common::crypto::decrypt(&key.value, encryption_key).map_err(|e| {
|
||||
let decrypted_value = attune_common::crypto::decrypt_json(&key.value, encryption_key)
|
||||
.map_err(|e| {
|
||||
tracing::error!("Failed to decrypt key '{}': {}", key_ref, e);
|
||||
ApiError::InternalServerError(format!("Failed to decrypt key: {}", e))
|
||||
})?;
|
||||
@@ -233,11 +233,11 @@ pub async fn create_key(
|
||||
)
|
||||
})?;
|
||||
|
||||
let encrypted_value = attune_common::crypto::encrypt(&request.value, encryption_key)
|
||||
let encrypted_value = attune_common::crypto::encrypt_json(&request.value, encryption_key)
|
||||
.map_err(|e| {
|
||||
tracing::error!("Failed to encrypt key value: {}", e);
|
||||
ApiError::InternalServerError(format!("Failed to encrypt value: {}", e))
|
||||
})?;
|
||||
tracing::error!("Failed to encrypt key value: {}", e);
|
||||
ApiError::InternalServerError(format!("Failed to encrypt value: {}", e))
|
||||
})?;
|
||||
|
||||
let key_hash = attune_common::crypto::hash_encryption_key(encryption_key);
|
||||
|
||||
@@ -270,10 +270,11 @@ pub async fn create_key(
|
||||
// Return decrypted value in response
|
||||
if key.encrypted {
|
||||
let encryption_key = state.config.security.encryption_key.as_ref().unwrap();
|
||||
key.value = attune_common::crypto::decrypt(&key.value, encryption_key).map_err(|e| {
|
||||
tracing::error!("Failed to decrypt newly created key: {}", e);
|
||||
ApiError::InternalServerError(format!("Failed to decrypt value: {}", e))
|
||||
})?;
|
||||
key.value =
|
||||
attune_common::crypto::decrypt_json(&key.value, encryption_key).map_err(|e| {
|
||||
tracing::error!("Failed to decrypt newly created key: {}", e);
|
||||
ApiError::InternalServerError(format!("Failed to decrypt value: {}", e))
|
||||
})?;
|
||||
}
|
||||
|
||||
let response = ApiResponse::with_message(KeyResponse::from(key), "Key created successfully");
|
||||
@@ -328,11 +329,11 @@ pub async fn update_key(
|
||||
)
|
||||
})?;
|
||||
|
||||
let encrypted_value = attune_common::crypto::encrypt(&new_value, encryption_key)
|
||||
let encrypted_value = attune_common::crypto::encrypt_json(&new_value, encryption_key)
|
||||
.map_err(|e| {
|
||||
tracing::error!("Failed to encrypt key value: {}", e);
|
||||
ApiError::InternalServerError(format!("Failed to encrypt value: {}", e))
|
||||
})?;
|
||||
tracing::error!("Failed to encrypt key value: {}", e);
|
||||
ApiError::InternalServerError(format!("Failed to encrypt value: {}", e))
|
||||
})?;
|
||||
|
||||
let key_hash = attune_common::crypto::hash_encryption_key(encryption_key);
|
||||
|
||||
@@ -366,7 +367,7 @@ pub async fn update_key(
|
||||
ApiError::InternalServerError("Encryption key not configured on server".to_string())
|
||||
})?;
|
||||
|
||||
updated_key.value = attune_common::crypto::decrypt(&updated_key.value, encryption_key)
|
||||
updated_key.value = attune_common::crypto::decrypt_json(&updated_key.value, encryption_key)
|
||||
.map_err(|e| {
|
||||
tracing::error!("Failed to decrypt updated key '{}': {}", key_ref, e);
|
||||
ApiError::InternalServerError(format!("Failed to decrypt value: {}", e))
|
||||
|
||||
Reference in New Issue
Block a user