gosecret

package module
v1.1.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 9, 2022 License: MIT Imports: 8 Imported by: 1

README

= libsecret/gosecret
Brent Saner <bts@square-r00t.net>
:doctype: book
:docinfo: shared
:data-uri:
:imagesdir: images
:sectlinks:
:sectnums:
:sectnumlevels: 7
:toc: preamble
:toc2: left
:idprefix:
:toclevels: 7
:source-highlighter: rouge

image::https://pkg.go.dev/badge/r00t2.io/gosecret.svg[link="https://pkg.go.dev/r00t2.io/gosecret"]

This project is originally forked from https://github.com/gsterjov/go-libsecret[go-libsecret^] due to:

* Lack of response from the developer
* Complete lack of documentation
* Poor, ineffecient, or just plain antipattern design
* Missing functionality

and as such, hopefully this library should serve as a more effective libsecret/SecretService interface.

== Backwards Compatability/Drop-In Replacement Support

Version series `v0.X.X` of this library promises full and non-breaking backwards support of API interaction with the original project. The only changes should be internal optimizations, adding documentation, some file reorganizing, adding Golang module support, etc. -- all transparent from the library API itself.

To use this library as a replacement without significantly modifying your code, you can simply use a `replace` directive:

// TODO: did I do this correctly? I never really use replacements so someone PR if this is incorrect.
.go.mod
[source]
----
// ...
replace (
	github.com/gsterjov/go-libsecret dev => r00t2.io/gosecret v0
)
----

and then run `go mod tidy`.

== New Developer API

Starting from `v1.0.0` onwards, entirely breaking changes can be assumed from the original project.

To use the new version,

[source,go]
----
import (
	`r00t2.io/gosecret/v1`
)
----

To reflect the absolute breaking changes, the module name changes as well from `libsecret` to `gosecret`.

=== 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.

== SecretService Concepts

For reference:

* 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).

Various interactions are handled by `*Prompts*`.

So the object hierarchy in *theory* looks kind of like this:

----
Service
├─ Session "A"
├─ 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 ``Collection``s:

* a default "system" one named `login` (usually unlocked upon login), and
* a temporary one that may or may not exist, running in memory for the current login session named `session`

== Usage

Full documentation can be found via inline documentation. Either via the https://pkg.go.dev/r00t2.io/gosecret[pkg.go.dev documentation^] or https://pkg.go.dev/golang.org/x/tools/cmd/godoc[`godoc`^] (or `go doc`) in the source root.

However, here's a quick demonstration.

[source,go]
----
package main

import (
	`fmt`
	`log`

	// "github.com/johnnybubonic/gosecret" // GitHub mirror
	"r00t2.io/gosecret"                   // real upstream; recommended
)

const (
	// The default collection; it should be available on all SecretService implementations.
	collectionName string = "login"
	// A label for an Item used in examples below.
	exampleLabel   string = "Some Website Credentials"
)

