Skip to content

Commit

Permalink
Merge branch 'master' of github.com:justwatchcom/gopass
Browse files Browse the repository at this point in the history
* 'master' of github.com:justwatchcom/gopass:
  add config option to toggle colored output (gopasspw#414)
  GPG related fixes (gopasspw#419)
  • Loading branch information
Dominik Schulz committed Oct 24, 2017
2 parents 9e279a8 + 8ab5b4e commit 895645e
Show file tree
Hide file tree
Showing 22 changed files with 205 additions and 25 deletions.
1 change: 1 addition & 0 deletions action/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type gpger interface {
CreatePrivateKeyBatch(context.Context, string, string, string) error
CreatePrivateKey(context.Context) error
ExportPublicKey(context.Context, string, string) error
Version(context.Context) semver.Version
}

// Action knows everything to run gopass CLI actions
Expand Down
4 changes: 4 additions & 0 deletions action/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"

"github.com/blang/semver"
"github.com/fatih/color"
"github.com/justwatchcom/gopass/config"
"github.com/justwatchcom/gopass/utils/ctxutil"
Expand All @@ -17,6 +18,9 @@ import (
// Initialized returns an error if the store is not properly
// prepared.
func (s *Action) Initialized(ctx context.Context, c *cli.Context) error {
if s.gpg.Version(ctx).LT(semver.Version{Major: 2, Minor: 0, Patch: 0}) {
out.Red(ctx, "Warning: Using GPG 1.x. Using GPG 2.0 or later is highly recommended")
}
if !s.Store.Initialized() {
if ctxutil.IsInteractive(ctx) {
if ok, err := s.askForBool(ctx, "It seems you are new to gopass. Do you want to run the onboarding wizard?", true); err == nil && ok {
Expand Down
53 changes: 46 additions & 7 deletions action/recipients.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,23 +68,42 @@ func (s *Action) RecipientsAdd(ctx context.Context, c *cli.Context) error {
store := c.String("store")
added := 0

// select store
if store == "" {
stores := []string{"<root>"}
stores = append(stores, s.Store.MountPoints()...)
act, sel := termwiz.GetSelection(ctx, "Store for secret", "<↑/↓> to change the selection, <→> to select, <ESC> to quit", stores)
switch act {
case "show":
store = stores[sel]
if store == "<root>" {
store = ""
}
default:
store = "" // root store
}
}

// select recipient
recipients := []string(c.Args())
if len(recipients) < 1 {
choices := []string{}
kl, _ := s.gpg.FindPublicKeys(ctx)
for _, key := range kl.UseableKeys() {
kl = kl.UseableKeys()
for _, key := range kl {
choices = append(choices, key.OneLine())
}
if len(choices) > 0 {
act, sel := termwiz.GetSelection(ctx, "Add Recipient -", "<↑/↓> to change the selection, <→> to add this recipient, <ESC> to quit", choices)
switch act {
case "show":
recipients = []string{choices[sel]}
recipients = []string{kl[sel].Fingerprint}
default:
return s.exitError(ctx, ExitAborted, nil, "user aborted")
}
}
}

for _, r := range recipients {
keys, err := s.gpg.FindPublicKeys(ctx, r)
if err != nil {
Expand All @@ -95,11 +114,12 @@ func (s *Action) RecipientsAdd(ctx context.Context, c *cli.Context) error {
if len(keys) < 1 {
out.Cyan(ctx, "Warning: No matching valid key found. If the key is in your keyring you may need to validate it.")
out.Cyan(ctx, "If this is your key: gpg --edit-key %s; trust (set to ultimate); quit", r)
out.Cyan(ctx, "If this is not your key: gpg --edit-key %s; lsign; save; quit", r)
out.Cyan(ctx, "If this is not your key: gpg --edit-key %s; lsign; trust; save; quit", r)
out.Cyan(ctx, "You may need to run 'gpg --update-trustdb' afterwards")
continue
}

if !s.AskForConfirmation(ctx, fmt.Sprintf("Do you want to add '%s' as an recipient?", keys[0].OneLine())) {
if !s.AskForConfirmation(ctx, fmt.Sprintf("Do you want to add '%s' as an recipient to the store '%s'?", keys[0].OneLine(), store)) {
continue
}

Expand All @@ -112,14 +132,32 @@ func (s *Action) RecipientsAdd(ctx context.Context, c *cli.Context) error {
return s.exitError(ctx, ExitUnknown, nil, "no key added")
}

out.Green(ctx, "Added %d recipients\n", added)
out.Green(ctx, "\nAdded %d recipients", added)
out.Cyan(ctx, "You need to run 'gopass sync' to push these changes")
return nil
}

// RecipientsRemove removes recipients
func (s *Action) RecipientsRemove(ctx context.Context, c *cli.Context) error {
store := c.String("store")

// select store
if store == "" {
stores := []string{"<root>"}
stores = append(stores, s.Store.MountPoints()...)
act, sel := termwiz.GetSelection(ctx, "Store for secret", "<↑/↓> to change the selection, <→> to select, <ESC> to quit", stores)
switch act {
case "show":
store = stores[sel]
if store == "<root>" {
store = ""
}
default:
store = "" // root store
}
}

// select recipient
recipients := []string(c.Args())
if len(recipients) < 1 {
ids := s.Store.ListRecipients(ctx, store)
Expand All @@ -138,7 +176,7 @@ func (s *Action) RecipientsRemove(ctx context.Context, c *cli.Context) error {
act, sel := termwiz.GetSelection(ctx, "Remove recipient -", "<↑/↓> to change the selection, <→> to remove this recipient, <ESC> to quit", choices)
switch act {
case "show":
recipients = []string{choices[sel]}
recipients = []string{ids[sel]}
default:
return s.exitError(ctx, ExitAborted, nil, "user aborted")
}
Expand All @@ -162,6 +200,7 @@ func (s *Action) RecipientsRemove(ctx context.Context, c *cli.Context) error {
removed++
}

fmt.Printf("Removed %d recipients\n", removed)
out.Green(ctx, "\nRemoved %d recipients", removed)
out.Cyan(ctx, "You need to run 'gopass sync' to push these changes")
return nil
}
17 changes: 8 additions & 9 deletions backend/gpg/cli/gpg.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package gpg
package cli

import (
"bufio"
Expand Down Expand Up @@ -58,25 +58,24 @@ func New(cfg Config) *GPG {
binary: "gpg",
args: cfg.Args,
}

for _, b := range []string{cfg.Binary, "gpg2", "gpg1", "gpg"} {
if p, err := exec.LookPath(b); err == nil {
g.binary = p
break
}
}
_ = g.detectBinary(cfg.Binary)

return g
}

// Binary returns the GPG binary location
func (g *GPG) Binary() string {
return g.binary
}

// listKey lists all keys of the given type and matching the search strings
func (g *GPG) listKeys(ctx context.Context, typ string, search ...string) (gpg.KeyList, error) {
args := []string{"--with-colons", "--with-fingerprint", "--fixed-list-mode", "--list-" + typ + "-keys"}
args = append(args, search...)
cmd := exec.CommandContext(ctx, g.binary, args...)
cmd.Stderr = nil

out.Debug(ctx, "[DEBUG] gpg.listKeys: %s %+v\n", cmd.Path, cmd.Args)
out.Debug(ctx, "gpg.listKeys: %s %+v\n", cmd.Path, cmd.Args)
cmdout, err := cmd.Output()
if err != nil {
if bytes.Contains(cmdout, []byte("secret key not available")) {
Expand Down
24 changes: 24 additions & 0 deletions backend/gpg/cli/gpg_others.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// +build !windows

package cli

import (
"context"
"os/exec"

"github.com/blang/semver"
)

func (g *GPG) detectBinary(bin string) error {
for _, b := range []string{bin, "gpg", "gpg2", "gpg1", "gpg"} {
if p, err := exec.LookPath(b); err == nil {
g.binary = p
// if we found a GPG 2.x binary we're good, otherwise we try the
// others as well
if g.Version(context.Background()).GTE(semver.Version{Major: 2}) {
break
}
}
}
return nil
}
2 changes: 1 addition & 1 deletion backend/gpg/cli/gpg_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package gpg
package cli

import "testing"

Expand Down
37 changes: 37 additions & 0 deletions backend/gpg/cli/gpg_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// +build windows
package cli

import (
"errors"
"path/filepath"

"github.com/justwatchcom/gopass/utils/fsutil"

"golang.org/x/sys/windows/registry"
)

func (g *GPG) detectBinary(bin string) error {
// set default
g.binary = "gpg.exe"

// try to detect location
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\GnuPG`, registry.QUERY_VALUE|registry.WOW64_32KEY)
if err != nil {
return err
}

v, _, err := k.GetStringValue("Install Directory")
if err != nil {
return err
}

// gpg.exe for GPG4Win 3.0.0; would be gpg2.exe for 2.x
for _, b := range []string{bin, "gpg.exe", "gpg2.exe"} {
gpgPath := filepath.Join(v, "bin", b)
if fsutil.IsFile(gpgPath) {
g.binary = gpgPath
return nil
}
}
return errors.New("gpg.exe not found")
}
2 changes: 1 addition & 1 deletion backend/gpg/cli/parse_colons.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package gpg
package cli

import (
"bufio"
Expand Down
2 changes: 1 addition & 1 deletion backend/gpg/cli/umask_others.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// +build !windows

package gpg
package cli

import "syscall"

Expand Down
2 changes: 1 addition & 1 deletion backend/gpg/cli/umask_windows.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// +build windows

package gpg
package cli

func umask(mask int) int {
return -1
Expand Down
2 changes: 1 addition & 1 deletion backend/gpg/cli/utils.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package gpg
package cli

import (
"strconv"
Expand Down
1 change: 1 addition & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func New() *Config {
NoPager: false,
SafeContent: false,
UseSymbols: false,
NoColor: false,
},
Mounts: make(map[string]*StoreConfig),
Version: "",
Expand Down
1 change: 1 addition & 0 deletions config/store_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type StoreConfig struct {
Path string `yaml:"path"` // path to the root store
SafeContent bool `yaml:"safecontent"` // avoid showing passwords in terminal
UseSymbols bool `yaml:"usesymbols"` // always use symbols when generating passwords
NoColor bool `yaml:"nocolor"` // do not use color when outputing text
}

// ConfigMap returns a map of stringified config values for easy printing
Expand Down
1 change: 1 addition & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ This is a list of options available:
| `path` | `string` | Path to the root store. |
| `safecontent` | `bool` | Only output _safe content_ (i.e. everything but the first line of a secret) to the terminal. Use _copy_ (`-c`) to retrieve the password in the clipboard. |
| `usesymbols` | `bool` | If enabled - it will use symbols when generating passwords. |
| `nocolor` | `bool` | Do not use color. |
11 changes: 9 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ func main() {
// always use symbols
ctx = ctxutil.WithUseSymbols(ctx, cfg.Root.UseSymbols)

// never use color
ctx = ctxutil.WithNoColor(ctx, cfg.Root.NoColor)

// check recipients conflicts with always trust, make sure it's not enabled
// when always trust is
if gpg.IsAlwaysTrust(ctx) {
Expand All @@ -95,7 +98,7 @@ func main() {
}

// need this override for our integration tests
if nc := os.Getenv("GOPASS_NOCOLOR"); nc == "true" {
if nc := os.Getenv("GOPASS_NOCOLOR"); nc == "true" || ctxutil.IsNoColor(ctx) {
color.NoColor = true
ctx = ctxutil.WithColor(ctx, false)
}
Expand Down Expand Up @@ -322,7 +325,11 @@ func main() {
Description: "" +
"This command clones an existing password store from a git remote to " +
"a local password store. Can be either used to initialize a new root store " +
"or to add a new mounted sub store.",
"or to add a new mounted sub store." +
"" +
"Needs at least one argument (git URL) to clone from. " +
"Accepts as second argument (mount location) to clone and mount a sub store, e.g. " +
"gopass clone git@example.com/store.git foo/bar",
Action: func(c *cli.Context) error {
return action.Clone(withGlobalFlags(ctx, c), c)
},
Expand Down
2 changes: 1 addition & 1 deletion store/sub/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (s *Store) gitFixConfig(ctx context.Context) error {
out.Yellow(ctx, "Error while initializing git: %s", err)
}

return nil
return s.gitFixConfigOSDep(ctx)
}

// GitInit initializes this store's git repo and
Expand Down
10 changes: 10 additions & 0 deletions store/sub/git_others.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// +build !windows

package sub

import "context"

func (s *Store) gitFixConfigOSDep(ctx context.Context) error {
// nothing to do
return nil
}
16 changes: 16 additions & 0 deletions store/sub/git_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// +build windows

package sub

import (
"context"

"github.com/pkg/errors"
)

func (s *Store) gitFixConfigOSDep(ctx context.Context) error {
if err := s.gitCmd(ctx, "gitFixConfigOSDep", "config", "--local", "gpg.program", "TODO"); err != nil {
return errors.Wrapf(err, "failed to set git config gpg.program")
}
return nil
}
3 changes: 2 additions & 1 deletion store/sub/recipients.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func (s *Store) AddRecipient(ctx context.Context, id string) error {
return errors.Wrapf(err, "failed to save recipients")
}

out.Cyan(ctx, "Reencrypting existing secrets. This may take some time ...")
return s.reencrypt(WithReason(ctx, "Added Recipient "+id))
}

Expand All @@ -69,7 +70,7 @@ func (s *Store) RemoveRecipient(ctx context.Context, id string) error {
// just try to remove it literally
keys, err := s.gpg.FindPublicKeys(ctx, id)
if err != nil {
fmt.Printf("Failed to get GPG Key Info for %s: %s\n", id, err)
out.Cyan(ctx, "Warning: Failed to get GPG Key Info for %s: %s", id, err)
}

rs, err := s.GetRecipients(ctx, "")
Expand Down
Loading

0 comments on commit 895645e

Please sign in to comment.