diff --git a/barbican.go b/barbican.go index 9c9ed05..a33cd13 100644 --- a/barbican.go +++ b/barbican.go @@ -4,9 +4,14 @@ import ( "context" "github.com/artashesbalabekyan/barbican-sdk-go/client" + "github.com/artashesbalabekyan/barbican-sdk-go/fake" "github.com/artashesbalabekyan/barbican-sdk-go/xhttp" ) func NewConnection(ctx context.Context, config *xhttp.Config) (client.Conn, error) { return client.New(ctx, config) } + +func NewFakeConnection(ctx context.Context, fakeData map[string][]byte) (client.Conn, error) { + return fake.New(ctx, fakeData) +} diff --git a/client/iterator.go b/client/iterator.go index d5a69d7..4b2343a 100644 --- a/client/iterator.go +++ b/client/iterator.go @@ -2,41 +2,13 @@ package client import "context" -type Iterator struct { - ch <-chan string - ctx context.Context - cancel context.CancelCauseFunc +type Iterator interface { + Next() (string, bool) + Close() error } -// Next moves the iterator to the next key, if any. -// This key is available until Next is called again. -// -// It returns true if and only if there is a new key -// available. If there are no more keys or an error -// has been encountered, Next returns false. -func (i *Iterator) Next() (string, bool) { - select { - case v, ok := <-i.ch: - return v, ok - case <-i.ctx.Done(): - return "", false - } -} - -// Err returns the first error, if any, encountered -// while iterating over the set of keys. -func (i *Iterator) Close() error { - // i.cancel(context.Canceled) - return context.Cause(i.ctx) -} - -type namePayload struct { - name string - payload []byte -} - -type IteratorWithPayload struct { - ch <-chan namePayload +type ListIterator struct { + ch <-chan string ctx context.Context cancel context.CancelCauseFunc } @@ -47,18 +19,18 @@ type IteratorWithPayload struct { // It returns true if and only if there is a new key // available. If there are no more keys or an error // has been encountered, Next returns false. -func (i *IteratorWithPayload) Next() (namePayload, bool) { +func (i *ListIterator) Next() (string, bool) { select { case v, ok := <-i.ch: return v, ok case <-i.ctx.Done(): - return namePayload{}, false + return "", false } } // Err returns the first error, if any, encountered // while iterating over the set of keys. -func (i *IteratorWithPayload) Close() error { +func (i *ListIterator) Close() error { // i.cancel(context.Canceled) return context.Cause(i.ctx) } diff --git a/client/secrets.go b/client/secrets.go index a5ff563..544308e 100644 --- a/client/secrets.go +++ b/client/secrets.go @@ -107,7 +107,7 @@ func (c *Client) DeleteSecret(ctx context.Context, name string) error { // concurrent changes to the Barbican - i.e. // creates or deletec. Further, it does not provide any // ordering guaranteec. -func (c *Client) ListSecrets(ctx context.Context) (*Iterator, error) { +func (c *Client) ListSecrets(ctx context.Context) (Iterator, error) { var cancel context.CancelCauseFunc ctx, cancel = context.WithCancelCause(ctx) values := make(chan string, 10) @@ -149,7 +149,7 @@ func (c *Client) ListSecrets(ctx context.Context) (*Iterator, error) { } } }() - return &Iterator{ + return &ListIterator{ ch: values, ctx: ctx, cancel: cancel, diff --git a/client/types.go b/client/types.go index 2b412a7..2a0ba17 100644 --- a/client/types.go +++ b/client/types.go @@ -11,7 +11,7 @@ type Conn interface { GetSecret(ctx context.Context, name string) (*BarbicanSecret, error) GetSecretWithPayload(ctx context.Context, name string) (*BarbicanSecretWithPayload, error) DeleteSecret(ctx context.Context, name string) error - ListSecrets(ctx context.Context) (*Iterator, error) + ListSecrets(ctx context.Context) (Iterator, error) } type Client struct { diff --git a/fake/client.go b/fake/client.go new file mode 100644 index 0000000..da23f52 --- /dev/null +++ b/fake/client.go @@ -0,0 +1,17 @@ +package fake + +import ( + "context" + + "github.com/artashesbalabekyan/barbican-sdk-go/client" +) + +func New(ctx context.Context, fakeData map[string][]byte) (client.Conn, error) { + return newConnection(ctx, fakeData) +} + +func newConnection(ctx context.Context, fakeData map[string][]byte) (client.Conn, error) { + return &Client{ + fakeData: NewFakeData(fakeData), + }, nil +} diff --git a/fake/data.go b/fake/data.go new file mode 100644 index 0000000..d70864c --- /dev/null +++ b/fake/data.go @@ -0,0 +1,71 @@ +package fake + +import ( + "time" + + "github.com/artashesbalabekyan/barbican-sdk-go/client" +) + +const ( + timeFormat = "2006-01-02T15:04:05" +) + +func newCreationTime() string { + return time.Now().Format(timeFormat) +} + +func NewFakeData(d map[string][]byte) *FakeData { + f := &FakeData{ + data: make(map[string]client.BarbicanSecretWithPayload), + } + for name, payload := range d { + f.Set(name, NewSecret(name, payload)) + } + return f +} + +func NewSecret(name string, payload []byte) client.BarbicanSecretWithPayload { + return client.BarbicanSecretWithPayload{ + Secret: client.BarbicanSecret{ + Algorithm: "aes", + BitLength: 256, + ContentTypes: map[string]string{"default": "application/octet-stream"}, + Created: newCreationTime(), + Mode: "cbc", + Name: name, + SecretType: "opaque", + Status: "ACTIVE", + Updated: newCreationTime(), + }, + Payload: payload, + } +} + +func (f *FakeData) Set(name string, secret client.BarbicanSecretWithPayload) { + f.Lock() + f.data[name] = secret + f.Unlock() +} + +func (f *FakeData) Get(name string) (secret client.BarbicanSecretWithPayload, exist bool) { + f.RLock() + secret, exist = f.data[name] + f.RUnlock() + return +} + +func (f *FakeData) Delete(name string) { + f.Lock() + delete(f.data, name) + f.Unlock() +} + +func (f *FakeData) List() []client.BarbicanSecret { + f.RLock() + list := []client.BarbicanSecret{} + for _, secret := range f.data { + list = append(list, secret.Secret) + } + f.RUnlock() + return list +} diff --git a/fake/iterator.go b/fake/iterator.go new file mode 100644 index 0000000..00255df --- /dev/null +++ b/fake/iterator.go @@ -0,0 +1,31 @@ +package fake + +import "context" + +type Iterator struct { + ch <-chan string + ctx context.Context + cancel context.CancelCauseFunc +} + +// Next moves the iterator to the next key, if any. +// This key is available until Next is called again. +// +// It returns true if and only if there is a new key +// available. If there are no more keys or an error +// has been encountered, Next returns false. +func (i *Iterator) Next() (string, bool) { + select { + case v, ok := <-i.ch: + return v, ok + case <-i.ctx.Done(): + return "", false + } +} + +// Err returns the first error, if any, encountered +// while iterating over the set of keys. +func (i *Iterator) Close() error { + // i.cancel(context.Canceled) + return context.Cause(i.ctx) +} diff --git a/fake/secrets.go b/fake/secrets.go new file mode 100644 index 0000000..16d74d8 --- /dev/null +++ b/fake/secrets.go @@ -0,0 +1,73 @@ +package fake + +import ( + "context" + "fmt" + + "github.com/artashesbalabekyan/barbican-sdk-go/client" + "github.com/artashesbalabekyan/barbican-sdk-go/xerror" +) + +// Create stores the given key in Barbican if and only +// if no entry with the given name existc. +// +// If no such entry exists, Create returns ErrKeyExistc. +func (c *Client) Create(ctx context.Context, name string, value []byte) error { + if len(value) == 0 { + return fmt.Errorf("couldn't create. Provided object does not match schema 'Secret': If 'payload' specified, must be non empty. Invalid property: 'payload'") + } + secret := NewSecret(name, value) + c.fakeData.Set(name, secret) + return nil +} + +func (c *Client) GetSecret(ctx context.Context, name string) (*client.BarbicanSecret, error) { + s, ok := c.fakeData.Get(name) + if !ok { + return nil, xerror.ErrKeyNotFound + } + + return &s.Secret, nil +} + +func (c *Client) GetSecretWithPayload(ctx context.Context, name string) (*client.BarbicanSecretWithPayload, error) { + s, ok := c.fakeData.Get(name) + if !ok { + return nil, xerror.ErrKeyNotFound + } + return &s, nil +} + +func (c *Client) DeleteSecret(ctx context.Context, name string) error { + c.fakeData.Delete(name) + return nil +} + +// List returns a new Iterator over the Barbican. +// +// The returned iterator may or may not reflect any +// concurrent changes to the Barbican - i.e. +// creates or deletec. Further, it does not provide any +// ordering guaranteec. +func (c *Client) ListSecrets(ctx context.Context) (client.Iterator, error) { + var cancel context.CancelCauseFunc + ctx, cancel = context.WithCancelCause(ctx) + values := make(chan string, 10) + + go func() { + defer close(values) + keys := c.fakeData.List() + for _, k := range keys { + select { + case values <- k.Name: + case <-ctx.Done(): + return + } + } + }() + return &Iterator{ + ch: values, + ctx: ctx, + cancel: cancel, + }, nil +} diff --git a/fake/types.go b/fake/types.go new file mode 100644 index 0000000..d94fc63 --- /dev/null +++ b/fake/types.go @@ -0,0 +1,16 @@ +package fake + +import ( + "sync" + + "github.com/artashesbalabekyan/barbican-sdk-go/client" +) + +type Client struct { + fakeData *FakeData +} + +type FakeData struct { + data map[string]client.BarbicanSecretWithPayload + sync.RWMutex +}