func main() {

	var err error
	var service *gosecret.Service
	var collection *gosecret.Collection
	var item *gosecret.Item
	var itemAttrs map[string]string
	var itemLabel string
	var secret *gosecret.Secret

	// All interactions with SecretService start with initiating a Service connection.
	if service, err = gosecret.NewService(); err != nil {
		log.Panicln(err)
	}
	defer service.Close()

	// And unless operating directly on a Service via its methods, you probably need a Collection as well.
	if collection, err = service.GetCollection(collectionName); err != nil {
		log.Panicln(err)
	}

	/*
		Create a Secret which gets stored in an Item which gets stored in a Collection.
		See the documentation for details.
	*/
	// Incidentally, I believe this is the only exported function/method that does not return an error returner.
	secret = gosecret.NewSecret(
		service.Session,                   // The session associated with this Secret. You're likely fine with the automatically-created *(Service).Session.
		[]byte{},                          // The "parameters". Likely this is an empty byteslice.
		[]byte("a super secret password"), // The actual secret value.
		"text/plain",                      // The content type (MIME type/media type). See https://www.iana.org/assignments/media-types/media-types.xhtml.
	)

	/*
		Item attributes are a map[string]string of *metadata* about a Secret/Item.
		Do *NOT* store sensitive information in these.
		They're primarily used for searching for Items.
	*/
	itemAttrs = map[string]string{
		"Use":      "an example secret",
		"note":     "These keys can be anything you want!",
		"url":      "https://somewebsite.tld/login",
		"username": "user.name",
	}

	// And create the Item (and add it to SecretService).
	if item, err = collection.CreateItem(
		exampleLabel, // The label of the item. This should also be considered not secret.
		itemAttrs,    // Attributes for the item; see above.
		secret,       // The actual secret.
		true,         // Whether to replace an existing item with the same label or not.
	); err != nil {
		log.Panicln(err)
	}

	/*
		Now let's fetch the same Item via its attributes.
		The results are split into locked items and unlocked items.
	*/
	var unlockedItems []*gosecret.Item
	var lockedItems []*gosecret.Item

	if unlockedItems, lockedItems, err = service.SearchItems(itemAttrs); err != nil {
		log.Panicln(err)
	}

	// We should only have one Item that matches the search attributes, and unless the item or collection is locked, ...
	item = unlockedItems[0]
	if itemLabel, err = item.Label(); err != nil {
		log.Panicln(err)
	}
	fmt.Printf("Found item: %v\n", itemLabel)

	// Alternatively if you are unsure of the attributes but know the label of the item you want, you can iterate through them.
	var itemResults []*gosecret.Item

	if itemResults, err = collection.Items(); err != nil {
		log.Panicln(err)
	}

	for idx, i := range itemResults {
		if itemLabel, err = i.Label(); err != nil {
			fmt.Printf("Cannot read label for item at path '%v'\n", i.Dbus.Path())
			continue
		}
		if itemLabel != exampleLabel { // Matching against a desired label - exampleLabel, in this case.
			continue
		}
		fmt.Printf("Found item labeled '%v'! Index number %v at path '%v'\n", itemLabel, idx, i.Dbus.Path())
		fmt.Printf("Password: %v\n", string(i.Secret.Value))
		break
	}
}
----

Note that many functions/methods may return a https://pkg.go.dev/r00t2.io/goutils/multierr#MultiError[`(r00t2.io/goutils/)multierr.MultiError`^], which you may attempt to typeswitch to receive the original errors in their native error format. The functions/methods which may return a MultiError are noted as such in their individual documentation.

== Library Hacking

=== Reference
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

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.

