mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-17 03:57:01 +02:00
* Adding explicit MPL license for sub-package. This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package. This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License. Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at https://hashi.co/bsl-blog, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUS-1.1 * Fix test that expected exact offset on hcl file --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> Co-authored-by: Sarah Thompson <sthompson@hashicorp.com> Co-authored-by: Brian Kassouf <bkassouf@hashicorp.com>
382 lines
9.9 KiB
Go
382 lines
9.9 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package cacheboltdb
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/hashicorp/go-hclog"
|
|
"github.com/hashicorp/vault/command/agentproxyshared/cache/keymanager"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
bolt "go.etcd.io/bbolt"
|
|
)
|
|
|
|
func getTestKeyManager(t *testing.T) keymanager.KeyManager {
|
|
t.Helper()
|
|
|
|
km, err := keymanager.NewPassthroughKeyManager(context.Background(), nil)
|
|
require.NoError(t, err)
|
|
|
|
return km
|
|
}
|
|
|
|
func TestBolt_SetGet(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
path, err := ioutil.TempDir("", "bolt-test")
|
|
require.NoError(t, err)
|
|
defer os.RemoveAll(path)
|
|
|
|
b, err := NewBoltStorage(&BoltStorageConfig{
|
|
Path: path,
|
|
Logger: hclog.Default(),
|
|
Wrapper: getTestKeyManager(t).Wrapper(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
secrets, err := b.GetByType(ctx, LeaseType)
|
|
assert.NoError(t, err)
|
|
require.Len(t, secrets, 0)
|
|
|
|
err = b.Set(ctx, "test1", []byte("hello"), LeaseType)
|
|
assert.NoError(t, err)
|
|
secrets, err = b.GetByType(ctx, LeaseType)
|
|
assert.NoError(t, err)
|
|
require.Len(t, secrets, 1)
|
|
assert.Equal(t, []byte("hello"), secrets[0])
|
|
}
|
|
|
|
func TestBoltDelete(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
path, err := ioutil.TempDir("", "bolt-test")
|
|
require.NoError(t, err)
|
|
defer os.RemoveAll(path)
|
|
|
|
b, err := NewBoltStorage(&BoltStorageConfig{
|
|
Path: path,
|
|
Logger: hclog.Default(),
|
|
Wrapper: getTestKeyManager(t).Wrapper(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = b.Set(ctx, "secret-test1", []byte("hello1"), LeaseType)
|
|
require.NoError(t, err)
|
|
err = b.Set(ctx, "secret-test2", []byte("hello2"), LeaseType)
|
|
require.NoError(t, err)
|
|
|
|
secrets, err := b.GetByType(ctx, LeaseType)
|
|
require.NoError(t, err)
|
|
assert.Len(t, secrets, 2)
|
|
assert.ElementsMatch(t, [][]byte{[]byte("hello1"), []byte("hello2")}, secrets)
|
|
|
|
err = b.Delete("secret-test1", LeaseType)
|
|
require.NoError(t, err)
|
|
secrets, err = b.GetByType(ctx, LeaseType)
|
|
require.NoError(t, err)
|
|
require.Len(t, secrets, 1)
|
|
assert.Equal(t, []byte("hello2"), secrets[0])
|
|
}
|
|
|
|
func TestBoltClear(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
path, err := ioutil.TempDir("", "bolt-test")
|
|
require.NoError(t, err)
|
|
defer os.RemoveAll(path)
|
|
|
|
b, err := NewBoltStorage(&BoltStorageConfig{
|
|
Path: path,
|
|
Logger: hclog.Default(),
|
|
Wrapper: getTestKeyManager(t).Wrapper(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Populate the bolt db
|
|
err = b.Set(ctx, "secret-test1", []byte("hello1"), LeaseType)
|
|
require.NoError(t, err)
|
|
secrets, err := b.GetByType(ctx, LeaseType)
|
|
require.NoError(t, err)
|
|
require.Len(t, secrets, 1)
|
|
assert.Equal(t, []byte("hello1"), secrets[0])
|
|
|
|
err = b.Set(ctx, "auth-test1", []byte("hello2"), LeaseType)
|
|
require.NoError(t, err)
|
|
auths, err := b.GetByType(ctx, LeaseType)
|
|
require.NoError(t, err)
|
|
require.Len(t, auths, 2)
|
|
assert.Equal(t, []byte("hello1"), auths[0])
|
|
assert.Equal(t, []byte("hello2"), auths[1])
|
|
|
|
err = b.Set(ctx, "token-test1", []byte("hello"), TokenType)
|
|
require.NoError(t, err)
|
|
tokens, err := b.GetByType(ctx, TokenType)
|
|
require.NoError(t, err)
|
|
require.Len(t, tokens, 1)
|
|
assert.Equal(t, []byte("hello"), tokens[0])
|
|
|
|
// Clear the bolt db, and check that it's indeed clear
|
|
err = b.Clear()
|
|
require.NoError(t, err)
|
|
auths, err = b.GetByType(ctx, LeaseType)
|
|
require.NoError(t, err)
|
|
assert.Len(t, auths, 0)
|
|
tokens, err = b.GetByType(ctx, TokenType)
|
|
require.NoError(t, err)
|
|
assert.Len(t, tokens, 0)
|
|
}
|
|
|
|
func TestBoltSetAutoAuthToken(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
path, err := ioutil.TempDir("", "bolt-test")
|
|
require.NoError(t, err)
|
|
defer os.RemoveAll(path)
|
|
|
|
b, err := NewBoltStorage(&BoltStorageConfig{
|
|
Path: path,
|
|
Logger: hclog.Default(),
|
|
Wrapper: getTestKeyManager(t).Wrapper(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
token, err := b.GetAutoAuthToken(ctx)
|
|
assert.NoError(t, err)
|
|
assert.Nil(t, token)
|
|
|
|
// set first token
|
|
err = b.Set(ctx, "token-test1", []byte("hello 1"), TokenType)
|
|
require.NoError(t, err)
|
|
secrets, err := b.GetByType(ctx, TokenType)
|
|
require.NoError(t, err)
|
|
require.Len(t, secrets, 1)
|
|
assert.Equal(t, []byte("hello 1"), secrets[0])
|
|
token, err = b.GetAutoAuthToken(ctx)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, []byte("hello 1"), token)
|
|
|
|
// set second token
|
|
err = b.Set(ctx, "token-test2", []byte("hello 2"), TokenType)
|
|
require.NoError(t, err)
|
|
secrets, err = b.GetByType(ctx, TokenType)
|
|
require.NoError(t, err)
|
|
require.Len(t, secrets, 2)
|
|
assert.ElementsMatch(t, [][]byte{[]byte("hello 1"), []byte("hello 2")}, secrets)
|
|
token, err = b.GetAutoAuthToken(ctx)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, []byte("hello 2"), token)
|
|
}
|
|
|
|
func TestDBFileExists(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
mkDir bool
|
|
createFile bool
|
|
expectExist bool
|
|
}{
|
|
{
|
|
name: "all exists",
|
|
mkDir: true,
|
|
createFile: true,
|
|
expectExist: true,
|
|
},
|
|
{
|
|
name: "dir exist, file missing",
|
|
mkDir: true,
|
|
createFile: false,
|
|
expectExist: false,
|
|
},
|
|
{
|
|
name: "all missing",
|
|
mkDir: false,
|
|
createFile: false,
|
|
expectExist: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var tmpPath string
|
|
var err error
|
|
if tc.mkDir {
|
|
tmpPath, err = ioutil.TempDir("", "test-db-path")
|
|
require.NoError(t, err)
|
|
}
|
|
if tc.createFile {
|
|
err = ioutil.WriteFile(path.Join(tmpPath, DatabaseFileName), []byte("test-db-path"), 0o600)
|
|
require.NoError(t, err)
|
|
}
|
|
exists, err := DBFileExists(tmpPath)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.expectExist, exists)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_SetGetRetrievalToken(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
tokenToSet []byte
|
|
expectedToken []byte
|
|
}{
|
|
{
|
|
name: "normal set and get",
|
|
tokenToSet: []byte("test token"),
|
|
expectedToken: []byte("test token"),
|
|
},
|
|
{
|
|
name: "no token set",
|
|
tokenToSet: nil,
|
|
expectedToken: nil,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
path, err := ioutil.TempDir("", "bolt-test")
|
|
require.NoError(t, err)
|
|
defer os.RemoveAll(path)
|
|
|
|
b, err := NewBoltStorage(&BoltStorageConfig{
|
|
Path: path,
|
|
Logger: hclog.Default(),
|
|
Wrapper: getTestKeyManager(t).Wrapper(),
|
|
})
|
|
require.NoError(t, err)
|
|
defer b.Close()
|
|
|
|
if tc.tokenToSet != nil {
|
|
err := b.StoreRetrievalToken(tc.tokenToSet)
|
|
require.NoError(t, err)
|
|
}
|
|
gotKey, err := b.GetRetrievalToken()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.expectedToken, gotKey)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBolt_MigrateFromV1ToV2Schema(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
path, err := ioutil.TempDir("", "bolt-test")
|
|
require.NoError(t, err)
|
|
defer os.RemoveAll(path)
|
|
|
|
dbPath := filepath.Join(path, DatabaseFileName)
|
|
db, err := bolt.Open(dbPath, 0o600, &bolt.Options{Timeout: 1 * time.Second})
|
|
require.NoError(t, err)
|
|
err = db.Update(func(tx *bolt.Tx) error {
|
|
return createBoltSchema(tx, "1")
|
|
})
|
|
require.NoError(t, err)
|
|
b := &BoltStorage{
|
|
db: db,
|
|
logger: hclog.Default(),
|
|
wrapper: getTestKeyManager(t).Wrapper(),
|
|
}
|
|
|
|
// Manually insert some items into the v1 schema.
|
|
err = db.Update(func(tx *bolt.Tx) error {
|
|
blob, err := b.wrapper.Encrypt(ctx, []byte("ignored-contents"))
|
|
if err != nil {
|
|
return fmt.Errorf("error encrypting contents: %w", err)
|
|
}
|
|
protoBlob, err := proto.Marshal(blob)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tx.Bucket([]byte(authLeaseType)).Put([]byte("test-auth-id-1"), protoBlob); err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Bucket([]byte(authLeaseType)).Put([]byte("test-auth-id-2"), protoBlob); err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Bucket([]byte(secretLeaseType)).Put([]byte("test-secret-id-1"), protoBlob); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Check we have the contents we would expect for the v1 schema.
|
|
leases, err := b.GetByType(ctx, authLeaseType)
|
|
require.NoError(t, err)
|
|
assert.Len(t, leases, 2)
|
|
leases, err = b.GetByType(ctx, secretLeaseType)
|
|
require.NoError(t, err)
|
|
assert.Len(t, leases, 1)
|
|
leases, err = b.GetByType(ctx, LeaseType)
|
|
require.Error(t, err)
|
|
assert.True(t, strings.Contains(err.Error(), "not found"))
|
|
|
|
// Now migrate to the v2 schema.
|
|
err = db.Update(migrateFromV1ToV2Schema)
|
|
require.NoError(t, err)
|
|
|
|
// Check all the leases have been migrated into one bucket.
|
|
leases, err = b.GetByType(ctx, authLeaseType)
|
|
require.Error(t, err)
|
|
assert.True(t, strings.Contains(err.Error(), "not found"))
|
|
leases, err = b.GetByType(ctx, secretLeaseType)
|
|
require.Error(t, err)
|
|
assert.True(t, strings.Contains(err.Error(), "not found"))
|
|
leases, err = b.GetByType(ctx, LeaseType)
|
|
require.NoError(t, err)
|
|
assert.Len(t, leases, 3)
|
|
}
|
|
|
|
func TestBolt_MigrateFromInvalidToV2Schema(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
path, err := ioutil.TempDir("", "bolt-test")
|
|
require.NoError(t, err)
|
|
defer os.RemoveAll(path)
|
|
|
|
dbPath := filepath.Join(path, DatabaseFileName)
|
|
db, err := bolt.Open(dbPath, 0o600, &bolt.Options{Timeout: 1 * time.Second})
|
|
require.NoError(t, err)
|
|
b := &BoltStorage{
|
|
db: db,
|
|
logger: hclog.Default(),
|
|
wrapper: getTestKeyManager(t).Wrapper(),
|
|
}
|
|
|
|
// All GetByType calls should fail as there's no schema
|
|
for _, bucket := range []string{authLeaseType, secretLeaseType, LeaseType} {
|
|
_, err = b.GetByType(ctx, bucket)
|
|
require.Error(t, err)
|
|
assert.True(t, strings.Contains(err.Error(), "not found"))
|
|
}
|
|
|
|
// Now migrate to the v2 schema.
|
|
err = db.Update(migrateFromV1ToV2Schema)
|
|
require.NoError(t, err)
|
|
|
|
// Deprecated auth and secret lease buckets still shouldn't exist
|
|
// All GetByType calls should fail as there's no schema
|
|
for _, bucket := range []string{authLeaseType, secretLeaseType} {
|
|
_, err = b.GetByType(ctx, bucket)
|
|
require.Error(t, err)
|
|
assert.True(t, strings.Contains(err.Error(), "not found"))
|
|
}
|
|
|
|
// GetByType for LeaseType should now return an empty result
|
|
leases, err := b.GetByType(ctx, LeaseType)
|
|
require.NoError(t, err)
|
|
require.Len(t, leases, 0)
|
|
}
|