6cb6157d37
* return 403 for wrapping requests when no token provided * add changelog entry * fix changelog * use errors.As * simplify error response string
387 lines
9.5 KiB
Go
387 lines
9.5 KiB
Go
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)
|
|
}
|
|
}
|