Refuse an existing vault before asking for the mnemonic (closes #151)
check / check (push) Waiting to run
check / check (push) Waiting to run
`secret init` and `secret vault create` asked for the mnemonic and the unlocker passphrase, and `secret init` printed "Initialized secrets manager at", before refusing a vault that already exists. The check `vault.CreateVault` made before writing is now `vault.CheckNewVaultName`, which both commands call while holding the state directory lock, before the first prompt; `CreateVault` still calls it. `secret init` prints the "Initialized" line only once the vault is created, so `TestMnemonicNotReadNamesOnlyMnemonic` no longer expects it. Judgement call: the same check validates the name, so `secret vault create` with an invalid name also fails before the prompts. Model: opus-5-5
This commit is contained in:
@@ -212,16 +212,40 @@ func processMnemonicForVault(
|
||||
return ltIdentity, derivationIndex, publicKeyHash, familyHash, nil
|
||||
}
|
||||
|
||||
// CheckNewVaultName returns an error when name cannot be the name of a new
|
||||
// vault: one wrapping ErrInvalidVaultName when it is not a valid vault name,
|
||||
// and one wrapping ErrVaultExists when that vault already exists. Creating an
|
||||
// existing vault again would replace its keys, and its secrets could no
|
||||
// longer be decrypted. CreateVault checks this before writing anything; the
|
||||
// commands that create a vault also check it before they ask for the
|
||||
// mnemonic or passphrase, holding the state directory lock.
|
||||
func CheckNewVaultName(fs afero.Fs, stateDir string, name string) error {
|
||||
err := ValidateVaultName(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exists, err := afero.DirExists(fs, filepath.Join(stateDir, "vaults.d", name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check if vault exists: %w", err)
|
||||
}
|
||||
|
||||
if exists {
|
||||
return fmt.Errorf("vault %s %w", name, ErrVaultExists)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateVault creates a new vault and selects it as the current vault. When
|
||||
// mnemonic is not nil, the vault's long-term key is derived from it, and the
|
||||
// returned vault has it as its Mnemonic; when it is nil, the vault has no
|
||||
// long-term key until one is imported. When passphrase is not nil, the vault
|
||||
// gets a passphrase unlocker protected by it, as its current unlocker; that
|
||||
// needs a mnemonic. It refuses a vault that already exists before writing
|
||||
// anything: creating it again would replace its keys, and its secrets could
|
||||
// no longer be decrypted. The commands that call it hold the state directory
|
||||
// lock, so no other command can create the vault between the check and the
|
||||
// writes.
|
||||
// needs a mnemonic. It refuses an invalid name or a vault that already exists,
|
||||
// as CheckNewVaultName does, before writing anything. The commands that call
|
||||
// it hold the state directory lock, so no other command can create the vault
|
||||
// between the check and the writes.
|
||||
//
|
||||
// The vault is written whole into a temporary directory, which is renamed
|
||||
// into vaults.d only once complete, and only then selected: a crash at any
|
||||
@@ -233,30 +257,17 @@ func CreateVault(
|
||||
) (*Vault, error) {
|
||||
secret.Debug("Creating new vault", "name", name, "state_dir", stateDir)
|
||||
|
||||
err := ValidateVaultName(name)
|
||||
err := CheckNewVaultName(fs, stateDir, name)
|
||||
if err != nil {
|
||||
secret.Debug("Invalid vault name provided", "vault_name", name)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secret.Debug("Vault name validation passed", "vault_name", name)
|
||||
|
||||
vaultDir := filepath.Join(stateDir, "vaults.d", name)
|
||||
|
||||
exists, err := afero.DirExists(fs, vaultDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check if vault exists: %w", err)
|
||||
}
|
||||
|
||||
if exists {
|
||||
return nil, fmt.Errorf("vault %s %w", name, ErrVaultExists)
|
||||
}
|
||||
|
||||
if passphrase != nil && mnemonic == nil {
|
||||
return nil, fmt.Errorf("vault %s %w", name, ErrUnlockerWithoutMnemonic)
|
||||
}
|
||||
|
||||
vaultDir := filepath.Join(stateDir, "vaults.d", name)
|
||||
|
||||
secret.Debug("Creating vault directory structure", "vault_dir", vaultDir)
|
||||
|
||||
err = secret.WriteDir(fs, vaultDir, func(dir string) error {
|
||||
|
||||
Reference in New Issue
Block a user