8 changed files with 76 additions and 7 deletions
@ -0,0 +1,13 @@ |
|||
package secretstore |
|||
|
|||
// FileReader resolves the current atomic snapshot written by another process.
|
|||
// It deliberately has no write methods and never falls back to stale secrets.
|
|||
type FileReader struct{ Path string } |
|||
|
|||
func (r FileReader) Get(key string) ([]byte, bool) { |
|||
store, err := Open(r.Path) |
|||
if err != nil { |
|||
return nil, false |
|||
} |
|||
return store.Get(key) |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
package secretstore |
|||
|
|||
import ( |
|||
"os" |
|||
"path/filepath" |
|||
"testing" |
|||
) |
|||
|
|||
func TestFileReaderObservesOtherProcessUpdatesAndFailsClosed(t *testing.T) { |
|||
path := filepath.Join(t.TempDir(), "secrets.json") |
|||
reader := FileReader{Path: path} |
|||
writer, err := Open(path) |
|||
if err != nil { |
|||
t.Fatal(err) |
|||
} |
|||
key := "running/email/password" |
|||
if _, ok := reader.Get(key); ok { |
|||
t.Fatal("unexpected initial secret") |
|||
} |
|||
for _, value := range []string{"first-app-password", "rotated-app-password"} { |
|||
if err := writer.Set(key, []byte(value)); err != nil { |
|||
t.Fatal(err) |
|||
} |
|||
got, ok := reader.Get(key) |
|||
if !ok || string(got) != value { |
|||
t.Fatal("reader did not observe current snapshot") |
|||
} |
|||
} |
|||
if err := writer.Delete(key); err != nil { |
|||
t.Fatal(err) |
|||
} |
|||
if _, ok := reader.Get(key); ok { |
|||
t.Fatal("deleted secret remained available") |
|||
} |
|||
if err := writer.Set(key, []byte("temporary")); err != nil { |
|||
t.Fatal(err) |
|||
} |
|||
if err := os.WriteFile(path, []byte("invalid JSON"), 0600); err != nil { |
|||
t.Fatal(err) |
|||
} |
|||
if _, ok := reader.Get(key); ok { |
|||
t.Fatal("invalid snapshot reused stale secret") |
|||
} |
|||
} |
|||
Binary file not shown.
Loading…
Reference in new issue