Add cross-vault move command for secrets
Implement syntax: secret move/mv <vault>:<secret> <vault>[:<secret>] - Copies all versions to destination vault with re-encryption - Deletes source after successful copy (true move) - Add --force flag to overwrite existing destination - Support both within-vault rename and cross-vault move - Add shell completion for vault:secret syntax - Include integration tests for cross-vault move
This commit is contained in:
@@ -200,6 +200,12 @@ func TestSecretManagerIntegration(t *testing.T) {
|
||||
// Expected: Secret moved to new location, old location removed
|
||||
test12bMoveSecret(t, testMnemonic, runSecret, runSecretWithStdin)
|
||||
|
||||
// Test 12c: Cross-vault move
|
||||
// Commands: secret move work:secret default, secret move work:secret default:newname
|
||||
// Purpose: Test moving secrets between vaults with re-encryption
|
||||
// Expected: Secret copied to destination vault with all versions, source deleted
|
||||
test12cCrossVaultMove(t, testMnemonic, runSecretWithEnv, runSecretWithStdin)
|
||||
|
||||
// Test 13: Unlocker management
|
||||
// Commands: secret unlocker list, secret unlocker add pgp
|
||||
// Purpose: Test multiple unlocker types
|
||||
@@ -1164,6 +1170,89 @@ func test12bMoveSecret(t *testing.T, testMnemonic string, runSecret func(...stri
|
||||
assert.Equal(t, "original-value", getOutput, "source should still have original value")
|
||||
}
|
||||
|
||||
func test12cCrossVaultMove(t *testing.T, testMnemonic string, runSecretWithEnv func(map[string]string, ...string) (string, error), runSecretWithStdin func(string, map[string]string, ...string) (string, error)) {
|
||||
env := map[string]string{
|
||||
"SB_SECRET_MNEMONIC": testMnemonic,
|
||||
}
|
||||
|
||||
// Create a test secret in the work vault
|
||||
_, err := runSecretWithEnv(env, "vault", "select", "work")
|
||||
require.NoError(t, err, "select work vault should succeed")
|
||||
|
||||
// Add a secret with a version
|
||||
_, err = runSecretWithStdin("cross-vault-value-v1", env, "add", "cross/move/test")
|
||||
require.NoError(t, err, "add cross/move/test should succeed")
|
||||
|
||||
// Add another version
|
||||
_, err = runSecretWithStdin("cross-vault-value-v2", env, "add", "--force", "cross/move/test")
|
||||
require.NoError(t, err, "add cross/move/test v2 should succeed")
|
||||
|
||||
// Move to default vault using cross-vault syntax
|
||||
output, err := runSecretWithEnv(env, "move", "work:cross/move/test", "default")
|
||||
require.NoError(t, err, "cross-vault move should succeed")
|
||||
assert.Contains(t, output, "Moved secret", "should show move confirmation")
|
||||
assert.Contains(t, output, "2 version(s)", "should show version count")
|
||||
|
||||
// Verify secret exists in default vault
|
||||
_, err = runSecretWithEnv(env, "vault", "select", "default")
|
||||
require.NoError(t, err, "select default vault should succeed")
|
||||
|
||||
value, err := runSecretWithEnv(env, "get", "cross/move/test")
|
||||
require.NoError(t, err, "get from default vault should succeed")
|
||||
assert.Equal(t, "cross-vault-value-v2", value, "should have latest version value")
|
||||
|
||||
// Verify secret no longer exists in work vault
|
||||
_, err = runSecretWithEnv(env, "vault", "select", "work")
|
||||
require.NoError(t, err, "select work vault should succeed")
|
||||
|
||||
_, err = runSecretWithEnv(env, "get", "cross/move/test")
|
||||
assert.Error(t, err, "get from work vault should fail after move")
|
||||
|
||||
// Test cross-vault move with rename
|
||||
_, err = runSecretWithStdin("rename-test", env, "add", "rename/source")
|
||||
require.NoError(t, err, "add rename/source should succeed")
|
||||
|
||||
output, err = runSecretWithEnv(env, "move", "work:rename/source", "default:renamed/dest")
|
||||
require.NoError(t, err, "cross-vault move with rename should succeed")
|
||||
assert.Contains(t, output, "Moved secret", "should show move confirmation")
|
||||
|
||||
// Verify renamed secret exists in default vault
|
||||
_, err = runSecretWithEnv(env, "vault", "select", "default")
|
||||
require.NoError(t, err, "select default vault should succeed")
|
||||
|
||||
value, err = runSecretWithEnv(env, "get", "renamed/dest")
|
||||
require.NoError(t, err, "get renamed secret should succeed")
|
||||
assert.Equal(t, "rename-test", value, "should have correct value")
|
||||
|
||||
// Test --force flag for overwriting
|
||||
_, err = runSecretWithStdin("existing-secret", env, "add", "force/test")
|
||||
require.NoError(t, err, "add force/test in default should succeed")
|
||||
|
||||
_, err = runSecretWithEnv(env, "vault", "select", "work")
|
||||
require.NoError(t, err, "select work vault should succeed")
|
||||
|
||||
_, err = runSecretWithStdin("new-value", env, "add", "force/test")
|
||||
require.NoError(t, err, "add force/test in work should succeed")
|
||||
|
||||
// Move without force should fail
|
||||
output, err = runSecretWithEnv(env, "move", "work:force/test", "default")
|
||||
assert.Error(t, err, "move without force should fail when dest exists")
|
||||
assert.Contains(t, output, "already exists", "should indicate destination exists")
|
||||
|
||||
// Move with force should succeed
|
||||
output, err = runSecretWithEnv(env, "move", "--force", "work:force/test", "default")
|
||||
require.NoError(t, err, "move with force should succeed")
|
||||
assert.Contains(t, output, "Moved secret", "should show move confirmation")
|
||||
|
||||
// Verify value was overwritten
|
||||
_, err = runSecretWithEnv(env, "vault", "select", "default")
|
||||
require.NoError(t, err, "select default vault should succeed")
|
||||
|
||||
value, err = runSecretWithEnv(env, "get", "force/test")
|
||||
require.NoError(t, err, "get overwritten secret should succeed")
|
||||
assert.Equal(t, "new-value", value, "should have new value after force move")
|
||||
}
|
||||
|
||||
func test13UnlockerManagement(t *testing.T, tempDir, testMnemonic string, runSecret func(...string) (string, error), runSecretWithEnv func(map[string]string, ...string) (string, error)) {
|
||||
// Make sure we're in default vault
|
||||
_, err := runSecret("vault", "select", "default")
|
||||
|
||||
Reference in New Issue
Block a user