open-vault/vault/expiration_test.go
Jeff Mitchell 75650ec1ad Keep the expiration manager from keeping old token entries.
The expiration manager would never be poked to remove token entries upon
token revocation, if that revocation was initiated in the token store
itself. It might have been to avoid deadlock, since during revocation of
tokens the expiration manager is called, which then calls back into the
token store, and so on.

This adds a way to skip that last call back into the token store if we
know that we're on the revocation path because we're in the middle of
revoking a token. That way the lease is cleaned up. This both prevents
log entries appearing for already-revoked tokens, and it also releases
timer/memory resources since we're not keeping the leases around.
2016-03-31 15:10:25 -04:00

1042 lines
21 KiB
Go

package vault
import (
"fmt"
"reflect"
"sort"
"strings"
"testing"
"time"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
// mockExpiration returns a mock expiration manager
func mockExpiration(t *testing.T) *ExpirationManager {
_, ts, _, _ := TestCoreWithTokenStore(t)
return ts.expiration
}
func TestExpiration_Restore(t *testing.T) {
exp := mockExpiration(t)
noop := &NoopBackend{}
_, barrier, _ := mockBarrier(t)
view := NewBarrierView(barrier, "logical/")
meUUID, err := uuid.GenerateUUID()
if err != nil {
t.Fatal(err)
}
exp.router.Mount(noop, "prod/aws/", &MountEntry{UUID: meUUID}, view)
paths := []string{
"prod/aws/foo",
"prod/aws/sub/bar",
"prod/aws/zip",
}
for _, path := range paths {
req := &logical.Request{
Operation: logical.ReadOperation,
Path: path,
}
resp := &logical.Response{
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: 20 * time.Millisecond,
},
},
Data: map[string]interface{}{
"access_key": "xyz",
"secret_key": "abcd",
},
}
_, err := exp.Register(req, resp)
if err != nil {
t.Fatalf("err: %v", err)
}
}
// Stop everything
err = exp.Stop()
if err != nil {
t.Fatalf("err: %v", err)
}
// Restore
err = exp.Restore()
if err != nil {
t.Fatalf("err: %v", err)
}
// Ensure all are reaped
start := time.Now()
for time.Now().Sub(start) < time.Second {
noop.Lock()
less := len(noop.Requests) < 3
noop.Unlock()
if less {
time.Sleep(5 * time.Millisecond)
continue
}
break
}
for _, req := range noop.Requests {
if req.Operation != logical.RevokeOperation {
t.Fatalf("Bad: %v", req)
}
}
}
func TestExpiration_Register(t *testing.T) {
exp := mockExpiration(t)
req := &logical.Request{
Operation: logical.ReadOperation,
Path: "prod/aws/foo",
}
resp := &logical.Response{
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: time.Hour,
},
},
Data: map[string]interface{}{
"access_key": "xyz",
"secret_key": "abcd",
},
}
id, err := exp.Register(req, resp)
if err != nil {
t.Fatalf("err: %v", err)
}
if !strings.HasPrefix(id, req.Path) {
t.Fatalf("bad: %s", id)
}
if len(id) <= len(req.Path) {
t.Fatalf("bad: %s", id)
}
}
func TestExpiration_RegisterAuth(t *testing.T) {
exp := mockExpiration(t)
root, err := exp.tokenStore.rootToken()
if err != nil {
t.Fatalf("err: %v", err)
}
auth := &logical.Auth{
ClientToken: root.ID,
LeaseOptions: logical.LeaseOptions{
TTL: time.Hour,
},
}
err = exp.RegisterAuth("auth/github/login", auth)
if err != nil {
t.Fatalf("err: %v", err)
}
}
func TestExpiration_RegisterAuth_NoLease(t *testing.T) {
exp := mockExpiration(t)
root, err := exp.tokenStore.rootToken()
if err != nil {
t.Fatalf("err: %v", err)
}
auth := &logical.Auth{
ClientToken: root.ID,
}
err = exp.RegisterAuth("auth/github/login", auth)
if err != nil {
t.Fatalf("err: %v", err)
}
// Should not be able to renew, no expiration
_, err = exp.RenewToken(&logical.Request{}, "auth/github/login", root.ID, 0)
if err.Error() != "lease not found or lease is not renewable" {
t.Fatalf("err: %v", err)
}
// Wait and check token is not invalidated
time.Sleep(20 * time.Millisecond)
// Verify token does not get revoked
out, err := exp.tokenStore.Lookup(root.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if out == nil {
t.Fatalf("missing token")
}
}
func TestExpiration_Revoke(t *testing.T) {
exp := mockExpiration(t)
noop := &NoopBackend{}
_, barrier, _ := mockBarrier(t)
view := NewBarrierView(barrier, "logical/")
meUUID, err := uuid.GenerateUUID()
if err != nil {
t.Fatal(err)
}
exp.router.Mount(noop, "prod/aws/", &MountEntry{UUID: meUUID}, view)
req := &logical.Request{
Operation: logical.ReadOperation,
Path: "prod/aws/foo",
}
resp := &logical.Response{
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: time.Hour,
},
},
Data: map[string]interface{}{
"access_key": "xyz",
"secret_key": "abcd",
},
}
id, err := exp.Register(req, resp)
if err != nil {
t.Fatalf("err: %v", err)
}
if err := exp.Revoke(id); err != nil {
t.Fatalf("err: %v", err)
}
req = noop.Requests[0]
if req.Operation != logical.RevokeOperation {
t.Fatalf("Bad: %v", req)
}
}
func TestExpiration_RevokeOnExpire(t *testing.T) {
exp := mockExpiration(t)
noop := &NoopBackend{}
_, barrier, _ := mockBarrier(t)
view := NewBarrierView(barrier, "logical/")
meUUID, err := uuid.GenerateUUID()
if err != nil {
t.Fatal(err)
}
exp.router.Mount(noop, "prod/aws/", &MountEntry{UUID: meUUID}, view)
req := &logical.Request{
Operation: logical.ReadOperation,
Path: "prod/aws/foo",
}
resp := &logical.Response{
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: 20 * time.Millisecond,
},
},
Data: map[string]interface{}{
"access_key": "xyz",
"secret_key": "abcd",
},
}
_, err = exp.Register(req, resp)
if err != nil {
t.Fatalf("err: %v", err)
}
start := time.Now()
for time.Now().Sub(start) < time.Second {
req = nil
noop.Lock()
if len(noop.Requests) > 0 {
req = noop.Requests[0]
}
noop.Unlock()
if req == nil {
time.Sleep(5 * time.Millisecond)
continue
}
if req.Operation != logical.RevokeOperation {
t.Fatalf("Bad: %v", req)
}
break
}
}
func TestExpiration_RevokePrefix(t *testing.T) {
exp := mockExpiration(t)
noop := &NoopBackend{}
_, barrier, _ := mockBarrier(t)
view := NewBarrierView(barrier, "logical/")
meUUID, err := uuid.GenerateUUID()
if err != nil {
t.Fatal(err)
}
exp.router.Mount(noop, "prod/aws/", &MountEntry{UUID: meUUID}, view)
paths := []string{
"prod/aws/foo",
"prod/aws/sub/bar",
"prod/aws/zip",
}
for _, path := range paths {
req := &logical.Request{
Operation: logical.ReadOperation,
Path: path,
}
resp := &logical.Response{
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: 20 * time.Millisecond,
},
},
Data: map[string]interface{}{
"access_key": "xyz",
"secret_key": "abcd",
},
}
_, err := exp.Register(req, resp)
if err != nil {
t.Fatalf("err: %v", err)
}
}
// Should nuke all the keys
if err := exp.RevokePrefix("prod/aws/"); err != nil {
t.Fatalf("err: %v", err)
}
if len(noop.Requests) != 3 {
t.Fatalf("Bad: %v", noop.Requests)
}
for _, req := range noop.Requests {
if req.Operation != logical.RevokeOperation {
t.Fatalf("Bad: %v", req)
}
}
expect := []string{
"foo",
"sub/bar",
"zip",
}
sort.Strings(noop.Paths)
sort.Strings(expect)
if !reflect.DeepEqual(noop.Paths, expect) {
t.Fatalf("bad: %v", noop.Paths)
}
}
func TestExpiration_RevokeByToken(t *testing.T) {
exp := mockExpiration(t)
noop := &NoopBackend{}
_, barrier, _ := mockBarrier(t)
view := NewBarrierView(barrier, "logical/")
meUUID, err := uuid.GenerateUUID()
if err != nil {
t.Fatal(err)
}
exp.router.Mount(noop, "prod/aws/", &MountEntry{UUID: meUUID}, view)
paths := []string{
"prod/aws/foo",
"prod/aws/sub/bar",
"prod/aws/zip",
}
for _, path := range paths {
req := &logical.Request{
Operation: logical.ReadOperation,
Path: path,
ClientToken: "foobarbaz",
}
resp := &logical.Response{
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: 20 * time.Millisecond,
},
},
Data: map[string]interface{}{
"access_key": "xyz",
"secret_key": "abcd",
},
}
_, err := exp.Register(req, resp)
if err != nil {
t.Fatalf("err: %v", err)
}
}
// Should nuke all the keys
te := &TokenEntry{
ID: "foobarbaz",
}
if err := exp.RevokeByToken(te); err != nil {
t.Fatalf("err: %v", err)
}
if len(noop.Requests) != 3 {
t.Fatalf("Bad: %v", noop.Requests)
}
for _, req := range noop.Requests {
if req.Operation != logical.RevokeOperation {
t.Fatalf("Bad: %v", req)
}
}
expect := []string{
"foo",
"sub/bar",
"zip",
}
sort.Strings(noop.Paths)
sort.Strings(expect)
if !reflect.DeepEqual(noop.Paths, expect) {
t.Fatalf("bad: %v", noop.Paths)
}
}
func TestExpiration_RenewToken(t *testing.T) {
exp := mockExpiration(t)
root, err := exp.tokenStore.rootToken()
if err != nil {
t.Fatalf("err: %v", err)
}
// Register a token
auth := &logical.Auth{
ClientToken: root.ID,
LeaseOptions: logical.LeaseOptions{
TTL: time.Hour,
Renewable: true,
},
}
err = exp.RegisterAuth("auth/token/login", auth)
if err != nil {
t.Fatalf("err: %v", err)
}
// Renew the token
out, err := exp.RenewToken(&logical.Request{}, "auth/token/login", root.ID, 0)
if err != nil {
t.Fatalf("err: %v", err)
}
if auth.ClientToken != out.Auth.ClientToken {
t.Fatalf("Bad: %#v", out)
}
}
func TestExpiration_RenewToken_NotRenewable(t *testing.T) {
exp := mockExpiration(t)
root, err := exp.tokenStore.rootToken()
if err != nil {
t.Fatalf("err: %v", err)
}
// Register a token
auth := &logical.Auth{
ClientToken: root.ID,
LeaseOptions: logical.LeaseOptions{
TTL: time.Hour,
Renewable: false,
},
}
err = exp.RegisterAuth("auth/github/login", auth)
if err != nil {
t.Fatalf("err: %v", err)
}
// Attempt to renew the token
_, err = exp.RenewToken(&logical.Request{}, "auth/github/login", root.ID, 0)
if err.Error() != "lease is not renewable" {
t.Fatalf("err: %v", err)
}
}
func TestExpiration_Renew(t *testing.T) {
exp := mockExpiration(t)
noop := &NoopBackend{}
_, barrier, _ := mockBarrier(t)
view := NewBarrierView(barrier, "logical/")
meUUID, err := uuid.GenerateUUID()
if err != nil {
t.Fatal(err)
}
exp.router.Mount(noop, "prod/aws/", &MountEntry{UUID: meUUID}, view)
req := &logical.Request{
Operation: logical.ReadOperation,
Path: "prod/aws/foo",
}
resp := &logical.Response{
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: 20 * time.Millisecond,
Renewable: true,
},
},
Data: map[string]interface{}{
"access_key": "xyz",
"secret_key": "abcd",
},
}
id, err := exp.Register(req, resp)
if err != nil {
t.Fatalf("err: %v", err)
}
noop.Response = &logical.Response{
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: 20 * time.Millisecond,
},
},
Data: map[string]interface{}{
"access_key": "123",
"secret_key": "abcd",
},
}
out, err := exp.Renew(id, 0)
if err != nil {
t.Fatalf("err: %v", err)
}
noop.Lock()
defer noop.Unlock()
if !reflect.DeepEqual(out, noop.Response) {
t.Fatalf("Bad: %#v", out)
}
if len(noop.Requests) != 1 {
t.Fatalf("Bad: %#v", noop.Requests)
}
req = noop.Requests[0]
if req.Operation != logical.RenewOperation {
t.Fatalf("Bad: %v", req)
}
}
func TestExpiration_Renew_NotRenewable(t *testing.T) {
exp := mockExpiration(t)
noop := &NoopBackend{}
_, barrier, _ := mockBarrier(t)
view := NewBarrierView(barrier, "logical/")
meUUID, err := uuid.GenerateUUID()
if err != nil {
t.Fatal(err)
}
exp.router.Mount(noop, "prod/aws/", &MountEntry{UUID: meUUID}, view)
req := &logical.Request{
Operation: logical.ReadOperation,
Path: "prod/aws/foo",
}
resp := &logical.Response{
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: 20 * time.Millisecond,
Renewable: false,
},
},
Data: map[string]interface{}{
"access_key": "xyz",
"secret_key": "abcd",
},
}
id, err := exp.Register(req, resp)
if err != nil {
t.Fatalf("err: %v", err)
}
_, err = exp.Renew(id, 0)
if err.Error() != "lease is not renewable" {
t.Fatalf("err: %v", err)
}
noop.Lock()
defer noop.Unlock()
if len(noop.Requests) != 0 {
t.Fatalf("Bad: %#v", noop.Requests)
}
}
func TestExpiration_Renew_RevokeOnExpire(t *testing.T) {
exp := mockExpiration(t)
noop := &NoopBackend{}
_, barrier, _ := mockBarrier(t)
view := NewBarrierView(barrier, "logical/")
meUUID, err := uuid.GenerateUUID()
if err != nil {
t.Fatal(err)
}
exp.router.Mount(noop, "prod/aws/", &MountEntry{UUID: meUUID}, view)
req := &logical.Request{
Operation: logical.ReadOperation,
Path: "prod/aws/foo",
}
resp := &logical.Response{
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: 20 * time.Millisecond,
Renewable: true,
},
},
Data: map[string]interface{}{
"access_key": "xyz",
"secret_key": "abcd",
},
}
id, err := exp.Register(req, resp)
if err != nil {
t.Fatalf("err: %v", err)
}
noop.Response = &logical.Response{
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: 20 * time.Millisecond,
},
},
Data: map[string]interface{}{
"access_key": "123",
"secret_key": "abcd",
},
}
_, err = exp.Renew(id, 0)
if err != nil {
t.Fatalf("err: %v", err)
}
start := time.Now()
for time.Now().Sub(start) < time.Second {
req = nil
noop.Lock()
if len(noop.Requests) >= 2 {
req = noop.Requests[1]
}
noop.Unlock()
if req == nil {
time.Sleep(5 * time.Millisecond)
continue
}
if req.Operation != logical.RevokeOperation {
t.Fatalf("Bad: %v", req)
}
break
}
}
func TestExpiration_revokeEntry(t *testing.T) {
exp := mockExpiration(t)
noop := &NoopBackend{}
_, barrier, _ := mockBarrier(t)
view := NewBarrierView(barrier, "logical/")
meUUID, err := uuid.GenerateUUID()
if err != nil {
t.Fatal(err)
}
exp.router.Mount(noop, "", &MountEntry{UUID: meUUID}, view)
le := &leaseEntry{
LeaseID: "foo/bar/1234",
Path: "foo/bar",
Data: map[string]interface{}{
"testing": true,
},
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: time.Minute,
},
},
IssueTime: time.Now(),
ExpireTime: time.Now(),
}
err = exp.revokeEntry(le)
if err != nil {
t.Fatalf("err: %v", err)
}
noop.Lock()
defer noop.Unlock()
req := noop.Requests[0]
if req.Operation != logical.RevokeOperation {
t.Fatalf("Bad: %v", req)
}
if req.Path != le.Path {
t.Fatalf("Bad: %v", req)
}
if !reflect.DeepEqual(req.Data, le.Data) {
t.Fatalf("Bad: %v", req)
}
}
func TestExpiration_revokeEntry_token(t *testing.T) {
exp := mockExpiration(t)
root, err := exp.tokenStore.rootToken()
if err != nil {
t.Fatalf("err: %v", err)
}
le := &leaseEntry{
LeaseID: "foo/bar/1234",
Auth: &logical.Auth{
ClientToken: root.ID,
LeaseOptions: logical.LeaseOptions{
TTL: time.Minute,
},
},
ClientToken: root.ID,
Path: "foo/bar",
IssueTime: time.Now(),
ExpireTime: time.Now(),
}
if err := exp.persistEntry(le); err != nil {
t.Fatalf("error persisting entry: %v", err)
}
if err := exp.createIndexByToken(le.ClientToken, le.LeaseID); err != nil {
t.Fatalf("error creating secondary index: %v", err)
}
exp.updatePending(le, le.Auth.LeaseTotal())
indexEntry, err := exp.indexByToken(le.ClientToken, le.LeaseID)
if err != nil {
t.Fatalf("err: %v")
}
if indexEntry == nil {
t.Fatalf("err: should have found a secondary index entry")
}
err = exp.revokeEntry(le)
if err != nil {
t.Fatalf("err: %v", err)
}
out, err := exp.tokenStore.Lookup(le.ClientToken)
if err != nil {
t.Fatalf("err: %v", err)
}
if out != nil {
t.Fatalf("bad: %v", out)
}
indexEntry, err = exp.indexByToken(le.ClientToken, le.LeaseID)
if err != nil {
t.Fatalf("err: %v")
}
if indexEntry != nil {
t.Fatalf("err: should not have found a secondary index entry")
}
}
func TestExpiration_renewEntry(t *testing.T) {
exp := mockExpiration(t)
noop := &NoopBackend{
Response: &logical.Response{
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
Renewable: true,
TTL: time.Hour,
},
},
Data: map[string]interface{}{
"testing": false,
},
},
}
_, barrier, _ := mockBarrier(t)
view := NewBarrierView(barrier, "logical/")
meUUID, err := uuid.GenerateUUID()
if err != nil {
t.Fatal(err)
}
exp.router.Mount(noop, "", &MountEntry{UUID: meUUID}, view)
le := &leaseEntry{
LeaseID: "foo/bar/1234",
Path: "foo/bar",
Data: map[string]interface{}{
"testing": true,
},
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: time.Minute,
},
},
IssueTime: time.Now(),
ExpireTime: time.Now(),
}
resp, err := exp.renewEntry(le, time.Second)
if err != nil {
t.Fatalf("err: %v", err)
}
noop.Lock()
defer noop.Unlock()
if !reflect.DeepEqual(resp, noop.Response) {
t.Fatalf("bad: %#v", resp)
}
req := noop.Requests[0]
if req.Operation != logical.RenewOperation {
t.Fatalf("Bad: %v", req)
}
if req.Path != le.Path {
t.Fatalf("Bad: %v", req)
}
if !reflect.DeepEqual(req.Data, le.Data) {
t.Fatalf("Bad: %v", req)
}
if req.Secret.Increment != time.Second {
t.Fatalf("Bad: %v", req)
}
if req.Secret.IssueTime.IsZero() {
t.Fatalf("Bad: %v", req)
}
}
func TestExpiration_renewAuthEntry(t *testing.T) {
exp := mockExpiration(t)
noop := &NoopBackend{
Response: &logical.Response{
Auth: &logical.Auth{
LeaseOptions: logical.LeaseOptions{
Renewable: true,
TTL: time.Hour,
},
},
},
}
_, barrier, _ := mockBarrier(t)
view := NewBarrierView(barrier, "auth/foo/")
meUUID, err := uuid.GenerateUUID()
if err != nil {
t.Fatal(err)
}
exp.router.Mount(noop, "auth/foo/", &MountEntry{UUID: meUUID}, view)
le := &leaseEntry{
LeaseID: "auth/foo/1234",
Path: "auth/foo/login",
Auth: &logical.Auth{
LeaseOptions: logical.LeaseOptions{
Renewable: true,
TTL: time.Minute,
},
InternalData: map[string]interface{}{
"MySecret": "secret",
},
},
IssueTime: time.Now(),
ExpireTime: time.Now().Add(time.Minute),
}
resp, err := exp.renewAuthEntry(&logical.Request{}, le, time.Second)
if err != nil {
t.Fatalf("err: %v", err)
}
noop.Lock()
defer noop.Unlock()
if !reflect.DeepEqual(resp, noop.Response) {
t.Fatalf("bad: %#v", resp)
}
req := noop.Requests[0]
if req.Operation != logical.RenewOperation {
t.Fatalf("Bad: %v", req)
}
if req.Path != "login" {
t.Fatalf("Bad: %v", req)
}
if req.Auth.Increment != time.Second {
t.Fatalf("Bad: %v", req)
}
if req.Auth.IssueTime.IsZero() {
t.Fatalf("Bad: %v", req)
}
if req.Auth.InternalData["MySecret"] != "secret" {
t.Fatalf("Bad: %v", req)
}
}
func TestExpiration_PersistLoadDelete(t *testing.T) {
exp := mockExpiration(t)
le := &leaseEntry{
LeaseID: "foo/bar/1234",
Path: "foo/bar",
Data: map[string]interface{}{
"testing": true,
},
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: time.Minute,
},
},
IssueTime: time.Now().UTC(),
ExpireTime: time.Now().UTC(),
LastRenewalTime: time.Time{}.UTC(),
}
if err := exp.persistEntry(le); err != nil {
t.Fatalf("err: %v", err)
}
out, err := exp.loadEntry("foo/bar/1234")
if err != nil {
t.Fatalf("err: %v", err)
}
if !reflect.DeepEqual(out, le) {
t.Fatalf("\nout: %#v\nexpect: %#v\n", out, le)
}
err = exp.deleteEntry("foo/bar/1234")
if err != nil {
t.Fatalf("err: %v", err)
}
out, err = exp.loadEntry("foo/bar/1234")
if err != nil {
t.Fatalf("err: %v", err)
}
if out != nil {
t.Fatalf("out: %#v", out)
}
}
func TestLeaseEntry(t *testing.T) {
le := &leaseEntry{
LeaseID: "foo/bar/1234",
Path: "foo/bar",
Data: map[string]interface{}{
"testing": true,
},
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: time.Minute,
},
},
IssueTime: time.Now().UTC(),
ExpireTime: time.Now().UTC(),
}
enc, err := le.encode()
if err != nil {
t.Fatalf("err: %v", err)
}
out, err := decodeLeaseEntry(enc)
if err != nil {
t.Fatalf("err: %v", err)
}
if !reflect.DeepEqual(out.Data, le.Data) {
t.Fatalf("got: %#v, expect %#v", out, le)
}
}
func TestExpiration_RevokeForce(t *testing.T) {
core, _, _, root := TestCoreWithTokenStore(t)
core.logicalBackends["badrenew"] = badRenewFactory
me := &MountEntry{
Path: "badrenew/",
Type: "badrenew",
}
err := core.mount(me)
if err != nil {
t.Fatal(err)
}
req := &logical.Request{
Operation: logical.ReadOperation,
Path: "badrenew/creds",
ClientToken: root,
}
resp, err := core.HandleRequest(req)
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("response was nil")
}
if resp.Secret == nil {
t.Fatalf("response secret was nil, response was %#v", *resp)
}
req.Operation = logical.UpdateOperation
req.Path = "sys/revoke-prefix/badrenew/creds"
resp, err = core.HandleRequest(req)
if err == nil {
t.Fatal("expected error")
}
req.Path = "sys/revoke-force/badrenew/creds"
resp, err = core.HandleRequest(req)
if err != nil {
t.Fatalf("got error: %s", err)
}
}
func badRenewFactory(conf *logical.BackendConfig) (logical.Backend, error) {
be := &framework.Backend{
Paths: []*framework.Path{
&framework.Path{
Pattern: "creds",
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.ReadOperation: func(*logical.Request, *framework.FieldData) (*logical.Response, error) {
resp := &logical.Response{
Secret: &logical.Secret{
InternalData: map[string]interface{}{
"secret_type": "badRenewBackend",
},
},
}
resp.Secret.TTL = time.Second * 30
return resp, nil
},
},
},
},
Secrets: []*framework.Secret{
&framework.Secret{
Type: "badRenewBackend",
Revoke: func(*logical.Request, *framework.FieldData) (*logical.Response, error) {
return nil, fmt.Errorf("always errors")
},
},
},
}
return be.Setup(conf)
}