Ask before removing a secret, version, vault or unlocker (closes #39)
check / check (push) Failing after 2s
check / check (push) Failing after 2s
secret rm, secret version rm, secret vault remove and secret unlocker remove ask [y/N] on a terminal, naming what they remove, and go ahead only on y or yes. Without --force, a command whose stdin is not a terminal fails at once. --force, now also on rm and version rm, removes without asking; it replaces the old refusals to remove a vault with secrets or the last unlocker without --force. The checks run and the question is asked before the state directory lock is taken; under the lock the checks run again, and nothing is removed if they would ask a different question. Model: opus-5-5
This commit is contained in:
@@ -5,14 +5,15 @@
|
||||
// one the commands act on, metadata that is not JSON, and check that the
|
||||
// commands step past it, and that it can itself be removed by its
|
||||
// directory name, which `secret unlocker list` names in its warning. A
|
||||
// last test checks that an unlocker whose metadata file cannot be read is
|
||||
// removed by its directory name only as the last unlocker is.
|
||||
// last test checks that an unlocker whose metadata file cannot be read
|
||||
// counts as the last unlocker when it is removed by its directory name.
|
||||
|
||||
//nolint:testpackage // white-box test of unexported internals
|
||||
package cli
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.eeqj.de/sneak/secret/internal/vault"
|
||||
@@ -56,37 +57,27 @@ func TestUnlockerSelectSkipsCorruptUnlocker(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestUnlockerRemoveWithCorruptUnlocker asserts that the second unlocker
|
||||
// can be removed, unless the vault holds secrets: the corrupt unlocker
|
||||
// cannot unlock the vault, so the second is its last. The corrupt one can
|
||||
// be removed by its directory name without --force even then.
|
||||
// counts as the vault's last one, since the corrupt unlocker cannot unlock
|
||||
// the vault, and that the corrupt one, removed by its directory name, does
|
||||
// not. Either is removed once the user confirms.
|
||||
func TestUnlockerRemoveWithCorruptUnlocker(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
unlockerID string
|
||||
withSecret bool
|
||||
wantErr error
|
||||
wantLast bool
|
||||
wantEntries []string
|
||||
}{
|
||||
{
|
||||
name: "the other unlocker",
|
||||
unlockerID: "pgp-" + listTestGPGKeyID + "B",
|
||||
wantLast: true,
|
||||
wantEntries: []string{listTestUnlockerDirOne},
|
||||
},
|
||||
{
|
||||
name: "the other unlocker, the last one, with secrets",
|
||||
unlockerID: "pgp-" + listTestGPGKeyID + "B",
|
||||
withSecret: true,
|
||||
wantErr: errLastUnlocker,
|
||||
wantEntries: []string{
|
||||
listTestUnlockerDirOne, listTestUnlockerDirTwo,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "the corrupt unlocker by its directory name",
|
||||
unlockerID: listTestUnlockerDirOne,
|
||||
withSecret: true,
|
||||
wantEntries: []string{listTestUnlockerDirTwo},
|
||||
},
|
||||
}
|
||||
@@ -96,14 +87,16 @@ func TestUnlockerRemoveWithCorruptUnlocker(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fs := newCorruptUnlockerVault(t)
|
||||
if tt.withSecret {
|
||||
writeTestSecret(t, fs, testVaultDir(listTestVaultName))
|
||||
}
|
||||
writeTestSecret(t, fs, testVaultDir(listTestVaultName))
|
||||
|
||||
instance, cmd := newTestInstance(fs)
|
||||
|
||||
err := instance.UnlockersRemove(tt.unlockerID, false, cmd)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
found, err := instance.findUnlockerToRemove(tt.unlockerID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.wantLast, found.last)
|
||||
|
||||
instance.terminal = strings.NewReader("y\n")
|
||||
require.NoError(t, instance.UnlockersRemove(tt.unlockerID, false, cmd))
|
||||
|
||||
assertDirEntries(t, fs,
|
||||
filepath.Join(testVaultDir(listTestVaultName),
|
||||
@@ -113,13 +106,14 @@ func TestUnlockerRemoveWithCorruptUnlocker(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestUnlockerRemoveWithUnreadableMetadata asserts that removing the only
|
||||
// unlocker of a vault with secrets by its directory name, when its
|
||||
// metadata file cannot be checked for or read, is refused without --force:
|
||||
// listing leaves it out, but it may still be the vault's only working
|
||||
// unlocker. With --force it is removed. The state directory lock refuses
|
||||
// the failing filesystem, so the test calls removeUnlocker, which
|
||||
// UnlockersRemove runs once it holds the lock.
|
||||
// TestUnlockerRemoveWithUnreadableMetadata asserts that the only unlocker
|
||||
// of a vault with secrets, removed by its directory name when its metadata
|
||||
// file cannot be checked for or read, counts as the vault's last unlocker,
|
||||
// so the question warns that it is: listing leaves it out, but it may
|
||||
// still be the vault's only working unlocker. It is then removed. The
|
||||
// state directory lock refuses the failing filesystem, so the test calls
|
||||
// findUnlockerToRemove and removeUnlocker, which UnlockersRemove runs to
|
||||
// make its checks and, once it holds the lock, to remove the unlocker.
|
||||
func TestUnlockerRemoveWithUnreadableMetadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -155,12 +149,13 @@ func TestUnlockerRemoveWithUnreadableMetadata(t *testing.T) {
|
||||
|
||||
instance, cmd := newTestInstance(tt.wrap(base))
|
||||
|
||||
err := instance.removeUnlocker(listTestUnlockerDirOne, false, cmd)
|
||||
require.ErrorIs(t, err, errLastUnlocker)
|
||||
assertDirEntries(t, base, unlockersDir, listTestUnlockerDirOne)
|
||||
found, err := instance.findUnlockerToRemove(listTestUnlockerDirOne)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, found.last)
|
||||
assert.Contains(t, found.question, "the last unlocker")
|
||||
|
||||
require.NoError(t,
|
||||
instance.removeUnlocker(listTestUnlockerDirOne, true, cmd))
|
||||
instance.removeUnlocker(listTestUnlockerDirOne, found, cmd))
|
||||
assertDirEntries(t, base, unlockersDir)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user