Obviously since this library interacts directly with Dbus (and I don't want to spend the time to mock up an entire Dbus-like interface to test), all tests are integration tests rather than unit tests. Therefore in the event of a failed run, you will need to open e.g. Seahorse or d-feet or some other Dbus/SecretService browser and manually delete the created Secret Service collection. It/they should be easily identified; they use a generated UUID4 string as the collection name and it is highly unlikely that you will see any other collections named as such. If running `go test` with the verbose flag (`-v`), the name and path of the collection will be printed out. If all tests pass, the test collection should be removed automatically.

The same UUID is used for all tests in a test run.

You may be prompted during a test run for a password; you can simply use a blank password for this as it is the password used to protect a collection. This prompt pops up during the creation of a Collection.

Documentation

Overview

Package gosecret is(/was originally) a fork of go-libsecret (see https://github.com/gsterjov/go-libsecret and https://pkg.go.dev/github.com/gsterjov/go-libsecret).

It was forked in order to present bugfixes, actually document the library, conform to more Go-like patterns, and provide missing functionality (as the original seems to be unmaintained). As such, hopefully this library should serve as a more effective libsecret/SecretService interface.

Backwards Compatibility

Version series `v0.X.X` of this library promises full and non-breaking backwards compatibility/drop-in support of API interaction with the original project. The only changes should be internal optimizations, adding documentation, some file reorganizing, adding Golang module support, etc. -- all transparent from the library API itself.

To use this library as a replacement without significantly modifying your code, you can simply use a `replace` directive in your go.mod file:

// ...
replace (
	github.com/gsterjov/go-libsecret dev => r00t2.io/gosecret v0
)

and then run `go mod tidy`.

Do NOT use the master branch. For anything. I make no promises on the stability of that branch at any given time. New features will be added to V1 branch, and stable releases will be tagged. V0 branch is reserved only for optimization and bug fixes.

New Developer API

Starting from `v1.0.0` onwards, entirely breaking changes can be assumed from the original project. To use the new version,

import (
	`r00t2.io/gosecret/v1`
)

To reflect the absolute breaking changes, the module name changes as well from `libsecret` to `gosecret`.

SecretService Concepts

For reference:

- 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).

Various interactions are handled by Prompts.

So the object hierarchy in THEORY looks kind of like this:

Service
├─ Session "A"
├─ 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 Collection items (a default "system" one named "login", which usually is unlocked upon login, and a temporary one that may or may not exist, running in memory for the current login session named `session`).

Usage

Full documentation can be found via inline documentation. Additionally, use either https://pkg.go.dev/r00t2.io/gosecret or https://pkg.go.dev/golang.org/x/tools/cmd/godoc (or `go doc`) in the source root.

Note that many functions/methods may return a (r00t2.io/goutils/)multierr.MultiError (https://pkg.go.dev/r00t2.io/goutils/multierr#MultiError), which you may attempt to typeswitch back to a *multierr.MultiErr to receive the original errors in their native error format (MultiError.Errors). The functions/methods which may return a MultiError are noted as such in their individual documentation.

Index

Constants

View Source
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"
	/*
		DbusDefaultItemType is the default type to use for Item.Type/Collection.CreateItem.
	*/
	DbusDefaultItemType string = DbusServiceBase + ".Generic"
)

Libsecret/SecretService Dbus interfaces.

View Source
const (
	/*
		DbusInterfaceService is the Dbus interface for working with a Service.
		Found at /org/freedesktop/secrets/(DbusInterfaceService)
	*/
	DbusInterfaceService string = DbusServiceBase + ".Service"

	// DbusServiceCreateCollection is used to create a new Collection via Service.CreateCollection.
	DbusServiceCreateCollection string = DbusInterfaceService + ".CreateCollection"

	// DbusServiceGetSecrets is used to fetch multiple Secret values from multiple Item items in a given Collection (via Service.GetSecrets).
	DbusServiceGetSecrets string = DbusInterfaceService + ".GetSecrets"

	// DbusServiceLock is used by Service.Lock.
	DbusServiceLock string = DbusInterfaceService + ".Lock"

	// DbusServiceOpenSession is used by Service.OpenSession.
	DbusServiceOpenSession string = DbusInterfaceService + ".OpenSession"

	// 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.
	DbusServiceSearchItems string = DbusInterfaceService + ".SearchItems"

	// DbusServiceSetAlias is used by Service.SetAlias to set an alias for a Collection.
	DbusServiceSetAlias string = DbusInterfaceService + ".SetAlias"

	// DbusServiceUnlock is used by Service.Unlock.
	DbusServiceUnlock string = DbusInterfaceService + ".Unlock"

	// DbusServiceCollections is used to get a Dbus array of Collection items (Service.Collections).
	DbusServiceCollections string = DbusInterfaceService + ".Collections"
)

Service interface.

View Source
const (
	/*
		DbusInterfaceSession is the Dbus interface for working with a Session.
		Found at /org/freedesktop/secrets/session/<session ID>/(DbusInterfaceSession)
	*/
	DbusInterfaceSession = DbusServiceBase + ".Session"

	// DbusSessionClose is used for Session.Close.
	DbusSessionClose string = DbusInterfaceSession + ".Close"
)

Session interface.

View Source
const (
	/*
		DbusInterfaceCollection is the Dbus interface for working with a Collection.
		Found at /org/freedesktop/secrets/collection/<collection name>/(DbusInterfaceCollection)
	*/
	DbusInterfaceCollection string = DbusServiceBase + ".Collection"

	// DbusCollectionCreateItem is used for Collection.CreateItem.
	DbusCollectionCreateItem string = DbusInterfaceCollection + ".CreateItem"

	// DbusCollectionDelete is used for Collection.Delete.
	DbusCollectionDelete string = DbusInterfaceCollection + ".Delete"

	// DbusCollectionSearchItems is used for Collection.SearchItems.
	DbusCollectionSearchItems string = DbusInterfaceCollection + ".SearchItems"

	// DbusCollectionItems is a Dbus array of Item.
	DbusCollectionItems string = DbusInterfaceCollection + ".Items"

	// DbusCollectionLocked is a Dbus boolean for Collection.Locked.
	DbusCollectionLocked string = DbusInterfaceCollection + ".Locked"

	// DbusCollectionLabel is the name (label) for Collection.Label.
	DbusCollectionLabel string = DbusInterfaceCollection + ".Label"

	// DbusCollectionCreated is the time a Collection was created (in a UNIX Epoch uint64) for Collection.Created.
	DbusCollectionCreated string = DbusInterfaceCollection + ".Created"

	// DbusCollectionModified is the time a Collection was last modified (in a UNIX Epoch uint64) for Collection.Modified.
	DbusCollectionModified string = DbusInterfaceCollection + ".Modified"
)

Collection interface.

View Source
const (
	/*
		DbusInterfaceItem is the Dbus interface for working with Item items.
		Found at /org/freedesktop/secrets/collection/<collection name>/<item index>/(DbusInterfaceItem)
	*/
	DbusInterfaceItem string = DbusServiceBase + ".Item"

	// DbusItemDelete is used by Item.Delete.
	DbusItemDelete string = DbusInterfaceItem + ".Delete"

	// DbusItemGetSecret is used by Item.GetSecret.
	DbusItemGetSecret string = DbusInterfaceItem + ".GetSecret"

	// DbusItemSetSecret is used by Item.SetSecret.
	DbusItemSetSecret string = DbusInterfaceItem + ".SetSecret"

	// DbusItemLocked is a Dbus boolean for Item.Locked.
	DbusItemLocked string = DbusInterfaceItem + ".Locked"

	/*
		DbusItemAttributes contains attributes (metadata, schema, etc.) for
		Item.Attributes, Item.ReplaceAttributes, and Item.ModifyAttributes.
	*/
	DbusItemAttributes string = DbusInterfaceItem + ".Attributes"

	// DbusItemLabel is the name (label) for Item.Label.
	DbusItemLabel string = DbusInterfaceItem + ".Label"

	// 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.
	DbusItemCreated string = DbusInterfaceItem + ".Created"

	// DbusItemModified is the time an Item was last modified (in a UNIX Epoch uint64) for Item.Modified.
	DbusItemModified string = DbusInterfaceItem + ".Modified"
)

Item interface.

View Source
const (
	// DbusPath is the path for DbusService.
	DbusPath string = "/org/freedesktop/secrets"
	// DbusPromptPrefix is the path used for prompts comparison.
	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/"
)

Dbus paths.

View Source
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%"
)

