Skip to content

Document gopass extension model #2329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Please see [docs/features.md](https://github.com/gopasspw/gopass/blob/master/doc
| Desktop Notifications | *stable* | Display desktop notifications and completing long running operations |
| REPL | *beta* | Integrated Read-Eval-Print-Loop shell with autocompletion by running `gopass`. |
| OTP support | *stable* | Generate TOTP/(HOTP) tokens based on the stored secret |
| Extensions | | Extend gopass with custom commands using our API |
| Extensions | | [Extend](docs/hacking.md#extending-gopass) gopass with custom commands using our [API](https://pkg.go.dev/github.com/gopasspw/gopass/pkg/gopass/api) |
| Fully open source! | | No need to trust it, check our code and/or improve it! |

## Design Principles
Expand All @@ -64,6 +64,7 @@ If you have [Go](https://golang.org/) 1.18 (or greater) installed:
```bash
go install github.com/gopasspw/gopass@latest
```

(and make sure your `$GOBIN` is in your `$PATH`.)

WARNING: Please prefer releases, unless you want to contribute to the
Expand Down Expand Up @@ -124,11 +125,13 @@ Your password store is ready to use! Have a look around: `gopass list`
## Upgrade

To use the self-updater run:

```bash
gopass update
```

or to upgrade with Go installed, run:

```bash
go install github.com/gopasspw/gopass@latest
```
Expand All @@ -153,7 +156,7 @@ Please refer to the Git commit log for a complete list of contributors.

gopass is developed in the open. Here are some of the channels we use to communicate and contribute:

* Issue tracker: Use the [GitHub issue tracker](https://github.com/gopasspw/gopass/issues) to file bugs and feature requests.
- Issue tracker: Use the [GitHub issue tracker](https://github.com/gopasspw/gopass/issues) to file bugs and feature requests.

## Integrations

Expand Down Expand Up @@ -187,12 +190,14 @@ We welcome any contributions. Please see the [CONTRIBUTING.md](https://github.co

## Further Documentation

* [Security, Known Limitations, and Caveats](https://github.com/gopasspw/gopass/blob/master/docs/security.md)
* [Configuration](https://github.com/gopasspw/gopass/blob/master/docs/config.md)
* [FAQ](https://github.com/gopasspw/gopass/blob/master/docs/faq.md)
* [JSON API](https://github.com/gopasspw/gopass-jsonapi)
* [Gopass as Summon provider](https://github.com/gopasspw/gopass-summon-provider)
- [Security, Known Limitations, and Caveats](https://github.com/gopasspw/gopass/blob/master/docs/security.md)
- [Configuration](https://github.com/gopasspw/gopass/blob/master/docs/config.md)
- [FAQ](https://github.com/gopasspw/gopass/blob/master/docs/faq.md)
- [JSON API](https://github.com/gopasspw/gopass-jsonapi)
- [Gopass as Summon provider](https://github.com/gopasspw/gopass-summon-provider)

## External Documentation

* [gopass cheat sheet](https://woile.github.io/gopass-cheat-sheet/) ([source on github](https://github.com/Woile/gopass-cheat-sheet))
* [gopass presentation](https://woile.github.io/gopass-presentation/) ([source on github](https://github.com/Woile/gopass-presentation))

- [gopass presentation](https://woile.github.io/gopass-presentation/) ([source on github](https://github.com/Woile/gopass-presentation))
65 changes: 65 additions & 0 deletions docs/hacking.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,68 @@ $ export GOPASS_HOMEDIR=/tmp/gp1
$ mkdir -p $GOPASS_HOMEDIR
$ go build && ./gopass setup --crypto age --storage gitfs
```

## Extending gopass

The main extension model are small binaries that use the [gopass API](https://pkg.go.dev/github.com/gopasspw/gopass/pkg/gopass/api) package. This package provides a small and easy to use API that should work with any up to date gopass setup.

This API encapsulates the exact same implementation that the CLI uses in a more nicely packaged format that's easier to use.

Note: The API is operating directly on the password store. It does not involve network operations or connecting to a gopass instance.

The API does not support setting up a new password store (yet). Users will need have an existing password store
or use `gopass setup` to create a new one. The API will attempt to load an existing configuration or use it's built-in
defaults. Then it initializes an existing password store and provides a simple set of CRUD operations.

Our API has some [examples](../pkg/gopass/api/api_test.go) on how to use the API. The [gopass-hibp](https://github.com/gopasspw/gopass-hibp/blob/master/main.go) binary should provide a more complete example that can be used as a blueprint.

```go
import (
"context"
"fmt"

"github.com/gopasspw/gopass/pkg/gopass/api"
"github.com/gopasspw/gopass/pkg/gopass/secrets"
)

ctx := context.Background()

gp, err := api.New(ctx)
if err != nil {
panic(err)
}

// Listing secrets by their names (path within the store).
ls, err := gp.List(ctx)
if err != nil {
panic(err)
}

for _, s := range ls {
fmt.Printf("Secret: %s", s)
}

// Writing secrets to a specific location (path) in the store.
sec := secrets.New()
sec.SetPassword("foobar")
if err := gp.Set(ctx, "my/new/secret", sec); err != nil {
panic(err)
}

// Reading secrets by their name and revision from within the store.
sec, err = gp.Get(ctx, "my/new/secret", "latest")
if err != nil {
panic(err)
}
fmt.Printf("content of %s: %s\n", "my/new/secret", string(sec.Bytes()))

// Removing a secret by their name.
if err := gp.Remove(ctx, "my/new/secret"); err != nil {
panic(err)
}

// Cleaning up (waiting for background processing to complete).
if err := gp.Close(ctx); err != nil {
panic(err)
}
```
11 changes: 10 additions & 1 deletion pkg/gopass/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ var ErrNotImplemented = fmt.Errorf("not yet implemented")
// ErrNotInitialized is returned when the store is not initialized.
var ErrNotInitialized = fmt.Errorf("password store not initialized. run 'gopass setup' first")

// New creates a new secret store.
// New initializes an existing password store. It will attempt to load an existing
// configuration or use the built-in defaults. If no password store is found and
// the user will need to initialize it with the gopass CLI (`gopass setup`) first.
//
// WARNING: This will need to change to accommodate for runtime configuration.
func New(ctx context.Context) (*Gopass, error) {
cfg := config.LoadWithFallbackRelaxed()
Expand All @@ -55,11 +58,13 @@ func (g *Gopass) List(ctx context.Context) ([]string, error) {
}

// Get returns a single, encrypted secret. It must be unwrapped before use.
// Use "latest" to get the latest revision.
func (g *Gopass) Get(ctx context.Context, name, revision string) (gopass.Secret, error) {
return g.rs.Get(ctx, name) //nolint:wrapcheck
}

// Set adds a new revision to an existing secret or creates a new one.
// Create new secrets with secrets.New().
func (g *Gopass) Set(ctx context.Context, name string, sec gopass.Byter) error {
return g.rs.Set(ctx, name, sec) //nolint:wrapcheck
}
Expand Down Expand Up @@ -94,6 +99,10 @@ func (g *Gopass) String() string {
}

// Close shuts down all background processes.
//
// MUST be called before existing to make sure any background processing
// (e.g. pending commits or pushes) are complete. Failing to do so might
// result in an invalid password store state.
func (g *Gopass) Close(ctx context.Context) error {
if err := queue.GetQueue(ctx).Close(ctx); err != nil {
return fmt.Errorf("failed to close queue: %w", err)
Expand Down
52 changes: 52 additions & 0 deletions pkg/gopass/api/api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package api_test

import (
"context"
"fmt"

"github.com/gopasspw/gopass/pkg/gopass/api"
"github.com/gopasspw/gopass/pkg/gopass/secrets"
)

func Example() {
ctx := context.Background()

gp, err := api.New(ctx)
if err != nil {
panic(err)
}

// Listing secrets
ls, err := gp.List(ctx)
if err != nil {
panic(err)
}

for _, s := range ls {
fmt.Printf("Secret: %s", s)
}

// Writing secrets
sec := secrets.New()
sec.SetPassword("foobar")
if err := gp.Set(ctx, "my/new/secret", sec); err != nil {
panic(err)
}

// Reading secrets
sec, err = gp.Get(ctx, "my/new/secret", "latest")
if err != nil {
panic(err)
}
fmt.Printf("content of %s: %s\n", "my/new/secret", string(sec.Bytes()))

// Removing a secret
if err := gp.Remove(ctx, "my/new/secret"); err != nil {
panic(err)
}

// Cleaning up
if err := gp.Close(ctx); err != nil {
panic(err)
}
}