2021-11-21 23:05:13 -05:00
|
|
|
package gosecret
|
2021-11-21 18:07:52 -05:00
|
|
|
|
|
|
|
import (
|
2021-12-14 04:36:53 -05:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
2021-12-06 03:24:55 -05:00
|
|
|
|
2021-12-14 04:36:53 -05:00
|
|
|
"github.com/godbus/dbus/v5"
|
2021-11-21 18:07:52 -05:00
|
|
|
)
|
|
|
|
|
2021-12-04 19:38:26 -05:00
|
|
|
// NewItem returns a pointer to an Item based on Collection and a Dbus path.
|
|
|
|
func NewItem(collection *Collection, path dbus.ObjectPath) (item *Item, err error) {
|
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
var splitPath []string
|
|
|
|
|
2021-12-04 19:38:26 -05:00
|
|
|
if collection == nil {
|
|
|
|
err = ErrNoDbusConn
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err = validConnPath(collection.Conn, path); err != nil {
|
|
|
|
return
|
|
|
|
}
|
2021-11-21 18:07:52 -05:00
|
|
|
|
|
|
|
item = &Item{
|
2021-12-06 03:24:55 -05:00
|
|
|
DbusObject: &DbusObject{
|
2021-12-04 19:38:26 -05:00
|
|
|
Conn: collection.Conn,
|
|
|
|
Dbus: collection.Conn.Object(DbusService, path),
|
|
|
|
},
|
2021-11-21 18:07:52 -05:00
|
|
|
}
|
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
splitPath = strings.Split(string(item.Dbus.Path()), "/")
|
|
|
|
|
|
|
|
item.idx, err = strconv.Atoi(splitPath[len(splitPath)-1])
|
|
|
|
item.collection = collection
|
|
|
|
|
2021-12-13 04:04:03 -05:00
|
|
|
// 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 = item.GetSecret(collection.service.Session); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, err = item.Locked(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, err = item.Attributes(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, err = item.Label(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, err = item.Type(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, err = item.Created(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, _, err = item.Modified(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
2021-12-06 03:24:55 -05:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-07 02:56:15 -05:00
|
|
|
// Attributes returns the Item's attributes from Dbus.
|
2021-12-06 03:24:55 -05:00
|
|
|
func (i *Item) Attributes() (attrs map[string]string, err error) {
|
|
|
|
|
|
|
|
var variant dbus.Variant
|
|
|
|
|
|
|
|
if variant, err = i.Dbus.GetProperty(DbusItemAttributes); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-07 02:56:15 -05:00
|
|
|
attrs = variant.Value().(map[string]string)
|
2021-12-13 04:04:03 -05:00
|
|
|
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
|
|
|
|
|
2022-01-09 17:11:50 -05:00
|
|
|
// Legacy spec.
|
|
|
|
if i.collection.service.Legacy {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-13 04:04:03 -05:00
|
|
|
if strings.TrimSpace(newItemType) == "" {
|
|
|
|
newItemType = DbusDefaultItemType
|
|
|
|
}
|
|
|
|
|
|
|
|
variant = dbus.MakeVariant(newItemType)
|
|
|
|
|
|
|
|
if err = i.Dbus.SetProperty(DbusItemType, variant); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
i.SecretType = newItemType
|
|
|
|
|
2021-12-13 05:34:53 -05:00
|
|
|
if _, _, err = i.Modified(); err != nil {
|
2021-12-13 04:04:03 -05:00
|
|
|
return
|
|
|
|
}
|
2021-12-06 03:24:55 -05:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete removes an Item from a Collection.
|
|
|
|
func (i *Item) Delete() (err error) {
|
|
|
|
|
2021-12-25 01:51:49 -05:00
|
|
|
var call *dbus.Call
|
2021-12-06 03:24:55 -05:00
|
|
|
var promptPath dbus.ObjectPath
|
|
|
|
var prompt *Prompt
|
|
|
|
|
2021-12-25 01:51:49 -05:00
|
|
|
if call = i.Dbus.Call(
|
|
|
|
DbusItemDelete, 0,
|
|
|
|
); call.Err != nil {
|
|
|
|
err = call.Err
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err = call.Store(&promptPath); err != nil {
|
2021-12-06 03:24:55 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if isPrompt(promptPath) {
|
|
|
|
|
|
|
|
prompt = NewPrompt(i.Conn, promptPath)
|
|
|
|
if _, err = prompt.Prompt(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSecret returns the Secret in an Item using a Session.
|
|
|
|
func (i *Item) GetSecret(session *Session) (secret *Secret, err error) {
|
|
|
|
|
2021-12-25 01:51:49 -05:00
|
|
|
var call *dbus.Call
|
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
if session == nil {
|
|
|
|
err = ErrNoDbusConn
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err = connIsValid(session.Conn); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-25 01:51:49 -05:00
|
|
|
if call = i.Dbus.Call(
|
2021-12-06 03:24:55 -05:00
|
|
|
DbusItemGetSecret, 0, session.Dbus.Path(),
|
2021-12-25 01:51:49 -05:00
|
|
|
); call.Err != nil {
|
|
|
|
err = call.Err
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err = call.Store(&secret); err != nil {
|
2021-12-06 03:24:55 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
secret.session = session
|
|
|
|
secret.item = i
|
2021-12-13 04:04:03 -05:00
|
|
|
i.Secret = secret
|
2021-12-06 03:24:55 -05:00
|
|
|
|
2021-11-21 18:07:52 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Label returns the label ("name") of an Item.
|
|
|
|
func (i *Item) Label() (label string, err error) {
|
|
|
|
|
|
|
|
var variant dbus.Variant
|
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
if variant, err = i.Dbus.GetProperty(DbusItemLabel); err != nil {
|
2021-11-21 18:07:52 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
label = variant.Value().(string)
|
2021-12-14 04:36:53 -05:00
|
|
|
i.LabelName = label
|
2021-11-21 18:07:52 -05:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
/*
|
2021-12-07 02:56:15 -05:00
|
|
|
ModifyAttributes modifies the Item's attributes in Dbus.
|
2021-12-06 03:24:55 -05:00
|
|
|
This is similar to Item.ReplaceAttributes but will only modify the map's given keys so you do not need to provide
|
|
|
|
the entire attribute map.
|
|
|
|
If you wish to remove an attribute, use the value "" (empty string).
|
|
|
|
If you wish to explicitly provide a blank value/empty string, use the constant gosecret.ExplicitAttrEmptyValue.
|
|
|
|
|
|
|
|
This is more or less a convenience/wrapper function around Item.ReplaceAttributes.
|
|
|
|
*/
|
|
|
|
func (i *Item) ModifyAttributes(replaceAttrs map[string]string) (err error) {
|
|
|
|
|
|
|
|
var ok bool
|
|
|
|
var currentProps map[string]string = make(map[string]string, 0)
|
|
|
|
var currentVal string
|
|
|
|
|
|
|
|
if replaceAttrs == nil || len(replaceAttrs) == 0 {
|
|
|
|
err = ErrMissingAttrs
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if currentProps, err = i.Attributes(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range replaceAttrs {
|
|
|
|
if currentVal, ok = currentProps[k]; !ok { // If it isn't in the replacement map, do nothing (i.e. keep it).
|
|
|
|
continue
|
|
|
|
} else if v == currentVal { // If the value is the same, do nothing.
|
|
|
|
continue
|
|
|
|
} else if v == ExplicitAttrEmptyValue { // If it's the "magic empty value" constant, delete the key/value pair.
|
|
|
|
delete(currentProps, k)
|
|
|
|
continue
|
|
|
|
} else { // Otherwise, replace the value.
|
|
|
|
currentProps[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-13 04:04:03 -05:00
|
|
|
if err = i.ReplaceAttributes(currentProps); err != nil {
|
|
|
|
return
|
|
|
|
}
|
2021-12-06 03:24:55 -05:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-07 02:56:15 -05:00
|
|
|
// Relabel modifies the Item's label in Dbus.
|
|
|
|
func (i *Item) Relabel(newLabel string) (err error) {
|
|
|
|
|
|
|
|
var variant dbus.Variant = dbus.MakeVariant(newLabel)
|
|
|
|
|
|
|
|
if err = i.Dbus.SetProperty(DbusItemLabel, variant); err != nil {
|
|
|
|
return
|
|
|
|
}
|
2021-12-13 04:04:03 -05:00
|
|
|
i.LabelName = newLabel
|
|
|
|
|
2021-12-13 05:34:53 -05:00
|
|
|
if _, _, err = i.Modified(); err != nil {
|
2021-12-13 04:04:03 -05:00
|
|
|
return
|
|
|
|
}
|
2021-12-07 02:56:15 -05:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReplaceAttributes replaces the Item's attributes in Dbus.
|
2021-12-06 03:24:55 -05:00
|
|
|
func (i *Item) ReplaceAttributes(newAttrs map[string]string) (err error) {
|
|
|
|
|
2021-12-13 00:15:38 -05:00
|
|
|
var props dbus.Variant
|
2021-12-06 03:24:55 -05:00
|
|
|
|
2021-12-13 00:15:38 -05:00
|
|
|
props = dbus.MakeVariant(newAttrs)
|
2021-12-06 03:24:55 -05:00
|
|
|
|
|
|
|
if err = i.Dbus.SetProperty(DbusItemAttributes, props); err != nil {
|
|
|
|
return
|
|
|
|
}
|
2021-12-13 04:04:03 -05:00
|
|
|
i.Attrs = newAttrs
|
|
|
|
|
2021-12-13 05:34:53 -05:00
|
|
|
if _, _, err = i.Modified(); err != nil {
|
2021-12-13 04:04:03 -05:00
|
|
|
return
|
|
|
|
}
|
2021-12-06 03:24:55 -05:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetSecret sets the Secret for an Item.
|
|
|
|
func (i *Item) SetSecret(secret *Secret) (err error) {
|
|
|
|
|
2021-12-25 01:51:49 -05:00
|
|
|
var call *dbus.Call
|
2021-12-06 03:24:55 -05:00
|
|
|
|
2021-12-25 01:51:49 -05:00
|
|
|
if call = i.Dbus.Call(
|
2021-12-06 03:24:55 -05:00
|
|
|
DbusItemSetSecret, 0,
|
2021-12-25 01:51:49 -05:00
|
|
|
); call.Err != nil {
|
|
|
|
err = call.Err
|
2021-12-06 03:24:55 -05:00
|
|
|
return
|
|
|
|
}
|
2021-12-25 01:51:49 -05:00
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
i.Secret = secret
|
|
|
|
|
2021-12-13 05:34:53 -05:00
|
|
|
if _, _, err = i.Modified(); err != nil {
|
2021-12-13 04:04:03 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Type updates the Item.ItemType from DBus (and returns it).
|
|
|
|
func (i *Item) Type() (itemType string, err error) {
|
|
|
|
|
|
|
|
var variant dbus.Variant
|
|
|
|
|
2022-01-09 00:56:56 -05:00
|
|
|
// Legacy spec.
|
|
|
|
if i.collection.service.Legacy {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
if variant, err = i.Dbus.GetProperty(DbusItemType); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-13 00:15:38 -05:00
|
|
|
itemType = variant.Value().(string)
|
2021-12-13 04:04:03 -05:00
|
|
|
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
|
2021-12-06 03:24:55 -05:00
|
|
|
|
2021-12-13 05:34:53 -05:00
|
|
|
if _, _, err = i.Modified(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Locked indicates if an Item is locked (true) or unlocked (false).
|
2021-11-21 18:07:52 -05:00
|
|
|
func (i *Item) Locked() (isLocked bool, err error) {
|
|
|
|
|
|
|
|
var variant dbus.Variant
|
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
if variant, err = i.Dbus.GetProperty(DbusItemLocked); err != nil {
|
2021-11-21 18:07:52 -05:00
|
|
|
isLocked = true
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
isLocked = variant.Value().(bool)
|
2021-12-13 04:04:03 -05:00
|
|
|
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
|
2021-11-21 18:07:52 -05:00
|
|
|
|
2021-12-13 05:34:53 -05:00
|
|
|
if _, _, err = i.Modified(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-11-21 18:07:52 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
// Created returns the time.Time of when an Item was created.
|
|
|
|
func (i *Item) Created() (created time.Time, err error) {
|
2021-11-21 18:07:52 -05:00
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
var variant dbus.Variant
|
|
|
|
var timeInt uint64
|
2021-11-21 18:07:52 -05:00
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
if variant, err = i.Dbus.GetProperty(DbusItemCreated); err != nil {
|
2021-11-21 18:07:52 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
timeInt = variant.Value().(uint64)
|
|
|
|
|
|
|
|
created = time.Unix(int64(timeInt), 0)
|
2021-12-13 04:04:03 -05:00
|
|
|
i.CreatedAt = created
|
2021-12-06 03:24:55 -05:00
|
|
|
|
2021-11-21 18:07:52 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
/*
|
|
|
|
Modified returns the time.Time of when an Item was last modified along with a boolean
|
|
|
|
that indicates if the collection has changed since the last call of Item.Modified.
|
2021-11-21 18:07:52 -05:00
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
Note that when calling NewItem, the internal library-tracked modification
|
2021-12-13 04:04:03 -05:00
|
|
|
time (Item.LastModified) will be set to the latest modification time of the Item
|
2021-12-06 03:24:55 -05:00
|
|
|
itself as reported by Dbus rather than the time that NewItem was called.
|
|
|
|
*/
|
|
|
|
func (i *Item) Modified() (modified time.Time, isChanged bool, err error) {
|
|
|
|
|
|
|
|
var variant dbus.Variant
|
|
|
|
var timeInt uint64
|
2021-11-21 18:07:52 -05:00
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
if variant, err = i.Dbus.GetProperty(DbusItemModified); err != nil {
|
2021-11-21 18:07:52 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
timeInt = variant.Value().(uint64)
|
2021-11-21 18:07:52 -05:00
|
|
|
|
2021-12-06 03:24:55 -05:00
|
|
|
modified = time.Unix(int64(timeInt), 0)
|
|
|
|
|
|
|
|
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.
|
2021-12-13 04:04:03 -05:00
|
|
|
i.LastModified = modified
|
2021-12-06 03:24:55 -05:00
|
|
|
i.lastModifiedSet = true
|
2021-11-21 18:07:52 -05:00
|
|
|
}
|
|
|
|
|
2021-12-13 04:04:03 -05:00
|
|
|
isChanged = modified.After(i.LastModified)
|
|
|
|
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()
|
2021-12-06 03:24:55 -05:00
|
|
|
|
2021-11-21 18:07:52 -05:00
|
|
|
return
|
|
|
|
}
|