vault/http/sys_wrapping_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

390 lines
9.6 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package http
import (
"encoding/json"
"errors"
"reflect"
"testing"
"time"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/sdk/helper/jsonutil"
"github.com/hashicorp/vault/vault"
)
// Test wrapping functionality
func TestHTTP_Wrapping(t *testing.T) {
cluster := vault.NewTestCluster(t, &vault.CoreConfig{}, &vault.TestClusterOptions{
HandlerFunc: Handler,
})
cluster.Start()
defer cluster.Cleanup()
cores := cluster.Cores
// make it easy to get access to the active
core := cores[0].Core
vault.TestWaitActive(t, core)
client := cores[0].Client
client.SetToken(cluster.RootToken)
// Write a value that we will use with wrapping for lookup
_, err := client.Logical().Write("secret/foo", map[string]interface{}{
"zip": "zap",
})
if err != nil {
t.Fatal(err)
}
// Set a wrapping lookup function for reads on that path
client.SetWrappingLookupFunc(func(operation, path string) string {
if operation == "GET" && path == "secret/foo" {
return "5m"
}
return api.DefaultWrappingLookupFunc(operation, path)
})
// First test: basic things that should fail, lookup edition
// Root token isn't a wrapping token
_, err = client.Logical().Write("sys/wrapping/lookup", nil)
if err == nil {
t.Fatal("expected error")
}
// Not supplied
_, err = client.Logical().Write("sys/wrapping/lookup", map[string]interface{}{
"foo": "bar",
})
if err == nil {
t.Fatal("expected error")
}
// Nonexistent token isn't a wrapping token
_, err = client.Logical().Write("sys/wrapping/lookup", map[string]interface{}{
"token": "bar",
})
if err == nil {
t.Fatal("expected error")
}
// Second: basic things that should fail, unwrap edition
// Root token isn't a wrapping token
_, err = client.Logical().Unwrap(cluster.RootToken)
if err == nil {
t.Fatal("expected error")
}
// Root token isn't a wrapping token
_, err = client.Logical().Write("sys/wrapping/unwrap", nil)
if err == nil {
t.Fatal("expected error")
}
// Not supplied
_, err = client.Logical().Write("sys/wrapping/unwrap", map[string]interface{}{
"foo": "bar",
})
if err == nil {
t.Fatal("expected error")
}
// Nonexistent token isn't a wrapping token
_, err = client.Logical().Write("sys/wrapping/unwrap", map[string]interface{}{
"token": "bar",
})
if err == nil {
t.Fatal("expected error")
}
//
// Test lookup
//
// Create a wrapping token
secret, err := client.Logical().Read("secret/foo")
if err != nil {
t.Fatal(err)
}
if secret == nil || secret.WrapInfo == nil {
t.Fatal("secret or wrap info is nil")
}
wrapInfo := secret.WrapInfo
// Test this twice to ensure no ill effect to the wrapping token as a result of the lookup
for i := 0; i < 2; i++ {
secret, err = client.Logical().Write("sys/wrapping/lookup", map[string]interface{}{
"token": wrapInfo.Token,
})
if err != nil {
t.Fatal(err)
}
if secret == nil || secret.Data == nil {
t.Fatal("secret or secret data is nil")
}
creationTTL, _ := secret.Data["creation_ttl"].(json.Number).Int64()
if int(creationTTL) != wrapInfo.TTL {
t.Fatalf("mismatched ttls: %d vs %d", creationTTL, wrapInfo.TTL)
}
if secret.Data["creation_time"].(string) != wrapInfo.CreationTime.Format(time.RFC3339Nano) {
t.Fatalf("mismatched creation times: %q vs %q", secret.Data["creation_time"].(string), wrapInfo.CreationTime.Format(time.RFC3339Nano))
}
}
//
// Test unwrap
//
// Create a wrapping token
secret, err = client.Logical().Read("secret/foo")
if err != nil {
t.Fatal(err)
}
if secret == nil || secret.WrapInfo == nil {
t.Fatal("secret or wrap info is nil")
}
wrapInfo = secret.WrapInfo
// Test unwrap via the client token
client.SetToken(wrapInfo.Token)
secret, err = client.Logical().Write("sys/wrapping/unwrap", nil)
if err != nil {
t.Fatal(err)
}
if secret.Warnings != nil {
t.Fatalf("Warnings found: %v", secret.Warnings)
}
if secret == nil || secret.Data == nil {
t.Fatal("secret or secret data is nil")
}
ret1 := secret
// Should be expired and fail
_, err = client.Logical().Write("sys/wrapping/unwrap", nil)
if err == nil {
t.Fatal("expected err")
}
// Create a wrapping token
client.SetToken(cluster.RootToken)
secret, err = client.Logical().Read("secret/foo")
if err != nil {
t.Fatal(err)
}
if secret == nil || secret.WrapInfo == nil {
t.Fatal("secret or wrap info is nil")
}
wrapInfo = secret.WrapInfo
// Test as a separate token
secret, err = client.Logical().Write("sys/wrapping/unwrap", map[string]interface{}{
"token": wrapInfo.Token,
})
if err != nil {
t.Fatal(err)
}
ret2 := secret
// Should be expired and fail
_, err = client.Logical().Write("sys/wrapping/unwrap", map[string]interface{}{
"token": wrapInfo.Token,
})
if err == nil {
t.Fatal("expected err")
}
// Create a wrapping token
secret, err = client.Logical().Read("secret/foo")
if err != nil {
t.Fatal(err)
}
if secret == nil || secret.WrapInfo == nil {
t.Fatal("secret or wrap info is nil")
}
wrapInfo = secret.WrapInfo
// Read response directly
client.SetToken(wrapInfo.Token)
secret, err = client.Logical().Read("cubbyhole/response")
if err != nil {
t.Fatal(err)
}
ret3 := secret
// Should be expired and fail
_, err = client.Logical().Write("cubbyhole/response", nil)
if err == nil {
t.Fatal("expected err")
}
// Create a wrapping token
client.SetToken(cluster.RootToken)
secret, err = client.Logical().Read("secret/foo")
if err != nil {
t.Fatal(err)
}
if secret == nil || secret.WrapInfo == nil {
t.Fatal("secret or wrap info is nil")
}
wrapInfo = secret.WrapInfo
// Read via Unwrap method
secret, err = client.Logical().Unwrap(wrapInfo.Token)
if err != nil {
t.Fatal(err)
}
if secret.Warnings != nil {
t.Fatalf("Warnings found: %v", secret.Warnings)
}
ret4 := secret
// Should be expired and fail
_, err = client.Logical().Unwrap(wrapInfo.Token)
if err == nil {
t.Fatal("expected err")
}
if !reflect.DeepEqual(ret1.Data, map[string]interface{}{
"zip": "zap",
}) {
t.Fatalf("ret1 data did not match expected: %#v", ret1.Data)
}
if !reflect.DeepEqual(ret2.Data, map[string]interface{}{
"zip": "zap",
}) {
t.Fatalf("ret2 data did not match expected: %#v", ret2.Data)
}
var ret3Secret api.Secret
err = jsonutil.DecodeJSON([]byte(ret3.Data["response"].(string)), &ret3Secret)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(ret3Secret.Data, map[string]interface{}{
"zip": "zap",
}) {
t.Fatalf("ret3 data did not match expected: %#v", ret3Secret.Data)
}
if !reflect.DeepEqual(ret4.Data, map[string]interface{}{
"zip": "zap",
}) {
t.Fatalf("ret4 data did not match expected: %#v", ret4.Data)
}
//
// Custom wrapping
//
client.SetToken(cluster.RootToken)
data := map[string]interface{}{
"zip": "zap",
"three": json.Number("2"),
}
// Don't set a request TTL on that path, should fail
client.SetWrappingLookupFunc(func(operation, path string) string {
return ""
})
secret, err = client.Logical().Write("sys/wrapping/wrap", data)
if err == nil {
t.Fatal("expected error")
}
// Re-set the lookup function
client.SetWrappingLookupFunc(func(operation, path string) string {
if operation == "GET" && path == "secret/foo" {
return "5m"
}
return api.DefaultWrappingLookupFunc(operation, path)
})
secret, err = client.Logical().Write("sys/wrapping/wrap", data)
if err != nil {
t.Fatal(err)
}
if secret.Warnings != nil {
t.Fatalf("Warnings found: %v", secret.Warnings)
}
secret, err = client.Logical().Unwrap(secret.WrapInfo.Token)
if err != nil {
t.Fatal(err)
}
if secret.Warnings != nil {
t.Fatalf("Warnings found: %v", secret.Warnings)
}
if !reflect.DeepEqual(data, secret.Data) {
t.Fatalf("custom wrap did not match expected: %#v", secret.Data)
}
//
// Test rewrap
//
// Create a wrapping token
secret, err = client.Logical().Read("secret/foo")
if err != nil {
t.Fatal(err)
}
if secret == nil || secret.WrapInfo == nil {
t.Fatal("secret or wrap info is nil")
}
wrapInfo = secret.WrapInfo
// Check for correct CreationPath before rewrap
if wrapInfo.CreationPath != "secret/foo" {
t.Fatalf("error on wrapInfo.CreationPath: expected: secret/foo, got: %s", wrapInfo.CreationPath)
}
// Test rewrapping
secret, err = client.Logical().Write("sys/wrapping/rewrap", map[string]interface{}{
"token": wrapInfo.Token,
})
if err != nil {
t.Fatal(err)
}
if secret.Warnings != nil {
t.Fatalf("Warnings found: %v", secret.Warnings)
}
// Check for correct Creation path after rewrap
if wrapInfo.CreationPath != "secret/foo" {
t.Fatalf("error on wrapInfo.CreationPath: expected: secret/foo, got: %s", wrapInfo.CreationPath)
}
// Should be expired and fail
_, err = client.Logical().Write("sys/wrapping/unwrap", map[string]interface{}{
"token": wrapInfo.Token,
})
if err == nil {
t.Fatal("expected err")
}
// Attempt unwrapping the rewrapped token
wrapToken := secret.WrapInfo.Token
secret, err = client.Logical().Unwrap(wrapToken)
if err != nil {
t.Fatal(err)
}
// Should be expired and fail
_, err = client.Logical().Unwrap(wrapToken)
if err == nil {
t.Fatal("expected err")
}
if !reflect.DeepEqual(secret.Data, map[string]interface{}{
"zip": "zap",
}) {
t.Fatalf("secret data did not match expected: %#v", secret.Data)
}
// Ensure that wrapping lookup without a client token responds correctly
client.ClearToken()
secret, err = client.Logical().Read("sys/wrapping/lookup")
if secret != nil {
t.Fatalf("expected no response: %#v", secret)
}
if err == nil {
t.Fatal("expected error")
}
var respError *api.ResponseError
if errors.As(err, &respError); respError.StatusCode != 403 {
t.Fatalf("expected 403 response, actual: %d", respError.StatusCode)
}
}