Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions secretspec-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1199,8 +1199,8 @@ mod builder_generation {
let provider_str = if let Some(provider_fn) = #provider_expr {
let provider_box = provider_fn()
.map_err(|e| secretspec::SecretSpecError::ProviderOperationFailed(e))?;
// Get the provider name to pass as a string to set_provider
Some(provider_box.name().to_string())
// Get the full URI to pass as a string to set_provider (preserves vault info)
Some(provider_box.uri())
} else {
None
};
Expand Down
17 changes: 17 additions & 0 deletions secretspec/src/provider/dotenv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,23 @@ impl Provider for DotEnvProvider {
fn name(&self) -> &'static str {
Self::PROVIDER_NAME
}

fn uri(&self) -> String {
// Dotenv uses single colon format: dotenv:path
// The path can be relative or absolute
let path_str = self.config.path.display().to_string();

if path_str == ".env" {
// Default case - just return "dotenv"
"dotenv".to_string()
} else if path_str.starts_with('/') {
// Absolute path
format!("dotenv:{}", path_str)
} else {
// Relative path
format!("dotenv:{}", path_str)
}
}

/// Retrieves a secret value from the .env file.
///
Expand Down
5 changes: 5 additions & 0 deletions secretspec/src/provider/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ impl Provider for EnvProvider {
fn name(&self) -> &'static str {
Self::PROVIDER_NAME
}

fn uri(&self) -> String {
// Env can be "env", "env:", or "env://"
"env".to_string()
}

/// Retrieves a secret value from environment variables.
///
Expand Down
5 changes: 5 additions & 0 deletions secretspec/src/provider/keyring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ impl Provider for KeyringProvider {
fn name(&self) -> &'static str {
Self::PROVIDER_NAME
}

fn uri(&self) -> String {
// Keyring can be just "keyring" or "keyring://"
"keyring".to_string()
}

/// Retrieves a secret from the system keychain.
///
Expand Down
19 changes: 19 additions & 0 deletions secretspec/src/provider/lastpass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,25 @@ impl Provider for LastPassProvider {
fn name(&self) -> &'static str {
Self::PROVIDER_NAME
}

fn uri(&self) -> String {
// LastPass can be "lastpass" (default) or "lastpass://folder" or "lastpass://Folder/Subfolder"
if let Some(ref prefix) = self.config.folder_prefix {
// The folder_prefix might be something like "SecretSpec/{project}/{profile}/{key}"
// We want to extract just the folder part for the URI
if let Some(folder) = prefix.split('/').next() {
if folder.is_empty() || folder == "Shared" {
"lastpass".to_string()
} else {
format!("lastpass://{}", folder)
}
} else {
"lastpass".to_string()
}
} else {
"lastpass".to_string()
}
}

/// Retrieves a secret from LastPass.
///
Expand Down
6 changes: 6 additions & 0 deletions secretspec/src/provider/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,12 @@ pub trait Provider: Send + Sync {
///
/// This should match the name registered with the provider macro.
fn name(&self) -> &'static str;

/// Returns the full URI representation of this provider.
///
/// This includes any configuration like vault names, paths, etc.
/// For example: "onepassword://VaultName" or "dotenv://.env.production"
fn uri(&self) -> String;

/// Discovers and returns all secrets available in this provider.
///
Expand Down
34 changes: 34 additions & 0 deletions secretspec/src/provider/onepassword.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,40 @@ impl Provider for OnePasswordProvider {
fn name(&self) -> &'static str {
Self::PROVIDER_NAME
}

fn uri(&self) -> String {
// Reconstruct the URI from the config
// Format: onepassword://[account@]vault or onepassword+token://[token@]vault

let scheme = if self.config.service_account_token.is_some() {
"onepassword+token"
} else {
"onepassword"
};

let mut uri = format!("{}://", scheme);

// For service account token, the token itself might be in the URI
// but we don't want to expose the actual token value, just indicate it's configured
if self.config.service_account_token.is_some() {
// Just indicate token auth is being used without exposing the token
if let Some(ref vault) = self.config.default_vault {
uri.push_str(vault);
}
} else {
// Regular auth: account@vault format
if let Some(ref account) = self.config.account {
uri.push_str(account);
uri.push('@');
}

if let Some(ref vault) = self.config.default_vault {
uri.push_str(vault);
}
}

uri
}

/// Retrieves a secret from OnePassword.
///
Expand Down
4 changes: 4 additions & 0 deletions secretspec/src/provider/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ impl Provider for MockProvider {
fn name(&self) -> &'static str {
"mock"
}

fn uri(&self) -> String {
"mock://".to_string()
}
}

#[test]
Expand Down
4 changes: 2 additions & 2 deletions secretspec/src/secrets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -896,14 +896,14 @@ impl Secrets {
missing_required,
missing_optional,
with_defaults,
backend.name().to_string(),
backend.uri(),
profile_name.to_string(),
)))
} else {
Ok(Ok(ValidatedSecrets {
resolved: Resolved::new(
secrets,
backend.name().to_string(),
backend.uri(),
profile_name.to_string(),
),
missing_optional,
Expand Down