From 24d24d4661c755f001606499105fc22a50479fa9 Mon Sep 17 00:00:00 2001 From: Martin Gysel Date: Thu, 17 Dec 2020 20:16:32 +0100 Subject: [PATCH 1/4] add kwalled (kde) backend and use it if available the kwallet (kde) backend communicates through dbus with the kwalletd. it is modeled after the qtkeychain implementation [1]. like qtkeychain, the keyring backend tries to get the kwallet's 'networkWallet' if this fails the 'secret service' is used as before. kwallet is tried first as the 'secrets service' may exists on systems running kde but it's very unlikely kwallet is running on non kde systems. [1] https://github.com/frankosterfeld/qtkeychain Signed-off-by: Martin Gysel --- keyring_linux.go | 39 +++++++++++++++++- kwallet/kwallet.go | 100 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 kwallet/kwallet.go diff --git a/keyring_linux.go b/keyring_linux.go index 9d65533..d56afcc 100644 --- a/keyring_linux.go +++ b/keyring_linux.go @@ -2,8 +2,10 @@ package keyring import ( "fmt" + dbus "github.com/godbus/dbus/v5" - "github.com/zalando/go-keyring/secret_service" + kw "github.com/zalando/go-keyring/kwallet" + ss "github.com/zalando/go-keyring/secret_service" ) type secretServiceProvider struct{} @@ -11,6 +13,13 @@ type secretServiceProvider struct{} // Set stores user and pass in the keyring under the defined service // name. func (s secretServiceProvider) Set(service, user, pass string) error { + if w, err := kw.NewKWallet(service); err == nil && w.IsAvailable() { + if err := w.Open(); err != nil { + return err + } + return w.Write(user, pass) + } + svc, err := ss.NewSecretService() if err != nil { return err @@ -75,6 +84,22 @@ func (s secretServiceProvider) findItem(svc *ss.SecretService, service, user str // Get gets a secret from the keyring given a service name and a user. func (s secretServiceProvider) Get(service, user string) (string, error) { + if w, err := kw.NewKWallet(service); err == nil && w.IsAvailable() { + if err := w.Open(); err != nil { + return "", err + } + if b, err := w.Has(user); err != nil { + return "", err + } else if !b { + return "", ErrNotFound + } + pw, err := w.Read(user) + if err != nil { + return "", err + } + return pw, nil + } + svc, err := ss.NewSecretService() if err != nil { return "", err @@ -102,6 +127,18 @@ func (s secretServiceProvider) Get(service, user string) (string, error) { // Delete deletes a secret, identified by service & user, from the keyring. func (s secretServiceProvider) Delete(service, user string) error { + if w, err := kw.NewKWallet(service); err == nil && w.IsAvailable() { + if err := w.Open(); err != nil { + return err + } + if b, err := w.Has(user); err != nil { + return err + } else if !b { + return ErrNotFound + } + return w.Delete(user) + } + svc, err := ss.NewSecretService() if err != nil { return err diff --git a/kwallet/kwallet.go b/kwallet/kwallet.go new file mode 100644 index 0000000..05f3e28 --- /dev/null +++ b/kwallet/kwallet.go @@ -0,0 +1,100 @@ +package kw + +import ( + "errors" + + "github.com/godbus/dbus" +) + +const ( + serviceName = "org.kde.kwalletd5" + servicePath = "/modules/kwalletd5" + methodInterface = "org.kde.KWallet" +) + +// KWallet is an interface for the KWallet dbus API. +type KWallet struct { + *dbus.Conn + object dbus.BusObject + service string + handle int +} + +// NewKWallet inializes a new NewKwallet object. +func NewKWallet(service string) (*KWallet, error) { + conn, err := dbus.SessionBus() + if err != nil { + return nil, err + } + + return &KWallet{ + conn, + conn.Object(serviceName, servicePath), + service, + 0, + }, nil +} + +// IsAvailable checks if the kwallet is available +func (k *KWallet) IsAvailable() bool { + var wallet string + if err := k.object.Call(methodInterface+".networkWallet", 0).Store(&wallet); err != nil { + return false + } + return true +} + +// Open the wallet +func (k *KWallet) Open() error { + var wallet string + if err := k.object.Call(methodInterface+".networkWallet", 0).Store(&wallet); err != nil { + return err + } + + if err := k.object.Call(methodInterface+".open", 0, wallet, int64(0), k.service).Store(&k.handle); err != nil { + return err + } + return nil +} + +// Read a value by key +func (k *KWallet) Read(key string) (string, error) { + var password string + if err := k.object.Call(methodInterface+".readPassword", 0, k.handle, k.service, key, k.service).Store(&password); err != nil { + return "", err + } + return password, nil +} + +// Write a key, value pair +func (k *KWallet) Write(key, value string) error { + var i int + if err := k.object.Call(methodInterface+".writePassword", 0, k.handle, k.service, key, value, k.service).Store(&i); err != nil { + return err + } + if i < 0 { + return errors.New("Could not write password") + } + return nil +} + +// Delete a value by key +func (k *KWallet) Delete(key string) error { + var i int + if err := k.object.Call(methodInterface+".removeEntry", 0, k.handle, k.service, key, k.service).Store(&i); err != nil { + return err + } + if i < 0 { + return errors.New("Could not delete password") + } + return nil +} + +// Has a key +func (k *KWallet) Has(key string) (bool, error) { + var b bool + if err := k.object.Call(methodInterface+".hasEntry", 0, k.handle, k.service, key, k.service).Store(&b); err != nil { + return b, err + } + return b, nil +} From 4f201304d2e67f248018bfe296ab61f73b876d10 Mon Sep 17 00:00:00 2001 From: SoMuchForSubtlety Date: Sun, 7 Nov 2021 00:25:35 +0100 Subject: [PATCH 2/4] make linux providers satisfy Keyring interface Signed-off-by: SoMuchForSubtlety --- errors/errors.go | 11 +++ keyring.go | 6 +- keyring_fallback.go | 7 +- keyring_linux.go | 160 +++---------------------------- kwallet/kwallet.go | 88 ++++++++++------- secret_service/secret_service.go | 87 ++++++++++++++++- 6 files changed, 168 insertions(+), 191 deletions(-) create mode 100644 errors/errors.go diff --git a/errors/errors.go b/errors/errors.go new file mode 100644 index 0000000..60ddbea --- /dev/null +++ b/errors/errors.go @@ -0,0 +1,11 @@ +package errors + +const ( + ErrNotFound = KeyringError("secret not found in keyring") +) + +type KeyringError string + +func (e KeyringError) Error() string { + return string(e) +} diff --git a/keyring.go b/keyring.go index 0b94ad5..1d41005 100644 --- a/keyring.go +++ b/keyring.go @@ -1,15 +1,15 @@ package keyring -import "fmt" +import "github.com/zalando/go-keyring/errors" // provider set in the init function by the relevant os file e.g.: // keyring_linux.go var provider Keyring = fallbackServiceProvider{} -var ( +const ( // ErrNotFound is the expected error if the secret isn't found in the // keyring. - ErrNotFound = fmt.Errorf("secret not found in keyring") + ErrNotFound = errors.ErrNotFound ) // Keyring provides a simple set/get interface for a keyring service. diff --git a/keyring_fallback.go b/keyring_fallback.go index cda7f6a..722a158 100644 --- a/keyring_fallback.go +++ b/keyring_fallback.go @@ -1,12 +1,15 @@ package keyring import ( - "errors" "runtime" + + "github.com/zalando/go-keyring/errors" ) // All of the following methods error out on unsupported platforms -var ErrUnsupportedPlatform = errors.New("Unsupported platform: " + runtime.GOOS) +const ( + ErrUnsupportedPlatform = errors.KeyringError("Unsupported platform: " + runtime.GOOS) +) type fallbackServiceProvider struct{} diff --git a/keyring_linux.go b/keyring_linux.go index d56afcc..80220f2 100644 --- a/keyring_linux.go +++ b/keyring_linux.go @@ -1,157 +1,21 @@ package keyring import ( - "fmt" - - dbus "github.com/godbus/dbus/v5" kw "github.com/zalando/go-keyring/kwallet" ss "github.com/zalando/go-keyring/secret_service" ) -type secretServiceProvider struct{} - -// Set stores user and pass in the keyring under the defined service -// name. -func (s secretServiceProvider) Set(service, user, pass string) error { - if w, err := kw.NewKWallet(service); err == nil && w.IsAvailable() { - if err := w.Open(); err != nil { - return err - } - return w.Write(user, pass) - } - - svc, err := ss.NewSecretService() - if err != nil { - return err - } - - // open a session - session, err := svc.OpenSession() - if err != nil { - return err - } - defer svc.Close(session) - - attributes := map[string]string{ - "username": user, - "service": service, - } - - secret := ss.NewSecret(session.Path(), pass) - - collection := svc.GetLoginCollection() - - err = svc.Unlock(collection.Path()) - if err != nil { - return err - } - - err = svc.CreateItem(collection, - fmt.Sprintf("Password for '%s' on '%s'", user, service), - attributes, secret) - if err != nil { - return err - } - - return nil -} - -// findItem looksup an item by service and user. -func (s secretServiceProvider) findItem(svc *ss.SecretService, service, user string) (dbus.ObjectPath, error) { - collection := svc.GetLoginCollection() - - search := map[string]string{ - "username": user, - "service": service, - } - - err := svc.Unlock(collection.Path()) - if err != nil { - return "", err - } - - results, err := svc.SearchItems(collection, search) - if err != nil { - return "", err - } - - if len(results) == 0 { - return "", ErrNotFound - } - - return results[0], nil -} - -// Get gets a secret from the keyring given a service name and a user. -func (s secretServiceProvider) Get(service, user string) (string, error) { - if w, err := kw.NewKWallet(service); err == nil && w.IsAvailable() { - if err := w.Open(); err != nil { - return "", err - } - if b, err := w.Has(user); err != nil { - return "", err - } else if !b { - return "", ErrNotFound - } - pw, err := w.Read(user) - if err != nil { - return "", err - } - return pw, nil - } - - svc, err := ss.NewSecretService() - if err != nil { - return "", err - } - - item, err := s.findItem(svc, service, user) - if err != nil { - return "", err - } - - // open a session - session, err := svc.OpenSession() - if err != nil { - return "", err - } - defer svc.Close(session) - - secret, err := svc.GetSecret(item, session.Path()) - if err != nil { - return "", err - } - - return string(secret.Value), nil -} - -// Delete deletes a secret, identified by service & user, from the keyring. -func (s secretServiceProvider) Delete(service, user string) error { - if w, err := kw.NewKWallet(service); err == nil && w.IsAvailable() { - if err := w.Open(); err != nil { - return err - } - if b, err := w.Has(user); err != nil { - return err - } else if !b { - return ErrNotFound - } - return w.Delete(user) - } - - svc, err := ss.NewSecretService() - if err != nil { - return err - } - - item, err := s.findItem(svc, service, user) - if err != nil { - return err - } - - return svc.Delete(item) -} - func init() { - provider = secretServiceProvider{} + // default to secret service and fall back to kwallet — most systems will only + // have one of the two available anyways + secretService, err := ss.NewSecretService() + if err == nil { + provider = secretService + return + } + kwallet, err := kw.NewKWallet() + if err == nil { + provider = kwallet + return + } } diff --git a/kwallet/kwallet.go b/kwallet/kwallet.go index 05f3e28..22de712 100644 --- a/kwallet/kwallet.go +++ b/kwallet/kwallet.go @@ -2,8 +2,10 @@ package kw import ( "errors" + "fmt" - "github.com/godbus/dbus" + "github.com/godbus/dbus/v5" + errs "github.com/zalando/go-keyring/errors" ) const ( @@ -15,61 +17,52 @@ const ( // KWallet is an interface for the KWallet dbus API. type KWallet struct { *dbus.Conn - object dbus.BusObject - service string - handle int + object dbus.BusObject + handle int } // NewKWallet inializes a new NewKwallet object. -func NewKWallet(service string) (*KWallet, error) { +func NewKWallet() (*KWallet, error) { conn, err := dbus.SessionBus() if err != nil { return nil, err } - return &KWallet{ - conn, - conn.Object(serviceName, servicePath), - service, - 0, - }, nil -} + kw := &KWallet{ + Conn: conn, + object: conn.Object(serviceName, servicePath), + } -// IsAvailable checks if the kwallet is available -func (k *KWallet) IsAvailable() bool { var wallet string - if err := k.object.Call(methodInterface+".networkWallet", 0).Store(&wallet); err != nil { - return false + if err := kw.object.Call(methodInterface+".networkWallet", 0).Store(&wallet); err != nil { + return nil, fmt.Errorf("Kwallet is not available: %w", err) } - return true + + return kw, nil } // Open the wallet -func (k *KWallet) Open() error { +func (k *KWallet) Open(service string) error { var wallet string if err := k.object.Call(methodInterface+".networkWallet", 0).Store(&wallet); err != nil { return err } - if err := k.object.Call(methodInterface+".open", 0, wallet, int64(0), k.service).Store(&k.handle); err != nil { + if err := k.object.Call(methodInterface+".open", 0, wallet, int64(0), service).Store(&k.handle); err != nil { return err } return nil } -// Read a value by key -func (k *KWallet) Read(key string) (string, error) { - var password string - if err := k.object.Call(methodInterface+".readPassword", 0, k.handle, k.service, key, k.service).Store(&password); err != nil { - return "", err +// Set stores user and pass in the keyring under the defined service +// name. +func (k *KWallet) Set(service, user, pass string) error { + if err := k.Open(service); err != nil { + return err } - return password, nil -} -// Write a key, value pair -func (k *KWallet) Write(key, value string) error { var i int - if err := k.object.Call(methodInterface+".writePassword", 0, k.handle, k.service, key, value, k.service).Store(&i); err != nil { + if err := k.object.Call(methodInterface+".writePassword", 0, k.handle, service, user, pass, service).Store(&i); err != nil { return err } if i < 0 { @@ -78,22 +71,47 @@ func (k *KWallet) Write(key, value string) error { return nil } -// Delete a value by key -func (k *KWallet) Delete(key string) error { +// Get gets a secret from the keyring given a service name and a user. +func (k *KWallet) Get(service, user string) (string, error) { + if err := k.Open(service); err != nil { + return "", err + } + if b, err := k.Has(service, user); err != nil { + return "", err + } else if !b { + return "", errs.ErrNotFound + } + var password string + err := k.object.Call(methodInterface+".readPassword", 0, k.handle, service, user, service).Store(&password) + return password, err +} + +// Delete deletes a secret, identified by service & user, from the keyring. +func (k *KWallet) Delete(service, user string) error { + if err := k.Open(service); err != nil { + return err + } + if b, err := k.Has(service, user); err != nil { + return err + } else if !b { + return errs.ErrNotFound + } + var i int - if err := k.object.Call(methodInterface+".removeEntry", 0, k.handle, k.service, key, k.service).Store(&i); err != nil { + if err := k.object.Call(methodInterface+".removeEntry", 0, k.handle, service, user, service).Store(&i); err != nil { return err } + if i < 0 { return errors.New("Could not delete password") } return nil } -// Has a key -func (k *KWallet) Has(key string) (bool, error) { +// Has a key +func (k *KWallet) Has(service, key string) (bool, error) { var b bool - if err := k.object.Call(methodInterface+".hasEntry", 0, k.handle, k.service, key, k.service).Store(&b); err != nil { + if err := k.object.Call(methodInterface+".hasEntry", 0, k.handle, service, key, service).Store(&b); err != nil { return b, err } return b, nil diff --git a/secret_service/secret_service.go b/secret_service/secret_service.go index e4644b1..983d42b 100644 --- a/secret_service/secret_service.go +++ b/secret_service/secret_service.go @@ -1,10 +1,11 @@ package ss import ( + "errors" "fmt" - "errors" dbus "github.com/godbus/dbus/v5" + errs "github.com/zalando/go-keyring/errors" ) const ( @@ -70,6 +71,82 @@ func (s *SecretService) OpenSession() (dbus.BusObject, error) { return s.Object(serviceName, sessionPath), nil } +func (s *SecretService) Set(service, user, password string) error { + // open a session + session, err := s.OpenSession() + if err != nil { + return err + } + defer s.Close(session) + + attributes := map[string]string{ + "username": user, + "service": service, + } + + secret := NewSecret(session.Path(), password) + + collection := s.GetLoginCollection() + + err = s.Unlock(collection.Path()) + if err != nil { + return err + } + + return s.CreateItem( + collection, + fmt.Sprintf("Password for '%s' on '%s'", user, service), + attributes, + secret, + ) +} + +func (s *SecretService) Get(service, user string) (string, error) { + item, err := s.find(service, user) + if err != nil { + return "", err + } + + session, err := s.OpenSession() + if err != nil { + return "", err + } + defer s.Close(session) + + secret, err := s.GetSecret(item, session.Path()) + if err != nil { + return "", err + } + + return string(secret.Value), nil +} + +func (s *SecretService) find(service, user string) (dbus.ObjectPath, error) { + collection := s.GetLoginCollection() + + err := s.Unlock(collection.Path()) + if err != nil { + return "", err + } + + results, err := s.SearchItems( + collection, + map[string]string{ + "username": user, + "service": service, + }, + ) + if err != nil { + return "", err + } + + if len(results) == 0 { + return "", errs.ErrNotFound + } + + return results[0], nil +} + // CheckCollectionPath accepts dbus path and returns nil if the path is found // in the collection interface (and can be used). func (s *SecretService) CheckCollectionPath(path dbus.ObjectPath) error { @@ -229,9 +306,13 @@ func (s *SecretService) GetSecret(itemPath dbus.ObjectPath, session dbus.ObjectP } // Delete deletes an item from the collection. -func (s *SecretService) Delete(itemPath dbus.ObjectPath) error { +func (s *SecretService) Delete(service, user string) error { + itemPath, err := s.find(service, user) + if err != nil { + return err + } var prompt dbus.ObjectPath - err := s.Object(serviceName, itemPath).Call(itemInterface+".Delete", 0).Store(&prompt) + err = s.Object(serviceName, itemPath).Call(itemInterface+".Delete", 0).Store(&prompt) if err != nil { return err } From 710d81cacb9a8c5d6e7af412d6dceb4576aa90b5 Mon Sep 17 00:00:00 2001 From: SoMuchForSubtlety Date: Sun, 7 Nov 2021 12:29:59 +0100 Subject: [PATCH 3/4] check that the secret service backend is available Signed-off-by: SoMuchForSubtlety --- secret_service/secret_service.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/secret_service/secret_service.go b/secret_service/secret_service.go index 983d42b..376007e 100644 --- a/secret_service/secret_service.go +++ b/secret_service/secret_service.go @@ -53,10 +53,19 @@ func NewSecretService() (*SecretService, error) { return nil, err } - return &SecretService{ + s := &SecretService{ conn, conn.Object(serviceName, servicePath), - }, nil + } + + // check that the secret service backend is available + session, err := s.OpenSession() + if err != nil { + return nil, fmt.Errorf("failed to open secret service session: %w", err) + } + s.Close(session) + + return s, nil } // OpenSession opens a secret service session. From 1a56a51b7c5509cd4ef997b8a4f4bbfdb2885e5f Mon Sep 17 00:00:00 2001 From: SoMuchForSubtlety Date: Sun, 7 Nov 2021 13:40:53 +0100 Subject: [PATCH 4/4] refactor kwallet package Signed-off-by: SoMuchForSubtlety --- errors/errors.go | 7 ++- keyring.go | 4 +- keyring_fallback.go | 6 +-- kwallet/kwallet.go | 92 ++++++++++++++++++++------------ secret_service/secret_service.go | 10 +++- 5 files changed, 74 insertions(+), 45 deletions(-) diff --git a/errors/errors.go b/errors/errors.go index 60ddbea..f76475d 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -1,7 +1,10 @@ -package errors +package errs + +import "runtime" const ( - ErrNotFound = KeyringError("secret not found in keyring") + ErrNotFound = KeyringError("secret not found in keyring") + ErrUnsupportedPlatform = KeyringError("Unsupported platform: " + runtime.GOOS) ) type KeyringError string diff --git a/keyring.go b/keyring.go index 1d41005..f7792eb 100644 --- a/keyring.go +++ b/keyring.go @@ -1,6 +1,6 @@ package keyring -import "github.com/zalando/go-keyring/errors" +import errs "github.com/zalando/go-keyring/errors" // provider set in the init function by the relevant os file e.g.: // keyring_linux.go @@ -9,7 +9,7 @@ var provider Keyring = fallbackServiceProvider{} const ( // ErrNotFound is the expected error if the secret isn't found in the // keyring. - ErrNotFound = errors.ErrNotFound + ErrNotFound = errs.ErrNotFound ) // Keyring provides a simple set/get interface for a keyring service. diff --git a/keyring_fallback.go b/keyring_fallback.go index 722a158..a195db5 100644 --- a/keyring_fallback.go +++ b/keyring_fallback.go @@ -1,14 +1,12 @@ package keyring import ( - "runtime" - - "github.com/zalando/go-keyring/errors" + errs "github.com/zalando/go-keyring/errors" ) // All of the following methods error out on unsupported platforms const ( - ErrUnsupportedPlatform = errors.KeyringError("Unsupported platform: " + runtime.GOOS) + ErrUnsupportedPlatform = errs.ErrUnsupportedPlatform ) type fallbackServiceProvider struct{} diff --git a/kwallet/kwallet.go b/kwallet/kwallet.go index 22de712..0ec6451 100644 --- a/kwallet/kwallet.go +++ b/kwallet/kwallet.go @@ -17,8 +17,9 @@ const ( // KWallet is an interface for the KWallet dbus API. type KWallet struct { *dbus.Conn - object dbus.BusObject - handle int + object dbus.BusObject + walletName string + handle int } // NewKWallet inializes a new NewKwallet object. @@ -33,37 +34,21 @@ func NewKWallet() (*KWallet, error) { object: conn.Object(serviceName, servicePath), } - var wallet string - if err := kw.object.Call(methodInterface+".networkWallet", 0).Store(&wallet); err != nil { - return nil, fmt.Errorf("Kwallet is not available: %w", err) - } - - return kw, nil -} - -// Open the wallet -func (k *KWallet) Open(service string) error { - var wallet string - if err := k.object.Call(methodInterface+".networkWallet", 0).Store(&wallet); err != nil { - return err - } - - if err := k.object.Call(methodInterface+".open", 0, wallet, int64(0), service).Store(&k.handle); err != nil { - return err - } - return nil + kw.walletName, err = kw.defaultWallet() + return kw, err } // Set stores user and pass in the keyring under the defined service // name. func (k *KWallet) Set(service, user, pass string) error { - if err := k.Open(service); err != nil { + if err := k.open(service); err != nil { return err } var i int + // org.kde.KWallet.writePassword(handle int, folder string, key string, value string, appId string) int if err := k.object.Call(methodInterface+".writePassword", 0, k.handle, service, user, pass, service).Store(&i); err != nil { - return err + return fmt.Errorf("failed to write password: %w", err) } if i < 0 { return errors.New("Could not write password") @@ -73,46 +58,83 @@ func (k *KWallet) Set(service, user, pass string) error { // Get gets a secret from the keyring given a service name and a user. func (k *KWallet) Get(service, user string) (string, error) { - if err := k.Open(service); err != nil { + if err := k.open(service); err != nil { return "", err } - if b, err := k.Has(service, user); err != nil { + if b, err := k.hasEntry(service, user); err != nil { return "", err } else if !b { return "", errs.ErrNotFound } + var password string - err := k.object.Call(methodInterface+".readPassword", 0, k.handle, service, user, service).Store(&password) - return password, err + // org.kde.KWallet.readPassword(handle int, folder string, key string, appId string) string + if err := k.object.Call(methodInterface+".readPassword", 0, k.handle, service, user, service).Store(&password); err != nil { + return "", fmt.Errorf("failed to read password: %w", err) + } + return password, nil } // Delete deletes a secret, identified by service & user, from the keyring. func (k *KWallet) Delete(service, user string) error { - if err := k.Open(service); err != nil { + if err := k.open(service); err != nil { return err } - if b, err := k.Has(service, user); err != nil { + + if b, err := k.hasEntry(service, user); err != nil { return err } else if !b { return errs.ErrNotFound } - var i int - if err := k.object.Call(methodInterface+".removeEntry", 0, k.handle, service, user, service).Store(&i); err != nil { - return err + return k.removeEntry(service, user) +} + +func (k *KWallet) open(service string) error { + var alreadyOpen bool + // org.kde.KWallet.isOpen(wallet string) bool + if err := k.object.Call(methodInterface+".isOpen", 0, k.handle).Store(&alreadyOpen); err != nil { + return fmt.Errorf("failed to check if wallet is open: %w", err) + } + if alreadyOpen { + return nil } + // org.kde.KWallet.open(wallet string, wId string, appId string) int + if err := k.object.Call(methodInterface+".open", 0, k.walletName, int64(0), service).Store(&k.handle); err != nil { + return fmt.Errorf("failed to open wallet: %w", err) + } + return nil +} + +func (k *KWallet) defaultWallet() (string, error) { + var wallet string + // org.kde.KWallet.networkWallet() string + if err := k.object.Call(methodInterface+".networkWallet", 0).Store(&wallet); err != nil { + return "", fmt.Errorf("KWallet is not available: %w", err) + } + + return wallet, nil +} + +func (k *KWallet) removeEntry(service, key string) error { + var i int + // org.kde.KWallet.removeEntry(handle int, folder string, key string, appId string) int + if err := k.object.Call(methodInterface+".removeEntry", 0, k.handle, service, key, service).Store(&i); err != nil { + return fmt.Errorf("failed to delete entry: %w", err) + } if i < 0 { return errors.New("Could not delete password") } + return nil } -// Has a key -func (k *KWallet) Has(service, key string) (bool, error) { +func (k *KWallet) hasEntry(service, key string) (bool, error) { var b bool + // org.kde.KWallet.hasEntry(handle int, folder string, key string, appId string) bool if err := k.object.Call(methodInterface+".hasEntry", 0, k.handle, service, key, service).Store(&b); err != nil { - return b, err + return b, fmt.Errorf("failed to check if entry exists: %w", err) } return b, nil } diff --git a/secret_service/secret_service.go b/secret_service/secret_service.go index 376007e..0a18986 100644 --- a/secret_service/secret_service.go +++ b/secret_service/secret_service.go @@ -58,12 +58,18 @@ func NewSecretService() (*SecretService, error) { conn.Object(serviceName, servicePath), } - // check that the secret service backend is available session, err := s.OpenSession() if err != nil { return nil, fmt.Errorf("failed to open secret service session: %w", err) } - s.Close(session) + defer s.Close(session) + + // check that the secret service backend is available + collection := s.GetLoginCollection() + err = s.Unlock(collection.Path()) + if err != nil { + return nil, fmt.Errorf("failed to open secret service session: %w", err) + } return s, nil }