Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
0767e9c0c1
|
|||
|
6dba963608
|
|||
|
851cc327e5
|
|||
|
09f3c9b73e
|
|||
|
b9f529ad56
|
|||
|
dcc8499efa
|
|||
|
e79d530e47
|
|||
|
b02aa9877c
|
@@ -226,6 +226,9 @@ func main() {
|
|||||||
|
|
||||||
== Library Hacking
|
== Library Hacking
|
||||||
|
|
||||||
|
=== Reference
|
||||||
|
Aside from the above (INCREDIBLY brief and perhaps slightly inaccurate) introduction to SecretService concepts, it is recommended to see the `.ref/` directory in git. Notably, the `URLS` file profides several excellent resources for understanding SecretService further. The Dbus specification (first URL in the file) is highly recommended if you are unfamiliar with SecretService internals.
|
||||||
|
|
||||||
=== Tests
|
=== Tests
|
||||||
|
|
||||||
Many functions are consolidated into a single test due to how dependent certain processes are on other objects. However, all functionality should be covered by test cases and the error string will always be passed through the stack to `go test -v` output.
|
Many functions are consolidated into a single test due to how dependent certain processes are on other objects. However, all functionality should be covered by test cases and the error string will always be passed through the stack to `go test -v` output.
|
||||||
|
|||||||
10
TODO
10
TODO
@@ -1,11 +1 @@
|
|||||||
- TEST CASES
|
|
||||||
-- https://pkg.go.dev/testing
|
|
||||||
-- https://go.dev/doc/tutorial/add-a-test
|
|
||||||
-- https://gobyexample.com/testing
|
|
||||||
-- https://blog.alexellis.io/golang-writing-unit-tests/
|
|
||||||
- Benchmarking?
|
- Benchmarking?
|
||||||
- Example usage
|
|
||||||
- Merge master into V1
|
|
||||||
-- and tag release (v1.0.0)
|
|
||||||
- Merge doc.go and README.adoc to V0
|
|
||||||
-- and tag release (v0.1.3)
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
package gosecret
|
package gosecret
|
||||||
|
|
||||||
import (
|
import (
|
||||||
`strings`
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
`github.com/godbus/dbus/v5`
|
"github.com/godbus/dbus/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -13,8 +12,6 @@ import (
|
|||||||
*/
|
*/
|
||||||
func NewCollection(service *Service, path dbus.ObjectPath) (coll *Collection, err error) {
|
func NewCollection(service *Service, path dbus.ObjectPath) (coll *Collection, err error) {
|
||||||
|
|
||||||
var splitPath []string
|
|
||||||
|
|
||||||
if service == nil {
|
if service == nil {
|
||||||
err = ErrNoDbusConn
|
err = ErrNoDbusConn
|
||||||
}
|
}
|
||||||
@@ -29,14 +26,23 @@ func NewCollection(service *Service, path dbus.ObjectPath) (coll *Collection, er
|
|||||||
Dbus: service.Conn.Object(DbusService, path),
|
Dbus: service.Conn.Object(DbusService, path),
|
||||||
},
|
},
|
||||||
service: service,
|
service: service,
|
||||||
// lastModified: time.Now(),
|
// LastModified: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
splitPath = strings.Split(string(coll.Dbus.Path()), "/")
|
// Populate the struct fields...
|
||||||
|
// TODO: use channel for errors; condense into a MultiError and switch to goroutines.
|
||||||
coll.name = splitPath[len(splitPath)-1]
|
if _, err = coll.Locked(); err != nil {
|
||||||
|
return
|
||||||
_, _, err = coll.Modified()
|
}
|
||||||
|
if _, err = coll.Label(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err = coll.Created(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, _, err = coll.Modified(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -46,7 +52,16 @@ func NewCollection(service *Service, path dbus.ObjectPath) (coll *Collection, er
|
|||||||
whether any existing secret with the same label should be replaced or not, and the optional itemType.
|
whether any existing secret with the same label should be replaced or not, and the optional itemType.
|
||||||
|
|
||||||
itemType is optional; if specified, it should be a Dbus interface (only the first element is used).
|
itemType is optional; if specified, it should be a Dbus interface (only the first element is used).
|
||||||
If not specified, the default DbusDefaultItemType will be used.
|
If not specified, the default DbusDefaultItemType will be used. The most common itemType is DbusDefaultItemType
|
||||||
|
and is the current recommendation.
|
||||||
|
Other types used are:
|
||||||
|
|
||||||
|
org.gnome.keyring.NetworkPassword
|
||||||
|
org.gnome.keyring.Note
|
||||||
|
|
||||||
|
These are libsecret schemas as defined at
|
||||||
|
https://gitlab.gnome.org/GNOME/libsecret/-/blob/master/libsecret/secret-schemas.c (and bundled in with libsecret).
|
||||||
|
Support for adding custom schemas MAY come in the future but is unsupported currently.
|
||||||
*/
|
*/
|
||||||
func (c *Collection) CreateItem(label string, attrs map[string]string, secret *Secret, replace bool, itemType ...string) (item *Item, err error) {
|
func (c *Collection) CreateItem(label string, attrs map[string]string, secret *Secret, replace bool, itemType ...string) (item *Item, err error) {
|
||||||
|
|
||||||
@@ -66,6 +81,8 @@ func (c *Collection) CreateItem(label string, attrs map[string]string, secret *S
|
|||||||
props[DbusItemLabel] = dbus.MakeVariant(label)
|
props[DbusItemLabel] = dbus.MakeVariant(label)
|
||||||
props[DbusItemType] = dbus.MakeVariant(typeString)
|
props[DbusItemType] = dbus.MakeVariant(typeString)
|
||||||
props[DbusItemAttributes] = dbus.MakeVariant(attrs)
|
props[DbusItemAttributes] = dbus.MakeVariant(attrs)
|
||||||
|
props[DbusItemCreated] = dbus.MakeVariant(uint64(time.Now().Unix()))
|
||||||
|
// props[DbusItemModified] = dbus.MakeVariant(uint64(time.Now().Unix()))
|
||||||
|
|
||||||
if err = c.Dbus.Call(
|
if err = c.Dbus.Call(
|
||||||
DbusCollectionCreateItem, 0, props, secret, replace,
|
DbusCollectionCreateItem, 0, props, secret, replace,
|
||||||
@@ -155,8 +172,28 @@ func (c *Collection) Label() (label string, err error) {
|
|||||||
|
|
||||||
label = variant.Value().(string)
|
label = variant.Value().(string)
|
||||||
|
|
||||||
if label != c.name {
|
c.LabelName = label
|
||||||
c.name = label
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock will lock an unlocked Collection. It will no-op if the Collection is currently locked.
|
||||||
|
func (c *Collection) Lock() (err error) {
|
||||||
|
|
||||||
|
if _, err = c.Locked(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.IsLocked {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = c.service.Lock(c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.IsLocked = true
|
||||||
|
|
||||||
|
if _, _, err = c.Modified(); err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -173,6 +210,7 @@ func (c *Collection) Locked() (isLocked bool, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isLocked = variant.Value().(bool)
|
isLocked = variant.Value().(bool)
|
||||||
|
c.IsLocked = isLocked
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -185,6 +223,11 @@ func (c *Collection) Relabel(newLabel string) (err error) {
|
|||||||
if err = c.Dbus.SetProperty(DbusCollectionLabel, variant); err != nil {
|
if err = c.Dbus.SetProperty(DbusCollectionLabel, variant); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
c.LabelName = newLabel
|
||||||
|
|
||||||
|
if _, _, err = c.Modified(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -225,6 +268,50 @@ func (c *Collection) SearchItems(profile string) (items []*Item, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetAlias is a thin wrapper/shorthand for Service.SetAlias (but specific to this Collection).
|
||||||
|
func (c *Collection) SetAlias(alias string) (err error) {
|
||||||
|
|
||||||
|
var call *dbus.Call
|
||||||
|
|
||||||
|
call = c.service.Dbus.Call(
|
||||||
|
DbusServiceSetAlias, 0, alias, c.Dbus.Path(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err = call.Err; err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Alias = alias
|
||||||
|
|
||||||
|
if _, _, err = c.Modified(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock will unlock a locked Collection. It will no-op if the Collection is currently unlocked.
|
||||||
|
func (c *Collection) Unlock() (err error) {
|
||||||
|
|
||||||
|
if _, err = c.Locked(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !c.IsLocked {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = c.service.Unlock(c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.IsLocked = false
|
||||||
|
|
||||||
|
if _, _, err = c.Modified(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Created returns the time.Time of when a Collection was created.
|
// Created returns the time.Time of when a Collection was created.
|
||||||
func (c *Collection) Created() (created time.Time, err error) {
|
func (c *Collection) Created() (created time.Time, err error) {
|
||||||
|
|
||||||
@@ -238,6 +325,7 @@ func (c *Collection) Created() (created time.Time, err error) {
|
|||||||
timeInt = variant.Value().(uint64)
|
timeInt = variant.Value().(uint64)
|
||||||
|
|
||||||
created = time.Unix(int64(timeInt), 0)
|
created = time.Unix(int64(timeInt), 0)
|
||||||
|
c.CreatedAt = created
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -247,7 +335,7 @@ func (c *Collection) Created() (created time.Time, err error) {
|
|||||||
that indicates if the collection has changed since the last call of Collection.Modified.
|
that indicates if the collection has changed since the last call of Collection.Modified.
|
||||||
|
|
||||||
Note that when calling NewCollection, the internal library-tracked modification
|
Note that when calling NewCollection, the internal library-tracked modification
|
||||||
time (Collection.lastModified) will be set to the latest modification time of the Collection
|
time (Collection.LastModified) will be set to the latest modification time of the Collection
|
||||||
itself as reported by Dbus rather than the time that NewCollection was called.
|
itself as reported by Dbus rather than the time that NewCollection was called.
|
||||||
*/
|
*/
|
||||||
func (c *Collection) Modified() (modified time.Time, isChanged bool, err error) {
|
func (c *Collection) Modified() (modified time.Time, isChanged bool, err error) {
|
||||||
@@ -265,39 +353,20 @@ func (c *Collection) Modified() (modified time.Time, isChanged bool, err error)
|
|||||||
|
|
||||||
if !c.lastModifiedSet {
|
if !c.lastModifiedSet {
|
||||||
// It's "nil", so set it to modified. We can't check for a zero-value in case Dbus has it as a zero-value.
|
// It's "nil", so set it to modified. We can't check for a zero-value in case Dbus has it as a zero-value.
|
||||||
c.lastModified = modified
|
c.LastModified = modified
|
||||||
c.lastModifiedSet = true
|
c.lastModifiedSet = true
|
||||||
}
|
}
|
||||||
|
|
||||||
isChanged = modified.After(c.lastModified)
|
isChanged = modified.After(c.LastModified)
|
||||||
c.lastModified = modified
|
c.LastModified = modified
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// path is a *very* thin wrapper around Collection.Dbus.Path(). It is needed for LockableObject interface membership.
|
||||||
PathName returns the "real" name of a Collection.
|
func (c *Collection) path() (dbusPath dbus.ObjectPath) {
|
||||||
In some cases, the Collection.Label may not be the actual *name* of the collection
|
|
||||||
(i.e. the label is different from the name used in the Dbus path).
|
|
||||||
This is a thin wrapper around simply extracting the last item from
|
|
||||||
the Collection.Dbus.Path().
|
|
||||||
*/
|
|
||||||
func (c *Collection) PathName() (realName string) {
|
|
||||||
|
|
||||||
var pathSplit []string = strings.Split(string(c.Dbus.Path()), "/")
|
dbusPath = c.Dbus.Path()
|
||||||
|
|
||||||
realName = pathSplit[len(pathSplit)-1]
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
setModify updates the Collection's modification time (as specified by Collection.Modified).
|
|
||||||
It seems that this does not update automatically.
|
|
||||||
*/
|
|
||||||
func (c *Collection) setModify() (err error) {
|
|
||||||
|
|
||||||
err = c.Dbus.SetProperty(DbusCollectionModified, uint64(time.Now().Unix()))
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,9 +207,9 @@ func TestCollection_Locked(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if isLocked, err = collection.Locked(); err != nil {
|
if isLocked, err = collection.Locked(); err != nil {
|
||||||
t.Errorf("failed to get lock status for collection '%v': %v", collection.PathName(), err.Error())
|
t.Errorf("failed to get lock status for collection '%v': %v", collection.path(), err.Error())
|
||||||
} else {
|
} else {
|
||||||
t.Logf("collection '%v' lock status: %v", collection.PathName(), isLocked)
|
t.Logf("collection '%v' lock status: %v", collection.path(), isLocked)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = svc.Close(); err != nil {
|
if err = svc.Close(); err != nil {
|
||||||
|
|||||||
14
consts.go
14
consts.go
@@ -1,5 +1,9 @@
|
|||||||
package gosecret
|
package gosecret
|
||||||
|
|
||||||
|
import (
|
||||||
|
`github.com/godbus/dbus/v5`
|
||||||
|
)
|
||||||
|
|
||||||
// Constants for use with gosecret.
|
// Constants for use with gosecret.
|
||||||
const (
|
const (
|
||||||
/*
|
/*
|
||||||
@@ -19,14 +23,16 @@ const (
|
|||||||
// DbusPrompterInterface is an interface for issuing a Prompt. Yes, it should be doubled up like that.
|
// DbusPrompterInterface is an interface for issuing a Prompt. Yes, it should be doubled up like that.
|
||||||
DbusPrompterInterface string = DbusServiceBase + ".Prompt.Prompt"
|
DbusPrompterInterface string = DbusServiceBase + ".Prompt.Prompt"
|
||||||
/*
|
/*
|
||||||
DbusDefaultItemType is the default type to use for Item.Type.
|
DbusDefaultItemType is the default type to use for Item.Type/Collection.CreateItem.
|
||||||
I've only ever seen "org.gnome.keyring.NetworkPassword" in the wild
|
|
||||||
aside from the below. It may be legacy (gnome-keyring is obsoleted by SecretService).
|
|
||||||
If in doubt, the below is considered the "proper" interface.
|
|
||||||
*/
|
*/
|
||||||
DbusDefaultItemType string = DbusServiceBase + ".Generic"
|
DbusDefaultItemType string = DbusServiceBase + ".Generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Libsecret/SecretService special values.
|
||||||
|
var (
|
||||||
|
DbusRemoveAliasPath dbus.ObjectPath = dbus.ObjectPath("/")
|
||||||
|
)
|
||||||
|
|
||||||
// Service interface.
|
// Service interface.
|
||||||
const (
|
const (
|
||||||
/*
|
/*
|
||||||
|
|||||||
4
errs.go
4
errs.go
@@ -12,8 +12,10 @@ var (
|
|||||||
ErrInvalidProperty error = errors.New("invalid variant type; cannot convert")
|
ErrInvalidProperty error = errors.New("invalid variant type; cannot convert")
|
||||||
// ErrNoDbusConn gets triggered if a connection to Dbus can't be detected.
|
// ErrNoDbusConn gets triggered if a connection to Dbus can't be detected.
|
||||||
ErrNoDbusConn error = errors.New("no valid dbus connection")
|
ErrNoDbusConn error = errors.New("no valid dbus connection")
|
||||||
// ErrMissingPaths gets triggered if one or more Dbus object paths are expected but non/not enough are received.
|
// ErrMissingPaths gets triggered if one or more Dbus object paths are expected but none/not enough are received.
|
||||||
ErrMissingPaths error = errors.New("one or more Dbus object paths were expected but an insufficient amount were received")
|
ErrMissingPaths error = errors.New("one or more Dbus object paths were expected but an insufficient amount were received")
|
||||||
|
// ErrMissingObj gets triggered if one or more gosecret-native objects are expected but none/not enough are received.
|
||||||
|
ErrMissingObj error = errors.New("one or more objects were expected but an insufficient amount were received")
|
||||||
// ErrMissingAttrs gets triggered if attributes were expected but not passed.
|
// ErrMissingAttrs gets triggered if attributes were expected but not passed.
|
||||||
ErrMissingAttrs error = errors.New("attributes must not be empty/nil")
|
ErrMissingAttrs error = errors.New("attributes must not be empty/nil")
|
||||||
// ErrDoesNotExist gets triggered if a Collection, Item, etc. is attempted to be fetched but none exists via the specified identifier.
|
// ErrDoesNotExist gets triggered if a Collection, Item, etc. is attempted to be fetched but none exists via the specified identifier.
|
||||||
|
|||||||
29
funcs.go
29
funcs.go
@@ -115,3 +115,32 @@ func pathsFromPath(bus dbus.BusObject, path string) (paths []dbus.ObjectPath, er
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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().
|
||||||
|
path is validated to ensure it is not an empty string.
|
||||||
|
*/
|
||||||
|
func NameFromPath(path dbus.ObjectPath) (name string, err error) {
|
||||||
|
|
||||||
|
var strSplit []string
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if ok, err = pathIsValid(path); err != nil {
|
||||||
|
return
|
||||||
|
} else if !ok {
|
||||||
|
err = ErrBadDbusPath
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
strSplit = strings.Split(string(path), "/")
|
||||||
|
|
||||||
|
if len(strSplit) < 1 {
|
||||||
|
err = ErrBadDbusPath
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
name = strSplit[len(strSplit)-1]
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
132
item_funcs.go
132
item_funcs.go
@@ -32,14 +32,30 @@ func NewItem(collection *Collection, path dbus.ObjectPath) (item *Item, err erro
|
|||||||
|
|
||||||
item.idx, err = strconv.Atoi(splitPath[len(splitPath)-1])
|
item.idx, err = strconv.Atoi(splitPath[len(splitPath)-1])
|
||||||
item.collection = collection
|
item.collection = collection
|
||||||
|
|
||||||
|
// Populate the struct fields...
|
||||||
|
// TODO: use channel for errors; condense into a MultiError and switch to goroutines.
|
||||||
|
if _, err = item.GetSecret(collection.service.Session); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err = item.Locked(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
if _, err = item.Attributes(); err != nil {
|
if _, err = item.Attributes(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if _, err = item.Label(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
if _, err = item.Type(); err != nil {
|
if _, err = item.Type(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if _, err = item.Created(); err != nil {
|
||||||
_, _, err = item.Modified()
|
return
|
||||||
|
}
|
||||||
|
if _, _, err = item.Modified(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -54,6 +70,35 @@ func (i *Item) Attributes() (attrs map[string]string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
attrs = variant.Value().(map[string]string)
|
attrs = variant.Value().(map[string]string)
|
||||||
|
i.Attrs = attrs
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ChangeItemType changes an Item.Type to newItemType.
|
||||||
|
Note that this is probably a bad idea unless you're also doing Item.SetSecret.
|
||||||
|
It must be a Dbus interface path (e.g. "foo.bar.Baz").
|
||||||
|
If newItemType is an empty string, DbusDefaultItemType will be used.
|
||||||
|
*/
|
||||||
|
func (i *Item) ChangeItemType(newItemType string) (err error) {
|
||||||
|
|
||||||
|
var variant dbus.Variant
|
||||||
|
|
||||||
|
if strings.TrimSpace(newItemType) == "" {
|
||||||
|
newItemType = DbusDefaultItemType
|
||||||
|
}
|
||||||
|
|
||||||
|
variant = dbus.MakeVariant(newItemType)
|
||||||
|
|
||||||
|
if err = i.Dbus.SetProperty(DbusItemType, variant); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i.SecretType = newItemType
|
||||||
|
|
||||||
|
if _, _, err = i.Modified(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -98,6 +143,7 @@ func (i *Item) GetSecret(session *Session) (secret *Secret, err error) {
|
|||||||
|
|
||||||
secret.session = session
|
secret.session = session
|
||||||
secret.item = i
|
secret.item = i
|
||||||
|
i.Secret = secret
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -153,7 +199,9 @@ func (i *Item) ModifyAttributes(replaceAttrs map[string]string) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = i.ReplaceAttributes(currentProps)
|
if err = i.ReplaceAttributes(currentProps); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -166,6 +214,11 @@ func (i *Item) Relabel(newLabel string) (err error) {
|
|||||||
if err = i.Dbus.SetProperty(DbusItemLabel, variant); err != nil {
|
if err = i.Dbus.SetProperty(DbusItemLabel, variant); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
i.LabelName = newLabel
|
||||||
|
|
||||||
|
if _, _, err = i.Modified(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -180,6 +233,11 @@ func (i *Item) ReplaceAttributes(newAttrs map[string]string) (err error) {
|
|||||||
if err = i.Dbus.SetProperty(DbusItemAttributes, props); err != nil {
|
if err = i.Dbus.SetProperty(DbusItemAttributes, props); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
i.Attrs = newAttrs
|
||||||
|
|
||||||
|
if _, _, err = i.Modified(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -196,9 +254,12 @@ func (i *Item) SetSecret(secret *Secret) (err error) {
|
|||||||
err = c.Err
|
err = c.Err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
i.Secret = secret
|
i.Secret = secret
|
||||||
|
|
||||||
|
if _, _, err = i.Modified(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +273,29 @@ func (i *Item) Type() (itemType string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
itemType = variant.Value().(string)
|
itemType = variant.Value().(string)
|
||||||
|
i.SecretType = itemType
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock will lock an unlocked Item. It will no-op if the Item is currently locked.
|
||||||
|
func (i *Item) Lock() (err error) {
|
||||||
|
|
||||||
|
if _, err = i.Locked(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if i.IsLocked {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = i.collection.service.Lock(i); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i.IsLocked = true
|
||||||
|
|
||||||
|
if _, _, err = i.Modified(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -227,6 +311,29 @@ func (i *Item) Locked() (isLocked bool, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isLocked = variant.Value().(bool)
|
isLocked = variant.Value().(bool)
|
||||||
|
i.IsLocked = isLocked
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock will unlock a locked Item. It will no-op if the Item is currently unlocked.
|
||||||
|
func (i *Item) Unlock() (err error) {
|
||||||
|
|
||||||
|
if _, err = i.Locked(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !i.IsLocked {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = i.collection.service.Unlock(i); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i.IsLocked = false
|
||||||
|
|
||||||
|
if _, _, err = i.Modified(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -244,6 +351,7 @@ func (i *Item) Created() (created time.Time, err error) {
|
|||||||
timeInt = variant.Value().(uint64)
|
timeInt = variant.Value().(uint64)
|
||||||
|
|
||||||
created = time.Unix(int64(timeInt), 0)
|
created = time.Unix(int64(timeInt), 0)
|
||||||
|
i.CreatedAt = created
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -253,7 +361,7 @@ func (i *Item) Created() (created time.Time, err error) {
|
|||||||
that indicates if the collection has changed since the last call of Item.Modified.
|
that indicates if the collection has changed since the last call of Item.Modified.
|
||||||
|
|
||||||
Note that when calling NewItem, the internal library-tracked modification
|
Note that when calling NewItem, the internal library-tracked modification
|
||||||
time (Item.lastModified) will be set to the latest modification time of the Item
|
time (Item.LastModified) will be set to the latest modification time of the Item
|
||||||
itself as reported by Dbus rather than the time that NewItem was called.
|
itself as reported by Dbus rather than the time that NewItem was called.
|
||||||
*/
|
*/
|
||||||
func (i *Item) Modified() (modified time.Time, isChanged bool, err error) {
|
func (i *Item) Modified() (modified time.Time, isChanged bool, err error) {
|
||||||
@@ -271,12 +379,20 @@ func (i *Item) Modified() (modified time.Time, isChanged bool, err error) {
|
|||||||
|
|
||||||
if !i.lastModifiedSet {
|
if !i.lastModifiedSet {
|
||||||
// It's "nil", so set it to modified. We can't check for a zero-value in case Dbus has it as a zero-value.
|
// It's "nil", so set it to modified. We can't check for a zero-value in case Dbus has it as a zero-value.
|
||||||
i.lastModified = modified
|
i.LastModified = modified
|
||||||
i.lastModifiedSet = true
|
i.lastModifiedSet = true
|
||||||
}
|
}
|
||||||
|
|
||||||
isChanged = modified.After(i.lastModified)
|
isChanged = modified.After(i.LastModified)
|
||||||
i.lastModified = modified
|
i.LastModified = modified
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// path is a *very* thin wrapper around Item.Dbus.Path(). It is needed for LockableObject membership.
|
||||||
|
func (i *Item) path() (dbusPath dbus.ObjectPath) {
|
||||||
|
|
||||||
|
dbusPath = i.Dbus.Path()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ func TestItem(t *testing.T) {
|
|||||||
|
|
||||||
if item, err = collection.CreateItem(testItemLabel, itemAttrs, secret, true); err != nil {
|
if item, err = collection.CreateItem(testItemLabel, itemAttrs, secret, true); err != nil {
|
||||||
t.Errorf("could not create item %v in collection '%v': %v", testItemLabel, collectionName.String(), err.Error())
|
t.Errorf("could not create item %v in collection '%v': %v", testItemLabel, collectionName.String(), err.Error())
|
||||||
|
if err = collection.Delete(); err != nil {
|
||||||
|
t.Errorf("could not delete collection '%v': %v", collectionName.String(), err.Error())
|
||||||
|
}
|
||||||
if err = svc.Close(); err != nil {
|
if err = svc.Close(); err != nil {
|
||||||
t.Fatalf("could not close Service.Session: %v", err.Error())
|
t.Fatalf("could not close Service.Session: %v", err.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package gosecret
|
package gosecret
|
||||||
|
|
||||||
|
import (
|
||||||
|
`fmt`
|
||||||
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
MarshalJSON converts a SecretValue to a JSON representation.
|
MarshalJSON converts a SecretValue to a JSON representation.
|
||||||
For compat reasons, the MarshalText is left "unmolested" (i.e. renders to a Base64 value).
|
For compat reasons, the MarshalText is left "unmolested" (i.e. renders to a Base64 value).
|
||||||
@@ -7,7 +11,7 @@ package gosecret
|
|||||||
*/
|
*/
|
||||||
func (s *SecretValue) MarshalJSON() (b []byte, err error) {
|
func (s *SecretValue) MarshalJSON() (b []byte, err error) {
|
||||||
|
|
||||||
b = []byte(string(*s))
|
b = []byte(fmt.Sprintf("\"%v\"", string(*s)))
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
118
service_funcs.go
118
service_funcs.go
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
`time`
|
||||||
|
|
||||||
"github.com/godbus/dbus/v5"
|
"github.com/godbus/dbus/v5"
|
||||||
)
|
)
|
||||||
@@ -85,6 +86,8 @@ func (s *Service) CreateAliasedCollection(label, alias string) (collection *Coll
|
|||||||
var props map[string]dbus.Variant = make(map[string]dbus.Variant)
|
var props map[string]dbus.Variant = make(map[string]dbus.Variant)
|
||||||
|
|
||||||
props[DbusCollectionLabel] = dbus.MakeVariant(label)
|
props[DbusCollectionLabel] = dbus.MakeVariant(label)
|
||||||
|
props[DbusCollectionCreated] = dbus.MakeVariant(uint64(time.Now().Unix()))
|
||||||
|
props[DbusCollectionModified] = dbus.MakeVariant(uint64(time.Now().Unix()))
|
||||||
|
|
||||||
if err = s.Dbus.Call(
|
if err = s.Dbus.Call(
|
||||||
DbusServiceCreateCollection, 0, props, alias,
|
DbusServiceCreateCollection, 0, props, alias,
|
||||||
@@ -124,12 +127,13 @@ func (s *Service) CreateCollection(label string) (collection *Collection, err er
|
|||||||
*/
|
*/
|
||||||
func (s *Service) GetCollection(name string) (c *Collection, err error) {
|
func (s *Service) GetCollection(name string) (c *Collection, err error) {
|
||||||
|
|
||||||
var errs []error
|
var errs []error = make([]error, 0)
|
||||||
var colls []*Collection
|
var colls []*Collection
|
||||||
var collLabel string
|
var pathName string
|
||||||
|
|
||||||
// First check for an alias.
|
// First check for an alias.
|
||||||
if c, err = s.ReadAlias(name); err != nil && err != ErrDoesNotExist {
|
if c, err = s.ReadAlias(name); err != nil && err != ErrDoesNotExist {
|
||||||
|
c = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if c != nil {
|
if c != nil {
|
||||||
@@ -143,7 +147,12 @@ func (s *Service) GetCollection(name string) (c *Collection, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, i := range colls {
|
for _, i := range colls {
|
||||||
if i.name == name {
|
if pathName, err = NameFromPath(i.Dbus.Path()); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
err = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if pathName == name {
|
||||||
c = i
|
c = i
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -151,12 +160,7 @@ func (s *Service) GetCollection(name string) (c *Collection, err error) {
|
|||||||
|
|
||||||
// Still nothing? Try by label.
|
// Still nothing? Try by label.
|
||||||
for _, i := range colls {
|
for _, i := range colls {
|
||||||
if collLabel, err = i.Label(); err != nil {
|
if i.LabelName == name {
|
||||||
errs = append(errs, err)
|
|
||||||
err = nil
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if collLabel == name {
|
|
||||||
c = i
|
c = i
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -177,6 +181,8 @@ func (s *Service) GetCollection(name string) (c *Collection, err error) {
|
|||||||
GetSecrets allows you to fetch values (Secret) from multiple Item object paths using this Service's Session.
|
GetSecrets allows you to fetch values (Secret) from multiple Item object paths using this Service's Session.
|
||||||
An ErrMissingPaths will be returned for err if itemPaths is nil or empty.
|
An ErrMissingPaths will be returned for err if itemPaths is nil or empty.
|
||||||
The returned secrets is a map with itemPaths as the keys and their corresponding Secret as the value.
|
The returned secrets is a map with itemPaths as the keys and their corresponding Secret as the value.
|
||||||
|
If you know which Collection your desired Secret is in, it is recommended to iterate through Collection.Items instead
|
||||||
|
(as Secrets returned here may have missing functionality).
|
||||||
*/
|
*/
|
||||||
func (s *Service) GetSecrets(itemPaths ...dbus.ObjectPath) (secrets map[dbus.ObjectPath]*Secret, err error) {
|
func (s *Service) GetSecrets(itemPaths ...dbus.ObjectPath) (secrets map[dbus.ObjectPath]*Secret, err error) {
|
||||||
|
|
||||||
@@ -226,24 +232,28 @@ func (s *Service) GetSession() (ssn *Session, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Lock locks an Unlocked Collection or Item (LockableObject).
|
||||||
Lock locks an Unlocked Service, Collection, etc.
|
func (s *Service) Lock(objects ...LockableObject) (err error) {
|
||||||
You can usually get objectPath for the object(s) to unlock via <object>.Dbus.Path().
|
|
||||||
If objectPaths is nil or empty, the Service's own path will be used.
|
|
||||||
*/
|
|
||||||
func (s *Service) Lock(objectPaths ...dbus.ObjectPath) (err error) {
|
|
||||||
|
|
||||||
|
var toLock []dbus.ObjectPath
|
||||||
// We only use these as destinations.
|
// We only use these as destinations.
|
||||||
var locked []dbus.ObjectPath
|
var locked []dbus.ObjectPath
|
||||||
var prompt *Prompt
|
var prompt *Prompt
|
||||||
var promptPath dbus.ObjectPath
|
var promptPath dbus.ObjectPath
|
||||||
|
|
||||||
if objectPaths == nil || len(objectPaths) == 0 {
|
if objects == nil || len(objects) == 0 {
|
||||||
objectPaths = []dbus.ObjectPath{s.Dbus.Path()}
|
err = ErrMissingObj
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
toLock = make([]dbus.ObjectPath, len(objects))
|
||||||
|
|
||||||
|
for idx, o := range objects {
|
||||||
|
toLock[idx] = o.path()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = s.Dbus.Call(
|
if err = s.Dbus.Call(
|
||||||
DbusServiceLock, 0, objectPaths,
|
DbusServiceLock, 0, toLock,
|
||||||
).Store(&locked, &promptPath); err != nil {
|
).Store(&locked, &promptPath); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -257,12 +267,20 @@ func (s *Service) Lock(objectPaths ...dbus.ObjectPath) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: use channels and goroutines here.
|
||||||
|
for _, o := range objects {
|
||||||
|
if _, err = o.Locked(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
OpenSession returns a pointer to a Session from the Service.
|
OpenSession returns a pointer to a Session from the Service.
|
||||||
It's a convenience function around NewSession.
|
It's a convenience function around NewSession.
|
||||||
|
However, NewService attaches a Session by default at Service.Session so this is likely unnecessary.
|
||||||
*/
|
*/
|
||||||
func (s *Service) OpenSession(algo, input string) (session *Session, output dbus.Variant, err error) {
|
func (s *Service) OpenSession(algo, input string) (session *Session, output dbus.Variant, err error) {
|
||||||
|
|
||||||
@@ -324,6 +342,16 @@ func (s *Service) ReadAlias(alias string) (collection *Collection, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveAlias is a thin wrapper around Service.SetAlias using the removal method specified there.
|
||||||
|
func (s *Service) RemoveAlias(alias string) (err error) {
|
||||||
|
|
||||||
|
if err = s.SetAlias(alias, DbusRemoveAliasPath); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
SearchItems searches all Collection objects and returns all matches based on the map of attributes.
|
SearchItems searches all Collection objects and returns all matches based on the map of attributes.
|
||||||
*/
|
*/
|
||||||
@@ -411,38 +439,57 @@ func (s *Service) SearchItems(attributes map[string]string) (unlockedItems []*It
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
SetAlias sets an alias for an existing Collection.
|
SetAlias sets an alias for an existing Collection.
|
||||||
|
(You can get its path via <Collection>.Dbus.Path().)
|
||||||
To remove an alias, set objectPath to dbus.ObjectPath("/").
|
To remove an alias, set objectPath to dbus.ObjectPath("/").
|
||||||
*/
|
*/
|
||||||
func (s *Service) SetAlias(alias string, objectPath dbus.ObjectPath) (err error) {
|
func (s *Service) SetAlias(alias string, objectPath dbus.ObjectPath) (err error) {
|
||||||
|
|
||||||
var c *dbus.Call
|
var c *dbus.Call
|
||||||
|
var collection *Collection
|
||||||
|
|
||||||
|
if collection, err = s.GetCollection(alias); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c = s.Dbus.Call(
|
c = s.Dbus.Call(
|
||||||
DbusServiceSetAlias, 0, alias, objectPath,
|
DbusServiceSetAlias, 0, alias, objectPath,
|
||||||
)
|
)
|
||||||
|
|
||||||
err = c.Err
|
if err = c.Err; err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if objectPath == DbusRemoveAliasPath {
|
||||||
|
collection.Alias = ""
|
||||||
|
} else {
|
||||||
|
collection.Alias = alias
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Unlock unlocks a locked Collection or Item (LockableObject).
|
||||||
Unlock unlocks a Locked Service, Collection, etc.
|
func (s *Service) Unlock(objects ...LockableObject) (err error) {
|
||||||
You can usually get objectPath for the object(s) to unlock via <object>.Dbus.Path().
|
|
||||||
If objectPaths is nil or empty, the Service's own path will be used.
|
|
||||||
*/
|
|
||||||
func (s *Service) Unlock(objectPaths ...dbus.ObjectPath) (err error) {
|
|
||||||
|
|
||||||
|
var toUnlock []dbus.ObjectPath
|
||||||
|
// We only use these as destinations.
|
||||||
var unlocked []dbus.ObjectPath
|
var unlocked []dbus.ObjectPath
|
||||||
var prompt *Prompt
|
var prompt *Prompt
|
||||||
var resultPath dbus.ObjectPath
|
var resultPath dbus.ObjectPath
|
||||||
|
|
||||||
if objectPaths == nil || len(objectPaths) == 0 {
|
if objects == nil || len(objects) == 0 {
|
||||||
objectPaths = []dbus.ObjectPath{s.Dbus.Path()}
|
err = ErrMissingObj
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
toUnlock = make([]dbus.ObjectPath, len(objects))
|
||||||
|
|
||||||
|
for idx, o := range objects {
|
||||||
|
toUnlock[idx] = o.path()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = s.Dbus.Call(
|
if err = s.Dbus.Call(
|
||||||
DbusServiceUnlock, 0, objectPaths,
|
DbusServiceUnlock, 0, toUnlock,
|
||||||
).Store(&unlocked, &resultPath); err != nil {
|
).Store(&unlocked, &resultPath); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -456,5 +503,20 @@ func (s *Service) Unlock(objectPaths ...dbus.ObjectPath) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: use channels and goroutines here.
|
||||||
|
for _, o := range objects {
|
||||||
|
if _, err = o.Locked(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// path is a *very* thin wrapper around Service.Dbus.Path().
|
||||||
|
func (s *Service) path() (dbusPath dbus.ObjectPath) {
|
||||||
|
|
||||||
|
dbusPath = s.Dbus.Path()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ func TestService_Collections(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Logf(
|
t.Logf(
|
||||||
"collection #%v (name '%v', label '%v'): created %v, last modified %v",
|
"collection #%v (name '%v', label '%v'): created %v, last modified %v",
|
||||||
idx, c.PathName(), collLabel, created, modified,
|
idx, c.path(), collLabel, created, modified,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,7 +177,7 @@ func TestService_GetCollection(t *testing.T) {
|
|||||||
if coll, err = svc.GetCollection(defaultCollection); err != nil {
|
if coll, err = svc.GetCollection(defaultCollection); err != nil {
|
||||||
t.Errorf("failed to get collection '%v' via Service.GetCollection: %v", defaultCollection, err.Error())
|
t.Errorf("failed to get collection '%v' via Service.GetCollection: %v", defaultCollection, err.Error())
|
||||||
} else {
|
} else {
|
||||||
t.Logf("got collection '%v' via reference '%v'", coll.name, defaultCollection)
|
t.Logf("got collection '%v' via reference '%v'", coll.LabelName, defaultCollection)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = svc.Close(); err != nil {
|
if err = svc.Close(); err != nil {
|
||||||
@@ -277,7 +277,13 @@ func TestService_Secrets(t *testing.T) {
|
|||||||
t.Errorf("at least one locked item in collection '%v'", collectionName.String())
|
t.Errorf("at least one locked item in collection '%v'", collectionName.String())
|
||||||
}
|
}
|
||||||
if len(itemResultsUnlocked) != 1 {
|
if len(itemResultsUnlocked) != 1 {
|
||||||
t.Errorf("number of unlocked items in collection '%v' is not equal to 1", collectionName.String())
|
t.Errorf(
|
||||||
|
"number of unlocked items in collection '%v' (%v) is not equal to 1; items dump pending...",
|
||||||
|
collectionName.String(), len(itemResultsUnlocked),
|
||||||
|
)
|
||||||
|
for idx, i := range itemResultsUnlocked {
|
||||||
|
t.Logf("ITEM #%v IN COLLECTION %v: %v ('%v')", idx, collectionName.String(), i.LabelName, string(i.Dbus.Path()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if resultItemName, err = itemResultsUnlocked[0].Label(); err != nil {
|
if resultItemName, err = itemResultsUnlocked[0].Label(); err != nil {
|
||||||
t.Errorf("cannot fetch test Item name from collection '%v' in SearchItems: %v", collectionName.String(), err.Error())
|
t.Errorf("cannot fetch test Item name from collection '%v' in SearchItems: %v", collectionName.String(), err.Error())
|
||||||
@@ -374,10 +380,11 @@ func TestService_Locking(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if collection, err = svc.CreateCollection(collectionName.String()); err != nil {
|
if collection, err = svc.CreateCollection(collectionName.String()); err != nil {
|
||||||
if err = svc.Close(); err != nil {
|
|
||||||
t.Errorf("could not close Service.Session: %v", err.Error())
|
|
||||||
}
|
|
||||||
t.Errorf("could not create collection '%v': %v", collectionName.String(), err.Error())
|
t.Errorf("could not create collection '%v': %v", collectionName.String(), err.Error())
|
||||||
|
if err = svc.Close(); err != nil {
|
||||||
|
t.Fatalf("could not close Service.Session: %v", err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
} else {
|
} else {
|
||||||
t.Logf("created collection '%v' at path '%v' successfully", collectionName.String(), string(collection.Dbus.Path()))
|
t.Logf("created collection '%v' at path '%v' successfully", collectionName.String(), string(collection.Dbus.Path()))
|
||||||
}
|
}
|
||||||
@@ -400,23 +407,23 @@ func TestService_Locking(t *testing.T) {
|
|||||||
|
|
||||||
// Change the state.
|
// Change the state.
|
||||||
if isLocked {
|
if isLocked {
|
||||||
if err = svc.Unlock(collection.Dbus.Path()); err != nil {
|
if err = collection.Unlock(); err != nil {
|
||||||
t.Errorf("could not unlock collection '%v': %v", collectionName.String(), err.Error())
|
t.Errorf("could not unlock collection '%v': %v", collectionName.String(), err.Error())
|
||||||
}
|
}
|
||||||
if stateChangeLock, err = collection.Locked(); err != nil {
|
if stateChangeLock, err = collection.Locked(); err != nil {
|
||||||
t.Errorf("received error when checking collection '%v' lock status: %v", collectionName.String(), err.Error())
|
t.Errorf("received error when checking collection '%v' lock status: %v", collectionName.String(), err.Error())
|
||||||
}
|
}
|
||||||
if err = svc.Lock(collection.Dbus.Path()); err != nil {
|
if err = collection.Lock(); err != nil {
|
||||||
t.Errorf("could not lock collection '%v': %v", collectionName.String(), err.Error())
|
t.Errorf("could not lock collection '%v': %v", collectionName.String(), err.Error())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err = svc.Lock(collection.Dbus.Path()); err != nil {
|
if err = collection.Lock(); err != nil {
|
||||||
t.Errorf("could not lock collection '%v': %v", collectionName.String(), err.Error())
|
t.Errorf("could not lock collection '%v': %v", collectionName.String(), err.Error())
|
||||||
}
|
}
|
||||||
if stateChangeLock, err = collection.Locked(); err != nil {
|
if stateChangeLock, err = collection.Locked(); err != nil {
|
||||||
t.Errorf("received error when checking collection '%v' lock status: %v", collectionName.String(), err.Error())
|
t.Errorf("received error when checking collection '%v' lock status: %v", collectionName.String(), err.Error())
|
||||||
}
|
}
|
||||||
if err = svc.Unlock(collection.Dbus.Path()); err != nil {
|
if err = collection.Unlock(); err != nil {
|
||||||
t.Errorf("could not unlock collection '%v': %v", collectionName.String(), err.Error())
|
t.Errorf("could not unlock collection '%v': %v", collectionName.String(), err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,3 +42,11 @@ func (s *Session) Close() (err error) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// path is a *very* thin wrapper around Session.Dbus.Path().
|
||||||
|
func (s *Session) path() (dbusPath dbus.ObjectPath) {
|
||||||
|
|
||||||
|
dbusPath = s.Dbus.Path()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
40
types.go
40
types.go
@@ -58,6 +58,11 @@ type Prompt struct {
|
|||||||
*DbusObject
|
*DbusObject
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LockableObject interface {
|
||||||
|
Locked() (bool, error)
|
||||||
|
path() dbus.ObjectPath
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Service is a general SecretService interface, sort of handler for Dbus - it's used for fetching a Session, Collections, etc.
|
Service is a general SecretService interface, sort of handler for Dbus - it's used for fetching a Session, Collections, etc.
|
||||||
https://developer-old.gnome.org/libsecret/0.18/SecretService.html
|
https://developer-old.gnome.org/libsecret/0.18/SecretService.html
|
||||||
@@ -67,6 +72,8 @@ type Service struct {
|
|||||||
*DbusObject
|
*DbusObject
|
||||||
// Session is a default Session initiated automatically.
|
// Session is a default Session initiated automatically.
|
||||||
Session *Session `json:"-"`
|
Session *Session `json:"-"`
|
||||||
|
// IsLocked indicates if the Service is locked or not. Status updated by Service.Locked.
|
||||||
|
IsLocked bool `json:"locked"`
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -82,24 +89,31 @@ type Session struct {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Collection is an accessor for libsecret collections, which contain multiple Secret Item items.
|
Collection is an accessor for libsecret collections, which contain multiple Secret Item items.
|
||||||
|
Do not change any of these values directly; use the associated methods instead.
|
||||||
Reference:
|
Reference:
|
||||||
https://developer-old.gnome.org/libsecret/0.18/SecretCollection.html
|
https://developer-old.gnome.org/libsecret/0.18/SecretCollection.html
|
||||||
https://specifications.freedesktop.org/secret-service/latest/ch03.html
|
https://specifications.freedesktop.org/secret-service/latest/ch03.html
|
||||||
*/
|
*/
|
||||||
type Collection struct {
|
type Collection struct {
|
||||||
*DbusObject
|
*DbusObject
|
||||||
// lastModified is unexported because it's important that API users don't change it; it's used by Collection.Modified.
|
// IsLocked indicates if the Collection is locked or not. Status updated by Collection.Locked.
|
||||||
lastModified time.Time
|
IsLocked bool `json:"locked"`
|
||||||
|
// LabelName is the Collection's label (as given by Collection.Label and modified by Collection.Relabel).
|
||||||
|
LabelName string `json:"label"`
|
||||||
|
// CreatedAt is when this Collection was created (used by Collection.Created).
|
||||||
|
CreatedAt time.Time `json:"created"`
|
||||||
|
// LastModified is when this Item was last changed; it's used by Collection.Modified.
|
||||||
|
LastModified time.Time `json:"modified"`
|
||||||
|
// Alias is the Collection's alias (as handled by Service.ReadAlias and Service.SetAlias).
|
||||||
|
Alias string `json:"alias"`
|
||||||
// lastModifiedSet is unexported; it's only used to determine if this is a first-initialization of the modification time or not.
|
// lastModifiedSet is unexported; it's only used to determine if this is a first-initialization of the modification time or not.
|
||||||
lastModifiedSet bool
|
lastModifiedSet bool
|
||||||
// name is used for the Collection's name/label so the Dbus path doesn't need to be parsed all the time.
|
|
||||||
name string
|
|
||||||
// service tracks the Service this Collection was created from.
|
// service tracks the Service this Collection was created from.
|
||||||
service *Service
|
service *Service
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Item is an entry in a Collection that contains a Secret.
|
Item is an entry in a Collection that contains a Secret. Do not change any of these values directly; use the associated methods instead.
|
||||||
https://developer-old.gnome.org/libsecret/0.18/SecretItem.html
|
https://developer-old.gnome.org/libsecret/0.18/SecretItem.html
|
||||||
https://specifications.freedesktop.org/secret-service/latest/re03.html
|
https://specifications.freedesktop.org/secret-service/latest/re03.html
|
||||||
*/
|
*/
|
||||||
@@ -107,8 +121,18 @@ type Item struct {
|
|||||||
*DbusObject
|
*DbusObject
|
||||||
// Secret is the corresponding Secret object.
|
// Secret is the corresponding Secret object.
|
||||||
Secret *Secret `json:"secret"`
|
Secret *Secret `json:"secret"`
|
||||||
// lastModified is unexported because it's important that API users don't change it; it's used by Collection.Modified.
|
// IsLocked indicates if the Item is locked or not. Status updated by Item.Locked.
|
||||||
lastModified time.Time
|
IsLocked bool
|
||||||
|
// Attrs are the Item's attributes (as would be returned via Item.Attributes).
|
||||||
|
Attrs map[string]string `json:"attributes"`
|
||||||
|
// LabelName is the Item's label (as given by Item.Label and modified by Item.Relabel).
|
||||||
|
LabelName string `json:"label"`
|
||||||
|
// SecretType is the Item's secret type (as returned by Item.Type).
|
||||||
|
SecretType string `json:"type"`
|
||||||
|
// CreatedAt is when this Item was created (used by Item.Created).
|
||||||
|
CreatedAt time.Time `json:"created"`
|
||||||
|
// LastModified is when this Item was last changed; it's used by Item.Modified.
|
||||||
|
LastModified time.Time `json:"modified"`
|
||||||
// lastModifiedSet is unexported; it's only used to determine if this is a first-initialization of the modification time or not.
|
// lastModifiedSet is unexported; it's only used to determine if this is a first-initialization of the modification time or not.
|
||||||
lastModifiedSet bool
|
lastModifiedSet bool
|
||||||
/*
|
/*
|
||||||
@@ -145,5 +169,5 @@ type Secret struct {
|
|||||||
session *Session
|
session *Session
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecretValue is a custom type that handles JSON encoding/decoding a little more easily.
|
// SecretValue is a custom type that handles JSON encoding a little more easily.
|
||||||
type SecretValue []byte
|
type SecretValue []byte
|
||||||
|
|||||||
Reference in New Issue
Block a user