mirror of
https://github.com/getnora-io/nora.git
synced 2026-04-12 09:10:32 +00:00
- Store metadata (.meta.json) alongside manifests with: - push_timestamp, last_pulled, downloads counter - size_bytes, os, arch, variant - layers list with digest and size - Update metadata on manifest pull (increment downloads, update last_pulled) - Extract OS/arch from config blob on push - Extend UI API TagInfo with metadata fields - Add public_url config option for pull commands - Add Docker upstream proxy with auth support - Add raw repository support - Bump version to 0.2.12
128 lines
3.6 KiB
Rust
128 lines
3.6 KiB
Rust
//! Environment variables secrets provider
|
|
//!
|
|
//! Reads secrets from environment variables. This is the default provider
|
|
//! following 12-Factor App principles.
|
|
|
|
use std::env;
|
|
|
|
use super::{SecretsError, SecretsProvider};
|
|
use crate::secrets::protected::ProtectedString;
|
|
use async_trait::async_trait;
|
|
|
|
/// Environment variables secrets provider
|
|
///
|
|
/// Reads secrets from environment variables.
|
|
/// Optionally clears variables after reading for extra security.
|
|
#[derive(Debug, Clone)]
|
|
pub struct EnvProvider {
|
|
/// Clear environment variables after reading
|
|
clear_after_read: bool,
|
|
}
|
|
|
|
impl EnvProvider {
|
|
/// Create a new environment provider
|
|
pub fn new() -> Self {
|
|
Self {
|
|
clear_after_read: false,
|
|
}
|
|
}
|
|
|
|
/// Create a provider that clears env vars after reading
|
|
///
|
|
/// This prevents secrets from being visible in `/proc/<pid>/environ`
|
|
pub fn with_clear_after_read(mut self) -> Self {
|
|
self.clear_after_read = true;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl Default for EnvProvider {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl SecretsProvider for EnvProvider {
|
|
async fn get_secret(&self, key: &str) -> Result<ProtectedString, SecretsError> {
|
|
let value = env::var(key).map_err(|_| SecretsError::NotFound(key.to_string()))?;
|
|
|
|
if self.clear_after_read {
|
|
env::remove_var(key);
|
|
}
|
|
|
|
Ok(ProtectedString::new(value))
|
|
}
|
|
|
|
async fn get_secret_optional(&self, key: &str) -> Option<ProtectedString> {
|
|
env::var(key).ok().map(|v| {
|
|
if self.clear_after_read {
|
|
env::remove_var(key);
|
|
}
|
|
ProtectedString::new(v)
|
|
})
|
|
}
|
|
|
|
fn provider_name(&self) -> &'static str {
|
|
"env"
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[tokio::test]
|
|
async fn test_get_secret_exists() {
|
|
env::set_var("TEST_SECRET_123", "secret-value");
|
|
let provider = EnvProvider::new();
|
|
let secret = provider.get_secret("TEST_SECRET_123").await.unwrap();
|
|
assert_eq!(secret.expose(), "secret-value");
|
|
env::remove_var("TEST_SECRET_123");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_get_secret_not_found() {
|
|
let provider = EnvProvider::new();
|
|
let result = provider.get_secret("NONEXISTENT_VAR_XYZ").await;
|
|
assert!(matches!(result, Err(SecretsError::NotFound(_))));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_get_secret_optional_exists() {
|
|
env::set_var("TEST_OPTIONAL_123", "optional-value");
|
|
let provider = EnvProvider::new();
|
|
let secret = provider.get_secret_optional("TEST_OPTIONAL_123").await;
|
|
assert!(secret.is_some());
|
|
assert_eq!(secret.unwrap().expose(), "optional-value");
|
|
env::remove_var("TEST_OPTIONAL_123");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_get_secret_optional_not_found() {
|
|
let provider = EnvProvider::new();
|
|
let secret = provider
|
|
.get_secret_optional("NONEXISTENT_OPTIONAL_XYZ")
|
|
.await;
|
|
assert!(secret.is_none());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_clear_after_read() {
|
|
env::set_var("TEST_CLEAR_123", "to-be-cleared");
|
|
let provider = EnvProvider::new().with_clear_after_read();
|
|
|
|
let secret = provider.get_secret("TEST_CLEAR_123").await.unwrap();
|
|
assert_eq!(secret.expose(), "to-be-cleared");
|
|
|
|
// Variable should be cleared
|
|
assert!(env::var("TEST_CLEAR_123").is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_provider_name() {
|
|
let provider = EnvProvider::new();
|
|
assert_eq!(provider.provider_name(), "env");
|
|
}
|
|
}
|