Skip to content

Commit

Permalink
Add gopass process
Browse files Browse the repository at this point in the history
This commit adds the process command to process templates into full (configuration) files.

Fixes gopasspw#1913

RELEASE_NOTES=[ENHANCEMENT] Add gopass process

Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>
  • Loading branch information
dominikschulz committed Dec 24, 2021
1 parent 03aa36b commit f4b7c35
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 0 deletions.
59 changes: 59 additions & 0 deletions docs/commands/process.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# `process` command

The `process` command extends the `gopass` templating to support user-supplied
template files that will be processed and any necessary credential lookups will
be done while they are processed. That way users can store their full
configuration files publicly accessible and have any of the recipients
automatically populate it to generate a complete configuration file.

`gopass process` writes the result to `STDOUT`. You'll likely want to redirect
it to a file.

## Synopsis

```
$ gopass process <TEMPLATE> > <OUTPUT>
```

## Flags

None.

## Examples

The templates are processed using Go's [`text/template`](https://pkg.go.dev/text/template) package.
A set of helpful template functions is added to the template. See below for a list.

### Populate a MySQL configuration

```
$ cat /etc/mysql/my.cnf.tpl
[client]
host=127.0.0.1
port=3306
user={{ getval "server/local/mysql" "username" }}
password={{ getpw "server/local/mysql" }}
$ gopass process /etc/mysql/my.cnf.tpl
[client]
host=127.0.0.1
port=3306
user=admin
password=hunter2
```

## Template functions

Function | Example | Description
-------- | ------- | -----------
`md5sum` | `{{ getpw "foo/bar" \| md5sum }}` | Calculate the hex md5sum of the input.
`sha1sum` | `{{ getpw "foo/bar" \| sha1sum }}` | Calculate the hex sha1sum of the input.
`md5crypt` | `{{ getpw "foo/bar" \| md5crypt }}` | Calculate the md5crypt of the input.
`ssha` | `{{ getpw "foo/bar" \| ssha }}` | Calculate the salted SHA-1 of the input.
`ssha256` | `{{ getpw "foo/bar" \| ssha256 }}` | Calculate the salted SHA-256 of the input.
`ssha512` | `{{ getpw "foo/bar" \| ssha512 }}` | Calculate the salted SHA-512 of the input.
`get` | `{{ get "foo/bar" }}` | Insert the full secret.
`getpw` | `{{ getpw "foo/bar" }}` | Insert the value of the password field from the given secret.
`getval` | `{{ getval "foo/bar" "baz" }}` | Insert the value of the named field from the given secret.
`argon2i` | `{{ getpw "foo/bar" \| argon2i }}` | Calculate the Argon2i hash of the input.
`argon2id` | `{{ getpw "foo/bar" \| argon2id }}` | Calculate the Argon2id hash of the input.
`bcrypt` | `{{ getpw "foo/bar" \| bcrypt }}` | Calculate the Bcrypt hash of the input.
9 changes: 9 additions & 0 deletions internal/action/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,15 @@ func (s *Action) GetCommands() []*cli.Command {
},
},
},
{
Name: "process",
Usage: "Process a template file",
Description: "" +
"This command processes a template file. It will read the template file " +
"and replace all variables with their values.",
Before: s.IsInitialized,
Action: s.Process,
},
{
Name: "recipients",
Usage: "Edit recipient permissions",
Expand Down
33 changes: 33 additions & 0 deletions internal/action/process.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package action

import (
"io/ioutil"

"github.com/gopasspw/gopass/internal/out"
"github.com/gopasspw/gopass/internal/tpl"
"github.com/gopasspw/gopass/pkg/ctxutil"
"github.com/urfave/cli/v2"
)

// Process is a command to process a template and replace secrets contained in it.
func (s *Action) Process(c *cli.Context) error {
ctx := ctxutil.WithGlobalFlags(c)
file := c.Args().First()
if file == "" {
return ExitError(ExitUsage, nil, "Usage: %s process <FILE>", s.Name)
}

buf, err := ioutil.ReadFile(file)
if err != nil {
return ExitError(ExitIO, err, "Failed to read file: %s", file)
}

obuf, err := tpl.Execute(ctx, string(buf), file, nil, s.Store)
if err != nil {
return ExitError(ExitIO, err, "Failed to process file: %s", file)
}

out.Print(ctx, string(obuf))

return nil
}
63 changes: 63 additions & 0 deletions internal/action/process_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package action

import (
"bytes"
"context"
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/gopasspw/gopass/internal/out"
"github.com/gopasspw/gopass/pkg/ctxutil"
"github.com/gopasspw/gopass/pkg/gopass/secrets"
"github.com/gopasspw/gopass/tests/gptest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestProcess(t *testing.T) {
u := gptest.NewUnitTester(t)
defer u.Remove()

ctx := context.Background()
ctx = ctxutil.WithAlwaysYes(ctx, true)

buf := &bytes.Buffer{}
out.Stdout = buf
stdout = buf
defer func() {
out.Stdout = os.Stdout
stdout = os.Stdout
}()

act, err := newMock(ctx, u)
require.NoError(t, err)
require.NotNil(t, act)

sec := secrets.New()
sec.Set("username", "admin")
sec.SetPassword("hunter2")
require.NoError(t, act.Store.Set(ctx, "server/local/mysql", sec))

infile := filepath.Join(u.Dir, "my.cnf.tpl")
err = ioutil.WriteFile(infile, []byte(`[client]
host=127.0.0.1
port=3306
user={{ getval "server/local/mysql" "username" }}
password={{ getpw "server/local/mysql" }}`), 0644)
require.NoError(t, err)

t.Run("process template", func(t *testing.T) {
defer buf.Reset()

err := act.Process(gptest.CliCtx(ctx, t, infile))
require.NoError(t, err)
assert.Equal(t, `[client]
host=127.0.0.1
port=3306
user=admin
password=hunter2
`, buf.String(), "processed template")
})
}

0 comments on commit f4b7c35

Please sign in to comment.