Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions store/keychain/keychain_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -689,10 +689,6 @@ func (k *keychainStore[T]) Filter(ctx context.Context, pattern store.Pattern) (m
return nil, fmt.Errorf("failed to search collection: %w", err)
}

if len(itemPaths) == 0 {
return nil, store.ErrCredentialNotFound
}

credentials := make(map[store.ID]store.Secret)
for _, itemPath := range itemPaths {
attributes, err := service.GetAttributes(itemPath)
Expand Down Expand Up @@ -728,9 +724,5 @@ func (k *keychainStore[T]) Filter(ctx context.Context, pattern store.Pattern) (m
credentials[secretID] = secret
}

if len(credentials) == 0 {
return nil, store.ErrCredentialNotFound
}

return credentials, nil
}
26 changes: 26 additions & 0 deletions store/keychain/keychain_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,32 @@ func TestKeychainFilterRetriesWhenCollectionRelocks(t *testing.T) {
assert.Equal(t, 2, fake.unlockCalls, "exactly one Unlock per relock retry")
}

func TestKeychainFilterEmpty(t *testing.T) {
fake := &fakeService{} // no items -> empty search
withFakeService(t, fake)

ks := setupKeychain(t, nil)
creds, err := ks.Filter(t.Context(), store.MustParsePattern("**"))
require.NoError(t, err)
assert.NotNil(t, creds)
assert.Empty(t, creds)
}

func TestKeychainFilterNoMatch(t *testing.T) {
fake := &fakeService{
items: []dbus.ObjectPath{"/org/freedesktop/secrets/collection/login/1"},
attributes: kc.Attributes{"id": "com.test.test/test/bob"},
}
withFakeService(t, fake)

ks := setupKeychain(t, nil)
creds, err := ks.Filter(t.Context(), store.MustParsePattern("com.test.test/test/alice"))
require.NoError(t, err)
assert.NotNil(t, creds)
assert.Empty(t, creds)
assert.Equal(t, 0, fake.getSecretCalls, "non-matching item must not be loaded")
}

// The real-keychain dedup tests use their own service group/name so their items
// are namespace-isolated from TestKeychain (which shares com.test.test/test).
// GetAllMetadata/Filter search by {service:group, service:name}, so a leaked
Expand Down
3 changes: 3 additions & 0 deletions store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ type Store interface {
Upsert(ctx context.Context, id ID, secret Secret) error
// Filter returns a map of secrets based on a [Pattern].
//
// A pattern matching nothing is a valid empty result: implementations
// return an empty (non-nil) map with a nil error, not ErrCredentialNotFound.
//
// Secrets returned will have both [Secret.SetMetadata] and [Secret.Unmarshal]
// called; in that order. Any error produced by any of them would result in
// an early return with a nil secrets map.
Expand Down
Loading