@@ -3,6 +3,11 @@ package keychain
33import (
44 "context"
55 "errors"
6+ "fmt"
7+ "maps"
8+ "os"
9+ "reflect"
10+ "slices"
611 "strings"
712
813 kc "github.com/keybase/go-keychain"
@@ -88,14 +93,14 @@ func (k *keychainStore[T]) Get(_ context.Context, id store.ID) (store.Secret, er
8893 return nil , err
8994 }
9095
91- secret := k .factory ()
96+ secret := k .factory (result . Attributes )
9297 if err := secret .Unmarshal (result .Data ); err != nil {
9398 return nil , err
9499 }
95100 return secret , nil
96101}
97102
98- func (k * keychainStore [T ]) GetAll (context.Context ) ([]store.ID , error ) {
103+ func (k * keychainStore [T ]) GetAll (context.Context ) ([]store.SecretMetadata , error ) {
99104 item := newKeychainItem ("" , k )
100105
101106 // We use the MatchLimitAll attribute to query for multiple items from the
@@ -108,13 +113,16 @@ func (k *keychainStore[T]) GetAll(context.Context) ([]store.ID, error) {
108113 return nil , mapKeychainError (err )
109114 }
110115
111- creds := make ([]store.ID , len (results ))
112- for _ , result := range results {
116+ creds := make ([]store.SecretMetadata , len (results ))
117+ for i , result := range results {
113118 id , err := store .ParseID (result .Account )
114119 if err != nil {
115120 continue
116121 }
117- creds = append (creds , id )
122+ creds [i ] = store.SecretMetadata {
123+ ID : id ,
124+ Metadata : result .Attributes ,
125+ }
118126 }
119127 return creds , nil
120128}
@@ -135,40 +143,56 @@ func (k *keychainStore[T]) Save(_ context.Context, id store.ID, secret store.Sec
135143 // https://developer.apple.com/documentation/security/ksecattrlabel
136144 item .SetLabel (k .itemLabel (id ))
137145
146+ metadata := make (map [string ]any )
147+ maps .Copy (metadata , secret .Metadata ())
138148 parts := strings .SplitSeq (id .String (), "/" )
139149 for p := range parts {
140150 if p == "" {
141151 continue
142152 }
143- item . SetString ( p , p )
153+ metadata [ p ] = p
144154 }
145155
146- for k , v := range secret .Metadata () {
147- item .SetString (k , v )
148- }
156+ item .SetGenericMetadata (metadata )
149157
150158 return mapKeychainError (kc .AddItem (item ))
151159}
152160
153- func (k * keychainStore [T ]) Filter (ctx context.Context , id store.ID , attributes map [string ]string ) (map [store.ID ]store.Secret , error ) {
161+ func hasAttribute (attributes map [string ]any , result kc.QueryResult ) bool {
162+ if attributes == nil {
163+ return true
164+ }
165+ fmt .Fprintf (os .Stdout , "Attributes: %v\n " , result .Attributes )
166+ allMatches := make ([]bool , len (attributes ))
167+ var i int
168+ for k , v := range result .Attributes {
169+ if needle , ok := attributes [k ]; ok && reflect .DeepEqual (needle , v ) {
170+ allMatches [i ] = true
171+ i ++
172+ }
173+ }
174+ return ! slices .Contains (allMatches , false )
175+ }
176+
177+ func (k * keychainStore [T ]) Filter (ctx context.Context , id store.ID , attributes map [string ]any ) (map [store.ID ]store.Secret , error ) {
154178 item := newKeychainItem ("" , k )
155179
156180 // We use the MatchLimitAll attribute to query for multiple items from the
157181 // store. It cannot be used with item.SetReturnData.
158182 // https://developer.apple.com/documentation/security/secitemcopymatching(_:_:)#Discussion
159183 item .SetMatchLimit (kc .MatchLimitAll )
160184
185+ metadata := make (map [string ]any )
186+ maps .Copy (metadata , attributes )
161187 parts := strings .SplitSeq (id .String (), "/" )
162188 for p := range parts {
163189 if p == "" {
164190 continue
165191 }
166- item . SetString ( p , p )
192+ metadata [ p ] = p
167193 }
168194
169- for k , v := range attributes {
170- item .SetString (k , v )
171- }
195+ item .SetGenericMetadata (metadata )
172196
173197 results , err := kc .QueryItem (item )
174198 if err != nil {
@@ -182,12 +206,16 @@ func (k *keychainStore[T]) Filter(ctx context.Context, id store.ID, attributes m
182206 continue
183207 }
184208
209+ if ! hasAttribute (attributes , result ) {
210+ continue
211+ }
212+
185213 i , err := getItemWithData (id .String (), k )
186214 if err != nil {
187215 return nil , err
188216 }
189217
190- secret := k .factory ()
218+ secret := k .factory (i . Attributes )
191219 if err := secret .Unmarshal (i .Data ); err != nil {
192220 return nil , err
193221 }
0 commit comments