Files
vault/builtin/logical/transit/path_restore_test.go
hashicorp-copywrite[bot] 0b12cdcfd1 [COMPLIANCE] License changes (#22290)
* 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>
2023-08-10 18:14:03 -07:00

264 lines
7.0 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package transit
import (
"context"
"fmt"
"testing"
"github.com/hashicorp/vault/helper/testhelpers"
"github.com/hashicorp/vault/sdk/logical"
)
func TestTransit_Restore(t *testing.T) {
// Test setup:
// - Create a key
// - Configure it to be exportable, allowing deletion, and backups
// - Capture backup
// - Delete key
// - Run test cases
//
// Each test case should start with no key present. If the 'Seed' parameter is
// in the struct, we'll start by restoring it (without force) to run that test
// as if the key already existed
keyType := "aes256-gcm96"
b, s := createBackendWithStorage(t)
keyName := testhelpers.RandomWithPrefix("my-key")
// Create a key
keyReq := &logical.Request{
Path: "keys/" + keyName,
Operation: logical.UpdateOperation,
Storage: s,
Data: map[string]interface{}{
"type": keyType,
"exportable": true,
},
}
resp, err := b.HandleRequest(context.Background(), keyReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("resp: %#v\nerr: %v", resp, err)
}
// Configure the key to allow its deletion and backup
configReq := &logical.Request{
Path: fmt.Sprintf("keys/%s/config", keyName),
Operation: logical.UpdateOperation,
Storage: s,
Data: map[string]interface{}{
"deletion_allowed": true,
"allow_plaintext_backup": true,
},
}
resp, err = b.HandleRequest(context.Background(), configReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("resp: %#v\nerr: %v", resp, err)
}
// Take a backup of the key
backupReq := &logical.Request{
Path: "backup/" + keyName,
Operation: logical.ReadOperation,
Storage: s,
}
resp, err = b.HandleRequest(context.Background(), backupReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("resp: %#v\nerr: %v", resp, err)
}
backupKey := resp.Data["backup"].(string)
if backupKey == "" {
t.Fatal("failed to get a backup")
}
// Delete the key to start test cases with clean slate
keyReq.Operation = logical.DeleteOperation
resp, err = b.HandleRequest(context.Background(), keyReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("resp: %#v\nerr: %v", resp, err)
}
// helper func to get a pointer value for a boolean
boolPtr := func(b bool) *bool {
return &b
}
keyExitsError := fmt.Errorf("key %q already exists", keyName)
testCases := []struct {
Name string
// Seed dermines if we start the test by restoring the initial backup we
// took, to test a restore operation based on the key existing or not
Seed bool
// Force is a pointer to differentiate between default false and given false
Force *bool
// The error we expect, if any
ExpectedErr error
// RestoreName is used to restore the key to a differnt name
RestoreName string
}{
{
// key does not already exist
Name: "Default restore",
},
{
// key already exists
Name: "Restore-without-force",
Seed: true,
ExpectedErr: keyExitsError,
},
{
// key already exists, use force to force a restore
Name: "Restore-with-force",
Seed: true,
Force: boolPtr(true),
},
{
// using force shouldn't matter if the key doesn't exist
Name: "Restore-with-force-no-seed",
Force: boolPtr(true),
},
{
// key already exists, restore to new name
Name: "Restore-new-name",
Seed: true,
RestoreName: "new-key",
},
{
// key already exists, restore to bad path, should error
Name: "Restore-new-name-bad-path",
Seed: true,
RestoreName: "sub/path/new-key",
ExpectedErr: ErrInvalidKeyName,
},
{
// using force shouldn't matter if the restore key name is different
Name: "Restore-with-force-seed-new-name",
Seed: true,
Force: boolPtr(true),
RestoreName: "other-key",
},
{
// using force shouldn't matter if the restore key name is different
Name: "Restore-with-out-force-seed-new-name",
Seed: true,
Force: boolPtr(false),
RestoreName: "other-key",
},
{
// using force shouldn't matter if the key doesn't exist
Name: "Restore-force-false",
Force: boolPtr(false),
},
{
// using false force should still error
Name: "Restore-force-false",
Seed: true,
Force: boolPtr(false),
ExpectedErr: keyExitsError,
},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
var resp *logical.Response
var err error
if tc.Seed {
// restore our key to test a pre-existing key
seedRestoreReq := &logical.Request{
Path: "restore",
Operation: logical.UpdateOperation,
Storage: s,
Data: map[string]interface{}{
"backup": backupKey,
},
}
resp, err := b.HandleRequest(context.Background(), seedRestoreReq)
if resp != nil && resp.IsError() {
t.Fatalf("resp: %#v\nerr: %v", resp, err)
}
if err != nil && tc.ExpectedErr == nil {
t.Fatalf("did not expect an error in SeedKey restore: %s", err)
}
}
restorePath := "restore"
if tc.RestoreName != "" {
restorePath = fmt.Sprintf("%s/%s", restorePath, tc.RestoreName)
}
restoreReq := &logical.Request{
Path: restorePath,
Operation: logical.UpdateOperation,
Storage: s,
Data: map[string]interface{}{
"backup": backupKey,
},
}
if tc.Force != nil {
restoreReq.Data["force"] = *tc.Force
}
resp, err = b.HandleRequest(context.Background(), restoreReq)
if resp != nil && resp.IsError() {
t.Fatalf("resp: %#v\nerr: %v", resp, err)
}
if err == nil && tc.ExpectedErr != nil {
t.Fatalf("expected an error, but got none")
}
if err != nil && tc.ExpectedErr == nil {
t.Fatalf("unexpected error:%s", err)
}
if err != nil && tc.ExpectedErr != nil {
if err.Error() != tc.ExpectedErr.Error() {
t.Fatalf("expected error: (%s), got: (%s)", tc.ExpectedErr.Error(), err.Error())
}
}
readKeyName := keyName
if tc.RestoreName != "" {
readKeyName = tc.RestoreName
}
// read the key and make sure it's there
readReq := &logical.Request{
Path: "keys/" + readKeyName,
Operation: logical.ReadOperation,
Storage: s,
}
resp, _ = b.HandleRequest(context.Background(), readReq)
if resp != nil && resp.IsError() {
t.Fatalf("resp: %#v\nerr: %v", resp, err)
}
if tc.ExpectedErr == nil && resp == nil {
t.Fatal("expected to find a key, but got none")
}
// cleanup / delete key after each run
keyReq.Operation = logical.DeleteOperation
resp, err = b.HandleRequest(context.Background(), keyReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("resp: %#v\nerr: %v", resp, err)
}
// cleanup / delete restore key after each run, if it was created
if tc.RestoreName != "" && tc.ExpectedErr == nil {
readReq.Operation = logical.DeleteOperation
resp, err = b.HandleRequest(context.Background(), readReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("resp: %#v\nerr: %v", resp, err)
}
}
})
}
}