Compare commits

...

3 Commits
master ... V0

Author SHA1 Message Date
brent s. df86089517
ready for new release; docs update. 2021-12-13 00:18:36 -05:00
brent s. 55e740793a
updating docs 2021-11-27 02:30:36 -05:00
brent s. b96f17fb19
updating docs back into v0 2021-11-26 00:08:30 -05:00
10 changed files with 346 additions and 6 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,33 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="OTHER_INDENT_OPTIONS">
<value>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</value>
</option>
<GoCodeStyleSettings>
<option name="USE_BACK_QUOTES_FOR_IMPORTS" value="true" />
<option name="ADD_PARENTHESES_FOR_SINGLE_IMPORT" value="true" />
<option name="REMOVE_REDUNDANT_IMPORT_ALIASES" value="true" />
<option name="ADD_LEADING_SPACE_TO_COMMENTS" value="true" />
<option name="MOVE_ALL_STDLIB_IMPORTS_IN_ONE_GROUP" value="true" />
<option name="GROUP_STDLIB_IMPORTS" value="true" />
<option name="WRAP_COMP_LIT" value="5" />
<option name="WRAP_FUNC_PARAMS" value="5" />
<option name="WRAP_FUNC_RESULT" value="5" />
</GoCodeStyleSettings>
<XML>
<option name="XML_KEEP_WHITESPACES" value="true" />
<option name="XML_KEEP_WHITE_SPACES_INSIDE_CDATA" value="true" />
</XML>
<codeStyleSettings language="XML">
<option name="WRAP_ON_TYPING" value="1" />
</codeStyleSettings>
<codeStyleSettings language="go">
<option name="RIGHT_MARGIN" value="180" />
<option name="CALL_PARAMETERS_WRAP" value="5" />
<option name="WRAP_ON_TYPING" value="1" />
</codeStyleSettings>
</code_scheme>
</component>

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="custom" />
</state>
</component>

7
.idea/discord.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT" />
<option name="description" value="" />
</component>
</project>

9
.idea/gosecret.iml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/gosecret.iml" filepath="$PROJECT_DIR$/.idea/gosecret.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -1,6 +1,5 @@
= libsecret/gosecret
Brent Saner <bts@square-r00t.net>
Last updated {localdatetime}
:doctype: book
:docinfo: shared
:data-uri:
@ -14,6 +13,8 @@ Last updated {localdatetime}
: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
@ -24,6 +25,7 @@ This project is originally forked from https://github.com/gsterjov/go-libsecret[
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:
@ -32,15 +34,16 @@ To use this library as a replacement without significantly modifying your code,
.go.mod
[source]
----
# ...
// ...
replace (
github.com/gsterjov/go-libsecret dev => r00t2.io/gosecret v0
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,
@ -48,15 +51,187 @@ To use the new version,
[source,go]
----
import (
`r00t2.io/gosecret/v1`
`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
}
}
----

== Library Hacking

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

1
TODO Normal file
View File

@ -0,0 +1 @@
- tests for V0

88
doc.go Normal file
View File

@ -0,0 +1,88 @@
// See LICENSE in source root directory for copyright and licensing information.

/*
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.
*/
package gosecret