secret/internal/cli/secrets.go
sneak 080a3dc253 fix: resolve all nlreturn linter errors
Add blank lines before return statements in all files to satisfy
the nlreturn linter. This improves code readability by providing
visual separation before return statements.

Changes made across 24 files:
- internal/cli/*.go
- internal/secret/*.go
- internal/vault/*.go
- pkg/agehd/agehd.go
- pkg/bip85/bip85.go

All 143 nlreturn issues have been resolved.
2025-07-15 06:00:32 +02:00

316 lines
8.6 KiB
Go

package cli
import (
"encoding/json"
"fmt"
"io"
"strings"
"git.eeqj.de/sneak/secret/internal/secret"
"git.eeqj.de/sneak/secret/internal/vault"
"github.com/spf13/afero"
"github.com/spf13/cobra"
)
func newAddCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "add <secret-name>",
Short: "Add a secret to the vault",
Long: `Add a secret to the current vault. The secret value is read from stdin.`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
secret.Debug("Add command RunE starting", "secret_name", args[0])
force, _ := cmd.Flags().GetBool("force")
secret.Debug("Got force flag", "force", force)
cli := NewCLIInstance()
cli.cmd = cmd // Set the command for stdin access
secret.Debug("Created CLI instance, calling AddSecret")
return cli.AddSecret(args[0], force)
},
}
cmd.Flags().BoolP("force", "f", false, "Overwrite existing secret")
return cmd
}
func newGetCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "get <secret-name>",
Short: "Retrieve a secret from the vault",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
version, _ := cmd.Flags().GetString("version")
cli := NewCLIInstance()
return cli.GetSecretWithVersion(cmd, args[0], version)
},
}
cmd.Flags().StringP("version", "v", "", "Get a specific version (default: current)")
return cmd
}
func newListCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "list [filter]",
Aliases: []string{"ls"},
Short: "List all secrets in the current vault",
Long: `List all secrets in the current vault. Optionally filter by substring match in secret name.`,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
jsonOutput, _ := cmd.Flags().GetBool("json")
var filter string
if len(args) > 0 {
filter = args[0]
}
cli := NewCLIInstance()
return cli.ListSecrets(cmd, jsonOutput, filter)
},
}
cmd.Flags().Bool("json", false, "Output in JSON format")
return cmd
}
func newImportCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "import <secret-name>",
Short: "Import a secret from a file",
Long: `Import a secret from a file and store it in the current vault under the given name.`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
sourceFile, _ := cmd.Flags().GetString("source")
force, _ := cmd.Flags().GetBool("force")
cli := NewCLIInstance()
return cli.ImportSecret(cmd, args[0], sourceFile, force)
},
}
cmd.Flags().StringP("source", "s", "", "Source file to import from (required)")
cmd.Flags().BoolP("force", "f", false, "Overwrite existing secret")
_ = cmd.MarkFlagRequired("source")
return cmd
}
// AddSecret adds a secret to the current vault
func (cli *Instance) AddSecret(secretName string, force bool) error {
secret.Debug("CLI AddSecret starting", "secret_name", secretName, "force", force)
// Get current vault
secret.Debug("Getting current vault")
vlt, err := vault.GetCurrentVault(cli.fs, cli.stateDir)
if err != nil {
return err
}
secret.Debug("Got current vault", "vault_name", vlt.GetName())
// Read secret value from stdin
secret.Debug("Reading secret value from stdin")
value, err := io.ReadAll(cli.cmd.InOrStdin())
if err != nil {
return fmt.Errorf("failed to read secret value: %w", err)
}
secret.Debug("Read secret value from stdin", "value_length", len(value))
// Remove trailing newline if present
if len(value) > 0 && value[len(value)-1] == '\n' {
value = value[:len(value)-1]
secret.Debug("Removed trailing newline", "new_length", len(value))
}
// Add the secret to the vault
secret.Debug("Calling vault.AddSecret", "secret_name", secretName, "value_length", len(value), "force", force)
if err := vlt.AddSecret(secretName, value, force); err != nil {
secret.Debug("vault.AddSecret failed", "error", err)
return err
}
secret.Debug("vault.AddSecret completed successfully")
return nil
}
// GetSecret retrieves and prints a secret from the current vault
func (cli *Instance) GetSecret(cmd *cobra.Command, secretName string) error {
return cli.GetSecretWithVersion(cmd, secretName, "")
}
// GetSecretWithVersion retrieves and prints a specific version of a secret
func (cli *Instance) GetSecretWithVersion(cmd *cobra.Command, secretName string, version string) error {
secret.Debug("GetSecretWithVersion called", "secretName", secretName, "version", version)
// Get current vault
vlt, err := vault.GetCurrentVault(cli.fs, cli.stateDir)
if err != nil {
secret.Debug("Failed to get current vault", "error", err)
return err
}
// Get the secret value
var value []byte
if version == "" {
value, err = vlt.GetSecret(secretName)
} else {
value, err = vlt.GetSecretVersion(secretName, version)
}
if err != nil {
secret.Debug("Failed to get secret", "error", err)
return err
}
secret.Debug("Got secret value", "valueLength", len(value))
// Print the secret value to stdout
cmd.Print(string(value))
secret.Debug("Printed value to cmd")
// Debug: Log what we're actually printing
secret.Debug("Secret retrieval debug info",
"secretName", secretName,
"version", version,
"valueLength", len(value),
"valueAsString", string(value),
"isEmpty", len(value) == 0)
return nil
}
// ListSecrets lists all secrets in the current vault
func (cli *Instance) ListSecrets(cmd *cobra.Command, jsonOutput bool, filter string) error {
// Get current vault
vlt, err := vault.GetCurrentVault(cli.fs, cli.stateDir)
if err != nil {
return err
}
// Get list of secrets
secrets, err := vlt.ListSecrets()
if err != nil {
return fmt.Errorf("failed to list secrets: %w", err)
}
// Filter secrets if filter is provided
var filteredSecrets []string
if filter != "" {
for _, secretName := range secrets {
if strings.Contains(secretName, filter) {
filteredSecrets = append(filteredSecrets, secretName)
}
}
} else {
filteredSecrets = secrets
}
if jsonOutput { //nolint:nestif // Separate JSON and table output formatting logic
// For JSON output, get metadata for each secret
secretsWithMetadata := make([]map[string]interface{}, 0, len(filteredSecrets))
for _, secretName := range filteredSecrets {
secretInfo := map[string]interface{}{
"name": secretName,
}
// Try to get metadata using GetSecretObject
if secretObj, err := vlt.GetSecretObject(secretName); err == nil {
metadata := secretObj.GetMetadata()
secretInfo["created_at"] = metadata.CreatedAt
secretInfo["updated_at"] = metadata.UpdatedAt
}
secretsWithMetadata = append(secretsWithMetadata, secretInfo)
}
output := map[string]interface{}{
"secrets": secretsWithMetadata,
}
if filter != "" {
output["filter"] = filter
}
jsonBytes, err := json.MarshalIndent(output, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal JSON: %w", err)
}
cmd.Println(string(jsonBytes))
} else {
// Pretty table output
if len(filteredSecrets) == 0 {
if filter != "" {
cmd.Printf("No secrets found in vault '%s' matching filter '%s'.\n", vlt.GetName(), filter)
} else {
cmd.Println("No secrets found in current vault.")
cmd.Println("Run 'secret add <name>' to create one.")
}
return nil
}
// Get current vault name for display
if filter != "" {
cmd.Printf("Secrets in vault '%s' matching '%s':\n\n", vlt.GetName(), filter)
} else {
cmd.Printf("Secrets in vault '%s':\n\n", vlt.GetName())
}
cmd.Printf("%-40s %-20s\n", "NAME", "LAST UPDATED")
cmd.Printf("%-40s %-20s\n", "----", "------------")
for _, secretName := range filteredSecrets {
lastUpdated := "unknown"
if secretObj, err := vlt.GetSecretObject(secretName); err == nil {
metadata := secretObj.GetMetadata()
lastUpdated = metadata.UpdatedAt.Format("2006-01-02 15:04")
}
cmd.Printf("%-40s %-20s\n", secretName, lastUpdated)
}
cmd.Printf("\nTotal: %d secret(s)", len(filteredSecrets))
if filter != "" {
cmd.Printf(" (filtered from %d)", len(secrets))
}
cmd.Println()
}
return nil
}
// ImportSecret imports a secret from a file
func (cli *Instance) ImportSecret(cmd *cobra.Command, secretName, sourceFile string, force bool) error {
// Get current vault
vlt, err := vault.GetCurrentVault(cli.fs, cli.stateDir)
if err != nil {
return err
}
// Read secret value from the source file
value, err := afero.ReadFile(cli.fs, sourceFile)
if err != nil {
return fmt.Errorf("failed to read secret from file %s: %w", sourceFile, err)
}
// Store the secret in the vault
if err := vlt.AddSecret(secretName, value, force); err != nil {
return err
}
cmd.Printf("Successfully imported secret '%s' from file '%s'\n", secretName, sourceFile)
return nil
}