checking in - all basic funcs in place; add a few more then v1 merge
This commit is contained in:
parent
1d093627f6
commit
0fc0e0c269
55
README.adoc
55
README.adoc
@ -59,16 +59,17 @@ To reflect the absolute breaking changes, the module name changes as well from `
|
||||
|
||||
=== Status
|
||||
|
||||
The new API is underway, and all functionality in V0 is present. However, It's not "complete". https://github.com/johnnybubonic/gosecret/pulls[PRs^] welcome, of course, but this will be an ongoing effort for a bit of time.
|
||||
The new API is underway, and all functionality in V0 is present. However, it's not "complete". https://github.com/johnnybubonic/gosecret/pulls[PRs^] welcome, of course, but this will be an ongoing effort for a bit of time.
|
||||
|
||||
== SecretService Concepts
|
||||
|
||||
For reference:
|
||||
|
||||
* A *`Service`* allows one to operate on/with *`Session`* objects.
|
||||
* A *`Session`* allows one to operate on/with `*Collection*` objects.
|
||||
* A `*Collection*` allows one to operate on/with `*Item*` objects.
|
||||
* An `*Item*` allows one to operate on/with `*Secrets*`.
|
||||
* A `*Service*` allows one to retrieve and operate on/with `*Session*` and `*Collection*` objects.
|
||||
* A `*Session*` allows one to operate on/with `*Item*` objects (e.g. parsing/decoding/decrypting them).
|
||||
* A `*Collection*` allows one to retrieve and operate on/with `*Item*` objects.
|
||||
* An `*Item*` allows one to retrieve and operate on/with `*Secret*` objects.
|
||||
|
||||
(`*Secrets*` are considered "terminating objects" in this model, and contain
|
||||
actual secret value(s) and metadata).
|
||||
|
||||
@ -79,35 +80,21 @@ So the object hierarchy in *theory* looks kind of like this:
|
||||
----
|
||||
Service
|
||||
├─ Session "A"
|
||||
│ ├─ Collection "A.1"
|
||||
│ │ ├─ Item "A.1.a"
|
||||
│ │ │ ├─ Secret "A_1_a_I"
|
||||
│ │ │ └─ Secret "A_1_a_II"
|
||||
│ │ └─ Item "A.1.b"
|
||||
│ │ ├─ Secret "A_1_b_I"
|
||||
│ │ └─ Secret "A_1_b_II"
|
||||
│ └─ Collection "A.2"
|
||||
│ ├─ Item "A.2.a"
|
||||
│ │ ├─ Secret "A_2_a_I"
|
||||
│ │ └─ Secret "A_2_a_II"
|
||||
│ └─ Item "A.2.b"
|
||||
│ ├─ Secret "A_2_b_I"
|
||||
│ └─ Secret "A_2_b_II"
|
||||
└─ Session "B"
|
||||
├─ Collection "B.1"
|
||||
│ ├─ Item "B.1.a"
|
||||
│ │ ├─ Secret "B_1_a_I"
|
||||
│ │ └─ Secret "B_1_a_II"
|
||||
│ └─ Item "B.1.b"
|
||||
│ ├─ Secret "B_1_b_I"
|
||||
│ └─ Secret "B_1_b_II"
|
||||
└─ Collection "B.2"#
|
||||
├─ Item "B.2.a"
|
||||
│ ├─ Secret "B_2_a_I"
|
||||
│ └─ Secret "B_2_a_II"
|
||||
└─ Item "B.2.b"
|
||||
├─ Secret "B_2_b_I"
|
||||
└─ Secret "B_2_b_II"
|
||||
├─ Session "B"
|
||||
├─ Collection "A"
|
||||
│ ├─ Item "A.1"
|
||||
│ │ ├─ Secret "A_1_a"
|
||||
│ │ └─ Secret "A_1_b"
|
||||
│ └─ Item "A.2"
|
||||
│ ├─ Secret "A_2_a"
|
||||
│ └─ Secret "A_2_b"
|
||||
└─ Collection "B"
|
||||
├─ Item "B.1"
|
||||
│ ├─ Secret "B_1_a"
|
||||
│ └─ Secret "B_1_b"
|
||||
└─ Item "B.2"
|
||||
├─ Secret "B_2_a"
|
||||
└─ Secret "B_2_b"
|
||||
----
|
||||
|
||||
And so on.
|
||||
|
10
TODO
Normal file
10
TODO
Normal file
@ -0,0 +1,10 @@
|
||||
- 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/
|
||||
- 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)
|
@ -4,9 +4,11 @@ import (
|
||||
`strings`
|
||||
"time"
|
||||
|
||||
`github.com/godbus/dbus`
|
||||
`github.com/godbus/dbus/v5`
|
||||
)
|
||||
|
||||
// TODO: add method Relabel
|
||||
|
||||
/*
|
||||
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.
|
||||
@ -28,10 +30,11 @@ func NewCollection(service *Service, path dbus.ObjectPath) (coll *Collection, er
|
||||
Conn: service.Conn,
|
||||
Dbus: service.Conn.Object(DbusService, path),
|
||||
},
|
||||
service: service,
|
||||
// lastModified: time.Now(),
|
||||
}
|
||||
|
||||
splitPath = strings.Split(string(coll.Dbus.Path()), "")
|
||||
splitPath = strings.Split(string(coll.Dbus.Path()), "/")
|
||||
|
||||
coll.name = splitPath[len(splitPath)-1]
|
||||
|
||||
@ -58,7 +61,6 @@ func (c *Collection) Items() (items []*Item, err error) {
|
||||
|
||||
for idx, path := range paths {
|
||||
if item, err = NewItem(c, path); err != nil {
|
||||
// return
|
||||
errs = append(errs, err)
|
||||
err = nil
|
||||
continue
|
||||
@ -120,22 +122,20 @@ func (c *Collection) SearchItems(profile string) (items []*Item, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// CreateItem returns a pointer to an Item based on a label, a Secret, and whether any existing secret with the same label should be replaced or not.
|
||||
func (c *Collection) CreateItem(label string, secret *Secret, replace bool) (item *Item, err error) {
|
||||
// CreateItem returns a pointer to an Item based on a label, some attributes, a Secret, and whether any existing secret with the same label should be replaced or not.
|
||||
func (c *Collection) CreateItem(label string, attrs map[string]string, secret *Secret, replace bool) (item *Item, err error) {
|
||||
|
||||
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 attrs map[string]string = make(map[string]string)
|
||||
|
||||
attrs["profile"] = label
|
||||
props["org.freedesktop.Secret.Item.Label"] = dbus.MakeVariant(label)
|
||||
props["org.freedesktop.Secret.Item.Attributes"] = dbus.MakeVariant(attrs)
|
||||
props[DbusItemLabel] = dbus.MakeVariant(label)
|
||||
props[DbusItemAttributes] = dbus.MakeVariant(attrs)
|
||||
|
||||
if err = c.Dbus.Call(
|
||||
"org.freedesktop.Secret.Collection.CreateItem", 0, props, secret, replace,
|
||||
DbusCollectionCreateItem, 0, props, secret, replace,
|
||||
).Store(&path, &promptPath); err != nil {
|
||||
return
|
||||
}
|
||||
@ -181,6 +181,10 @@ func (c *Collection) Label() (label string, err error) {
|
||||
|
||||
label = variant.Value().(string)
|
||||
|
||||
if label != c.name {
|
||||
c.name = label
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
24
consts.go
24
consts.go
@ -1,11 +1,23 @@
|
||||
package gosecret
|
||||
|
||||
// Constants for use with gosecret.
|
||||
const (
|
||||
/*
|
||||
ExplicitAttrEmptyValue is the constant used in Item.ModifyAttributes to explicitly set a value as empty.
|
||||
Between the surrounding with %'s, the weird name that includes "gosecret", and the UUID4...
|
||||
I am fairly confident this is unique enough.
|
||||
*/
|
||||
ExplicitAttrEmptyValue string = "%EXPLICIT_GOSECRET_BLANK_VALUE_8A4E3D7D-F30E-4754-8C56-9C172D1400F6%"
|
||||
)
|
||||
|
||||
// Libsecret/SecretService Dbus interfaces.
|
||||
const (
|
||||
// DbusService is the Dbus service bus identifier.
|
||||
DbusService string = "org.freedesktop.secrets"
|
||||
// DbusServiceBase is the base identifier used by interfaces.
|
||||
DbusServiceBase string = "org.freedesktop.Secret"
|
||||
// DbusPrompterInterface is an interface for issuing a Prompt. Yes, it should be doubled up like that.
|
||||
DbusPrompterInterface string = DbusServiceBase + ".Prompt.Prompt"
|
||||
)
|
||||
|
||||
// Service interface.
|
||||
@ -37,10 +49,10 @@ const (
|
||||
// DbusServiceLockService is [FUNCTION UNKNOWN/UNDOCUMENTED; TODO? NOT IMPLEMENTED.]
|
||||
DbusServiceLockService string = DbusInterfaceService + ".LockService"
|
||||
|
||||
// DbusServiceOpenSession is used by Service.Open.
|
||||
// DbusServiceOpenSession is used by Service.OpenSession.
|
||||
DbusServiceOpenSession string = DbusInterfaceService + ".OpenSession"
|
||||
|
||||
// DbusServiceReadAlias is used by Service.GetAlias to return a Collection based on its aliased name.
|
||||
// DbusServiceReadAlias is used by Service.ReadAlias to return a Collection based on its aliased name.
|
||||
DbusServiceReadAlias string = DbusInterfaceService + ".ReadAlias"
|
||||
|
||||
// DbusServiceSearchItems is used by Service.SearchItems to get arrays of locked and unlocked Item objects.
|
||||
@ -135,13 +147,13 @@ const (
|
||||
// DbusItemLocked is a Dbus boolean for Item.Locked.
|
||||
DbusItemLocked string = DbusInterfaceItem + ".Locked"
|
||||
|
||||
// DbusItemAttributes contains attributes (metadata, schema, etc.) for Item.Attributes.
|
||||
// DbusItemAttributes contains attributes (metadata, schema, etc.) for Item.Attrs.
|
||||
DbusItemAttributes string = DbusInterfaceItem + ".Attributes"
|
||||
|
||||
// DbusItemLabel is the name (label) for Item.Label.
|
||||
DbusItemLabel string = DbusInterfaceItem + ".Label"
|
||||
|
||||
// DbusItemType is the type of an Item (Item.Type).
|
||||
// DbusItemType is the type of Item (Item.ItemType).
|
||||
DbusItemType string = DbusInterfaceItem + ".Type"
|
||||
|
||||
// DbusItemCreated is the time an Item was created (in a UNIX Epoch uint64) for Item.Created.
|
||||
@ -159,6 +171,8 @@ const (
|
||||
DbusPromptPrefix string = DbusPath + "/prompt/"
|
||||
// DbusNewCollectionPath is used to create a new Collection.
|
||||
DbusNewCollectionPath string = DbusPath + "/collection/"
|
||||
// DbusNewSessionPath is used to create a new Session.
|
||||
DbusNewSessionPath string = DbusPath + "/session/"
|
||||
)
|
||||
|
||||
// FLAGS
|
||||
@ -166,7 +180,7 @@ const (
|
||||
|
||||
// SERVICE
|
||||
|
||||
// ServiceInitFlag is a flag for Service.Open.
|
||||
// ServiceInitFlag is a flag for Service.OpenSession.
|
||||
type ServiceInitFlag int
|
||||
|
||||
const (
|
||||
|
54
doc.go
54
doc.go
@ -43,13 +43,13 @@ SecretService Concepts
|
||||
|
||||
For reference:
|
||||
|
||||
- A Service allows one to operate on/with Session objects.
|
||||
- A Service allows one to retrieve and operate on/with Session and Collection objects.
|
||||
|
||||
- A Session allows one to operate on/with Collection objects.
|
||||
- A Session allows one to operate on/with Item objects (e.g. parsing/decoding/decrypting them).
|
||||
|
||||
- A Collection allows one to operate on/with Item objects.
|
||||
- A Collection allows one to retrieve and operate on/with Item objects.
|
||||
|
||||
- An Item allows one to operate on/with Secrets.
|
||||
- An Item allows one to retrieve and operate on/with Secret objects.
|
||||
|
||||
(Secrets are considered "terminating objects" in this model, and contain actual secret value(s) and metadata).
|
||||
|
||||
@ -59,38 +59,24 @@ So the object hierarchy in THEORY looks kind of like this:
|
||||
|
||||
Service
|
||||
├─ Session "A"
|
||||
│ ├─ Collection "A.1"
|
||||
│ │ ├─ Item "A.1.a"
|
||||
│ │ │ ├─ Secret "A_1_a_I"
|
||||
│ │ │ └─ Secret "A_1_a_II"
|
||||
│ │ └─ Item "A.1.b"
|
||||
│ │ ├─ Secret "A_1_b_I"
|
||||
│ │ └─ Secret "A_1_b_II"
|
||||
│ └─ Collection "A.2"
|
||||
│ ├─ Item "A.2.a"
|
||||
│ │ ├─ Secret "A_2_a_I"
|
||||
│ │ └─ Secret "A_2_a_II"
|
||||
│ └─ Item "A.2.b"
|
||||
│ ├─ Secret "A_2_b_I"
|
||||
│ └─ Secret "A_2_b_II"
|
||||
└─ Session "B"
|
||||
├─ Collection "B.1"
|
||||
│ ├─ Item "B.1.a"
|
||||
│ │ ├─ Secret "B_1_a_I"
|
||||
│ │ └─ Secret "B_1_a_II"
|
||||
│ └─ Item "B.1.b"
|
||||
│ ├─ Secret "B_1_b_I"
|
||||
│ └─ Secret "B_1_b_II"
|
||||
└─ Collection "B.2"#
|
||||
├─ Item "B.2.a"
|
||||
│ ├─ Secret "B_2_a_I"
|
||||
│ └─ Secret "B_2_a_II"
|
||||
└─ Item "B.2.b"
|
||||
├─ Secret "B_2_b_I"
|
||||
└─ Secret "B_2_b_II"
|
||||
├─ Session "B"
|
||||
├─ Collection "A"
|
||||
│ ├─ Item "A.1"
|
||||
│ │ ├─ Secret "A_1_a"
|
||||
│ │ └─ Secret "A_1_b"
|
||||
│ └─ Item "A.2"
|
||||
│ ├─ Secret "A_2_a"
|
||||
│ └─ Secret "A_2_b"
|
||||
└─ Collection "B"
|
||||
├─ Item "B.1"
|
||||
│ ├─ Secret "B_1_a"
|
||||
│ └─ Secret "B_1_b"
|
||||
└─ Item "B.2"
|
||||
├─ Secret "B_2_a"
|
||||
└─ Secret "B_2_b"
|
||||
|
||||
And so on.
|
||||
In PRACTICE, however, most users will only have two Session types
|
||||
In PRACTICE, however, most users will only have two Session items
|
||||
(a default "system" one and a temporary one that may or may not exist, running in memory for the current login session)
|
||||
and a single Collection, named "login" (and aliased to "default", usually).
|
||||
|
||||
|
2
funcs.go
2
funcs.go
@ -3,7 +3,7 @@ package gosecret
|
||||
import (
|
||||
`strings`
|
||||
|
||||
`github.com/godbus/dbus`
|
||||
`github.com/godbus/dbus/v5`
|
||||
)
|
||||
|
||||
// isPrompt returns a boolean that is true if path is/requires a prompt(ed path) and false if it is/does not.
|
||||
|
2
go.mod
2
go.mod
@ -2,4 +2,4 @@ module r00t2.io/gosecret
|
||||
|
||||
go 1.17
|
||||
|
||||
require github.com/godbus/dbus v4.1.0+incompatible
|
||||
require github.com/godbus/dbus/v5 v5.0.6
|
||||
|
4
go.sum
4
go.sum
@ -1,2 +1,2 @@
|
||||
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
|
||||
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
|
||||
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
|
234
item_funcs.go
234
item_funcs.go
@ -1,12 +1,20 @@
|
||||
package gosecret
|
||||
|
||||
import (
|
||||
`github.com/godbus/dbus`
|
||||
`strconv`
|
||||
`strings`
|
||||
`time`
|
||||
|
||||
`github.com/godbus/dbus/v5`
|
||||
)
|
||||
|
||||
// TODO: add method Relabel
|
||||
|
||||
// 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) {
|
||||
|
||||
var splitPath []string
|
||||
|
||||
if collection == nil {
|
||||
err = ErrNoDbusConn
|
||||
}
|
||||
@ -16,12 +24,84 @@ func NewItem(collection *Collection, path dbus.ObjectPath) (item *Item, err erro
|
||||
}
|
||||
|
||||
item = &Item{
|
||||
&DbusObject{
|
||||
DbusObject: &DbusObject{
|
||||
Conn: collection.Conn,
|
||||
Dbus: collection.Conn.Object(DbusService, path),
|
||||
},
|
||||
}
|
||||
|
||||
splitPath = strings.Split(string(item.Dbus.Path()), "/")
|
||||
|
||||
item.idx, err = strconv.Atoi(splitPath[len(splitPath)-1])
|
||||
item.collection = collection
|
||||
if _, err = item.Attributes(); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = item.Type(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, _, err = item.Modified()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Attributes updates the Item.Attrs from Dbus (and returns them).
|
||||
func (i *Item) Attributes() (attrs map[string]string, err error) {
|
||||
|
||||
var variant dbus.Variant
|
||||
|
||||
if variant, err = i.Dbus.GetProperty(DbusItemAttributes); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
i.Attrs = variant.Value().(map[string]string)
|
||||
attrs = i.Attrs
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Delete removes an Item from a Collection.
|
||||
func (i *Item) Delete() (err error) {
|
||||
|
||||
var promptPath dbus.ObjectPath
|
||||
var prompt *Prompt
|
||||
|
||||
if err = i.Dbus.Call(DbusItemDelete, 0).Store(&promptPath); err != nil {
|
||||
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) {
|
||||
|
||||
if session == nil {
|
||||
err = ErrNoDbusConn
|
||||
}
|
||||
|
||||
if _, err = connIsValid(session.Conn); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = i.Dbus.Call(
|
||||
DbusItemGetSecret, 0, session.Dbus.Path(),
|
||||
).Store(&secret); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
secret.session = session
|
||||
secret.item = i
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -30,7 +110,7 @@ func (i *Item) Label() (label string, err error) {
|
||||
|
||||
var variant dbus.Variant
|
||||
|
||||
if variant, err = i.Dbus.GetProperty("org.freedesktop.Secret.Item.Label"); err != nil {
|
||||
if variant, err = i.Dbus.GetProperty(DbusItemLabel); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -39,12 +119,109 @@ func (i *Item) Label() (label string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Locked indicates that an Item is locked (true) or unlocked (false).
|
||||
/*
|
||||
ModifyAttributes modifies the Item.Attrs, both in the object and in Dbus.
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
err = i.ReplaceAttributes(currentProps)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ReplaceAttributes replaces the Item.Attrs, both in the object and in Dbus.
|
||||
func (i *Item) ReplaceAttributes(newAttrs map[string]string) (err error) {
|
||||
|
||||
var label string
|
||||
var props map[string]dbus.Variant = make(map[string]dbus.Variant, 0)
|
||||
|
||||
if label, err = i.Label(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
props[DbusItemLabel] = dbus.MakeVariant(label)
|
||||
props[DbusItemAttributes] = dbus.MakeVariant(newAttrs)
|
||||
|
||||
if err = i.Dbus.SetProperty(DbusItemAttributes, props); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
i.Attrs = newAttrs
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SetSecret sets the Secret for an Item.
|
||||
func (i *Item) SetSecret(secret *Secret) (err error) {
|
||||
|
||||
var c *dbus.Call
|
||||
|
||||
c = i.Dbus.Call(
|
||||
DbusItemSetSecret, 0,
|
||||
)
|
||||
if c.Err != nil {
|
||||
err = c.Err
|
||||
return
|
||||
}
|
||||
|
||||
i.Secret = secret
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Type updates the Item.ItemType from DBus (and returns it).
|
||||
func (i *Item) Type() (itemType string, err error) {
|
||||
|
||||
var variant dbus.Variant
|
||||
|
||||
if variant, err = i.Dbus.GetProperty(DbusItemType); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
i.ItemType = variant.Value().(string)
|
||||
itemType = i.ItemType
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Locked indicates if an Item is locked (true) or unlocked (false).
|
||||
func (i *Item) Locked() (isLocked bool, err error) {
|
||||
|
||||
var variant dbus.Variant
|
||||
|
||||
if variant, err = i.Dbus.GetProperty("org.freedesktop.Secret.Item.Locked"); err != nil {
|
||||
if variant, err = i.Dbus.GetProperty(DbusItemLocked); err != nil {
|
||||
isLocked = true
|
||||
return
|
||||
}
|
||||
@ -54,37 +231,52 @@ func (i *Item) Locked() (isLocked bool, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// GetSecret returns the Secret in an Item using a Session.
|
||||
func (i *Item) GetSecret(session *Session) (secret *Secret, err error) {
|
||||
// Created returns the time.Time of when an Item was created.
|
||||
func (i *Item) Created() (created time.Time, err error) {
|
||||
|
||||
secret = new(Secret)
|
||||
var variant dbus.Variant
|
||||
var timeInt uint64
|
||||
|
||||
if err = i.Dbus.Call(
|
||||
"org.freedesktop.Secret.Item.GetSecret", 0, session.Path(),
|
||||
).Store(&secret); err != nil {
|
||||
if variant, err = i.Dbus.GetProperty(DbusItemCreated); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
timeInt = variant.Value().(uint64)
|
||||
|
||||
created = time.Unix(int64(timeInt), 0)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Delete removes an Item from a Collection.
|
||||
func (i *Item) Delete() (err error) {
|
||||
/*
|
||||
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.
|
||||
|
||||
var prompt *Prompt
|
||||
var promptPath dbus.ObjectPath
|
||||
Note that when calling NewItem, the internal library-tracked modification
|
||||
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.
|
||||
*/
|
||||
func (i *Item) Modified() (modified time.Time, isChanged bool, err error) {
|
||||
|
||||
if err = i.Dbus.Call("org.freedesktop.Secret.Item.Delete", 0).Store(&promptPath); err != nil {
|
||||
var variant dbus.Variant
|
||||
var timeInt uint64
|
||||
|
||||
if variant, err = i.Dbus.GetProperty(DbusItemModified); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if isPrompt(promptPath) {
|
||||
prompt = NewPrompt(i.Conn, promptPath)
|
||||
timeInt = variant.Value().(uint64)
|
||||
|
||||
if _, err = prompt.Prompt(); err != nil {
|
||||
return
|
||||
}
|
||||
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.
|
||||
i.lastModified = modified
|
||||
i.lastModifiedSet = true
|
||||
}
|
||||
|
||||
isChanged = modified.After(i.lastModified)
|
||||
i.lastModified = modified
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -1,29 +1,22 @@
|
||||
package gosecret
|
||||
|
||||
import (
|
||||
`github.com/godbus/dbus`
|
||||
`github.com/godbus/dbus/v5`
|
||||
)
|
||||
|
||||
// NewPrompt returns a pointer to a new Prompt based on a Dbus connection and a Dbus path.
|
||||
func NewPrompt(conn *dbus.Conn, path dbus.ObjectPath) (prompt *Prompt) {
|
||||
|
||||
prompt = &Prompt{
|
||||
DbusObject: &DbusObject{
|
||||
Conn: conn,
|
||||
Dbus: conn.Object(DbusService, path),
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Path returns the path of the underlying Dbus connection.
|
||||
func (p Prompt) Path() (path dbus.ObjectPath) {
|
||||
|
||||
// Remove this method in V1. It's bloat since we now have an exported Dbus.
|
||||
path = p.Dbus.Path()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Prompt issues/waits for a prompt for unlocking a Locked Collection or Secret / Item.
|
||||
func (p *Prompt) Prompt() (promptValue *dbus.Variant, err error) {
|
||||
|
||||
@ -37,12 +30,14 @@ func (p *Prompt) Prompt() (promptValue *dbus.Variant, err error) {
|
||||
p.Conn.Signal(c)
|
||||
defer p.Conn.RemoveSignal(c)
|
||||
|
||||
if err = p.Dbus.Call("org.freedesktop.Secret.Prompt.Prompt", 0, "").Store(); err != nil {
|
||||
if err = p.Dbus.Call(
|
||||
DbusPrompterInterface, 0, "", // TODO: This last argument, the string, is for "window ID". I'm unclear what for.
|
||||
).Store(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
if result = <-c; result.Path == p.Path() {
|
||||
if result = <-c; result.Path == p.Dbus.Path() {
|
||||
*promptValue = result.Body[1].(dbus.Variant)
|
||||
return
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ package gosecret
|
||||
func NewSecret(session *Session, params []byte, value []byte, contentType string) (secret *Secret) {
|
||||
|
||||
secret = &Secret{
|
||||
Session: session.Path(),
|
||||
Session: session.Dbus.Path(),
|
||||
Parameters: params,
|
||||
Value: value,
|
||||
ContentType: contentType,
|
||||
|
13
secretvalue_funcs.go
Normal file
13
secretvalue_funcs.go
Normal file
@ -0,0 +1,13 @@
|
||||
package gosecret
|
||||
|
||||
/*
|
||||
MarshalJSON converts a SecretValue to a JSON representation.
|
||||
For compat reasons, the MarshalText is left "unmolested" (i.e. renders to a Base64 value).
|
||||
I don't bother with an UnmarshalJSON because it makes exactly 0 sense to unmarshal due to runtime and unexported fields in Secret.
|
||||
*/
|
||||
func (s *SecretValue) MarshalJSON() (b []byte, err error) {
|
||||
|
||||
b = []byte(string(*s))
|
||||
|
||||
return
|
||||
}
|
222
service_funcs.go
222
service_funcs.go
@ -1,9 +1,16 @@
|
||||
package gosecret
|
||||
|
||||
import (
|
||||
"github.com/godbus/dbus"
|
||||
`errors`
|
||||
`fmt`
|
||||
`path/filepath`
|
||||
`strings`
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
// TODO: Lock method (DbusServiceLockService)?
|
||||
|
||||
// NewService returns a pointer to a new Service connection.
|
||||
func NewService() (service *Service, err error) {
|
||||
|
||||
@ -14,7 +21,7 @@ func NewService() (service *Service, err error) {
|
||||
}
|
||||
svc.Dbus = service.Conn.Object(DbusService, dbus.ObjectPath(DbusPath))
|
||||
|
||||
if svc.Session, _, err = svc.Open(); err != nil {
|
||||
if svc.Session, err = svc.GetSession(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -108,40 +115,6 @@ func (s *Service) CreateCollection(label string) (collection *Collection, err er
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
GetAlias allows one to fetch a Collection based on an alias name.
|
||||
An ErrDoesNotExist will be raised if the alias does not exist.
|
||||
You will almost assuredly want to use Service.GetCollection instead; it works for both alias names and real names.
|
||||
*/
|
||||
func (s *Service) GetAlias(alias string) (collection *Collection, err error) {
|
||||
|
||||
var objectPath dbus.ObjectPath
|
||||
|
||||
err = s.Dbus.Call(
|
||||
DbusServiceReadAlias, 0, alias,
|
||||
).Store(&objectPath)
|
||||
|
||||
/*
|
||||
TODO: Confirm that a nonexistent alias will NOT cause an error to return.
|
||||
If it does, alter the below logic.
|
||||
*/
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// If the alias does not exist, objectPath will be dbus.ObjectPath("/").
|
||||
if objectPath == dbus.ObjectPath("/") {
|
||||
err = ErrDoesNotExist
|
||||
return
|
||||
}
|
||||
|
||||
if collection, err = NewCollection(s, objectPath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
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.
|
||||
@ -151,7 +124,7 @@ func (s *Service) GetCollection(name string) (c *Collection, err error) {
|
||||
var colls []*Collection
|
||||
|
||||
// First check for an alias.
|
||||
if c, err = s.GetAlias(name); err != nil && err != ErrDoesNotExist{
|
||||
if c, err = s.ReadAlias(name); err != nil && err != ErrDoesNotExist {
|
||||
return
|
||||
}
|
||||
if c != nil {
|
||||
@ -192,16 +165,6 @@ func (s *Service) GetSecrets(itemPaths ...dbus.ObjectPath) (secrets map[dbus.Obj
|
||||
secrets = make(map[dbus.ObjectPath]*Secret, len(itemPaths))
|
||||
|
||||
// TODO: trigger a Service.Unlock for any locked items?
|
||||
/*
|
||||
// TODO: make any errs in here a MultiError instead.
|
||||
for _, secretPath := range itemPaths {
|
||||
if err = s.Dbus.Call(
|
||||
DbusServiceGetSecrets, 0, secretPath,
|
||||
).Store(&result); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
*/
|
||||
if err = s.Dbus.Call(
|
||||
DbusServiceGetSecrets, 0, itemPaths,
|
||||
).Store(&secrets); err != nil {
|
||||
@ -211,6 +174,17 @@ func (s *Service) GetSecrets(itemPaths ...dbus.ObjectPath) (secrets map[dbus.Obj
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
GetSession returns a single Session.
|
||||
It's a helper function that wraps Service.OpenSession.
|
||||
*/
|
||||
func (s *Service) GetSession() (ssn *Session, err error) {
|
||||
|
||||
ssn, _, err = s.OpenSession("", "")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Lock locks an Unlocked Service, Collection, etc.
|
||||
You can usually get objectPath for the object(s) to unlock via <object>.Dbus.Path().
|
||||
@ -218,6 +192,8 @@ func (s *Service) GetSecrets(itemPaths ...dbus.ObjectPath) (secrets map[dbus.Obj
|
||||
*/
|
||||
func (s *Service) Lock(objectPaths ...dbus.ObjectPath) (err error) {
|
||||
|
||||
var errs []error = make([]error, 0)
|
||||
// We only use these as destinations.
|
||||
var locked []dbus.ObjectPath
|
||||
var prompt *Prompt
|
||||
var resultPath dbus.ObjectPath
|
||||
@ -226,12 +202,13 @@ func (s *Service) Lock(objectPaths ...dbus.ObjectPath) (err error) {
|
||||
objectPaths = []dbus.ObjectPath{s.Dbus.Path()}
|
||||
}
|
||||
|
||||
// TODO: make any errs in here a MultiError instead.
|
||||
for _, p := range objectPaths {
|
||||
if err = s.Dbus.Call(
|
||||
DbusServiceLock, 0, p,
|
||||
).Store(&locked, &resultPath); err != nil {
|
||||
return
|
||||
errs = append(errs, err)
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
|
||||
if isPrompt(resultPath) {
|
||||
@ -239,28 +216,44 @@ func (s *Service) Lock(objectPaths ...dbus.ObjectPath) (err error) {
|
||||
prompt = NewPrompt(s.Conn, resultPath)
|
||||
|
||||
if _, err = prompt.Prompt(); err != nil {
|
||||
return
|
||||
errs = append(errs, err)
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if errs != nil && len(errs) > 0 {
|
||||
err = NewErrors(errs...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Open 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.
|
||||
*/
|
||||
func (s *Service) Open() (session *Session, output dbus.Variant, err error) {
|
||||
func (s *Service) OpenSession(algo, input string) (session *Session, output dbus.Variant, err error) {
|
||||
|
||||
var path dbus.ObjectPath
|
||||
var algoVariant dbus.Variant
|
||||
var inputVariant dbus.Variant
|
||||
|
||||
if strings.TrimSpace(algo) == "" {
|
||||
algoVariant = dbus.MakeVariant("plain")
|
||||
} else {
|
||||
algoVariant = dbus.MakeVariant(algo)
|
||||
}
|
||||
|
||||
inputVariant = dbus.MakeVariant(input)
|
||||
|
||||
// In *theory*, SecretService supports multiple "algorithms" for encryption in-transit, but I don't think it's implemented (yet)?
|
||||
// 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 err = s.Dbus.Call(
|
||||
DbusServiceOpenSession, 0, "plain", dbus.MakeVariant(""),
|
||||
DbusServiceOpenSession, 0, algoVariant, inputVariant,
|
||||
).Store(&output, &path); err != nil {
|
||||
return
|
||||
}
|
||||
@ -271,10 +264,52 @@ func (s *Service) Open() (session *Session, output dbus.Variant, err error) {
|
||||
}
|
||||
|
||||
/*
|
||||
SearchItems searches all Collection objects and returns all matches based on the map of attributes.
|
||||
TODO: return arrays of Items instead of dbus.ObjectPaths.
|
||||
ReadAlias allows one to fetch a Collection based on an alias name.
|
||||
An ErrDoesNotExist will be raised if the alias does not exist.
|
||||
You will almost assuredly want to use Service.GetCollection instead; it works for both alias names and real names.
|
||||
*/
|
||||
func (s *Service) SearchItems(attributes map[string]string) (unlockedItems []dbus.ObjectPath, lockedItems []dbus.ObjectPath, err error) {
|
||||
func (s *Service) ReadAlias(alias string) (collection *Collection, err error) {
|
||||
|
||||
var objectPath dbus.ObjectPath
|
||||
|
||||
err = s.Dbus.Call(
|
||||
DbusServiceReadAlias, 0, alias,
|
||||
).Store(&objectPath)
|
||||
|
||||
/*
|
||||
TODO: Confirm that a nonexistent alias will NOT cause an error to return.
|
||||
If it does, alter the below logic.
|
||||
*/
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// If the alias does not exist, objectPath will be dbus.ObjectPath("/").
|
||||
if objectPath == dbus.ObjectPath("/") {
|
||||
err = ErrDoesNotExist
|
||||
return
|
||||
}
|
||||
|
||||
if collection, err = NewCollection(s, objectPath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SearchItems searches all Collection objects and returns all matches based on the map of attributes.
|
||||
*/
|
||||
func (s *Service) SearchItems(attributes map[string]string) (unlockedItems []*Item, lockedItems []*Item, err error) {
|
||||
|
||||
var locked []dbus.ObjectPath
|
||||
var unlocked []dbus.ObjectPath
|
||||
var collectionObjs []*Collection
|
||||
var collections map[dbus.ObjectPath]*Collection = make(map[dbus.ObjectPath]*Collection, 0)
|
||||
var ok bool
|
||||
var c *Collection
|
||||
var cPath dbus.ObjectPath
|
||||
var errs []error = make([]error, 0)
|
||||
|
||||
if attributes == nil || len(attributes) == 0 {
|
||||
err = ErrMissingAttrs
|
||||
@ -283,7 +318,66 @@ func (s *Service) SearchItems(attributes map[string]string) (unlockedItems []dbu
|
||||
|
||||
err = s.Dbus.Call(
|
||||
DbusServiceSearchItems, 0, attributes,
|
||||
).Store(&unlockedItems, &lockedItems)
|
||||
).Store(&unlocked, &locked)
|
||||
|
||||
lockedItems = make([]*Item, len(locked))
|
||||
unlockedItems = make([]*Item, len(unlocked))
|
||||
|
||||
if collectionObjs, err = s.Collections(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, c = range collectionObjs {
|
||||
if _, ok = collections[c.Dbus.Path()]; !ok {
|
||||
collections[c.Dbus.Path()] = c
|
||||
}
|
||||
}
|
||||
|
||||
// Locked items
|
||||
for idx, i := range locked {
|
||||
|
||||
cPath = dbus.ObjectPath(filepath.Dir(string(i)))
|
||||
|
||||
if c, ok = collections[cPath]; !ok {
|
||||
errs = append(errs, errors.New(fmt.Sprintf(
|
||||
"could not find matching Collection for locked item %v", string(i),
|
||||
)))
|
||||
continue
|
||||
}
|
||||
|
||||
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),
|
||||
)))
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Unlocked items
|
||||
for idx, i := range unlocked {
|
||||
|
||||
cPath = dbus.ObjectPath(filepath.Dir(string(i)))
|
||||
|
||||
if c, ok = collections[cPath]; !ok {
|
||||
errs = append(errs, errors.New(fmt.Sprintf(
|
||||
"could not find matching Collection for unlocked item %v", string(i),
|
||||
)))
|
||||
continue
|
||||
}
|
||||
|
||||
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),
|
||||
)))
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if errs != nil && len(errs) > 0 {
|
||||
err = NewErrors(errs...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@ -312,6 +406,7 @@ func (s *Service) SetAlias(alias string, objectPath dbus.ObjectPath) (err error)
|
||||
*/
|
||||
func (s *Service) Unlock(objectPaths ...dbus.ObjectPath) (err error) {
|
||||
|
||||
var errs []error = make([]error, 0)
|
||||
var unlocked []dbus.ObjectPath
|
||||
var prompt *Prompt
|
||||
var resultPath dbus.ObjectPath
|
||||
@ -320,12 +415,13 @@ func (s *Service) Unlock(objectPaths ...dbus.ObjectPath) (err error) {
|
||||
objectPaths = []dbus.ObjectPath{s.Dbus.Path()}
|
||||
}
|
||||
|
||||
// TODO: make any errs in here a MultiError instead.
|
||||
for _, p := range objectPaths {
|
||||
if err = s.Dbus.Call(
|
||||
DbusServiceUnlock, 0, p,
|
||||
).Store(&unlocked, &resultPath); err != nil {
|
||||
return
|
||||
errs = append(errs, err)
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
|
||||
if isPrompt(resultPath) {
|
||||
@ -333,10 +429,16 @@ func (s *Service) Unlock(objectPaths ...dbus.ObjectPath) (err error) {
|
||||
prompt = NewPrompt(s.Conn, resultPath)
|
||||
|
||||
if _, err = prompt.Prompt(); err != nil {
|
||||
return
|
||||
errs = append(errs, err)
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if errs != nil && len(errs) > 0 {
|
||||
err = NewErrors(errs...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -1,21 +1,22 @@
|
||||
package gosecret
|
||||
|
||||
import (
|
||||
"github.com/godbus/dbus"
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
// I'm still not 100% certain what Sessions are used for?
|
||||
// I'm still not 100% certain what Sessions are used for, aside from getting Secrets from Items.
|
||||
|
||||
/*
|
||||
NewSession returns a pointer to a new Session based on a Service and a dbus.ObjectPath.
|
||||
If path is empty (""), the default
|
||||
You will almost always want to use Service.GetSession or Service.OpenSession instead.
|
||||
*/
|
||||
func NewSession(service *Service, path dbus.ObjectPath) (session *Session) {
|
||||
|
||||
var ssn Session = Session{
|
||||
&DbusObject{
|
||||
DbusObject: &DbusObject{
|
||||
Conn: service.Conn,
|
||||
},
|
||||
service: service,
|
||||
}
|
||||
session.Dbus = session.Conn.Object(DbusInterfaceSession, path)
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
package gosecret
|
||||
|
||||
// This currently is not used.
|
||||
|
||||
/*
|
||||
TranslateError translates a SecretServiceErrEnum into a SecretServiceError.
|
||||
If a matching error was found, ok will be true and err will be the matching SecretServiceError.
|
||||
@ -20,6 +22,7 @@ func TranslateError(ssErr SecretServiceErrEnum) (ok bool, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Error returns the string format of the error; this is necessary to be considered a valid error interface.
|
||||
func (e SecretServiceError) Error() (errStr string) {
|
||||
|
||||
errStr = e.ErrDesc
|
||||
|
53
types.go
53
types.go
@ -3,9 +3,11 @@ package gosecret
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/godbus/dbus"
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
// TODO: add label fields to Collection and Item, make their respective Label methods update the field.
|
||||
|
||||
/*
|
||||
MultiError is a type of error.Error that can contain multiple error.Errors. Confused? Don't worry about it.
|
||||
*/
|
||||
@ -65,6 +67,7 @@ type Prompt struct {
|
||||
*/
|
||||
type Service struct {
|
||||
*DbusObject
|
||||
// Session is a default Session initiated automatically.
|
||||
Session *Session `json:"-"`
|
||||
}
|
||||
|
||||
@ -75,6 +78,8 @@ type Service struct {
|
||||
*/
|
||||
type Session struct {
|
||||
*DbusObject
|
||||
// collection tracks the Service this Session was created from.
|
||||
service *Service
|
||||
}
|
||||
|
||||
/*
|
||||
@ -89,8 +94,10 @@ type Collection struct {
|
||||
lastModified time.Time
|
||||
// lastModifiedSet is unexported; it's only used to determine if this is a first-initialization of the modification time or not.
|
||||
lastModifiedSet bool
|
||||
// name is used for the Collection's name/label so the Dnus path doesn't need to be parsed all the time.
|
||||
// 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 *Service
|
||||
}
|
||||
|
||||
/*
|
||||
@ -100,6 +107,30 @@ type Collection struct {
|
||||
*/
|
||||
type Item struct {
|
||||
*DbusObject
|
||||
/*
|
||||
Attrs are the attributes to assign to this Item.
|
||||
They should be considered non-secret; they're primarily used to *look up* an Item.
|
||||
*Do NOT put secret/sensitive data in an Item's Attrs!*
|
||||
*/
|
||||
Attrs map[string]string `json:"attributes"`
|
||||
// Secret is the corresponding Secret object.
|
||||
Secret *Secret `json:"secret"`
|
||||
/*
|
||||
ItemType is the type of this Item as a Dbus interface name.
|
||||
e.g. org.gnome.keyring.NetworkPassword, org.freedesktop.Secret.Generic, org.remmina.Password, etc.
|
||||
*/
|
||||
ItemType string `json:"dbus_type"`
|
||||
// lastModified is unexported because it's important that API users don't change it; it's used by Collection.Modified.
|
||||
lastModified time.Time
|
||||
// lastModifiedSet is unexported; it's only used to determine if this is a first-initialization of the modification time or not.
|
||||
lastModifiedSet bool
|
||||
/*
|
||||
idx is the index identifier of the Item.
|
||||
It SHOULD correlate to indices in Collection.Items, but don't rely on this.
|
||||
*/
|
||||
idx int
|
||||
// collection tracks the Collection this Item is in.
|
||||
collection *Collection
|
||||
}
|
||||
|
||||
/*
|
||||
@ -109,12 +140,22 @@ type Item struct {
|
||||
https://specifications.freedesktop.org/secret-service/latest/ch14.html#type-Secret
|
||||
*/
|
||||
type Secret struct {
|
||||
// Session is a Dbus object path for the associated Session.
|
||||
Session dbus.ObjectPath `json:"-"`
|
||||
// Parameters are "algorithm dependent parameters for secret value encoding" - likely this will just be an empty byteslice.
|
||||
// Session is a Dbus object path for the associated Session (the actual Session is stored in an unexported field).
|
||||
Session dbus.ObjectPath `json:"session_path"`
|
||||
/*
|
||||
Parameters are "algorithm dependent parameters for secret value encoding" - likely this will just be an empty byteslice.
|
||||
Refer to Session for more information.
|
||||
*/
|
||||
Parameters []byte `json:"params"`
|
||||
// Value is the secret's content in []byte format.
|
||||
Value []byte `json:"value"`
|
||||
Value SecretValue `json:"value"`
|
||||
// ContentType is the MIME type of Value.
|
||||
ContentType string `json:"content_type"`
|
||||
// item is the Item this Secret belongs to.
|
||||
item *Item
|
||||
// session is the Session used to decode/decrypt this Secret.
|
||||
session *Session
|
||||
}
|
||||
|
||||
// SecretValue is a custom type that handles JSON encoding/decoding a little more easily.
|
||||
type SecretValue []byte
|
||||
|
Loading…
Reference in New Issue
Block a user