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 28, 2021
1 parent ad47305 commit 1236af6
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 2 deletions.
60 changes: 60 additions & 0 deletions docs/commands/process.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# `process` command

The `process` command extends the `gopass` templating to support user-supplied
template files that will be processed. These templates can access the users
credentials with the template functions documented below. 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
on the fly.

`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")
})
}
3 changes: 2 additions & 1 deletion main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ var commandsWithError = map[string]struct{}{
".mounts.remove": {},
".move": {},
".otp": {},
".process": {},
".recipients.add": {},
".recipients.remove": {},
".show": {},
Expand Down Expand Up @@ -123,7 +124,7 @@ func TestGetCommands(t *testing.T) {
c.Context = ctx

commands := getCommands(act, app)
assert.Equal(t, 38, len(commands))
assert.Equal(t, 39, len(commands))

prefix := ""
testCommands(t, c, commands, prefix)
Expand Down
1 change: 0 additions & 1 deletion pkg/tempfile/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ func Example() {
panic(err)
}
fmt.Println(string(out))
// Output: foobar
}

func TestTempdirBase(t *testing.T) {
Expand Down

0 comments on commit 1236af6

Please sign in to comment.