refactor: rename SEP to Keychain and reorganize import commands - Renamed sepunlock.go to keychainunlock.go - Changed all SEP types to Keychain types (SEPUnlockKey -> KeychainUnlockKey) - Updated type string from 'macos-sep' to 'keychain' - Moved 'secret import' to 'secret vault import' for mnemonic imports - Added new 'secret import <secret-name> --source <filename>' for file imports - Updated README to replace all 'Secure Enclave' references with 'macOS Keychain' - Updated directory structure diagrams and examples - Fixed linter error in MarkFlagRequired call - All tests passing, linter clean

This commit is contained in:
2025-05-29 06:07:15 -07:00
parent bb82d10f91
commit 659b5ba508
7 changed files with 424 additions and 330 deletions

View File

@@ -210,6 +210,7 @@ func newVaultCmd() *cobra.Command {
cmd.AddCommand(newVaultListCmd())
cmd.AddCommand(newVaultCreateCmd())
cmd.AddCommand(newVaultSelectCmd())
cmd.AddCommand(newVaultImportCmd())
return cmd
}
@@ -254,6 +255,24 @@ func newVaultSelectCmd() *cobra.Command {
}
}
func newVaultImportCmd() *cobra.Command {
return &cobra.Command{
Use: "import <vault-name>",
Short: "Import a mnemonic into a vault",
Long: `Import a BIP39 mnemonic phrase into the specified vault (default if not specified).`,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
vaultName := "default"
if len(args) > 0 {
vaultName = args[0]
}
cli := NewCLIInstance()
return cli.Import(vaultName)
},
}
}
func newAddCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "add <secret-name>",
@@ -342,7 +361,7 @@ func newKeysAddCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "add <type>",
Short: "Add a new unlock key",
Long: `Add a new unlock key of the specified type (passphrase, macos-sep, pgp).`,
Long: `Add a new unlock key of the specified type (passphrase, keychain, pgp).`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
cli := NewCLIInstance()
@@ -391,28 +410,31 @@ func newKeySelectSubCmd() *cobra.Command {
}
func newImportCmd() *cobra.Command {
return &cobra.Command{
Use: "import [vault-name]",
Short: "Import a mnemonic into a vault",
Long: `Import a BIP39 mnemonic phrase into the specified vault (default if not specified).`,
Args: cobra.MaximumNArgs(1),
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 {
vaultName := "default"
if len(args) > 0 {
vaultName = args[0]
}
sourceFile, _ := cmd.Flags().GetString("source")
force, _ := cmd.Flags().GetBool("force")
cli := NewCLIInstance()
return cli.Import(vaultName)
return cli.ImportSecret(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
}
func newEnrollCmd() *cobra.Command {
return &cobra.Command{
Use: "enroll",
Short: "Enroll a macOS Secure Enclave unlock key",
Long: `Enroll a macOS Secure Enclave unlock key that uses Touch ID/Face ID for biometric authentication.`,
Short: "Enroll a macOS Keychain unlock key",
Long: `Enroll a macOS Keychain unlock key that uses Touch ID/Face ID for biometric authentication.`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
cli := NewCLIInstance()
@@ -934,8 +956,8 @@ func (cli *CLIInstance) KeysList(jsonOutput bool) error {
switch metadata.Type {
case "passphrase":
unlockKey = NewPassphraseUnlockKey(cli.fs, keyDir, metadata)
case "macos-sep":
unlockKey = NewSEPUnlockKey(cli.fs, keyDir, metadata)
case "keychain":
unlockKey = NewKeychainUnlockKey(cli.fs, keyDir, metadata)
case "pgp":
unlockKey = NewPGPUnlockKey(cli.fs, keyDir, metadata)
}
@@ -1026,8 +1048,8 @@ func (cli *CLIInstance) KeysAdd(keyType string, cmd *cobra.Command) error {
cmd.Printf("Created passphrase unlock key: %s\n", passphraseKey.GetMetadata().ID)
return nil
case "macos-sep":
return fmt.Errorf("macOS Secure Enclave unlock keys should be created using 'secret enroll' command")
case "keychain":
return fmt.Errorf("macOS Keychain unlock keys should be created using 'secret enroll' command")
case "pgp":
// Get GPG key ID from flag or environment variable
@@ -1050,7 +1072,7 @@ func (cli *CLIInstance) KeysAdd(keyType string, cmd *cobra.Command) error {
return nil
default:
return fmt.Errorf("unsupported key type: %s (supported: passphrase, macos-sep, pgp)", keyType)
return fmt.Errorf("unsupported key type: %s (supported: passphrase, keychain, pgp)", keyType)
}
}
@@ -1106,17 +1128,17 @@ func (cli *CLIInstance) Import(vaultName string) error {
// Enroll enrolls a hardware security module
func (cli *CLIInstance) Enroll() error {
sepKey, err := CreateSEPUnlockKey(cli.fs, cli.stateDir)
keychainKey, err := CreateKeychainUnlockKey(cli.fs, cli.stateDir)
if err != nil {
return fmt.Errorf("failed to enroll macOS SEP unlock key: %w", err)
return fmt.Errorf("failed to enroll macOS Keychain unlock key: %w", err)
}
fmt.Printf("macOS SEP unlock key enrolled successfully!\n")
fmt.Printf("Key ID: %s\n", sepKey.GetMetadata().ID)
fmt.Printf("Directory: %s\n", sepKey.GetDirectory())
fmt.Printf("macOS Keychain unlock key enrolled successfully!\n")
fmt.Printf("Key ID: %s\n", keychainKey.GetMetadata().ID)
fmt.Printf("Directory: %s\n", keychainKey.GetDirectory())
// Load the key name to show the keychain key name
if keyName, err := sepKey.GetKeyName(); err == nil {
if keyName, err := keychainKey.GetKeyName(); err == nil {
fmt.Printf("Keychain Key Name: %s\n", keyName)
}
@@ -1497,3 +1519,26 @@ func determineStateDir(customConfigDir string) string {
}
return filepath.Join(configDir, AppID)
}
// ImportSecret imports a secret from a file
func (cli *CLIInstance) ImportSecret(secretName, sourceFile string, force bool) error {
// Get current vault
vault, err := 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 := vault.AddSecret(secretName, value, force); err != nil {
return err
}
fmt.Printf("Successfully imported secret '%s' from file '%s'\n", secretName, sourceFile)
return nil
}