Constants for use with gosecret.

Variables

View Source
var (
	// ErrBadDbusPath indicates an invalid path - either nothing exists at that path or the path is malformed.
	ErrBadDbusPath error = errors.New("invalid dbus path")
	// ErrInvalidProperty indicates a dbus.Variant is not the "real" type expected.
	ErrInvalidProperty error = errors.New("invalid variant type; cannot convert")
	// ErrNoDbusConn gets triggered if a connection to Dbus can't be detected.
	ErrNoDbusConn error = errors.New("no valid dbus connection")
	// 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")
	// 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 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 error = errors.New("the object under that name/label/alias does not exist")
)

General errors.

View Source
var (
	ErrUnknownSecretServiceErr error              = errors.New("cannot find matching SecretService error")
	ErrSecretServiceProto      SecretServiceError = SecretServiceError{
		ErrCode: EnumErrProtocol,
		ErrName: "SECRET_ERROR_PROTOCOL",
		ErrDesc: "an invalid message or data was received from SecretService",
	}
	ErrSecretServiceLocked SecretServiceError = SecretServiceError{
		ErrCode: EnumErrIsLocked,
		ErrName: "SECRET_ERROR_IS_LOCKED",
		ErrDesc: "the item/collection is locked; the specified operation cannot be performed",
	}
	ErrSecretServiceNoObj SecretServiceError = SecretServiceError{
		ErrCode: EnumErrNoSuchObject,
		ErrName: "SECRET_ERROR_NO_SUCH_OBJECT",
		ErrDesc: "no such item/collection was found in SecretService",
	}
	ErrSecretServiceExists SecretServiceError = SecretServiceError{
		ErrCode: EnumErrAlreadyExists,
		ErrName: "SECRET_ERROR_ALREADY_EXISTS",
		ErrDesc: "a relevant item/collection already exists",
	}
	ErrSecretServiceInvalidFormat SecretServiceError = SecretServiceError{
		ErrCode: EnumErrInvalidFileFormat,
		ErrName: "SECRET_ERROR_INVALID_FILE_FORMAT",
		ErrDesc: "the file/content format is invalid",
	}
	/*
		AllSecretServiceErrs provides a slice of these for easier iteration when translating.
		TECHNICALLY, because they are indexed in the order of their enums, you could
		simplify and optimize translation by just doing e.g.

			err = AllSecretServiceErrs[EnumErrProtocol]

		But this should be considered UNSTABLE and UNSAFE due to it being potentially unpredictable in the future.
		There are only 5 errors currently, so the performance benefits would be negligible compared to iteration.
		If SecretService adds more errors, however, this may be more desirable.
	*/
	AllSecretServiceErrs []SecretServiceError = []SecretServiceError{
		ErrSecretServiceProto,
		ErrSecretServiceLocked,
		ErrSecretServiceNoObj,
		ErrSecretServiceExists,
		ErrSecretServiceInvalidFormat,
	}
)

