open-vault/sdk/framework/backend_test.go

763 lines
17 KiB
Go
Raw Normal View History

2015-03-15 23:39:49 +00:00
package framework
2015-03-14 04:15:20 +00:00
import (
"context"
"net/http"
2015-03-14 04:15:20 +00:00
"reflect"
"strings"
2015-03-18 00:15:23 +00:00
"sync/atomic"
2015-03-14 04:15:20 +00:00
"testing"
"time"
2015-03-14 06:58:20 +00:00
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/logical"
2015-03-14 04:15:20 +00:00
)
2015-03-14 06:22:48 +00:00
func BenchmarkBackendRoute(b *testing.B) {
patterns := []string{
"foo",
"bar/(?P<name>.+?)",
"baz/(?P<name>what)",
`aws/policy/(?P<policy>\w)`,
`aws/(?P<policy>\w)`,
}
backend := &Backend{Paths: make([]*Path, 0, len(patterns))}
for _, p := range patterns {
backend.Paths = append(backend.Paths, &Path{Pattern: p})
}
// Warm any caches
backend.Route("aws/policy/foo")
// Reset the timer since we did a lot above
2015-03-14 06:22:48 +00:00
b.ResetTimer()
// Run through and route. We do a sanity check of the return value
2015-03-14 06:22:48 +00:00
for i := 0; i < b.N; i++ {
if p := backend.Route("aws/policy/foo"); p == nil {
2015-03-14 06:22:48 +00:00
b.Fatal("p should not be nil")
}
}
}
2015-03-14 06:58:20 +00:00
func TestBackend_impl(t *testing.T) {
2015-03-15 21:57:19 +00:00
var _ logical.Backend = new(Backend)
2015-03-14 06:58:20 +00:00
}
func TestBackendHandleRequest(t *testing.T) {
callback := func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) {
2015-03-15 21:57:19 +00:00
return &logical.Response{
2015-03-14 06:58:20 +00:00
Data: map[string]interface{}{
vault: clean up VaultID duplications, make secret responses clearer /cc @armon - This is a reasonably major refactor that I think cleans up a lot of the logic with secrets in responses. The reason for the refactor is that while implementing Renew/Revoke in logical/framework I found the existing API to be really awkward to work with. Primarily, we needed a way to send down internal data for Vault core to store since not all the data you need to revoke a key is always sent down to the user (for example the user than AWS key belongs to). At first, I was doing this manually in logical/framework with req.Storage, but this is going to be such a common event that I think its something core should assist with. Additionally, I think the added context for secrets will be useful in the future when we have a Vault API for returning orphaned out keys: we can also return the internal data that might help an operator. So this leads me to this refactor. I've removed most of the fields in `logical.Response` and replaced it with a single `*Secret` pointer. If this is non-nil, then the response represents a secret. The Secret struct encapsulates all the lease info and such. It also has some fields on it that are only populated at _request_ time for Revoke/Renew operations. There is precedent for this sort of behavior in the Go stdlib where http.Request/http.Response have fields that differ based on client/server. I copied this style. All core unit tests pass. The APIs fail for obvious reasons but I'll fix that up in the next commit.
2015-03-19 22:11:42 +00:00
"value": data.Get("value"),
2015-03-14 06:58:20 +00:00
},
}, nil
}
handler := func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) {
return &logical.Response{
Data: map[string]interface{}{
"amount": data.Get("amount"),
},
}, nil
}
2015-03-14 06:58:20 +00:00
b := &Backend{
Paths: []*Path{
{
2015-03-14 06:58:20 +00:00
Pattern: "foo/bar",
Fields: map[string]*FieldSchema{
"value": &FieldSchema{Type: TypeInt},
},
2015-03-15 21:57:19 +00:00
Callbacks: map[logical.Operation]OperationFunc{
logical.ReadOperation: callback,
},
2015-03-14 06:58:20 +00:00
},
{
Pattern: "foo/baz/handler",
Fields: map[string]*FieldSchema{
"amount": &FieldSchema{Type: TypeInt},
},
Operations: map[logical.Operation]OperationHandler{
logical.ReadOperation: &PathOperation{Callback: handler},
},
},
{
Pattern: "foo/both/handler",
Fields: map[string]*FieldSchema{
"amount": &FieldSchema{Type: TypeInt},
},
Callbacks: map[logical.Operation]OperationFunc{
logical.ReadOperation: callback,
},
Operations: map[logical.Operation]OperationHandler{
logical.ReadOperation: &PathOperation{Callback: handler},
},
},
2015-03-14 06:58:20 +00:00
},
system: &logical.StaticSystemView{},
2015-03-14 06:58:20 +00:00
}
for _, path := range []string{"foo/bar", "foo/baz/handler", "foo/both/handler"} {
key := "value"
if strings.Contains(path, "handler") {
key = "amount"
}
resp, err := b.HandleRequest(context.Background(), &logical.Request{
Operation: logical.ReadOperation,
Path: path,
Data: map[string]interface{}{key: "42"},
})
if err != nil {
t.Fatalf("err: %s", err)
}
if resp.Data[key] != 42 {
t.Fatalf("bad: %#v", resp)
}
2015-03-14 06:58:20 +00:00
}
}
func TestBackendHandleRequest_Forwarding(t *testing.T) {
tests := map[string]struct {
fwdStandby bool
fwdSecondary bool
isLocal bool
isStandby bool
isSecondary bool
expectFwd bool
nilSysView bool
}{
"no forward": {
expectFwd: false,
},
"no forward, local restricted": {
isSecondary: true,
fwdSecondary: true,
isLocal: true,
expectFwd: false,
},
"no forward, forwarding not requested": {
isSecondary: true,
isStandby: true,
expectFwd: false,
},
"forward, secondary": {
fwdSecondary: true,
isSecondary: true,
expectFwd: true,
},
"forward, standby": {
fwdStandby: true,
isStandby: true,
expectFwd: true,
},
"no forward, only secondary": {
fwdSecondary: true,
isStandby: true,
expectFwd: false,
},
"no forward, only standby": {
fwdStandby: true,
isSecondary: true,
expectFwd: false,
},
"nil system view": {
nilSysView: true,
expectFwd: false,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
var replState consts.ReplicationState
if test.isStandby {
replState.AddState(consts.ReplicationPerformanceStandby)
}
if test.isSecondary {
replState.AddState(consts.ReplicationPerformanceSecondary)
}
b := &Backend{
Paths: []*Path{
{
Pattern: "foo",
Operations: map[logical.Operation]OperationHandler{
logical.ReadOperation: &PathOperation{
Callback: func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) {
return nil, nil
},
ForwardPerformanceSecondary: test.fwdSecondary,
ForwardPerformanceStandby: test.fwdStandby,
},
},
},
},
system: &logical.StaticSystemView{
LocalMountVal: test.isLocal,
ReplicationStateVal: replState,
},
}
if test.nilSysView {
b.system = nil
}
_, err := b.HandleRequest(context.Background(), &logical.Request{
Operation: logical.ReadOperation,
Path: "foo",
})
if !test.expectFwd && err != nil {
t.Fatalf("unexpected err: %v", err)
}
if test.expectFwd && err != logical.ErrReadOnly {
t.Fatalf("expected ErrReadOnly, got: %v", err)
}
})
}
}
func TestBackendHandleRequest_badwrite(t *testing.T) {
callback := func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) {
return &logical.Response{
Data: map[string]interface{}{
"value": data.Get("value").(bool),
},
}, nil
}
b := &Backend{
Paths: []*Path{
&Path{
Pattern: "foo/bar",
Fields: map[string]*FieldSchema{
"value": &FieldSchema{Type: TypeBool},
},
Callbacks: map[logical.Operation]OperationFunc{
2016-01-07 15:30:47 +00:00
logical.UpdateOperation: callback,
},
},
},
}
_, err := b.HandleRequest(context.Background(), &logical.Request{
2016-01-07 15:30:47 +00:00
Operation: logical.UpdateOperation,
Path: "foo/bar",
Data: map[string]interface{}{"value": "3false3"},
})
if err == nil {
t.Fatalf("should have thrown a conversion error")
}
}
2015-03-14 06:58:20 +00:00
func TestBackendHandleRequest_404(t *testing.T) {
callback := func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) {
2015-03-15 21:57:19 +00:00
return &logical.Response{
2015-03-14 06:58:20 +00:00
Data: map[string]interface{}{
vault: clean up VaultID duplications, make secret responses clearer /cc @armon - This is a reasonably major refactor that I think cleans up a lot of the logic with secrets in responses. The reason for the refactor is that while implementing Renew/Revoke in logical/framework I found the existing API to be really awkward to work with. Primarily, we needed a way to send down internal data for Vault core to store since not all the data you need to revoke a key is always sent down to the user (for example the user than AWS key belongs to). At first, I was doing this manually in logical/framework with req.Storage, but this is going to be such a common event that I think its something core should assist with. Additionally, I think the added context for secrets will be useful in the future when we have a Vault API for returning orphaned out keys: we can also return the internal data that might help an operator. So this leads me to this refactor. I've removed most of the fields in `logical.Response` and replaced it with a single `*Secret` pointer. If this is non-nil, then the response represents a secret. The Secret struct encapsulates all the lease info and such. It also has some fields on it that are only populated at _request_ time for Revoke/Renew operations. There is precedent for this sort of behavior in the Go stdlib where http.Request/http.Response have fields that differ based on client/server. I copied this style. All core unit tests pass. The APIs fail for obvious reasons but I'll fix that up in the next commit.
2015-03-19 22:11:42 +00:00
"value": data.Get("value"),
2015-03-14 06:58:20 +00:00
},
}, nil
}
b := &Backend{
Paths: []*Path{
&Path{
Pattern: `foo/bar`,
Fields: map[string]*FieldSchema{
"value": &FieldSchema{Type: TypeInt},
},
2015-03-15 21:57:19 +00:00
Callbacks: map[logical.Operation]OperationFunc{
logical.ReadOperation: callback,
},
2015-03-14 06:58:20 +00:00
},
},
}
_, err := b.HandleRequest(context.Background(), &logical.Request{
2015-03-15 21:57:19 +00:00
Operation: logical.ReadOperation,
Path: "foo/baz",
Data: map[string]interface{}{"value": "84"},
2015-03-14 06:58:20 +00:00
})
2015-03-15 21:57:19 +00:00
if err != logical.ErrUnsupportedPath {
2015-03-14 06:58:20 +00:00
t.Fatalf("err: %s", err)
}
}
func TestBackendHandleRequest_help(t *testing.T) {
b := &Backend{
Paths: []*Path{
&Path{
Pattern: "foo/bar",
Fields: map[string]*FieldSchema{
"value": &FieldSchema{Type: TypeInt},
},
HelpSynopsis: "foo",
HelpDescription: "bar",
},
},
}
resp, err := b.HandleRequest(context.Background(), &logical.Request{
2015-03-15 21:57:19 +00:00
Operation: logical.HelpOperation,
Path: "foo/bar",
Data: map[string]interface{}{"value": "42"},
})
if err != nil {
t.Fatalf("err: %s", err)
}
if resp.Data["help"] == nil {
t.Fatalf("bad: %#v", resp)
}
}
2015-04-04 03:36:47 +00:00
func TestBackendHandleRequest_helpRoot(t *testing.T) {
b := &Backend{
Help: "42",
}
resp, err := b.HandleRequest(context.Background(), &logical.Request{
2015-04-04 03:36:47 +00:00
Operation: logical.HelpOperation,
Path: "",
})
if err != nil {
t.Fatalf("err: %s", err)
}
if resp.Data["help"] == nil {
t.Fatalf("bad: %#v", resp)
}
}
func TestBackendHandleRequest_renewAuth(t *testing.T) {
b := &Backend{}
resp, err := b.HandleRequest(context.Background(), logical.RenewAuthRequest("/foo", &logical.Auth{}, nil))
if err != nil {
t.Fatalf("err: %s", err)
}
if !resp.IsError() {
t.Fatalf("bad: %#v", resp)
}
}
func TestBackendHandleRequest_renewAuthCallback(t *testing.T) {
called := new(uint32)
callback := func(context.Context, *logical.Request, *FieldData) (*logical.Response, error) {
atomic.AddUint32(called, 1)
return nil, nil
}
b := &Backend{
AuthRenew: callback,
}
_, err := b.HandleRequest(context.Background(), logical.RenewAuthRequest("/foo", &logical.Auth{}, nil))
if err != nil {
t.Fatalf("err: %s", err)
}
if v := atomic.LoadUint32(called); v != 1 {
t.Fatalf("bad: %#v", v)
}
}
2015-03-19 19:20:25 +00:00
func TestBackendHandleRequest_renew(t *testing.T) {
called := new(uint32)
callback := func(context.Context, *logical.Request, *FieldData) (*logical.Response, error) {
atomic.AddUint32(called, 1)
2015-03-19 19:20:25 +00:00
return nil, nil
}
vault: clean up VaultID duplications, make secret responses clearer /cc @armon - This is a reasonably major refactor that I think cleans up a lot of the logic with secrets in responses. The reason for the refactor is that while implementing Renew/Revoke in logical/framework I found the existing API to be really awkward to work with. Primarily, we needed a way to send down internal data for Vault core to store since not all the data you need to revoke a key is always sent down to the user (for example the user than AWS key belongs to). At first, I was doing this manually in logical/framework with req.Storage, but this is going to be such a common event that I think its something core should assist with. Additionally, I think the added context for secrets will be useful in the future when we have a Vault API for returning orphaned out keys: we can also return the internal data that might help an operator. So this leads me to this refactor. I've removed most of the fields in `logical.Response` and replaced it with a single `*Secret` pointer. If this is non-nil, then the response represents a secret. The Secret struct encapsulates all the lease info and such. It also has some fields on it that are only populated at _request_ time for Revoke/Renew operations. There is precedent for this sort of behavior in the Go stdlib where http.Request/http.Response have fields that differ based on client/server. I copied this style. All core unit tests pass. The APIs fail for obvious reasons but I'll fix that up in the next commit.
2015-03-19 22:11:42 +00:00
secret := &Secret{
Type: "foo",
Renew: callback,
}
2015-03-19 19:20:25 +00:00
b := &Backend{
vault: clean up VaultID duplications, make secret responses clearer /cc @armon - This is a reasonably major refactor that I think cleans up a lot of the logic with secrets in responses. The reason for the refactor is that while implementing Renew/Revoke in logical/framework I found the existing API to be really awkward to work with. Primarily, we needed a way to send down internal data for Vault core to store since not all the data you need to revoke a key is always sent down to the user (for example the user than AWS key belongs to). At first, I was doing this manually in logical/framework with req.Storage, but this is going to be such a common event that I think its something core should assist with. Additionally, I think the added context for secrets will be useful in the future when we have a Vault API for returning orphaned out keys: we can also return the internal data that might help an operator. So this leads me to this refactor. I've removed most of the fields in `logical.Response` and replaced it with a single `*Secret` pointer. If this is non-nil, then the response represents a secret. The Secret struct encapsulates all the lease info and such. It also has some fields on it that are only populated at _request_ time for Revoke/Renew operations. There is precedent for this sort of behavior in the Go stdlib where http.Request/http.Response have fields that differ based on client/server. I copied this style. All core unit tests pass. The APIs fail for obvious reasons but I'll fix that up in the next commit.
2015-03-19 22:11:42 +00:00
Secrets: []*Secret{secret},
2015-03-19 19:20:25 +00:00
}
_, err := b.HandleRequest(context.Background(), logical.RenewRequest("/foo", secret.Response(nil, nil).Secret, nil))
2015-03-19 19:20:25 +00:00
if err != nil {
t.Fatalf("err: %s", err)
}
if v := atomic.LoadUint32(called); v != 1 {
2015-03-19 19:20:25 +00:00
t.Fatalf("bad: %#v", v)
}
}
2015-03-19 18:41:41 +00:00
func TestBackendHandleRequest_revoke(t *testing.T) {
called := new(uint32)
callback := func(context.Context, *logical.Request, *FieldData) (*logical.Response, error) {
atomic.AddUint32(called, 1)
2015-03-19 18:41:41 +00:00
return nil, nil
}
vault: clean up VaultID duplications, make secret responses clearer /cc @armon - This is a reasonably major refactor that I think cleans up a lot of the logic with secrets in responses. The reason for the refactor is that while implementing Renew/Revoke in logical/framework I found the existing API to be really awkward to work with. Primarily, we needed a way to send down internal data for Vault core to store since not all the data you need to revoke a key is always sent down to the user (for example the user than AWS key belongs to). At first, I was doing this manually in logical/framework with req.Storage, but this is going to be such a common event that I think its something core should assist with. Additionally, I think the added context for secrets will be useful in the future when we have a Vault API for returning orphaned out keys: we can also return the internal data that might help an operator. So this leads me to this refactor. I've removed most of the fields in `logical.Response` and replaced it with a single `*Secret` pointer. If this is non-nil, then the response represents a secret. The Secret struct encapsulates all the lease info and such. It also has some fields on it that are only populated at _request_ time for Revoke/Renew operations. There is precedent for this sort of behavior in the Go stdlib where http.Request/http.Response have fields that differ based on client/server. I copied this style. All core unit tests pass. The APIs fail for obvious reasons but I'll fix that up in the next commit.
2015-03-19 22:11:42 +00:00
secret := &Secret{
Type: "foo",
Revoke: callback,
}
2015-03-19 18:41:41 +00:00
b := &Backend{
vault: clean up VaultID duplications, make secret responses clearer /cc @armon - This is a reasonably major refactor that I think cleans up a lot of the logic with secrets in responses. The reason for the refactor is that while implementing Renew/Revoke in logical/framework I found the existing API to be really awkward to work with. Primarily, we needed a way to send down internal data for Vault core to store since not all the data you need to revoke a key is always sent down to the user (for example the user than AWS key belongs to). At first, I was doing this manually in logical/framework with req.Storage, but this is going to be such a common event that I think its something core should assist with. Additionally, I think the added context for secrets will be useful in the future when we have a Vault API for returning orphaned out keys: we can also return the internal data that might help an operator. So this leads me to this refactor. I've removed most of the fields in `logical.Response` and replaced it with a single `*Secret` pointer. If this is non-nil, then the response represents a secret. The Secret struct encapsulates all the lease info and such. It also has some fields on it that are only populated at _request_ time for Revoke/Renew operations. There is precedent for this sort of behavior in the Go stdlib where http.Request/http.Response have fields that differ based on client/server. I copied this style. All core unit tests pass. The APIs fail for obvious reasons but I'll fix that up in the next commit.
2015-03-19 22:11:42 +00:00
Secrets: []*Secret{secret},
2015-03-19 18:41:41 +00:00
}
_, err := b.HandleRequest(context.Background(), logical.RevokeRequest("/foo", secret.Response(nil, nil).Secret, nil))
2015-03-19 18:41:41 +00:00
if err != nil {
t.Fatalf("err: %s", err)
}
if v := atomic.LoadUint32(called); v != 1 {
2015-03-19 18:41:41 +00:00
t.Fatalf("bad: %#v", v)
}
}
2015-03-18 00:15:23 +00:00
func TestBackendHandleRequest_rollback(t *testing.T) {
called := new(uint32)
callback := func(_ context.Context, req *logical.Request, kind string, data interface{}) error {
2015-03-18 00:15:23 +00:00
if data == "foo" {
atomic.AddUint32(called, 1)
2015-03-18 00:15:23 +00:00
}
return nil
2015-03-18 00:15:23 +00:00
}
b := &Backend{
2016-05-14 23:35:36 +00:00
WALRollback: callback,
WALRollbackMinAge: 1 * time.Millisecond,
2015-03-18 00:15:23 +00:00
}
storage := new(logical.InmemStorage)
if _, err := PutWAL(context.Background(), storage, "kind", "foo"); err != nil {
2015-03-18 00:15:23 +00:00
t.Fatalf("err: %s", err)
}
time.Sleep(10 * time.Millisecond)
_, err := b.HandleRequest(context.Background(), &logical.Request{
2015-03-18 00:15:23 +00:00
Operation: logical.RollbackOperation,
Path: "",
Storage: storage,
})
if err != nil {
t.Fatalf("err: %s", err)
}
if v := atomic.LoadUint32(called); v != 1 {
2015-03-18 00:15:23 +00:00
t.Fatalf("bad: %#v", v)
}
}
func TestBackendHandleRequest_rollbackMinAge(t *testing.T) {
called := new(uint32)
callback := func(_ context.Context, req *logical.Request, kind string, data interface{}) error {
if data == "foo" {
atomic.AddUint32(called, 1)
}
return nil
}
b := &Backend{
2016-05-14 23:35:36 +00:00
WALRollback: callback,
WALRollbackMinAge: 5 * time.Second,
}
storage := new(logical.InmemStorage)
if _, err := PutWAL(context.Background(), storage, "kind", "foo"); err != nil {
t.Fatalf("err: %s", err)
}
_, err := b.HandleRequest(context.Background(), &logical.Request{
Operation: logical.RollbackOperation,
Path: "",
Storage: storage,
})
if err != nil {
t.Fatalf("err: %s", err)
}
if v := atomic.LoadUint32(called); v != 0 {
t.Fatalf("bad: %#v", v)
}
}
func TestBackendHandleRequest_unsupportedOperation(t *testing.T) {
callback := func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) {
2015-03-15 21:57:19 +00:00
return &logical.Response{
Data: map[string]interface{}{
vault: clean up VaultID duplications, make secret responses clearer /cc @armon - This is a reasonably major refactor that I think cleans up a lot of the logic with secrets in responses. The reason for the refactor is that while implementing Renew/Revoke in logical/framework I found the existing API to be really awkward to work with. Primarily, we needed a way to send down internal data for Vault core to store since not all the data you need to revoke a key is always sent down to the user (for example the user than AWS key belongs to). At first, I was doing this manually in logical/framework with req.Storage, but this is going to be such a common event that I think its something core should assist with. Additionally, I think the added context for secrets will be useful in the future when we have a Vault API for returning orphaned out keys: we can also return the internal data that might help an operator. So this leads me to this refactor. I've removed most of the fields in `logical.Response` and replaced it with a single `*Secret` pointer. If this is non-nil, then the response represents a secret. The Secret struct encapsulates all the lease info and such. It also has some fields on it that are only populated at _request_ time for Revoke/Renew operations. There is precedent for this sort of behavior in the Go stdlib where http.Request/http.Response have fields that differ based on client/server. I copied this style. All core unit tests pass. The APIs fail for obvious reasons but I'll fix that up in the next commit.
2015-03-19 22:11:42 +00:00
"value": data.Get("value"),
},
}, nil
}
b := &Backend{
Paths: []*Path{
&Path{
Pattern: `foo/bar`,
Fields: map[string]*FieldSchema{
"value": &FieldSchema{Type: TypeInt},
},
2015-03-15 21:57:19 +00:00
Callbacks: map[logical.Operation]OperationFunc{
logical.ReadOperation: callback,
},
},
},
}
_, err := b.HandleRequest(context.Background(), &logical.Request{
2016-01-07 15:30:47 +00:00
Operation: logical.UpdateOperation,
Path: "foo/bar",
Data: map[string]interface{}{"value": "84"},
})
2015-03-15 21:57:19 +00:00
if err != logical.ErrUnsupportedOperation {
t.Fatalf("err: %s", err)
}
}
2015-03-14 06:58:20 +00:00
func TestBackendHandleRequest_urlPriority(t *testing.T) {
callback := func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) {
2015-03-15 21:57:19 +00:00
return &logical.Response{
2015-03-14 06:58:20 +00:00
Data: map[string]interface{}{
vault: clean up VaultID duplications, make secret responses clearer /cc @armon - This is a reasonably major refactor that I think cleans up a lot of the logic with secrets in responses. The reason for the refactor is that while implementing Renew/Revoke in logical/framework I found the existing API to be really awkward to work with. Primarily, we needed a way to send down internal data for Vault core to store since not all the data you need to revoke a key is always sent down to the user (for example the user than AWS key belongs to). At first, I was doing this manually in logical/framework with req.Storage, but this is going to be such a common event that I think its something core should assist with. Additionally, I think the added context for secrets will be useful in the future when we have a Vault API for returning orphaned out keys: we can also return the internal data that might help an operator. So this leads me to this refactor. I've removed most of the fields in `logical.Response` and replaced it with a single `*Secret` pointer. If this is non-nil, then the response represents a secret. The Secret struct encapsulates all the lease info and such. It also has some fields on it that are only populated at _request_ time for Revoke/Renew operations. There is precedent for this sort of behavior in the Go stdlib where http.Request/http.Response have fields that differ based on client/server. I copied this style. All core unit tests pass. The APIs fail for obvious reasons but I'll fix that up in the next commit.
2015-03-19 22:11:42 +00:00
"value": data.Get("value"),
2015-03-14 06:58:20 +00:00
},
}, nil
}
b := &Backend{
Paths: []*Path{
&Path{
Pattern: `foo/(?P<value>\d+)`,
Fields: map[string]*FieldSchema{
"value": &FieldSchema{Type: TypeInt},
},
2015-03-15 21:57:19 +00:00
Callbacks: map[logical.Operation]OperationFunc{
logical.ReadOperation: callback,
},
2015-03-14 06:58:20 +00:00
},
},
}
resp, err := b.HandleRequest(context.Background(), &logical.Request{
2015-03-15 21:57:19 +00:00
Operation: logical.ReadOperation,
Path: "foo/42",
Data: map[string]interface{}{"value": "84"},
2015-03-14 06:58:20 +00:00
})
if err != nil {
t.Fatalf("err: %s", err)
}
if resp.Data["value"] != 42 {
t.Fatalf("bad: %#v", resp)
}
}
func TestBackendRoute(t *testing.T) {
cases := map[string]struct {
Patterns []string
Path string
Match string
}{
"no match": {
[]string{"foo"},
"bar",
"",
},
"exact": {
[]string{"foo"},
"foo",
"^foo$",
},
"regexp": {
[]string{"fo+"},
"foo",
"^fo+$",
},
"anchor-start": {
[]string{"bar"},
"foobar",
"",
},
"anchor-end": {
[]string{"bar"},
"barfoo",
"",
},
"anchor-ambiguous": {
[]string{"mounts", "sys/mounts"},
"sys/mounts",
"^sys/mounts$",
},
}
for n, tc := range cases {
paths := make([]*Path, len(tc.Patterns))
for i, pattern := range tc.Patterns {
paths[i] = &Path{Pattern: pattern}
}
b := &Backend{Paths: paths}
result := b.Route(tc.Path)
match := ""
if result != nil {
match = result.Pattern
}
if match != tc.Match {
t.Fatalf("bad: %s\n\nExpected: %s\nGot: %s",
n, tc.Match, match)
}
}
}
func TestBackendSecret(t *testing.T) {
cases := map[string]struct {
Secrets []*Secret
Search string
Match bool
}{
"no match": {
[]*Secret{&Secret{Type: "foo"}},
"bar",
false,
},
"match": {
[]*Secret{&Secret{Type: "foo"}},
"foo",
true,
},
}
for n, tc := range cases {
b := &Backend{Secrets: tc.Secrets}
result := b.Secret(tc.Search)
if tc.Match != (result != nil) {
t.Fatalf("bad: %s\n\nExpected match: %v", n, tc.Match)
}
if result != nil && result.Type != tc.Search {
t.Fatalf("bad: %s\n\nExpected matching type: %#v", n, result)
}
}
}
2015-03-14 04:15:20 +00:00
func TestFieldSchemaDefaultOrZero(t *testing.T) {
cases := map[string]struct {
Schema *FieldSchema
Value interface{}
}{
"default set": {
&FieldSchema{Type: TypeString, Default: "foo"},
"foo",
},
"default not set": {
&FieldSchema{Type: TypeString},
"",
},
"default duration set": {
&FieldSchema{Type: TypeDurationSecond, Default: 60},
60,
},
"default duration int64": {
&FieldSchema{Type: TypeDurationSecond, Default: int64(60)},
60,
},
"default duration string": {
&FieldSchema{Type: TypeDurationSecond, Default: "60s"},
60,
},
"illegal default duration string": {
&FieldSchema{Type: TypeDurationSecond, Default: "h1"},
0,
},
"default duration time.Duration": {
&FieldSchema{Type: TypeDurationSecond, Default: 60 * time.Second},
60,
},
"default duration not set": {
&FieldSchema{Type: TypeDurationSecond},
0,
},
2018-08-13 18:02:44 +00:00
"default signed positive duration set": {
&FieldSchema{Type: TypeSignedDurationSecond, Default: 60},
60,
},
"default signed positive duration int64": {
&FieldSchema{Type: TypeSignedDurationSecond, Default: int64(60)},
60,
},
"default signed positive duration string": {
&FieldSchema{Type: TypeSignedDurationSecond, Default: "60s"},
60,
},
"illegal default signed duration string": {
&FieldSchema{Type: TypeDurationSecond, Default: "-h1"},
0,
},
"default signed positive duration time.Duration": {
&FieldSchema{Type: TypeSignedDurationSecond, Default: 60 * time.Second},
60,
},
"default signed negative duration set": {
&FieldSchema{Type: TypeSignedDurationSecond, Default: -60},
-60,
},
"default signed negative duration int64": {
&FieldSchema{Type: TypeSignedDurationSecond, Default: int64(-60)},
-60,
},
"default signed negative duration string": {
&FieldSchema{Type: TypeSignedDurationSecond, Default: "-60s"},
-60,
},
"default signed negative duration time.Duration": {
&FieldSchema{Type: TypeSignedDurationSecond, Default: -60 * time.Second},
-60,
},
"default signed negative duration not set": {
&FieldSchema{Type: TypeSignedDurationSecond},
0,
},
2018-08-13 18:02:44 +00:00
"default header not set": {
&FieldSchema{Type: TypeHeader},
http.Header{},
},
2015-03-14 04:15:20 +00:00
}
for name, tc := range cases {
actual := tc.Schema.DefaultOrZero()
if !reflect.DeepEqual(actual, tc.Value) {
t.Errorf("bad: %s\n\nExpected: %#v\nGot: %#v",
2015-03-14 04:15:20 +00:00
name, tc.Value, actual)
}
}
}
AWS upgrade role entries (#7025) * upgrade aws roles * test upgrade aws roles * Initialize aws credential backend at mount time * add a TODO * create end-to-end test for builtin/credential/aws * fix bug in initializer * improve comments * add Initialize() to logical.Backend * use Initialize() in Core.enableCredentialInternal() * use InitializeRequest to call Initialize() * improve unit testing for framework.Backend * call logical.Backend.Initialize() from all of the places that it needs to be called. * implement backend.proto changes for logical.Backend.Initialize() * persist current role storage version when upgrading aws roles * format comments correctly * improve comments * use postUnseal funcs to initialize backends * simplify test suite * improve test suite * simplify logic in aws role upgrade * simplify aws credential initialization logic * simplify logic in aws role upgrade * use the core's activeContext for initialization * refactor builtin/plugin/Backend * use a goroutine to upgrade the aws roles * misc improvements and cleanup * do not run AWS role upgrade on DR Secondary * always call logical.Backend.Initialize() when loading a plugin. * improve comments * on standbys and DR secondaries we do not want to run any kind of upgrade logic * fix awsVersion struct * clarify aws version upgrade * make the upgrade logic for aws auth more explicit * aws upgrade is now called from a switch * fix fallthrough bug * simplify logic * simplify logic * rename things * introduce currentAwsVersion const to track aws version * improve comments * rearrange things once more * conglomerate things into one function * stub out aws auth initialize e2e test * improve aws auth initialize e2e test * finish aws auth initialize e2e test * tinker with aws auth initialize e2e test * tinker with aws auth initialize e2e test * tinker with aws auth initialize e2e test * fix typo in test suite * simplify logic a tad * rearrange assignment * Fix a few lifecycle related issues in #7025 (#7075) * Fix panic when plugin fails to load
2019-07-05 23:55:40 +00:00
func TestInitializeBackend(t *testing.T) {
var inited bool
backend := &Backend{InitializeFunc: func(context.Context, *logical.InitializationRequest) error {
inited = true
return nil
}}
backend.Initialize(nil, &logical.InitializationRequest{Storage: nil})
if !inited {
t.Fatal("backend should be open")
}
}