diff --git a/VERSION b/VERSION index 04a373e..c5523bd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.16.0 +0.17.0 diff --git a/entry_credential.go b/entry_credential.go index 70807ed..0b0fac3 100644 --- a/entry_credential.go +++ b/entry_credential.go @@ -4,11 +4,15 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "net/http" "net/url" ) +var ErrEntryNotFound = errors.New("entry not found") +var ErrMultipleEntriesFound = errors.New("multiple entries found") + const ( EntryCredentialType string = "Credential" @@ -476,3 +480,35 @@ func (c *EntryCredentialService) GetEntriesWithContext(ctx context.Context, vaul return credentials, nil } + +// GetByName retrieves a single credential entry by name, path, and subType. +// Returns ErrMultipleEntriesFound if more than one match exists. +func (c *EntryCredentialService) GetByName(vaultId, name, path, subType string) (Entry, error) { + return c.GetByNameWithContext(context.Background(), vaultId, name, path, subType) +} + +// GetByNameWithContext retrieves a single credential entry by name, path, and subType. +// Returns ErrMultipleEntriesFound if more than one match exists. +// The provided context can be used to cancel the request. +func (c *EntryCredentialService) GetByNameWithContext(ctx context.Context, vaultId, name, path, subType string) (Entry, error) { + entries, err := c.GetEntriesWithContext(ctx, vaultId, name, path) + if err != nil { + return Entry{}, err + } + + var matches []Entry + for _, e := range entries { + if e.SubType == subType { + matches = append(matches, e) + } + } + + switch len(matches) { + case 0: + return Entry{}, ErrEntryNotFound + case 1: + return c.GetByIdWithContext(ctx, vaultId, matches[0].Id) + default: + return Entry{}, ErrMultipleEntriesFound + } +} diff --git a/entry_credential_test.go b/entry_credential_test.go index 23dba8b..b738ddf 100644 --- a/entry_credential_test.go +++ b/entry_credential_test.go @@ -1,6 +1,7 @@ package dvls import ( + "errors" "strings" "testing" @@ -264,3 +265,48 @@ func Test_GetEntries_Filters(t *testing.T) { } t.Log("Cleanup complete") } + +func Test_GetByName(t *testing.T) { + vault := createTestVault(t, "getbyname") + testPath := "go-dvls\\getbyname" + + entry := Entry{ + VaultId: vault.Id, + Name: "MyCredential", + Path: testPath, + Type: EntryCredentialType, + SubType: EntryCredentialSubTypeDefault, + Data: &EntryCredentialDefaultData{Username: "user", Password: "pass"}, + } + + id, err := testClient.Entries.Credential.New(entry) + require.NoError(t, err) + + // Single match + t.Log("Test: single match by name") + got, err := testClient.Entries.Credential.GetByName(vault.Id, "MyCredential", testPath, EntryCredentialSubTypeDefault) + require.NoError(t, err) + assert.Equal(t, id, got.Id) + assert.Equal(t, "MyCredential", got.Name) + + // Not found + t.Log("Test: not found") + _, err = testClient.Entries.Credential.GetByName(vault.Id, "NonExistent", testPath, EntryCredentialSubTypeDefault) + assert.Error(t, err) + assert.False(t, errors.Is(err, ErrMultipleEntriesFound)) + + // Multiple entries found + t.Log("Test: multiple entries found") + _, err = testClient.Entries.Credential.New(Entry{ + VaultId: vault.Id, + Name: "MyCredential", + Path: testPath, + Type: EntryCredentialType, + SubType: EntryCredentialSubTypeDefault, + Data: &EntryCredentialDefaultData{Username: "user2", Password: "pass2"}, + }) + require.NoError(t, err) + + _, err = testClient.Entries.Credential.GetByName(vault.Id, "MyCredential", testPath, EntryCredentialSubTypeDefault) + assert.True(t, errors.Is(err, ErrMultipleEntriesFound)) +}