Skip to content

Commit

Permalink
Implement config options per mount
Browse files Browse the repository at this point in the history
Fixes #35
  • Loading branch information
dominikschulz authored and Dominik Schulz committed Sep 13, 2017
1 parent ca803cf commit 2b5e7d8
Show file tree
Hide file tree
Showing 22 changed files with 263 additions and 121 deletions.
2 changes: 1 addition & 1 deletion action/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func (s *Action) binaryCopy(ctx context.Context, from, to string, deleteSource b
case fsutil.IsFile(from) && fsutil.IsFile(to):
// copying from on file to another file is not supported
return errors.New("ambiquity detected. Only from or to can be a file")
case s.Store.Exists(from) && s.Store.Exists(to):
case s.Store.Exists(ctx, from) && s.Store.Exists(ctx, to):
// copying from one secret to another secret is not supported
return errors.New("ambiquity detected. Either from or to must be a file")
case fsutil.IsFile(from) && !fsutil.IsFile(to):
Expand Down
4 changes: 2 additions & 2 deletions action/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ func (s *Action) Copy(ctx context.Context, c *cli.Context) error {
from := c.Args()[0]
to := c.Args()[1]

if !s.Store.Exists(from) {
if !s.Store.Exists(ctx, from) {
return s.exitError(ctx, ExitNotFound, nil, "%s does not exist", from)
}

if !force {
if s.Store.Exists(to) && !s.AskForConfirmation(ctx, fmt.Sprintf("%s already exists. Overwrite it?", to)) {
if s.Store.Exists(ctx, to) && !s.AskForConfirmation(ctx, fmt.Sprintf("%s already exists. Overwrite it?", to)) {
return s.exitError(ctx, ExitAborted, nil, "not overwriting your current secret")
}
}
Expand Down
4 changes: 2 additions & 2 deletions action/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (s *Action) Delete(ctx context.Context, c *cli.Context) error {
if recursive {
recStr = "recursively "
}
if (s.Store.Exists(name) || s.Store.IsDir(name)) && key == "" && !s.AskForConfirmation(ctx, fmt.Sprintf("Are you sure you would like to %sdelete %s?", recStr, name)) {
if (s.Store.Exists(ctx, name) || s.Store.IsDir(ctx, name)) && key == "" && !s.AskForConfirmation(ctx, fmt.Sprintf("Are you sure you would like to %sdelete %s?", recStr, name)) {
return nil
}
}
Expand All @@ -38,7 +38,7 @@ func (s *Action) Delete(ctx context.Context, c *cli.Context) error {
return nil
}

if s.Store.IsDir(name) {
if s.Store.IsDir(ctx, name) {
return errors.Errorf("Cannot remove '%s': Is a directory. Use 'gopass rm -r %s' to delete", name, name)
}

Expand Down
4 changes: 2 additions & 2 deletions action/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (s *Action) Edit(ctx context.Context, c *cli.Context) error {

var content []byte
var changed bool
if s.Store.Exists(name) {
if s.Store.Exists(ctx, name) {
sec, err := s.Store.Get(ctx, name)
if err != nil {
return errors.Errorf("failed to decrypt %s: %v", name, err)
Expand All @@ -37,7 +37,7 @@ func (s *Action) Edit(ctx context.Context, c *cli.Context) error {
if err != nil {
return errors.Errorf("failed to decode %s: %v", name, err)
}
} else if tmpl, found := s.Store.LookupTemplate(name); found {
} else if tmpl, found := s.Store.LookupTemplate(ctx, name); found {
changed = true
// load template if it exists
content = pwgen.GeneratePassword(defaultLength, false)
Expand Down
4 changes: 2 additions & 2 deletions action/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (s *Action) Generate(ctx context.Context, c *cli.Context) error {
}

if !force { // don't check if it's force anyway
if s.Store.Exists(name) && key == "" && !s.AskForConfirmation(ctx, fmt.Sprintf("An entry already exists for %s. Overwrite the current password?", name)) {
if s.Store.Exists(ctx, name) && key == "" && !s.AskForConfirmation(ctx, fmt.Sprintf("An entry already exists for %s. Overwrite the current password?", name)) {
return s.exitError(ctx, ExitAborted, nil, "user aborted. not overwriting your current password")
}
}
Expand Down Expand Up @@ -82,7 +82,7 @@ func (s *Action) Generate(ctx context.Context, c *cli.Context) error {
if err := s.Store.Set(sub.WithReason(ctx, "Generated password for YAML key"), name, sec); err != nil {
return s.exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err)
}
} else if s.Store.Exists(name) {
} else if s.Store.Exists(ctx, name) {
sec, err := s.Store.Get(ctx, name)
if err != nil {
return s.exitError(ctx, ExitEncrypt, err, "failed to set key '%s' of '%s': %s", key, name, err)
Expand Down
8 changes: 4 additions & 4 deletions action/insert.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (s *Action) Insert(ctx context.Context, c *cli.Context) error {
}

sec := secret.New("", "")
if s.Store.Exists(name) {
if s.Store.Exists(ctx, name) {
var err error
sec, err = s.Store.Get(ctx, name)
if err != nil {
Expand Down Expand Up @@ -84,15 +84,15 @@ func (s *Action) Insert(ctx context.Context, c *cli.Context) error {
}

if !force { // don't check if it's force anyway
if s.Store.Exists(name) && !s.AskForConfirmation(ctx, fmt.Sprintf("An entry already exists for %s. Overwrite it?", name)) {
if s.Store.Exists(ctx, name) && !s.AskForConfirmation(ctx, fmt.Sprintf("An entry already exists for %s. Overwrite it?", name)) {
return s.exitError(ctx, ExitAborted, nil, "not overwriting your current secret")
}
}

// if multi-line input is requested start an editor
if multiline && ctxutil.IsInteractive(ctx) {
buf := []byte{}
if s.Store.Exists(name) {
if s.Store.Exists(ctx, name) {
var err error
sec, err := s.Store.Get(ctx, name)
if err != nil {
Expand Down Expand Up @@ -131,7 +131,7 @@ func (s *Action) Insert(ctx context.Context, c *cli.Context) error {
}

var sec *secret.Secret
if s.Store.Exists(name) {
if s.Store.Exists(ctx, name) {
var err error
sec, err = s.Store.Get(ctx, name)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion action/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func (s *Action) MountAdd(ctx context.Context, c *cli.Context) error {
keys = append(keys, k)
}

if s.Store.Exists(alias) {
if s.Store.Exists(ctx, alias) {
fmt.Printf(color.YellowString("WARNING: shadowing %s entry\n"), alias)
}

Expand Down
2 changes: 1 addition & 1 deletion action/move.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func (s *Action) Move(ctx context.Context, c *cli.Context) error {
to := c.Args()[1]

if !force {
if s.Store.Exists(to) && !s.AskForConfirmation(ctx, fmt.Sprintf("%s already exists. Overwrite it?", to)) {
if s.Store.Exists(ctx, to) && !s.AskForConfirmation(ctx, fmt.Sprintf("%s already exists. Overwrite it?", to)) {
return s.exitError(ctx, ExitAborted, nil, "not overwriting your current secret")
}
}
Expand Down
4 changes: 2 additions & 2 deletions action/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ func (s *Action) show(ctx context.Context, c *cli.Context, name, key string, cli
return s.exitError(ctx, ExitUsage, nil, "Usage: %s show [name]", s.Name)
}

if s.Store.IsDir(name) {
if s.Store.IsDir(ctx, name) {
return s.List(ctx, c)
}

// auto-fallback to binary files with b64 suffix, if unique
if !s.Store.Exists(name) && s.Store.Exists(name+BinarySuffix) {
if !s.Store.Exists(ctx, name) && s.Store.Exists(ctx, name+BinarySuffix) {
name += BinarySuffix
}

Expand Down
12 changes: 6 additions & 6 deletions action/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func (s *Action) TemplatesPrint(ctx context.Context, c *cli.Context) error {
func (s *Action) TemplatePrint(ctx context.Context, c *cli.Context) error {
name := c.Args().First()

content, err := s.Store.GetTemplate(name)
content, err := s.Store.GetTemplate(ctx, name)
if err != nil {
return s.exitError(ctx, ExitIO, err, "failed to retrieve template: %s", err)
}
Expand All @@ -59,9 +59,9 @@ func (s *Action) TemplateEdit(ctx context.Context, c *cli.Context) error {
}

var content []byte
if s.Store.HasTemplate(name) {
if s.Store.HasTemplate(ctx, name) {
var err error
content, err = s.Store.GetTemplate(name)
content, err = s.Store.GetTemplate(ctx, name)
if err != nil {
return s.exitError(ctx, ExitIO, err, "failed to retrieve template: %s", err)
}
Expand All @@ -79,7 +79,7 @@ func (s *Action) TemplateEdit(ctx context.Context, c *cli.Context) error {
return nil
}

return s.Store.SetTemplate(name, nContent)
return s.Store.SetTemplate(ctx, name, nContent)
}

// TemplateRemove will remove a single template
Expand All @@ -89,11 +89,11 @@ func (s *Action) TemplateRemove(ctx context.Context, c *cli.Context) error {
return s.exitError(ctx, ExitUsage, nil, "usage: %s templates remove [name]", s.Name)
}

if !s.Store.HasTemplate(name) {
if !s.Store.HasTemplate(ctx, name) {
return s.exitError(ctx, ExitNotFound, nil, "template '%s' not found", name)
}

return s.Store.RemoveTemplate(name)
return s.Store.RemoveTemplate(ctx, name)
}

// TemplatesComplete prints a list of all templates for bash completion
Expand Down
126 changes: 96 additions & 30 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,66 @@ import (
"strconv"
"strings"

"github.com/ghodss/yaml"
"github.com/justwatchcom/gopass/utils/fsutil"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
)

var (
// ErrConfigNotFound is returned on load if the config was not found
ErrConfigNotFound = errors.Errorf("config not found")
// ErrConfigNotParsed is returned on load if the config could not be decoded
ErrConfigNotParsed = errors.Errorf("config not parseable")
debug = false
)

// Config is the gopass config structure
func init() {
if gdb := os.Getenv("GOPASS_DEBUG"); gdb != "" {
debug = true
}
}

type Config struct {
AskForMore bool `json:"askformore"` // ask for more data on generate
AutoImport bool `json:"autoimport"` // import missing public keys w/o asking
AutoSync bool `json:"autosync"` // push to git remote after commit, pull before push if necessary
ClipTimeout int `json:"cliptimeout"` // clear clipboard after seconds
Debug bool `json:"-"`
Mounts map[string]string `json:"mounts,omitempty"`
NoColor bool `json:"-"`
NoPager bool `json:"-"`
NoConfirm bool `json:"noconfirm"` // do not confirm recipients when encrypting
Path string `json:"path"` // path to the root store
SafeContent bool `json:"safecontent"` // avoid showing passwords in terminal
Version string `json:"version"`
Root StoreConfig `yaml:"root"`
Mounts map[string]StoreConfig `yaml:"mounts"`
Version string `yaml:"version"`

// Catches all undefined files and must be empty after parsing
XXX map[string]interface{} `yaml:",inline"`
}

type StoreConfig struct {
AskForMore bool `yaml:"askformore"` // ask for more data on generate
AutoImport bool `yaml:"autoimport"` // import missing public keys w/o asking
AutoSync bool `yaml:"autosync"` // push to git remote after commit, pull before push if necessary
ClipTimeout int `yaml:"cliptimeout"` // clear clipboard after seconds
NoConfirm bool `yaml:"noconfirm"` // do not confirm recipients when encrypting
NoPager bool `yaml:"nopager"` // do not invoke a pager to display long lists
Path string `yaml:"path"` // path to the root store
SafeContent bool `yaml:"safecontent"` // avoid showing passwords in terminal
}

// New creates a new config with sane default values
func New() *Config {
return &Config{
AskForMore: false,
AutoImport: true,
AutoSync: true,
ClipTimeout: 45,
Mounts: make(map[string]string),
NoConfirm: false,
SafeContent: false,
Version: "",
Root: StoreConfig{
AskForMore: false,
AutoImport: true,
AutoSync: true,
ClipTimeout: 45,
NoConfirm: false,
NoPager: false,
SafeContent: false,
},
Mounts: make(map[string]StoreConfig),
Version: "",
}
}

func (c *Config) CheckOverflow() error {
return checkOverflow(c.XXX, "config")
}

// ConfigMap returns a map of stringified config values for easy printing
func (c *Config) ConfigMap() map[string]string {
m := make(map[string]string, 20)
Expand Down Expand Up @@ -119,6 +137,10 @@ func (c *Config) SetConfigValue(key, value string) error {
return c.Save()
}

func (c *Config) Config() *Config {
return c
}

// Load will try to load the config from one of the default locations
func Load() *Config {
for _, l := range configLocations() {
Expand All @@ -129,13 +151,13 @@ func Load() *Config {
if err != nil {
panic(err)
}
if gdb := os.Getenv("GOPASS_DEBUG"); gdb == "true" {
if debug {
fmt.Printf("[DEBUG] Loaded config from %s: %+v\n", l, cfg)
}
return cfg
}
cfg := New()
cfg.Path = PwStoreDir("")
cfg.Root.Path = PwStoreDir("")
return cfg
}

Expand All @@ -150,20 +172,64 @@ func load(cf string) (*Config, error) {
fmt.Printf("Error reading config from %s: %s\n", cf, err)
return nil, ErrConfigNotFound
}
cfg := &Config{
AutoSync: true,
}
err = yaml.Unmarshal(buf, &cfg)

cfg, err := decode(buf)
if err != nil {
fmt.Printf("Error reading config from %s: %s\n", cf, err)
return nil, ErrConfigNotParsed
}
if cfg.Mounts == nil {
cfg.Mounts = make(map[string]string)
cfg.Mounts = make(map[string]StoreConfig)
}
return cfg, nil
}

func checkOverflow(m map[string]interface{}, section string) error {
if len(m) < 1 {
return nil
}

var keys []string
for k := range m {
keys = append(keys, k)
}
return errors.Errorf("unknown fields in %s: %+v", section, keys)
}

type configer interface {
Config() *Config
CheckOverflow() error
}

func decode(buf []byte) (*Config, error) {
cfgs := []configer{
&Config{
Root: StoreConfig{
AutoSync: true,
},
},
&ConfigPre140{
AutoSync: true,
},
}
for _, cfg := range cfgs {
if err := yaml.Unmarshal(buf, cfg); err != nil {
continue
}
if err := cfg.CheckOverflow(); err != nil {
if debug {
fmt.Printf("[DEBUG] Extra elements in config: %s\n", err)
}
continue
}
if debug {
fmt.Printf("[DEBUG] Loaded config: %+v\n", cfg)
}
return cfg.Config(), nil
}
return nil, ErrConfigNotParsed
}

// Save saves the config
func (c *Config) Save() error {
buf, err := yaml.Marshal(c)
Expand All @@ -180,7 +246,7 @@ func (c *Config) Save() error {
if err := ioutil.WriteFile(cfgLoc, buf, 0600); err != nil {
return errors.Wrapf(err, "failed to write config file to '%s'", cfgLoc)
}
if gdb := os.Getenv("GOPASS_DEBUG"); gdb == "true" {
if debug {
fmt.Printf("[DEBUG] Saved config to %s: %+v\n", cfgLoc, c)
}
return nil
Expand Down

0 comments on commit 2b5e7d8

Please sign in to comment.