Translated SecretService errors. See https://developer-old.gnome.org/libsecret/unstable/libsecret-SecretError.html#SecretError. Used by TranslateError.

View Source
var (
	// DbusRemoveAliasPath is used to remove an alias from a Collection and/or Item.
	DbusRemoveAliasPath dbus.ObjectPath = dbus.ObjectPath("/")
)

Libsecret/SecretService special values.

Functions

func CheckErrIsFromLegacy added in v1.1.5

func CheckErrIsFromLegacy(err error) (isLegacy, parsed bool)

CheckErrIsFromLegacy takes an error.Error from e.g.:

Service.SearchItems
Collection.CreateItem
NewItem
Item.ChangeItemType
Item.Type

and (in order) attempt to typeswitch to a *multierr.MultiError, then iterate through the *multierr.MultiError.Errors, attempt to typeswitch each of them to a Dbus.Error, and then finally check if it is regarding a missing Type property.

This is *very explicitly* only useful for the above functions/methods. If used anywhere else, it's liable to return an incorrect isLegacy even if parsed == true.

It is admittedly convoluted and obtuse, but this saves a lot of boilerplate for users. It wouldn't be necessary if projects didn't insist on using the legacy draft SecretService specification. But here we are.

isLegacy is true if this Service's API destination is legacy spec. Note that this is checking for very explicit conditions; isLegacy may return false but it is in fact running on a legacy API. Don't rely on this too much.

parsed is true if we found an error type we were able to perform logic of determination on.

func NameFromPath added in v1.1.0

func NameFromPath(path dbus.ObjectPath) (name string, err error)

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 TranslateError added in v1.0.0

func TranslateError(ssErr SecretServiceErrEnum) (ok bool, err error)

TranslateError translates a SecretServiceErrEnum into a SecretServiceError. If a matching error was found, ok will be true and err will be the matching SecretServiceError. If no matching error was found, however, then ok will be false and err will be ErrUnknownSecretServiceErr.

Types

type Collection

type Collection struct {
	*DbusObject
	// IsLocked indicates if the Collection is locked or not. Status updated by Collection.Locked.
	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"`
	// contains filtered or unexported fields
}

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: https://developer-old.gnome.org/libsecret/0.18/SecretCollection.html https://specifications.freedesktop.org/secret-service/latest/ch03.html

func NewCollection

func NewCollection(service *Service, path dbus.ObjectPath) (coll *Collection, err error)

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 (*Collection) CreateItem

func (c *Collection) CreateItem(label string, attrs map[string]string, secret *Secret, replace bool, itemType ...string) (item *Item, err error)

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). 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 (*Collection) Created added in v1.0.0

func (c *Collection) Created() (created time.Time, err error)

Created returns the time.Time of when a Collection was created.

func (*Collection) Delete

func (c *Collection) Delete() (err error)

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 (*Collection) Items

func (c *Collection) Items() (items []*Item, err error)

Items returns a slice of Item pointers in the Collection. err MAY be a *multierr.MultiError.

func (*Collection) Label added in v1.0.0

func (c *Collection) Label() (label string, err error)

Label returns the Collection label (name).

func (*Collection) Lock added in v1.1.0

func (c *Collection) Lock() (err error)

Lock will lock an unlocked Collection. It will no-op if the Collection is currently locked.

func (*Collection) Locked

func (c *Collection) Locked() (isLocked bool, err error)

Locked indicates if a Collection is locked (true) or unlocked (false).

func (*Collection) Modified added in v1.0.0

func (c *Collection) Modified() (modified time.Time, isChanged bool, err error)

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 (*Collection) Relabel added in v1.0.0

func (c *Collection) Relabel(newLabel string) (err error)

Relabel modifies the Collection's label in Dbus.

func (*Collection) SearchItems deprecated

func (c *Collection) SearchItems(profile string) (items []*Item, err error)

SearchItems searches a Collection for a matching "profile" string. It's mostly a carry-over from go-libsecret, and is here for convenience. IT MAY BE REMOVED IN THE FUTURE.

I promise it's not useful for any other implementation/storage of SecretService whatsoever.

err MAY be a *multierr.MultiError.

Deprecated: Use Service.SearchItems instead.

func (*Collection) SetAlias added in v1.1.0

