Skip to content

Commit 604b1cd

Browse files
committed
store/keychain: handle secret metadata
Signed-off-by: Alano Terblanche <18033717+Benehiko@users.noreply.github.com>
1 parent ddf32ed commit 604b1cd

13 files changed

Lines changed: 179 additions & 160 deletions

go.work.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f h1:
1818
github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q=
1919
github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8=
2020
github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
21+
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
2122
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
2223
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
2324
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=

store/examples/secret.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
type secret struct {
1111
AccessToken string
1212
RefreshToken string
13-
Attributes map[string]any
13+
Attributes map[string]string
1414
}
1515

1616
var _ secrets.Secret = &secret{}
@@ -29,6 +29,11 @@ func (s *secret) Unmarshal(data []byte) error {
2929
return nil
3030
}
3131

32-
func (s *secret) Metadata() map[string]any {
32+
func (s *secret) Metadata() map[string]string {
3333
return s.Attributes
3434
}
35+
36+
func (s *secret) SetMetadata(attr map[string]string) error {
37+
s.Attributes = attr
38+
return nil
39+
}

store/go.sum

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7
66
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
77
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
88
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
9-
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
109
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
1110
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
1211
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=

store/keychain/cmd/main.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@ func newCommand() (*cobra.Command, error) {
2121
kc, err := keychain.New(
2222
"io.docker.Secrets",
2323
"docker-example-cli",
24-
func(metadata map[string]any) *mocks.MockCredential {
25-
return &mocks.MockCredential{
26-
Attributes: metadata,
27-
}
24+
func() *mocks.MockCredential {
25+
return &mocks.MockCredential{}
2826
},
2927
)
3028
if err != nil {
@@ -44,9 +42,9 @@ func newCommand() (*cobra.Command, error) {
4442
}
4543

4644
var e error
47-
for _, v := range secrets {
48-
fmt.Printf("\nID: %s\n", v.ID)
49-
fmt.Printf("\nMetadata: %+v", v.Metadata)
45+
for id, v := range secrets {
46+
fmt.Printf("\nID: %s\n", id.String())
47+
fmt.Printf("\nMetadata: %+v", v.Metadata())
5048
}
5149
return e
5250
},
@@ -66,7 +64,7 @@ func newCommand() (*cobra.Command, error) {
6664
return err
6765
}
6866

69-
secretMetadata := make(map[string]any)
67+
secretMetadata := make(map[string]string)
7068
for _, vals := range metadata {
7169
vv := strings.Split(vals, ":")
7270
if len(vv) == 2 && vv[0] != "" && vv[1] != "" {

store/keychain/keychain.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type keychainStore[T store.Secret] struct {
1414

1515
var _ store.Store = &keychainStore[store.Secret]{}
1616

17-
type Factory[T store.Secret] func(metadata map[string]any) T
17+
type Factory[T store.Secret] func() T
1818

1919
// New creates a new keychain store.
2020
//

store/keychain/keychain_darwin.go

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ import (
44
"context"
55
"errors"
66
"fmt"
7-
"maps"
8-
"os"
9-
"reflect"
107
"slices"
118
"strings"
129

@@ -70,6 +67,19 @@ func getItemWithData[T store.Secret](id string, k *keychainStore[T]) (*kc.QueryR
7067
return &results[0], nil
7168
}
7269

70+
func convertAttributes(attributes map[string]any) (map[string]string, error) {
71+
attr := make(map[string]string, len(attributes))
72+
for k, v := range attributes {
73+
switch t := any(v).(type) {
74+
case string:
75+
attr[k] = t
76+
default:
77+
return nil, fmt.Errorf("attributes of key %s has unsupported type %T", k, t)
78+
}
79+
}
80+
return attr, nil
81+
}
82+
7383
func (k *keychainStore[T]) Delete(_ context.Context, id store.ID) error {
7484
if err := id.Valid(); err != nil {
7585
return err
@@ -93,14 +103,22 @@ func (k *keychainStore[T]) Get(_ context.Context, id store.ID) (store.Secret, er
93103
return nil, err
94104
}
95105

96-
secret := k.factory(result.Attributes)
106+
attr, err := convertAttributes(result.Attributes)
107+
if err != nil {
108+
return nil, err
109+
}
110+
111+
secret := k.factory()
112+
if err := secret.SetMetadata(attr); err != nil {
113+
return nil, err
114+
}
97115
if err := secret.Unmarshal(result.Data); err != nil {
98116
return nil, err
99117
}
100118
return secret, nil
101119
}
102120

103-
func (k *keychainStore[T]) GetAll(context.Context) ([]store.SecretMetadata, error) {
121+
func (k *keychainStore[T]) GetAll(context.Context) (map[store.ID]store.Secret, error) {
104122
item := newKeychainItem("", k)
105123

106124
// We use the MatchLimitAll attribute to query for multiple items from the
@@ -113,16 +131,21 @@ func (k *keychainStore[T]) GetAll(context.Context) ([]store.SecretMetadata, erro
113131
return nil, mapKeychainError(err)
114132
}
115133

116-
creds := make([]store.SecretMetadata, len(results))
117-
for i, result := range results {
134+
creds := make(map[store.ID]store.Secret, len(results))
135+
for _, result := range results {
118136
id, err := store.ParseID(result.Account)
119137
if err != nil {
120138
continue
121139
}
122-
creds[i] = store.SecretMetadata{
123-
ID: id,
124-
Metadata: result.Attributes,
140+
attr, err := convertAttributes(result.Attributes)
141+
if err != nil {
142+
return nil, err
143+
}
144+
secret := k.factory()
145+
if err := secret.SetMetadata(attr); err != nil {
146+
return nil, err
125147
}
148+
creds[id] = secret
126149
}
127150
return creds, nil
128151
}
@@ -144,7 +167,9 @@ func (k *keychainStore[T]) Save(_ context.Context, id store.ID, secret store.Sec
144167
item.SetLabel(k.itemLabel(id))
145168

146169
metadata := make(map[string]any)
147-
maps.Copy(metadata, secret.Metadata())
170+
for k, v := range secret.Metadata() {
171+
metadata[k] = v
172+
}
148173
parts := strings.SplitSeq(id.String(), "/")
149174
for p := range parts {
150175
if p == "" {
@@ -158,23 +183,22 @@ func (k *keychainStore[T]) Save(_ context.Context, id store.ID, secret store.Sec
158183
return mapKeychainError(kc.AddItem(item))
159184
}
160185

161-
func hasAttribute(attributes map[string]any, result kc.QueryResult) bool {
162-
if attributes == nil {
186+
func hasAttribute(searchAttributes, queryAttributes map[string]string) bool {
187+
if searchAttributes == nil {
163188
return true
164189
}
165-
fmt.Fprintf(os.Stdout, "Attributes: %v\n", result.Attributes)
166-
allMatches := make([]bool, len(attributes))
190+
allMatches := make([]bool, len(searchAttributes))
167191
var i int
168-
for k, v := range result.Attributes {
169-
if needle, ok := attributes[k]; ok && reflect.DeepEqual(needle, v) {
192+
for k, v := range queryAttributes {
193+
if needle, ok := searchAttributes[k]; ok && strings.EqualFold(needle, v) {
170194
allMatches[i] = true
171195
i++
172196
}
173197
}
174198
return !slices.Contains(allMatches, false)
175199
}
176200

177-
func (k *keychainStore[T]) Filter(ctx context.Context, id store.ID, attributes map[string]any) (map[store.ID]store.Secret, error) {
201+
func (k *keychainStore[T]) Filter(ctx context.Context, id store.ID, attributes map[string]string) (map[store.ID]store.Secret, error) {
178202
item := newKeychainItem("", k)
179203

180204
// We use the MatchLimitAll attribute to query for multiple items from the
@@ -183,7 +207,9 @@ func (k *keychainStore[T]) Filter(ctx context.Context, id store.ID, attributes m
183207
item.SetMatchLimit(kc.MatchLimitAll)
184208

185209
metadata := make(map[string]any)
186-
maps.Copy(metadata, attributes)
210+
for k, v := range attributes {
211+
metadata[k] = v
212+
}
187213
parts := strings.SplitSeq(id.String(), "/")
188214
for p := range parts {
189215
if p == "" {
@@ -206,7 +232,12 @@ func (k *keychainStore[T]) Filter(ctx context.Context, id store.ID, attributes m
206232
continue
207233
}
208234

209-
if !hasAttribute(attributes, result) {
235+
attr, err := convertAttributes(result.Attributes)
236+
if err != nil {
237+
return nil, err
238+
}
239+
240+
if !hasAttribute(attributes, attr) {
210241
continue
211242
}
212243

@@ -215,7 +246,10 @@ func (k *keychainStore[T]) Filter(ctx context.Context, id store.ID, attributes m
215246
return nil, err
216247
}
217248

218-
secret := k.factory(i.Attributes)
249+
secret := k.factory()
250+
if err := secret.SetMetadata(attr); err != nil {
251+
return nil, err
252+
}
219253
if err := secret.Unmarshal(i.Data); err != nil {
220254
return nil, err
221255
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//go:build cgo && darwin
2+
3+
package keychain
4+
5+
import "testing"
6+
7+
func TestSecretAttributes(t *testing.T) {
8+
}

0 commit comments

Comments
 (0)