Compare commits
No commits in common. "master" and "v1.1.0" have entirely different histories.
@ -224,8 +224,6 @@ func main() {
|
||||
}
|
||||
----
|
||||
|
||||
Note that many functions/methods may return a https://pkg.go.dev/r00t2.io/goutils/multierr#MultiError[`(r00t2.io/goutils/)multierr.MultiError`^], which you may attempt to typeswitch to receive the original errors in their native error format. The functions/methods which may return a MultiError are noted as such in their individual documentation.
|
||||
|
||||
== Library Hacking
|
||||
|
||||
=== Reference
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
"r00t2.io/goutils/multierr"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -66,7 +65,6 @@ func NewCollection(service *Service, path dbus.ObjectPath) (coll *Collection, er
|
||||
*/
|
||||
func (c *Collection) CreateItem(label string, attrs map[string]string, secret *Secret, replace bool, itemType ...string) (item *Item, err error) {
|
||||
|
||||
var call *dbus.Call
|
||||
var prompt *Prompt
|
||||
var path dbus.ObjectPath
|
||||
var promptPath dbus.ObjectPath
|
||||
@ -81,20 +79,14 @@ func (c *Collection) CreateItem(label string, attrs map[string]string, secret *S
|
||||
}
|
||||
|
||||
props[DbusItemLabel] = dbus.MakeVariant(label)
|
||||
if !c.service.Legacy {
|
||||
props[DbusItemType] = dbus.MakeVariant(typeString)
|
||||
}
|
||||
props[DbusItemType] = dbus.MakeVariant(typeString)
|
||||
props[DbusItemAttributes] = dbus.MakeVariant(attrs)
|
||||
props[DbusItemCreated] = dbus.MakeVariant(uint64(time.Now().Unix()))
|
||||
// props[DbusItemModified] = dbus.MakeVariant(uint64(time.Now().Unix()))
|
||||
|
||||
if call = c.Dbus.Call(
|
||||
if err = c.Dbus.Call(
|
||||
DbusCollectionCreateItem, 0, props, secret, replace,
|
||||
); call.Err != nil {
|
||||
err = call.Err
|
||||
return
|
||||
}
|
||||
if err = call.Store(&path, &promptPath); err != nil {
|
||||
).Store(&path, &promptPath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -122,17 +114,10 @@ func (c *Collection) CreateItem(label string, attrs map[string]string, secret *S
|
||||
*/
|
||||
func (c *Collection) Delete() (err error) {
|
||||
|
||||
var call *dbus.Call
|
||||
var promptPath dbus.ObjectPath
|
||||
var prompt *Prompt
|
||||
|
||||
if call = c.Dbus.Call(
|
||||
DbusCollectionDelete, 0,
|
||||
); call.Err != nil {
|
||||
err = call.Err
|
||||
return
|
||||
}
|
||||
if err = call.Store(&promptPath); err != nil {
|
||||
if err = c.Dbus.Call(DbusCollectionDelete, 0).Store(&promptPath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -147,16 +132,13 @@ func (c *Collection) Delete() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Items returns a slice of Item pointers in the Collection.
|
||||
err MAY be a *multierr.MultiError.
|
||||
*/
|
||||
// Items returns a slice of Item pointers in the Collection.
|
||||
func (c *Collection) Items() (items []*Item, err error) {
|
||||
|
||||
var paths []dbus.ObjectPath
|
||||
var item *Item
|
||||
var variant dbus.Variant
|
||||
var errs *multierr.MultiError = multierr.NewMultiError()
|
||||
var errs []error = make([]error, 0)
|
||||
|
||||
if variant, err = c.Dbus.GetProperty(DbusCollectionItems); err != nil {
|
||||
return
|
||||
@ -164,21 +146,17 @@ func (c *Collection) Items() (items []*Item, err error) {
|
||||
|
||||
paths = variant.Value().([]dbus.ObjectPath)
|
||||
|
||||
items = make([]*Item, 0)
|
||||
items = make([]*Item, len(paths))
|
||||
|
||||
for _, path := range paths {
|
||||
item = nil
|
||||
for idx, path := range paths {
|
||||
if item, err = NewItem(c, path); err != nil {
|
||||
errs.AddError(err)
|
||||
errs = append(errs, err)
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
if !errs.IsEmpty() {
|
||||
err = errs
|
||||
items[idx] = item
|
||||
}
|
||||
err = NewErrors(err)
|
||||
|
||||
return
|
||||
}
|
||||
@ -255,50 +233,37 @@ func (c *Collection) Relabel(newLabel string) (err error) {
|
||||
}
|
||||
|
||||
/*
|
||||
SearchItems searches a Collection for a matching "profile" string.
|
||||
SearchItems searches a Collection for a matching profile string.
|
||||
It's mostly a carry-over from go-libsecret, and is here for convenience. IT MAY BE REMOVED IN THE FUTURE.
|
||||
|
||||
I promise it's not useful for any other implementation/storage of SecretService whatsoever.
|
||||
|
||||
err MAY be a *multierr.MultiError.
|
||||
|
||||
Deprecated: Use Service.SearchItems instead.
|
||||
*/
|
||||
func (c *Collection) SearchItems(profile string) (items []*Item, err error) {
|
||||
|
||||
var call *dbus.Call
|
||||
var paths []dbus.ObjectPath
|
||||
var errs *multierr.MultiError = multierr.NewMultiError()
|
||||
var errs []error = make([]error, 0)
|
||||
var attrs map[string]string = make(map[string]string, 0)
|
||||
var item *Item
|
||||
|
||||
attrs["profile"] = profile
|
||||
|
||||
if call = c.Dbus.Call(
|
||||
if err = c.Dbus.Call(
|
||||
DbusCollectionSearchItems, 0, attrs,
|
||||
); call.Err != nil {
|
||||
err = call.Err
|
||||
return
|
||||
}
|
||||
if err = call.Store(&paths); err != nil {
|
||||
).Store(&paths); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
items = make([]*Item, 0)
|
||||
items = make([]*Item, len(paths))
|
||||
|
||||
for _, path := range paths {
|
||||
item = nil
|
||||
if item, err = NewItem(c, path); err != nil {
|
||||
errs.AddError(err)
|
||||
for idx, path := range paths {
|
||||
if items[idx], err = NewItem(c, path); err != nil {
|
||||
errs = append(errs, err)
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
if !errs.IsEmpty() {
|
||||
err = errs
|
||||
}
|
||||
err = NewErrors(err)
|
||||
|
||||
return
|
||||
}
|
||||
@ -308,10 +273,11 @@ func (c *Collection) SetAlias(alias string) (err error) {
|
||||
|
||||
var call *dbus.Call
|
||||
|
||||
if call = c.service.Dbus.Call(
|
||||
call = c.service.Dbus.Call(
|
||||
DbusServiceSetAlias, 0, alias, c.Dbus.Path(),
|
||||
); call.Err != nil {
|
||||
err = call.Err
|
||||
)
|
||||
|
||||
if err = call.Err; err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
package gosecret
|
||||
|
||||
import (
|
||||
"testing"
|
||||
`testing`
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
`github.com/godbus/dbus/v5`
|
||||
)
|
||||
|
||||
// Some functions are covered in the Service tests.
|
||||
@ -57,8 +57,7 @@ func TestCollection_Items(t *testing.T) {
|
||||
var collection *Collection
|
||||
var items []*Item
|
||||
var item *Item
|
||||
var searchResultsUnlocked []*Item
|
||||
var searchResultsLocked []*Item
|
||||
var searchItemResults []*Item
|
||||
var secret *Secret
|
||||
var err error
|
||||
|
||||
@ -110,12 +109,12 @@ func TestCollection_Items(t *testing.T) {
|
||||
)
|
||||
} else {
|
||||
|
||||
if searchResultsUnlocked, searchResultsLocked, err = collection.service.SearchItems(itemAttrs); err != nil {
|
||||
if searchItemResults, err = collection.SearchItems(testItemLabel); err != nil {
|
||||
t.Errorf("failed to find item '%v' via Collection.SearchItems: %v", string(item.Dbus.Path()), err.Error())
|
||||
} else if (len(searchResultsLocked) + len(searchResultsUnlocked)) == 0 {
|
||||
} else if len(searchItemResults) == 0 {
|
||||
t.Errorf("failed to find item '%v' via Collection.SearchItems, returned 0 results (should be at least 1)", testItemLabel)
|
||||
} else {
|
||||
t.Logf("found %v results for Collection.SearchItems", len(searchResultsUnlocked)+len(searchResultsLocked))
|
||||
t.Logf("found %v results for Collection.SearchItems", len(searchItemResults))
|
||||
}
|
||||
|
||||
if err = item.Delete(); err != nil {
|
||||
@ -149,7 +148,6 @@ func TestCollection_Label(t *testing.T) {
|
||||
t.Fatalf("NewService failed: %v", err.Error())
|
||||
}
|
||||
|
||||
t.Logf("Attempting to get label of collection: %v", defaultCollectionLabel)
|
||||
if collection, err = svc.GetCollection(defaultCollectionLabel); err != nil {
|
||||
t.Errorf(
|
||||
"failed when fetching collection '%v': %v",
|
||||
|
@ -1,7 +1,7 @@
|
||||
package gosecret
|
||||
|
||||
import (
|
||||
"github.com/godbus/dbus/v5"
|
||||
`github.com/godbus/dbus/v5`
|
||||
)
|
||||
|
||||
// Constants for use with gosecret.
|
||||
@ -30,7 +30,6 @@ const (
|
||||
|
||||
// Libsecret/SecretService special values.
|
||||
var (
|
||||
// DbusRemoveAliasPath is used to remove an alias from a Collection and/or Item.
|
||||
DbusRemoveAliasPath dbus.ObjectPath = dbus.ObjectPath("/")
|
||||
)
|
||||
|
||||
|
4
doc.go
4
doc.go
@ -84,9 +84,5 @@ Usage
|
||||
|
||||
Full documentation can be found via inline documentation.
|
||||
Additionally, use either https://pkg.go.dev/r00t2.io/gosecret or https://pkg.go.dev/golang.org/x/tools/cmd/godoc (or `go doc`) in the source root.
|
||||
|
||||
Note that many functions/methods may return a (r00t2.io/goutils/)multierr.MultiError (https://pkg.go.dev/r00t2.io/goutils/multierr#MultiError),
|
||||
which you may attempt to typeswitch back to a *multierr.MultiErr to receive the original errors in their native error format (MultiError.Errors).
|
||||
The functions/methods which may return a MultiError are noted as such in their individual documentation.
|
||||
*/
|
||||
package gosecret
|
||||
|
76
funcs.go
76
funcs.go
@ -4,7 +4,6 @@ import (
|
||||
`strings`
|
||||
|
||||
`github.com/godbus/dbus/v5`
|
||||
`r00t2.io/goutils/multierr`
|
||||
)
|
||||
|
||||
// isPrompt returns a boolean that is true if path is/requires a prompt(ed path) and false if it is/does not.
|
||||
@ -64,27 +63,19 @@ func pathIsValid(path interface{}) (ok bool, err error) {
|
||||
|
||||
/*
|
||||
validConnPath condenses the checks for connIsValid and pathIsValid into one func due to how frequently this check is done.
|
||||
|
||||
If err is not nil, it IS a *multierr.MultiError.
|
||||
err is a MultiError, which can be treated as an error.error. (See https://pkg.go.dev/builtin#error)
|
||||
*/
|
||||
func validConnPath(conn *dbus.Conn, path interface{}) (cr *ConnPathCheckResult, err error) {
|
||||
|
||||
var errs *multierr.MultiError = multierr.NewMultiError()
|
||||
var connErr error
|
||||
var pathErr error
|
||||
|
||||
cr = new(ConnPathCheckResult)
|
||||
|
||||
if cr.ConnOK, err = connIsValid(conn); err != nil {
|
||||
errs.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
if cr.PathOK, err = pathIsValid(path); err != nil {
|
||||
errs.AddError(err)
|
||||
err = nil
|
||||
}
|
||||
cr.ConnOK, connErr = connIsValid(conn)
|
||||
cr.PathOK, pathErr = pathIsValid(path)
|
||||
|
||||
if !errs.IsEmpty() {
|
||||
err = errs
|
||||
}
|
||||
err = NewErrors(connErr, pathErr)
|
||||
|
||||
return
|
||||
}
|
||||
@ -127,7 +118,7 @@ func pathsFromPath(bus dbus.BusObject, path string) (paths []dbus.ObjectPath, er
|
||||
|
||||
/*
|
||||
NameFromPath returns an actual name (as it appears in Dbus) from a dbus.ObjectPath.
|
||||
Note that you can get any object's dbus.ObjectPath via <object>.Dbus.Path().
|
||||
Note that you can get any object's dbus.ObjectPath via <object.Dbus.Path().
|
||||
path is validated to ensure it is not an empty string.
|
||||
*/
|
||||
func NameFromPath(path dbus.ObjectPath) (name string, err error) {
|
||||
@ -153,56 +144,3 @@ func NameFromPath(path dbus.ObjectPath) (name string, err error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
CheckErrIsFromLegacy takes an error.Error from e.g.:
|
||||
|
||||
Service.SearchItems
|
||||
Collection.CreateItem
|
||||
NewItem
|
||||
Item.ChangeItemType
|
||||
Item.Type
|
||||
|
||||
and (in order) attempt to typeswitch to a *multierr.MultiError, then iterate through
|
||||
the *multierr.MultiError.Errors, attempt to typeswitch each of them to a Dbus.Error, and then finally
|
||||
check if it is regarding a missing Type property.
|
||||
|
||||
This is *very explicitly* only useful for the above functions/methods. If used anywhere else,
|
||||
it's liable to return an incorrect isLegacy even if parsed == true.
|
||||
|
||||
It is admittedly convoluted and obtuse, but this saves a lot of boilerplate for users.
|
||||
It wouldn't be necessary if projects didn't insist on using the legacy draft SecretService specification.
|
||||
But here we are.
|
||||
|
||||
isLegacy is true if this Service's API destination is legacy spec. Note that this is checking for
|
||||
very explicit conditions; isLegacy may return false but it is in fact running on a legacy API.
|
||||
Don't rely on this too much.
|
||||
|
||||
parsed is true if we found an error type we were able to perform logic of determination on.
|
||||
*/
|
||||
func CheckErrIsFromLegacy(err error) (isLegacy, parsed bool) {
|
||||
|
||||
switch e := err.(type) {
|
||||
case *multierr.MultiError:
|
||||
parsed = true
|
||||
for _, i := range e.Errors {
|
||||
switch e2 := i.(type) {
|
||||
case dbus.Error:
|
||||
if e2.Name == "org.freedesktop.DBus.Error.UnknownProperty" {
|
||||
isLegacy = true
|
||||
return
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
case dbus.Error:
|
||||
parsed = true
|
||||
if e.Name == "org.freedesktop.DBus.Error.UnknownProperty" {
|
||||
isLegacy = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
1
go.mod
1
go.mod
@ -5,5 +5,4 @@ go 1.17
|
||||
require (
|
||||
github.com/godbus/dbus/v5 v5.0.6
|
||||
github.com/google/uuid v1.3.0
|
||||
r00t2.io/goutils v1.1.2
|
||||
)
|
||||
|
5
go.sum
5
go.sum
@ -1,9 +1,4 @@
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
|
||||
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
r00t2.io/goutils v1.1.2 h1:zOOqNHQ/HpJVggV5NTXBcd7FQtBP2C/sMLkHw3YvBzU=
|
||||
r00t2.io/goutils v1.1.2/go.mod h1:9ObJI9S71wDLTOahwoOPs19DhZVYrOh4LEHmQ8SW4Lk=
|
||||
r00t2.io/sysutils v1.1.1/go.mod h1:Wlfi1rrJpoKBOjWiYM9rw2FaiZqraD6VpXyiHgoDo/o=
|
||||
|
@ -1,11 +1,11 @@
|
||||
package gosecret
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
`strconv`
|
||||
`strings`
|
||||
`time`
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
`github.com/godbus/dbus/v5`
|
||||
)
|
||||
|
||||
// NewItem returns a pointer to an Item based on Collection and a Dbus path.
|
||||
@ -85,11 +85,6 @@ func (i *Item) ChangeItemType(newItemType string) (err error) {
|
||||
|
||||
var variant dbus.Variant
|
||||
|
||||
// Legacy spec.
|
||||
if i.collection.service.Legacy {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.TrimSpace(newItemType) == "" {
|
||||
newItemType = DbusDefaultItemType
|
||||
}
|
||||
@ -111,17 +106,10 @@ func (i *Item) ChangeItemType(newItemType string) (err error) {
|
||||
// Delete removes an Item from a Collection.
|
||||
func (i *Item) Delete() (err error) {
|
||||
|
||||
var call *dbus.Call
|
||||
var promptPath dbus.ObjectPath
|
||||
var prompt *Prompt
|
||||
|
||||
if call = i.Dbus.Call(
|
||||
DbusItemDelete, 0,
|
||||
); call.Err != nil {
|
||||
err = call.Err
|
||||
return
|
||||
}
|
||||
if err = call.Store(&promptPath); err != nil {
|
||||
if err = i.Dbus.Call(DbusItemDelete, 0).Store(&promptPath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -139,8 +127,6 @@ func (i *Item) Delete() (err error) {
|
||||
// GetSecret returns the Secret in an Item using a Session.
|
||||
func (i *Item) GetSecret(session *Session) (secret *Secret, err error) {
|
||||
|
||||
var call *dbus.Call
|
||||
|
||||
if session == nil {
|
||||
err = ErrNoDbusConn
|
||||
}
|
||||
@ -149,13 +135,9 @@ func (i *Item) GetSecret(session *Session) (secret *Secret, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if call = i.Dbus.Call(
|
||||
if err = i.Dbus.Call(
|
||||
DbusItemGetSecret, 0, session.Dbus.Path(),
|
||||
); call.Err != nil {
|
||||
err = call.Err
|
||||
return
|
||||
}
|
||||
if err = call.Store(&secret); err != nil {
|
||||
).Store(&secret); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -176,7 +158,6 @@ func (i *Item) Label() (label string, err error) {
|
||||
}
|
||||
|
||||
label = variant.Value().(string)
|
||||
i.LabelName = label
|
||||
|
||||
return
|
||||
}
|
||||
@ -264,15 +245,15 @@ func (i *Item) ReplaceAttributes(newAttrs map[string]string) (err error) {
|
||||
// SetSecret sets the Secret for an Item.
|
||||
func (i *Item) SetSecret(secret *Secret) (err error) {
|
||||
|
||||
var call *dbus.Call
|
||||
var c *dbus.Call
|
||||
|
||||
if call = i.Dbus.Call(
|
||||
c = i.Dbus.Call(
|
||||
DbusItemSetSecret, 0,
|
||||
); call.Err != nil {
|
||||
err = call.Err
|
||||
)
|
||||
if c.Err != nil {
|
||||
err = c.Err
|
||||
return
|
||||
}
|
||||
|
||||
i.Secret = secret
|
||||
|
||||
if _, _, err = i.Modified(); err != nil {
|
||||
@ -287,11 +268,6 @@ func (i *Item) Type() (itemType string, err error) {
|
||||
|
||||
var variant dbus.Variant
|
||||
|
||||
// Legacy spec.
|
||||
if i.collection.service.Legacy {
|
||||
return
|
||||
}
|
||||
|
||||
if variant, err = i.Dbus.GetProperty(DbusItemType); err != nil {
|
||||
return
|
||||
}
|
||||
|
57
multierr_funcs.go
Normal file
57
multierr_funcs.go
Normal file
@ -0,0 +1,57 @@
|
||||
package gosecret
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
)
|
||||
|
||||
/*
|
||||
NewErrors returns a new MultiError based on a slice of error.Error (errs).
|
||||
Any nil errors are trimmed. If there are no actual errors after trimming, err will be nil.
|
||||
*/
|
||||
func NewErrors(errs ...error) (err error) {
|
||||
|
||||
if errs == nil || len(errs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var realErrs []error = make([]error, 0)
|
||||
|
||||
for _, e := range errs {
|
||||
if e == nil {
|
||||
continue
|
||||
}
|
||||
realErrs = append(realErrs, e)
|
||||
}
|
||||
|
||||
if len(realErrs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
err = &MultiError{
|
||||
Errors: realErrs,
|
||||
ErrorSep: "\n",
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (e *MultiError) Error() (errStr string) {
|
||||
|
||||
var numErrs int
|
||||
|
||||
if e == nil || len(e.Errors) == 0 {
|
||||
return
|
||||
} else {
|
||||
numErrs = len(e.Errors)
|
||||
}
|
||||
|
||||
for idx, err := range e.Errors {
|
||||
if (idx + 1) < numErrs {
|
||||
errStr += fmt.Sprintf(err.Error(), e.ErrorSep)
|
||||
} else {
|
||||
errStr += err.Error()
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
179
service_funcs.go
179
service_funcs.go
@ -5,10 +5,9 @@ import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
`time`
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
`r00t2.io/goutils/multierr`
|
||||
)
|
||||
|
||||
// NewService returns a pointer to a new Service connection.
|
||||
@ -39,28 +38,18 @@ func NewService() (service *Service, err error) {
|
||||
// Close cleanly closes a Service and all its underlying connections (e.g. Service.Session).
|
||||
func (s *Service) Close() (err error) {
|
||||
|
||||
if err = s.Session.Close(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.Conn.Close(); err != nil {
|
||||
return
|
||||
}
|
||||
err = s.Session.Close()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Collections returns a slice of Collection items accessible to this Service.
|
||||
|
||||
err MAY be a *multierr.MultiError.
|
||||
*/
|
||||
// Collections returns a slice of Collection items accessible to this Service.
|
||||
func (s *Service) Collections() (collections []*Collection, err error) {
|
||||
|
||||
var paths []dbus.ObjectPath
|
||||
var variant dbus.Variant
|
||||
var coll *Collection
|
||||
var errs *multierr.MultiError = multierr.NewMultiError()
|
||||
var errs []error = make([]error, 0)
|
||||
|
||||
if variant, err = s.Dbus.GetProperty(DbusServiceCollections); err != nil {
|
||||
return
|
||||
@ -68,21 +57,18 @@ func (s *Service) Collections() (collections []*Collection, err error) {
|
||||
|
||||
paths = variant.Value().([]dbus.ObjectPath)
|
||||
|
||||
collections = make([]*Collection, 0)
|
||||
collections = make([]*Collection, len(paths))
|
||||
|
||||
for _, path := range paths {
|
||||
coll = nil
|
||||
for idx, path := range paths {
|
||||
if coll, err = NewCollection(s, path); err != nil {
|
||||
errs.AddError(err)
|
||||
// return
|
||||
errs = append(errs, err)
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
collections = append(collections, coll)
|
||||
}
|
||||
|
||||
if !errs.IsEmpty() {
|
||||
err = errs
|
||||
collections[idx] = coll
|
||||
}
|
||||
err = NewErrors(err)
|
||||
|
||||
return
|
||||
}
|
||||
@ -93,7 +79,6 @@ func (s *Service) Collections() (collections []*Collection, err error) {
|
||||
*/
|
||||
func (s *Service) CreateAliasedCollection(label, alias string) (collection *Collection, err error) {
|
||||
|
||||
var call *dbus.Call
|
||||
var variant *dbus.Variant
|
||||
var path dbus.ObjectPath
|
||||
var promptPath dbus.ObjectPath
|
||||
@ -104,13 +89,9 @@ func (s *Service) CreateAliasedCollection(label, alias string) (collection *Coll
|
||||
props[DbusCollectionCreated] = dbus.MakeVariant(uint64(time.Now().Unix()))
|
||||
props[DbusCollectionModified] = dbus.MakeVariant(uint64(time.Now().Unix()))
|
||||
|
||||
if call = s.Dbus.Call(
|
||||
if err = s.Dbus.Call(
|
||||
DbusServiceCreateCollection, 0, props, alias,
|
||||
); call.Err != nil {
|
||||
err = call.Err
|
||||
return
|
||||
}
|
||||
if err = call.Store(&path, &promptPath); err != nil {
|
||||
).Store(&path, &promptPath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -143,12 +124,10 @@ func (s *Service) CreateCollection(label string) (collection *Collection, err er
|
||||
/*
|
||||
GetCollection returns a single Collection based on the name (name can also be an alias).
|
||||
It's a helper function that avoids needing to make multiple calls in user code.
|
||||
|
||||
err MAY be a *multierr.MultiError.
|
||||
*/
|
||||
func (s *Service) GetCollection(name string) (c *Collection, err error) {
|
||||
|
||||
var errs *multierr.MultiError = multierr.NewMultiError()
|
||||
var errs []error = make([]error, 0)
|
||||
var colls []*Collection
|
||||
var pathName string
|
||||
|
||||
@ -169,7 +148,7 @@ func (s *Service) GetCollection(name string) (c *Collection, err error) {
|
||||
}
|
||||
for _, i := range colls {
|
||||
if pathName, err = NameFromPath(i.Dbus.Path()); err != nil {
|
||||
errs.AddError(err)
|
||||
errs = append(errs, err)
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
@ -188,8 +167,9 @@ func (s *Service) GetCollection(name string) (c *Collection, err error) {
|
||||
}
|
||||
|
||||
// Couldn't find it by the given name.
|
||||
if !errs.IsEmpty() {
|
||||
err = errs
|
||||
if errs != nil || len(errs) > 0 {
|
||||
errs = append([]error{ErrDoesNotExist}, errs...)
|
||||
err = NewErrors(errs...)
|
||||
} else {
|
||||
err = ErrDoesNotExist
|
||||
}
|
||||
@ -216,7 +196,6 @@ func (s *Service) GetSecrets(itemPaths ...dbus.ObjectPath) (secrets map[dbus.Obj
|
||||
}
|
||||
*/
|
||||
var results map[dbus.ObjectPath][]interface{}
|
||||
var call *dbus.Call
|
||||
|
||||
if itemPaths == nil || len(itemPaths) == 0 {
|
||||
err = ErrMissingPaths
|
||||
@ -227,13 +206,9 @@ func (s *Service) GetSecrets(itemPaths ...dbus.ObjectPath) (secrets map[dbus.Obj
|
||||
results = make(map[dbus.ObjectPath][]interface{}, len(itemPaths))
|
||||
|
||||
// TODO: trigger a Service.Unlock for any locked items?
|
||||
if call = s.Dbus.Call(
|
||||
if err = s.Dbus.Call(
|
||||
DbusServiceGetSecrets, 0, itemPaths, s.Session.Dbus.Path(),
|
||||
); call.Err != nil {
|
||||
err = call.Err
|
||||
return
|
||||
}
|
||||
if err = call.Store(&results); err != nil {
|
||||
).Store(&results); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -257,31 +232,9 @@ func (s *Service) GetSession() (ssn *Session, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Scrapping this idea for now; it would require introspection on a known Item path.
|
||||
/*
|
||||
IsLegacy indicates with a decent likelihood of accuracy if this Service is
|
||||
connected to a legacy spec Secret Service (true) or if the spec is current (false).
|
||||
|
||||
It also returns a confidence indicator as a float, which indicates how accurate
|
||||
the guess (because it is a guess) may/is likely to be (as a percentage). For example,
|
||||
if confidence is expressed as 0.25, the result of legacyAPI has a 25% of being accurate.
|
||||
*/
|
||||
/*
|
||||
func (s *Service) IsLegacy() (legacyAPI bool, confidence int) {
|
||||
|
||||
var maxCon int
|
||||
|
||||
// Test 1, property introspection on Item. We're looking for a Type property.
|
||||
DbusInterfaceItem
|
||||
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
// Lock locks an Unlocked Collection or Item (LockableObject).
|
||||
func (s *Service) Lock(objects ...LockableObject) (err error) {
|
||||
|
||||
var call *dbus.Call
|
||||
var toLock []dbus.ObjectPath
|
||||
// We only use these as destinations.
|
||||
var locked []dbus.ObjectPath
|
||||
@ -299,13 +252,9 @@ func (s *Service) Lock(objects ...LockableObject) (err error) {
|
||||
toLock[idx] = o.path()
|
||||
}
|
||||
|
||||
if call = s.Dbus.Call(
|
||||
if err = s.Dbus.Call(
|
||||
DbusServiceLock, 0, toLock,
|
||||
); call.Err != nil {
|
||||
err = call.Err
|
||||
return
|
||||
}
|
||||
if err = call.Store(&locked, &promptPath); err != nil {
|
||||
).Store(&locked, &promptPath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -335,7 +284,6 @@ func (s *Service) Lock(objects ...LockableObject) (err error) {
|
||||
*/
|
||||
func (s *Service) OpenSession(algo, input string) (session *Session, output dbus.Variant, err error) {
|
||||
|
||||
var call *dbus.Call
|
||||
var path dbus.ObjectPath
|
||||
var inputVariant dbus.Variant
|
||||
|
||||
@ -349,13 +297,9 @@ func (s *Service) OpenSession(algo, input string) (session *Session, output dbus
|
||||
// TODO: confirm this.
|
||||
// Possible flags are dbus.Flags consts: https://pkg.go.dev/github.com/godbus/dbus#Flags
|
||||
// Oddly, there is no "None" flag. So it's explicitly specified as a null byte.
|
||||
if call = s.Dbus.Call(
|
||||
if err = s.Dbus.Call(
|
||||
DbusServiceOpenSession, 0, algo, inputVariant,
|
||||
); call.Err != nil {
|
||||
err = call.Err
|
||||
return
|
||||
}
|
||||
if err = call.Store(&output, &path); err != nil {
|
||||
).Store(&output, &path); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -371,20 +315,17 @@ func (s *Service) OpenSession(algo, input string) (session *Session, output dbus
|
||||
*/
|
||||
func (s *Service) ReadAlias(alias string) (collection *Collection, err error) {
|
||||
|
||||
var call *dbus.Call
|
||||
var objectPath dbus.ObjectPath
|
||||
|
||||
if call = s.Dbus.Call(
|
||||
err = s.Dbus.Call(
|
||||
DbusServiceReadAlias, 0, alias,
|
||||
); call.Err != nil {
|
||||
err = call.Err
|
||||
return
|
||||
}
|
||||
).Store(&objectPath)
|
||||
|
||||
/*
|
||||
TODO: Confirm that a nonexistent alias will NOT cause an error to return.
|
||||
If it does, alter the below logic.
|
||||
*/
|
||||
if err = call.Store(&objectPath); err != nil {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -413,12 +354,9 @@ func (s *Service) RemoveAlias(alias string) (err error) {
|
||||
|
||||
/*
|
||||
SearchItems searches all Collection objects and returns all matches based on the map of attributes.
|
||||
|
||||
err MAY be a *multierr.MultiError.
|
||||
*/
|
||||
func (s *Service) SearchItems(attributes map[string]string) (unlockedItems []*Item, lockedItems []*Item, err error) {
|
||||
|
||||
var call *dbus.Call
|
||||
var locked []dbus.ObjectPath
|
||||
var unlocked []dbus.ObjectPath
|
||||
var collectionObjs []*Collection
|
||||
@ -426,24 +364,19 @@ func (s *Service) SearchItems(attributes map[string]string) (unlockedItems []*It
|
||||
var ok bool
|
||||
var c *Collection
|
||||
var cPath dbus.ObjectPath
|
||||
var item *Item
|
||||
var errs *multierr.MultiError = multierr.NewMultiError()
|
||||
var errs []error = make([]error, 0)
|
||||
|
||||
if attributes == nil || len(attributes) == 0 {
|
||||
err = ErrMissingAttrs
|
||||
return
|
||||
}
|
||||
|
||||
if call = s.Dbus.Call(
|
||||
err = s.Dbus.Call(
|
||||
DbusServiceSearchItems, 0, attributes,
|
||||
); call.Err != nil {
|
||||
}
|
||||
if err = call.Store(&unlocked, &locked); err != nil {
|
||||
return
|
||||
}
|
||||
).Store(&unlocked, &locked)
|
||||
|
||||
lockedItems = make([]*Item, 0)
|
||||
unlockedItems = make([]*Item, 0)
|
||||
lockedItems = make([]*Item, len(locked))
|
||||
unlockedItems = make([]*Item, len(unlocked))
|
||||
|
||||
if collectionObjs, err = s.Collections(); err != nil {
|
||||
return
|
||||
@ -456,55 +389,49 @@ func (s *Service) SearchItems(attributes map[string]string) (unlockedItems []*It
|
||||
}
|
||||
|
||||
// Locked items
|
||||
for _, i := range locked {
|
||||
for idx, i := range locked {
|
||||
|
||||
item = nil
|
||||
cPath = dbus.ObjectPath(filepath.Dir(string(i)))
|
||||
|
||||
if c, ok = collections[cPath]; !ok {
|
||||
errs.AddError(errors.New(fmt.Sprintf(
|
||||
errs = append(errs, errors.New(fmt.Sprintf(
|
||||
"could not find matching Collection for locked item %v", string(i),
|
||||
)))
|
||||
continue
|
||||
}
|
||||
|
||||
if item, err = NewItem(c, i); err != nil {
|
||||
errs.AddError(errors.New(fmt.Sprintf(
|
||||
"could not create Item for locked item %v; error follows", string(i),
|
||||
if lockedItems[idx], err = NewItem(c, i); err != nil {
|
||||
errs = append(errs, errors.New(fmt.Sprintf(
|
||||
"could not create Item for locked item %v", string(i),
|
||||
)))
|
||||
errs.AddError(err)
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
lockedItems = append(lockedItems, item)
|
||||
}
|
||||
|
||||
// Unlocked items
|
||||
for _, i := range unlocked {
|
||||
for idx, i := range unlocked {
|
||||
|
||||
item = nil
|
||||
cPath = dbus.ObjectPath(filepath.Dir(string(i)))
|
||||
|
||||
if c, ok = collections[cPath]; !ok {
|
||||
errs.AddError(errors.New(fmt.Sprintf(
|
||||
errs = append(errs, errors.New(fmt.Sprintf(
|
||||
"could not find matching Collection for unlocked item %v", string(i),
|
||||
)))
|
||||
continue
|
||||
}
|
||||
|
||||
if item, err = NewItem(c, i); err != nil {
|
||||
errs.AddError(errors.New(fmt.Sprintf(
|
||||
"could not create Item for unlocked item %v; error follows", string(i),
|
||||
if unlockedItems[idx], err = NewItem(c, i); err != nil {
|
||||
errs = append(errs, errors.New(fmt.Sprintf(
|
||||
"could not create Item for unlocked item %v", string(i),
|
||||
)))
|
||||
errs.AddError(err)
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
unlockedItems = append(unlockedItems, item)
|
||||
}
|
||||
|
||||
if !errs.IsEmpty() {
|
||||
err = errs
|
||||
if errs != nil && len(errs) > 0 {
|
||||
err = NewErrors(errs...)
|
||||
}
|
||||
|
||||
return
|
||||
@ -517,17 +444,18 @@ func (s *Service) SearchItems(attributes map[string]string) (unlockedItems []*It
|
||||
*/
|
||||
func (s *Service) SetAlias(alias string, objectPath dbus.ObjectPath) (err error) {
|
||||
|
||||
var call *dbus.Call
|
||||
var c *dbus.Call
|
||||
var collection *Collection
|
||||
|
||||
if collection, err = s.GetCollection(alias); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if call = s.Dbus.Call(
|
||||
c = s.Dbus.Call(
|
||||
DbusServiceSetAlias, 0, alias, objectPath,
|
||||
); call.Err != nil {
|
||||
err = call.Err
|
||||
)
|
||||
|
||||
if err = c.Err; err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -543,7 +471,6 @@ func (s *Service) SetAlias(alias string, objectPath dbus.ObjectPath) (err error)
|
||||
// Unlock unlocks a locked Collection or Item (LockableObject).
|
||||
func (s *Service) Unlock(objects ...LockableObject) (err error) {
|
||||
|
||||
var call *dbus.Call
|
||||
var toUnlock []dbus.ObjectPath
|
||||
// We only use these as destinations.
|
||||
var unlocked []dbus.ObjectPath
|
||||
@ -561,13 +488,9 @@ func (s *Service) Unlock(objects ...LockableObject) (err error) {
|
||||
toUnlock[idx] = o.path()
|
||||
}
|
||||
|
||||
if call = s.Dbus.Call(
|
||||
if err = s.Dbus.Call(
|
||||
DbusServiceUnlock, 0, toUnlock,
|
||||
); call.Err != nil {
|
||||
err = call.Err
|
||||
return
|
||||
}
|
||||
if err = call.Store(&unlocked, &resultPath); err != nil {
|
||||
).Store(&unlocked, &resultPath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
package gosecret
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
@ -34,21 +32,13 @@ func NewSession(service *Service, path dbus.ObjectPath) (session *Session, err e
|
||||
// Close cleanly closes a Session.
|
||||
func (s *Session) Close() (err error) {
|
||||
|
||||
var call *dbus.Call
|
||||
var c *dbus.Call
|
||||
|
||||
if call = s.Dbus.Call(
|
||||
c = s.Dbus.Call(
|
||||
DbusSessionClose, 0,
|
||||
); call.Err != nil {
|
||||
/*
|
||||
I... still haven't 100% figured out why this happens, but the session DOES seem to close...?
|
||||
PRs or input welcome.
|
||||
TODO: figure out why this error gets triggered.
|
||||
*/
|
||||
if call.Err.Error() != fmt.Sprintf("The name %v was not provided by any .service files", DbusInterfaceSession) {
|
||||
err = call.Err
|
||||
return
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
_ = c
|
||||
|
||||
return
|
||||
}
|
||||
|
37
types.go
37
types.go
@ -6,6 +6,16 @@ import (
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
/*
|
||||
MultiError is a type of error.Error that can contain multiple error.Errors. Confused? Don't worry about it.
|
||||
*/
|
||||
type MultiError struct {
|
||||
// Errors is a slice of errors to combine/concatenate when .Error() is called.
|
||||
Errors []error `json:"errors"`
|
||||
// ErrorSep is a string to use to separate errors for .Error(). The default is "\n".
|
||||
ErrorSep string `json:"separator"`
|
||||
}
|
||||
|
||||
/*
|
||||
SecretServiceError is a translated error from SecretService API.
|
||||
See https://developer-old.gnome.org/libsecret/unstable/libsecret-SecretError.html#SecretError and
|
||||
@ -64,31 +74,6 @@ type Service struct {
|
||||
Session *Session `json:"-"`
|
||||
// IsLocked indicates if the Service is locked or not. Status updated by Service.Locked.
|
||||
IsLocked bool `json:"locked"`
|
||||
/*
|
||||
Legacy indicates that this SecretService implementation breaks current spec
|
||||
by implementing the legacy/obsolete draft spec rather than current libsecret spec
|
||||
for the Dbus API.
|
||||
|
||||
If you're using SecretService with KeePassXC, for instance, or a much older version
|
||||
of Gnome-Keyring *before* libsecret integration(?), or if you are getting strange errors
|
||||
when performing a Service.SearchItems, you probably need to enable this field on the
|
||||
Service returned by NewService. The coverage of this field may expand in the future, but
|
||||
currently it only prevents/suppresses the (non-existent, in legacy spec) Type property
|
||||
from being read or written on Items during e.g.:
|
||||
|
||||
Service.SearchItems
|
||||
Collection.CreateItem
|
||||
NewItem
|
||||
Item.ChangeItemType
|
||||
Item.Type
|
||||
|
||||
It will perform a no-op if enabled in the above contexts to maintain cross-compatability
|
||||
in codebase between legacy and proper current spec systems, avoiding an error return.
|
||||
|
||||
You can use CheckErrIsFromLegacy if Service.Legacy is false and Service.SearchItems returns
|
||||
a non-nil err to determine if this Service is (probably) interfacing with a legacy spec API.
|
||||
*/
|
||||
Legacy bool `json:"is_legacy"`
|
||||
}
|
||||
|
||||
/*
|
||||
@ -98,7 +83,7 @@ type Service struct {
|
||||
*/
|
||||
type Session struct {
|
||||
*DbusObject
|
||||
// service tracks the Service this Session was created from.
|
||||
// collection tracks the Service this Session was created from.
|
||||
service *Service
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user