- Vault creation now prompts for mnemonic if not in environment - Automatically creates passphrase unlocker during vault creation - Prevents 'missing public key' error when adding secrets to new vaults - Updates tests to reflect new vault creation flow
145 lines
4.1 KiB
Go
145 lines
4.1 KiB
Go
package cli
|
|
|
|
import (
|
|
"encoding/json"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"git.eeqj.de/sneak/secret/internal/secret"
|
|
"git.eeqj.de/sneak/secret/internal/vault"
|
|
"github.com/spf13/afero"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// getSecretNamesCompletionFunc returns a completion function that provides secret names
|
|
func getSecretNamesCompletionFunc(fs afero.Fs, stateDir string) func(
|
|
cmd *cobra.Command, args []string, toComplete string,
|
|
) ([]string, cobra.ShellCompDirective) {
|
|
return func(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
|
// Get current vault
|
|
vlt, err := vault.GetCurrentVault(fs, stateDir)
|
|
if err != nil {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
// Get list of secrets
|
|
secrets, err := vlt.ListSecrets()
|
|
if err != nil {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
// Filter secrets based on what user has typed
|
|
var completions []string
|
|
for _, secret := range secrets {
|
|
if strings.HasPrefix(secret, toComplete) {
|
|
completions = append(completions, secret)
|
|
}
|
|
}
|
|
|
|
return completions, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
}
|
|
|
|
// getUnlockerIDsCompletionFunc returns a completion function that provides unlocker IDs
|
|
func getUnlockerIDsCompletionFunc(fs afero.Fs, stateDir string) func(
|
|
cmd *cobra.Command, args []string, toComplete string,
|
|
) ([]string, cobra.ShellCompDirective) {
|
|
return func(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
|
// Get current vault
|
|
vlt, err := vault.GetCurrentVault(fs, stateDir)
|
|
if err != nil {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
// Get unlocker metadata list
|
|
unlockerMetadataList, err := vlt.ListUnlockers()
|
|
if err != nil {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
// Get vault directory
|
|
vaultDir, err := vlt.GetDirectory()
|
|
if err != nil {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
// Collect unlocker IDs
|
|
var completions []string
|
|
|
|
for _, metadata := range unlockerMetadataList {
|
|
// Get the actual unlocker ID by creating the unlocker instance
|
|
unlockersDir := filepath.Join(vaultDir, "unlockers.d")
|
|
files, err := afero.ReadDir(fs, unlockersDir)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
for _, file := range files {
|
|
if !file.IsDir() {
|
|
continue
|
|
}
|
|
|
|
unlockerDir := filepath.Join(unlockersDir, file.Name())
|
|
metadataPath := filepath.Join(unlockerDir, "unlocker-metadata.json")
|
|
|
|
// Check if this is the right unlocker by comparing metadata
|
|
metadataBytes, err := afero.ReadFile(fs, metadataPath)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
var diskMetadata secret.UnlockerMetadata
|
|
if err := json.Unmarshal(metadataBytes, &diskMetadata); err != nil {
|
|
continue
|
|
}
|
|
|
|
// Match by type and creation time
|
|
if diskMetadata.Type == metadata.Type && diskMetadata.CreatedAt.Equal(metadata.CreatedAt) {
|
|
// Create the appropriate unlocker instance
|
|
var unlocker secret.Unlocker
|
|
switch metadata.Type {
|
|
case "passphrase":
|
|
unlocker = secret.NewPassphraseUnlocker(fs, unlockerDir, diskMetadata)
|
|
case "keychain":
|
|
unlocker = secret.NewKeychainUnlocker(fs, unlockerDir, diskMetadata)
|
|
case "pgp":
|
|
unlocker = secret.NewPGPUnlocker(fs, unlockerDir, diskMetadata)
|
|
}
|
|
|
|
if unlocker != nil {
|
|
id := unlocker.GetID()
|
|
if strings.HasPrefix(id, toComplete) {
|
|
completions = append(completions, id)
|
|
}
|
|
}
|
|
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return completions, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
}
|
|
|
|
// getVaultNamesCompletionFunc returns a completion function that provides vault names
|
|
func getVaultNamesCompletionFunc(fs afero.Fs, stateDir string) func(
|
|
cmd *cobra.Command, args []string, toComplete string,
|
|
) ([]string, cobra.ShellCompDirective) {
|
|
return func(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
|
vaults, err := vault.ListVaults(fs, stateDir)
|
|
if err != nil {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
var completions []string
|
|
for _, v := range vaults {
|
|
if strings.HasPrefix(v, toComplete) {
|
|
completions = append(completions, v)
|
|
}
|
|
}
|
|
|
|
return completions, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
}
|