100 lines
2.7 KiB
Go
100 lines
2.7 KiB
Go
package manager
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"cloud.campbellwireless.net/git/barkstack/treatvault/internal/provider"
|
|
"cloud.campbellwireless.net/git/barkstack/treatvault/internal/vault"
|
|
"filippo.io/age"
|
|
)
|
|
|
|
func TestRunSynchronizesExternalEncryptedFileChanges(t *testing.T) {
|
|
identity, err := age.GenerateX25519Identity()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
path := t.TempDir() + "/secrets.age"
|
|
if err := vault.Initialize(path, identity); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
store := vault.New(path, identity)
|
|
provider := &recordingProvider{synced: make(chan vault.Snapshot, 4)}
|
|
manager := New(store, provider, 10*time.Millisecond)
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
go manager.Run(ctx)
|
|
|
|
initial := waitSnapshot(t, provider.synced)
|
|
if len(initial.Secrets) != 0 {
|
|
t.Fatalf("initial secrets = %#v", initial.Secrets)
|
|
}
|
|
if _, err := store.Set("database_password", []byte("changed outside manager")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
changed := waitSnapshot(t, provider.synced)
|
|
if string(changed.Secrets["database_password"].Value) != "changed outside manager" {
|
|
t.Fatalf("changed snapshot = %#v", changed)
|
|
}
|
|
}
|
|
|
|
func TestDeleteRefusesSecretInUse(t *testing.T) {
|
|
identity, err := age.GenerateX25519Identity()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
path := t.TempDir() + "/secrets.age"
|
|
if err := vault.Initialize(path, identity); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
store := vault.New(path, identity)
|
|
if _, err := store.Set("database_password", []byte("value")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
manager := New(store, &recordingProvider{inUse: true}, time.Second)
|
|
if err := manager.Delete(context.Background(), "database_password"); !IsSecretInUse(err) {
|
|
t.Fatalf("Delete() error = %v", err)
|
|
}
|
|
loaded, err := store.Load()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, exists := loaded.Secrets["database_password"]; !exists {
|
|
t.Fatal("in-use secret was removed from encrypted file")
|
|
}
|
|
}
|
|
|
|
type recordingProvider struct {
|
|
mu sync.Mutex
|
|
synced chan vault.Snapshot
|
|
inUse bool
|
|
}
|
|
|
|
func (p *recordingProvider) Sync(_ context.Context, snapshot vault.Snapshot) ([]provider.SecretState, error) {
|
|
if p.synced != nil {
|
|
p.synced <- snapshot
|
|
}
|
|
states := make([]provider.SecretState, 0, len(snapshot.Secrets))
|
|
for name, record := range snapshot.Secrets {
|
|
states = append(states, provider.SecretState{Name: name, Revision: record.Revision, InUse: p.inUse})
|
|
}
|
|
return states, nil
|
|
}
|
|
|
|
func (p *recordingProvider) InUse(context.Context, string) (bool, error) {
|
|
return p.inUse, nil
|
|
}
|
|
|
|
func waitSnapshot(t *testing.T, snapshots <-chan vault.Snapshot) vault.Snapshot {
|
|
t.Helper()
|
|
select {
|
|
case snapshot := <-snapshots:
|
|
return snapshot
|
|
case <-time.After(time.Second):
|
|
t.Fatal("timed out waiting for synchronization")
|
|
return vault.Snapshot{}
|
|
}
|
|
}
|