forked from sneak/secret
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.
316 lines
8.6 KiB
Go
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
|
|
}
|