Skip to content

Commit

Permalink
feat: support delete all credentials from keyring
Browse files Browse the repository at this point in the history
  • Loading branch information
sweatybridge committed Sep 11, 2024
1 parent 787bc92 commit 90220bb
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 1 deletion.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/charmbracelet/glamour v0.7.0
github.com/charmbracelet/lipgloss v0.12.1
github.com/containers/common v0.59.2
github.com/danieljoos/wincred v1.2.1
github.com/deepmap/oapi-codegen/v2 v2.2.0
github.com/docker/cli v26.1.5+incompatible
github.com/docker/docker v26.1.5+incompatible
Expand Down Expand Up @@ -114,7 +115,6 @@ require (
github.com/curioswitch/go-reassign v0.2.0 // indirect
github.com/cyphar/filepath-securejoin v0.2.5 // indirect
github.com/daixiang0/gci v0.13.4 // indirect
github.com/danieljoos/wincred v1.2.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/denis-tingaikin/go-header v0.5.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
Expand Down
34 changes: 34 additions & 0 deletions internal/utils/credentials/keyring_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//go:build darwin

package credentials

import (
"os/exec"

"github.com/go-errors/errors"
)

const execPathKeychain = "/usr/bin/security"

func deleteAll(service string) error {
if len(service) == 0 {
return errors.New("missing service name")
}
// Delete each secret in a while loop until there is no more left
for {
if err := exec.Command(
execPathKeychain,
"delete-generic-password",
"-s", service,
).Run(); err == nil {
continue
} else if errors.Is(err, exec.ErrNotFound) {
return errors.New(ErrNotSupported)
} else if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() == 44 {
// Exit 44 means no item exists for this service name
return nil
} else {
return errors.Errorf("failed to delete all credentials: %w", err)
}
}
}
33 changes: 33 additions & 0 deletions internal/utils/credentials/keyring_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//go:build linux

package credentials

import (
"github.com/go-errors/errors"
ss "github.com/zalando/go-keyring/secret_service"
)

func deleteAll(service string) error {
svc, err := ss.NewSecretService()
if err != nil {
return errors.Errorf("failed to create secret service: %w", err)
}

collection := svc.GetLoginCollection()
if err := svc.Unlock(collection.Path()); err != nil {
return errors.Errorf("failed to unlock collection: %w", err)
}

search := map[string]string{"service": service}
results, err := svc.SearchItems(collection, search)
if err != nil {
return errors.Errorf("failed to search items: %w", err)
}

for _, item := range results {
if err := svc.Delete(item); err != nil {
return errors.Errorf("failed to delete all credentials: %w", err)
}
}
return nil
}
28 changes: 28 additions & 0 deletions internal/utils/credentials/keyring_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package credentials

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/zalando/go-keyring"
)

func TestDeleteAll(t *testing.T) {
service := "test-cli"
// Nothing to delete
err := deleteAll(service)
assert.NoError(t, err)
// Setup 2 items
err = keyring.Set(service, "key1", "value")
assert.NoError(t, err)
err = keyring.Set(service, "key2", "value")
assert.NoError(t, err)
// Delete all items
err = deleteAll(service)
assert.NoError(t, err)
// Check items are gone
_, err = keyring.Get(service, "key1")
assert.ErrorIs(t, err, keyring.ErrNotFound)
_, err = keyring.Get(service, "key2")
assert.ErrorIs(t, err, keyring.ErrNotFound)
}
25 changes: 25 additions & 0 deletions internal/utils/credentials/keyring_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//go:build windows

package credentials

import (
"github.com/danieljoos/wincred"
"github.com/go-errors/errors"
)

func deleteAll(service string) error {
if err := assertKeyringSupported(); err != nil {
return err
}
creds, err := wincred.FilteredList(service + ":")
if err != nil {
return errors.Errorf("failed to list credentials: %w", err)
}
for _, c := range creds {
gc := wincred.GenericCredential{Credential: *c}
if err := gc.Delete(); err != nil {
return errors.Errorf("failed to delete all credentials: %w", err)
}
}
return nil
}
5 changes: 5 additions & 0 deletions internal/utils/credentials/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ func Delete(project string) error {
return nil
}

// Deletes all stored credentials for the namespace
func DeleteAll() error {
return deleteAll(namespace)
}

func assertKeyringSupported() error {
// Suggested check: https://github.com/microsoft/WSL/issues/423
if f, err := os.ReadFile("/proc/sys/kernel/osrelease"); err == nil {
Expand Down

0 comments on commit 90220bb

Please sign in to comment.