6df72e6efd
Add forced revocation.
1039 lines
21 KiB
Go
1039 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
|
|
if err := exp.RevokeByToken("foobarbaz"); 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)
|
|
}
|