gosecret/collection_funcs.go

373 lines
8.6 KiB
Go
Raw Normal View History

package gosecret
import (
"time"
2021-12-13 00:40:11 -05:00
"github.com/godbus/dbus/v5"
)
/*
NewCollection returns a pointer to a Collection based on a Service and a Dbus path.
You will almost always want to use Service.GetCollection instead.
*/
func NewCollection(service *Service, path dbus.ObjectPath) (coll *Collection, err error) {
if service == nil {
2021-11-21 23:12:25 -05:00
err = ErrNoDbusConn
}
if _, err = validConnPath(service.Conn, path); err != nil {
2021-11-21 23:12:25 -05:00
return
}
coll = &Collection{
DbusObject: &DbusObject{
Conn: service.Conn,
Dbus: service.Conn.Object(DbusService, path),
},
service: service,
// LastModified: time.Now(),
}
// Populate the struct fields...
2021-12-13 04:33:43 -05:00
// TODO: use channel for errors; condense into a MultiError and switch to goroutines.
if _, err = coll.Locked(); err != nil {
return
}
if _, err = coll.Label(); err != nil {
return
}
if _, err = coll.Created(); err != nil {
return
}
if _, _, err = coll.Modified(); err != nil {
return
}
return
}
/*
CreateItem returns a pointer to an Item based on a label, some attributes, a Secret,
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).
2021-12-13 00:40:11 -05:00
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) {
2021-12-12 02:29:29 -05:00
var prompt *Prompt
var path dbus.ObjectPath
var promptPath dbus.ObjectPath
var variant *dbus.Variant
var props map[string]dbus.Variant = make(map[string]dbus.Variant)
var typeString string
if itemType != nil && len(itemType) > 0 {
typeString = itemType[0]
} else {
typeString = DbusDefaultItemType
}
2021-12-12 02:29:29 -05:00
props[DbusItemLabel] = dbus.MakeVariant(label)
props[DbusItemType] = dbus.MakeVariant(typeString)
2021-12-12 02:29:29 -05:00
props[DbusItemAttributes] = dbus.MakeVariant(attrs)
props[DbusItemCreated] = dbus.MakeVariant(uint64(time.Now().Unix()))
// props[DbusItemModified] = dbus.MakeVariant(uint64(time.Now().Unix()))
2021-12-12 02:29:29 -05:00
if err = c.Dbus.Call(
DbusCollectionCreateItem, 0, props, secret, replace,
).Store(&path, &promptPath); err != nil {
return
}
2021-12-12 02:29:29 -05:00
if isPrompt(promptPath) {
prompt = NewPrompt(c.Conn, promptPath)
2021-12-12 02:29:29 -05:00
if variant, err = prompt.Prompt(); err != nil {
return
}
2021-12-12 02:29:29 -05:00
path = variant.Value().(dbus.ObjectPath)
}
2021-12-12 02:29:29 -05:00
item, err = NewItem(c, path)
return
}
2021-12-12 02:29:29 -05:00
/*
Delete removes a Collection.
While *technically* not necessary, it is recommended that you iterate through
Collection.Items and do an Item.Delete for each item *before* calling Collection.Delete;
the item paths are cached as "orphaned paths" in Dbus otherwise if not deleted before deleting
their Collection. They should clear on a reboot or restart of Dbus (but rebooting Dbus on a system in use is... troublesome).
*/
func (c *Collection) Delete() (err error) {
var promptPath dbus.ObjectPath
var prompt *Prompt
2021-11-21 23:12:25 -05:00
if err = c.Dbus.Call(DbusCollectionDelete, 0).Store(&promptPath); err != nil {
return
}
if isPrompt(promptPath) {
prompt = NewPrompt(c.Conn, promptPath)
if _, err = prompt.Prompt(); err != nil {
return
}
}
return
}
2021-12-12 02:29:29 -05:00
// Items returns a slice of Item pointers in the Collection.
func (c *Collection) Items() (items []*Item, err error) {
var paths []dbus.ObjectPath
2021-12-12 02:29:29 -05:00
var item *Item
var variant dbus.Variant
var errs []error = make([]error, 0)
2021-12-12 02:29:29 -05:00
if variant, err = c.Dbus.GetProperty(DbusCollectionItems); err != nil {
return
}
2021-12-12 02:29:29 -05:00
paths = variant.Value().([]dbus.ObjectPath)
items = make([]*Item, len(paths))
for idx, path := range paths {
2021-12-12 02:29:29 -05:00
if item, err = NewItem(c, path); err != nil {
errs = append(errs, err)
err = nil
continue
}
2021-12-12 02:29:29 -05:00
items[idx] = item
}
err = NewErrors(err)
return
}
2021-12-12 02:29:29 -05:00
// Label returns the Collection label (name).
func (c *Collection) Label() (label string, err error) {
2021-12-12 02:29:29 -05:00
var variant dbus.Variant
2021-12-12 02:29:29 -05:00
if variant, err = c.Dbus.GetProperty(DbusCollectionLabel); err != nil {
return
}
2021-12-12 02:29:29 -05:00
label = variant.Value().(string)
c.LabelName = 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
}
// Locked indicates if a Collection is locked (true) or unlocked (false).
func (c *Collection) Locked() (isLocked bool, err error) {
var variant dbus.Variant
if variant, err = c.Dbus.GetProperty(DbusCollectionLocked); err != nil {
isLocked = true
return
}
isLocked = variant.Value().(bool)
c.IsLocked = isLocked
return
}
2021-12-12 02:29:29 -05:00
// Relabel modifies the Collection's label in Dbus.
func (c *Collection) Relabel(newLabel string) (err error) {
2021-12-12 02:29:29 -05:00
var variant dbus.Variant = dbus.MakeVariant(newLabel)
2021-12-12 02:29:29 -05:00
if err = c.Dbus.SetProperty(DbusCollectionLabel, variant); err != nil {
return
}
c.LabelName = newLabel
if _, _, err = c.Modified(); err != nil {
return
}
return
}
2021-12-12 02:29:29 -05:00
/*
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.
2021-12-12 02:29:29 -05:00
I promise it's not useful for any other implementation/storage of SecretService whatsoever.
Deprecated: Use Service.SearchItems instead.
2021-12-12 02:29:29 -05:00
*/
func (c *Collection) SearchItems(profile string) (items []*Item, err error) {
2021-12-12 02:29:29 -05:00
var paths []dbus.ObjectPath
var errs []error = make([]error, 0)
var attrs map[string]string = make(map[string]string, 0)
attrs["profile"] = profile
2021-12-12 02:29:29 -05:00
if err = c.Dbus.Call(
DbusCollectionSearchItems, 0, attrs,
).Store(&paths); err != nil {
return
}
2021-12-12 02:29:29 -05:00
items = make([]*Item, len(paths))
for idx, path := range paths {
if items[idx], err = NewItem(c, path); err != nil {
errs = append(errs, err)
err = nil
continue
}
}
err = NewErrors(err)
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.
func (c *Collection) Created() (created time.Time, err error) {
var variant dbus.Variant
var timeInt uint64
if variant, err = c.Dbus.GetProperty(DbusCollectionCreated); err != nil {
return
}
timeInt = variant.Value().(uint64)
created = time.Unix(int64(timeInt), 0)
c.CreatedAt = created
return
}
/*
Modified returns the time.Time of when a Collection was last modified along with a boolean
that indicates if the collection has changed since the last call of Collection.Modified.
Note that when calling NewCollection, the internal library-tracked modification
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.
*/
func (c *Collection) Modified() (modified time.Time, isChanged bool, err error) {
var variant dbus.Variant
var timeInt uint64
if variant, err = c.Dbus.GetProperty(DbusCollectionModified); err != nil {
return
}
timeInt = variant.Value().(uint64)
modified = time.Unix(int64(timeInt), 0)
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.
c.LastModified = modified
c.lastModifiedSet = true
}
isChanged = modified.After(c.LastModified)
c.LastModified = modified
return
}
2021-12-12 02:29:29 -05:00
// path is a *very* thin wrapper around Collection.Dbus.Path(). It is needed for LockableObject interface membership.
func (c *Collection) path() (dbusPath dbus.ObjectPath) {
dbusPath = c.Dbus.Path()
2021-12-12 02:29:29 -05:00
return
}