func (c *Collection) SetAlias(alias string) (err error)

SetAlias is a thin wrapper/shorthand for Service.SetAlias (but specific to this Collection).

func (*Collection) Unlock added in v1.1.0

func (c *Collection) Unlock() (err error)

Unlock will unlock a locked Collection. It will no-op if the Collection is currently unlocked.

type CollectionInitFlag added in v1.0.0

type CollectionInitFlag int

CollectionInitFlag is a flag for Collection.SearchItems and Collection.Items.

const (
	FlagCollectionNone CollectionInitFlag = iota
	FlagCollectionLoadItems
)

type ConnPathCheckResult added in v1.0.0

type ConnPathCheckResult struct {
	// ConnOK is true if the dbus.Conn is valid.
	ConnOK bool `json:"conn"`
	// PathOK is true if the Dbus path given is a valid type and value.
	PathOK bool `json:"path"`
}

ConnPathCheckResult contains the result of validConnPath.

type DbusObject added in v1.0.0

type DbusObject struct {
	// Conn is an active connection to the Dbus.
	Conn *dbus.Conn `json:"-"`
	// Dbus is the Dbus bus object.
	Dbus dbus.BusObject `json:"-"`
}

DbusObject is a base struct type to be anonymized by other types.

type Item

type Item struct {
	*DbusObject
	// Secret is the corresponding Secret object.
	Secret *Secret `json:"secret"`
	// IsLocked indicates if the Item is locked or not. Status updated by Item.Locked.
	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"`
	// contains filtered or unexported fields
}

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://specifications.freedesktop.org/secret-service/latest/re03.html

func NewItem

func NewItem(collection *Collection, path dbus.ObjectPath) (item *Item, err error)

NewItem returns a pointer to an Item based on Collection and a Dbus path.

func (*Item) Attributes added in v1.0.0

func (i *Item) Attributes() (attrs map[string]string, err error)

Attributes returns the Item's attributes from Dbus.

func (*Item) ChangeItemType added in v1.1.0

func (i *Item) ChangeItemType(newItemType string) (err error)

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 (*Item) Created added in v1.0.0

func (i *Item) Created() (created time.Time, err error)

Created returns the time.Time of when an Item was created.

func (*Item) Delete

func (i *Item) Delete() (err error)

Delete removes an Item from a Collection.

func (*Item) GetSecret

func (i *Item) GetSecret(session *Session) (secret *Secret, err error)

GetSecret returns the Secret in an Item using a Session.

func (*Item) Label

func (i *Item) Label() (label string, err error)

Label returns the label ("name") of an Item.

func (*Item) Lock added in v1.1.0

func (i *Item) Lock() (err error)

Lock will lock an unlocked Item. It will no-op if the Item is currently locked.

func (*Item) Locked

func (i *Item) Locked() (isLocked bool, err error)

Locked indicates if an Item is locked (true) or unlocked (false).

func (*Item) Modified added in v1.0.0

func (i *Item) Modified() (modified time.Time, isChanged bool, 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.

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 (*Item) ModifyAttributes added in v1.0.0

func (i *Item) ModifyAttributes(replaceAttrs map[string]string) (err error)

ModifyAttributes modifies the Item's attributes 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 (*Item) Relabel added in v1.0.0

func (i *Item) Relabel(newLabel string) (err error)

Relabel modifies the Item's label in Dbus.

func (*Item) ReplaceAttributes added in v1.0.0

func (i *Item) ReplaceAttributes(newAttrs map[string]string) (err error)

ReplaceAttributes replaces the Item's attributes in Dbus.

func (*Item) SetSecret added in v1.0.0

func (i *Item) SetSecret(secret *Secret) (err error)

SetSecret sets the Secret for an Item.

func (*Item) Type added in v1.0.0

func (i *Item) Type() (itemType string, err error)

Type updates the Item.ItemType from DBus (and returns it).

func (*Item) Unlock added in v1.1.0

func (i *Item) Unlock() (err error)

Unlock will unlock a locked Item. It will no-op if the Item is currently unlocked.

type ItemInitFlag added in v1.0.0

type ItemInitFlag int

ItemInitFlag are flags for Collection.SearchItems and Collection.Items.

const (
	FlagItemNone ItemInitFlag = iota
	FlagItemLoadSecret
)

type ItemSearchFlag added in v1.0.0

type ItemSearchFlag int

ItemSearchFlag are flags for Collection.CreateItem.

const (
	FlagItemCreateNone ItemSearchFlag = iota
	FlatItemCreateReplace
)

type LockableObject added in v1.1.0

type LockableObject interface {
	Locked() (bool, error)
	// contains filtered or unexported methods
}

type Prompt

type Prompt struct {
	*DbusObject
}

Prompt is an interface to handling unlocking prompts. https://developer-old.gnome.org/libsecret/0.18/SecretPrompt.html https://specifications.freedesktop.org/secret-service/latest/ch09.html

func NewPrompt

func NewPrompt(conn *dbus.Conn, path dbus.ObjectPath) (prompt *Prompt)

NewPrompt returns a pointer to a new Prompt based on a Dbus connection and a Dbus path.

func (*Prompt) Prompt

func (p *Prompt) Prompt() (promptValue *dbus.Variant, err error)

Prompt issues/waits for a prompt for unlocking a Locked Collection or Secret / Item.

type Secret

type Secret struct {
	// 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 SecretValue `json:"value"`
	// ContentType is the MIME type of Value.
	ContentType string `json:"content_type"`
	// contains filtered or unexported fields
}

Secret is the "Good Stuff" - the actual secret content. https://developer-old.gnome.org/libsecret/0.18/SecretValue.html https://specifications.freedesktop.org/secret-service/latest/re03.html https://specifications.freedesktop.org/secret-service/latest/ch14.html#type-Secret

func NewSecret

func NewSecret(session *Session, params []byte, value []byte, contentType string) (secret *Secret)

NewSecret returns a pointer to a new Secret based on a Session, parameters, (likely an empty byte slice), a value, and the MIME content type.

type SecretServiceErrEnum added in v1.0.0

type SecretServiceErrEnum int

SecretServiceErrEnum are just constants for the enum'd errors; see SecretServiceError type and ErrSecretService* vars for what actually gets returned. They're used for finding the appropriate matching error.

const (
	EnumErrProtocol SecretServiceErrEnum = iota
	EnumErrIsLocked
	EnumErrNoSuchObject
	EnumErrAlreadyExists
	EnumErrInvalidFileFormat
)

type SecretServiceError added in v1.0.0

type SecretServiceError struct {
	// ErrCode is the SecretService API's enum value.
	ErrCode SecretServiceErrEnum `json:"code"`
	// ErrName is the SecretService API's error name.
	ErrName string `json:"name"`
	/*
		ErrDesc is the actual error description/text.
		This is what should be displayed to users, and is returned by SecretServiceError.Error.
	*/
	ErrDesc string `json:"desc"`
}

SecretServiceError is a translated error from SecretService API. See https://developer-old.gnome.org/libsecret/unstable/libsecret-SecretError.html#SecretError and ErrSecretService* errors.

func (SecretServiceError) Error added in v1.0.0

func (e SecretServiceError) Error() (errStr string)

Error returns the string format of the error; this is necessary to be considered a valid error interface.

type SecretValue added in v1.0.0

type SecretValue []byte

SecretValue is a custom type that handles JSON encoding a little more easily.

func (*SecretValue) MarshalJSON added in v1.0.0

func (s *SecretValue) MarshalJSON() (b []byte, err error)

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.

type Service

type Service struct {
	*DbusObject
	// Session is a default Session initiated automatically.
	Session *Session `json:"-"`
	// IsLocked indicates if the Service is locked or not. Status updated by Service.Locked.
	IsLocked bool `json:"locked"`
	/*
		Legacy indicates that this SecretService implementation breaks current spec
		by implementing the legacy/obsolete draft spec rather than current libsecret spec
		for the Dbus API.

		If you're using SecretService with KeePassXC, for instance, or a much older version
		of Gnome-Keyring *before* libsecret integration(?),	or if you are getting strange errors
		when performing a Service.SearchItems, you probably need to enable this field on the
		Service returned by NewService. The coverage of this field may expand in the future, but
		currently it only prevents/suppresses the (non-existent, in legacy spec) Type property
		from being read or written on Items during e.g.:

			Service.SearchItems
			Collection.CreateItem
			NewItem
			Item.ChangeItemType
			Item.Type

		It will perform a no-op if enabled in the above contexts to maintain cross-compatability
		in codebase between legacy and proper current spec systems, avoiding an error return.

		You can use CheckErrIsFromLegacy if Service.Legacy is false and Service.SearchItems returns
		a non-nil err to determine if this Service is (probably) interfacing with a legacy spec API.
	*/
	Legacy bool `json:"is_legacy"`
}

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://specifications.freedesktop.org/secret-service/latest/re01.html

func NewService

func NewService() (service *Service, err error)

NewService returns a pointer to a new Service connection.

func (*Service) Close added in v1.0.0

func (s *Service) Close() (err error)

Close cleanly closes a Service and all its underlying connections (e.g. Service.Session).

func (*Service) Collections

func (s *Service) Collections() (collections []*Collection, err error)

Collections returns a slice of Collection items accessible to this Service.

err MAY be a *multierr.MultiError.

func (*Service) CreateAliasedCollection added in v1.0.0

func (s *Service) CreateAliasedCollection(label, alias string) (collection *Collection, err error)

CreateAliasedCollection creates a new Collection (keyring) via a Service with the name specified by label, aliased to the name specified by alias, and returns the new Collection.

func (*Service) CreateCollection

func (s *Service) CreateCollection(label string) (collection *Collection, err error)

CreateCollection creates a new Collection (keyring) via a Service with the name specified by label and returns the new Collection. It is a *very* thin wrapper around Service.CreateAliasedCollection, but with a blank alias.

func (*Service) GetCollection added in v1.0.0

func (s *Service) GetCollection(name string) (c *Collection, err error)

GetCollection returns a single Collection based on the name (name can also be an alias). It's a helper function that avoids needing to make multiple calls in user code.

err MAY be a *multierr.MultiError.

func (*Service) GetSecrets added in v1.0.0

func (s *Service) GetSecrets(itemPaths ...dbus.ObjectPath) (secrets map[dbus.ObjectPath]*Secret, err error)

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. 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 (*Service) GetSession added in v1.0.0

func (s *Service) GetSession() (ssn *Session, err error)

GetSession returns a single Session. It's a helper function that wraps Service.OpenSession.

func (*Service) Lock

func (s *Service) Lock(objects ...LockableObject) (err error)

Lock locks an Unlocked Collection or Item (LockableObject).

func (*Service) OpenSession added in v1.0.0

func (s *Service) OpenSession(algo, input string) (session *Session, output dbus.Variant, err error)

OpenSession returns a pointer to a Session from the Service. It's a convenience function around NewSession. However, NewService attaches a Session by default at Service.Session so this is likely unnecessary.

func (*Service) ReadAlias added in v1.0.0

func (s *Service) ReadAlias(alias string) (collection *Collection, err error)

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 (*Service) RemoveAlias added in v1.1.0

func (s *Service) RemoveAlias(alias string) (err error)

RemoveAlias is a thin wrapper around Service.SetAlias using the removal method specified there.

func (*Service) SearchItems added in v1.0.0

func (s *Service) SearchItems(attributes map[string]string) (unlockedItems []*Item, lockedItems []*Item, err error)

SearchItems searches all Collection objects and returns all matches based on the map of attributes.

err MAY be a *multierr.MultiError.

func (*Service) SetAlias added in v1.0.0

func (s *Service) SetAlias(alias string, objectPath dbus.ObjectPath) (err error)

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("/").

func (*Service) Unlock

func (s *Service) Unlock(objects ...LockableObject) (err error)

Unlock unlocks a locked Collection or Item (LockableObject).

type ServiceInitFlag added in v1.0.0

type ServiceInitFlag int

ServiceInitFlag is a flag for Service.OpenSession.

const (
	FlagServiceNone ServiceInitFlag = iota
	FlagServiceOpenSession
	FlagServiceLoadCollections
)

type ServiceSearchFlag added in v1.0.0

type ServiceSearchFlag int

ServiceSearchFlag is a flag for Service.SearchItems.

const (
	FlagServiceSearchNone ServiceSearchFlag = iota
	FlagServiceSearchAll
	FlagServiceSearchUnlock
	FlagServiceSearchLoadSecrets
)

type Session

type Session struct {
	*DbusObject
	// contains filtered or unexported fields
}

Session is a session/instance/connection to SecretService. https://developer-old.gnome.org/libsecret/0.18/SecretService.html https://specifications.freedesktop.org/secret-service/latest/ch06.html

func NewSession

func NewSession(service *Service, path dbus.ObjectPath) (session *Session, err error)

NewSession returns a pointer to a new Session based on a Service and a dbus.ObjectPath. You will almost always want to use Service.GetSession or Service.OpenSession instead.

func (*Session) Close added in v1.0.0

func (s *Session) Close() (err error)

Close cleanly closes a Session.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL