Skip to content

Commit

Permalink
Add keepassxcAttribute template function (#418)
Browse files Browse the repository at this point in the history
Add keepassxcAttribute template function
  • Loading branch information
twpayne authored Sep 7, 2019
2 parents d4355dd + 4203598 commit 6ccd08c
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 13 deletions.
58 changes: 48 additions & 10 deletions cmd/secretkeepassxc.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,22 @@ type keePassXCCmdConfig struct {
Args []string
}

type keePassXCAttributeCacheKey struct {
entry string
attribute string
}

var (
keePassXCCache = make(map[string]map[string]string)
keePassXCPairRegexp = regexp.MustCompile(`^([^:]+): (.*)$`)
keePassXCPassword string
keePassXCCache = make(map[string]map[string]string)
keePassXCAttributeCache = make(map[keePassXCAttributeCacheKey]string)
keePassXCPairRegexp = regexp.MustCompile(`^([^:]+): (.*)$`)
keePassXCPassword string
)

func init() {
config.KeePassXC.Command = "keepassxc-cli"
config.addTemplateFunc("keepassxc", config.keePassXCFunc)
config.addTemplateFunc("keepassxcAttribute", config.keePassXCAttributeFunc)

secretCmd.AddCommand(keePassXCCmd)
}
Expand All @@ -45,7 +52,7 @@ func (c *Config) runKeePassXCCmd(fs vfs.FS, args []string) error {
return c.exec(append([]string{c.KeePassXC.Command}, args...))
}

func (c *Config) keePassXCFunc(entry string) interface{} {
func (c *Config) keePassXCFunc(entry string) map[string]string {
if data, ok := keePassXCCache[entry]; ok {
return data
}
Expand All @@ -59,15 +66,46 @@ func (c *Config) keePassXCFunc(entry string) interface{} {
if c.Verbose {
fmt.Printf("%s %s\n", name, strings.Join(args, " "))
}
data, err := c.runKeePassXCCLICommand(name, args)
output, err := c.runKeePassXCCLICommand(name, args)
if err != nil {
panic(fmt.Errorf("keepassxc: %s %s: %s", name, strings.Join(args, " "), err))
}
data, err := parseKeyPassXCOutput(output)
if err != nil {
panic(fmt.Errorf("keepassxc: %s %s: %s", name, strings.Join(args, " "), err))
}
keePassXCCache[entry] = data
return data
}

func (c *Config) runKeePassXCCLICommand(name string, args []string) (map[string]string, error) {
func (c *Config) keePassXCAttributeFunc(entry, attribute string) string {
key := keePassXCAttributeCacheKey{
entry: entry,
attribute: attribute,
}
if data, ok := keePassXCAttributeCache[key]; ok {
return data
}
if c.KeePassXC.Database == "" {
panic(errors.New("keepassxc: keepassxc.database not set"))
}
name := c.KeePassXC.Command
args := []string{"show", "--attributes", attribute, "--quiet"}
args = append(args, c.KeePassXC.Args...)
args = append(args, c.KeePassXC.Database, entry)
if c.Verbose {
fmt.Printf("%s %s\n", name, strings.Join(args, " "))
}
output, err := c.runKeePassXCCLICommand(name, args)
if err != nil {
panic(fmt.Errorf("keepassxc: %s %s: %s", name, strings.Join(args, " "), err))
}
outputStr := strings.TrimSpace(string(output))
keePassXCAttributeCache[key] = outputStr
return outputStr
}

func (c *Config) runKeePassXCCLICommand(name string, args []string) ([]byte, error) {
if keePassXCPassword == "" {
fmt.Printf("Insert password to unlock %s: ", c.KeePassXC.Database)
password, err := terminal.ReadPassword(int(os.Stdout.Fd()))
Expand All @@ -80,10 +118,10 @@ func (c *Config) runKeePassXCCLICommand(name string, args []string) (map[string]
cmd := exec.Command(name, args...)
cmd.Stdin = bytes.NewBufferString(keePassXCPassword + "\n")
cmd.Stderr = c.Stderr()
output, err := cmd.Output()
if err != nil {
return nil, err
}
return cmd.Output()
}

func parseKeyPassXCOutput(output []byte) (map[string]string, error) {
data := make(map[string]string)
s := bufio.NewScanner(bytes.NewReader(output))
for i := 0; s.Scan(); i++ {
Expand Down
6 changes: 6 additions & 0 deletions docs/HOWTO.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,12 @@ The structured data from `keepassxc-cli show $database` is available as the
username = {{ (keepassxc "example.com").UserName }}
password = {{ (keepassxc "example.com").Password }}

Additional attributes are available through the `keepassxcAttribute` function.
For example, if you have an entry called `SSH Key` with an additional attribute
called `private-key`, its value is available as:

{{ keepassxcAttribute "SSH Key" "private-key" }}

### Use a keyring to keep your secrets

chezmoi includes support for Keychain (on macOS), GNOME Keyring (on Linux), and
Expand Down
17 changes: 14 additions & 3 deletions docs/REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Manage your dotfiles securely across multiple machines.
* [Template functions](#template-functions)
* [`bitwarden` [*args*]](#bitwarden-args)
* [`keepassxc` *entry*](#keepassxc-entry)
* [`keepassxcAttribute` *entry* *attribute*](#keepassxcattribute-entry-attribute)
* [`keyring` *service* *user*](#keyring-service-user)
* [`lastpass` *id*](#lastpass-id)
* [`onepassword` *uuid*](#onepassword-uuid)
Expand Down Expand Up @@ -739,15 +740,25 @@ the configuration file. *database* and *entry* are passed to `keepassxc-cli
show`. You will be prompted for the database password the first time
`keepassxc-cli` is run, and the password is cached, in plain text, in memory
until chezmoi terminates. The output from `keepassxc-cli` is parsed into
key-value pairs. The output from `keepassxc-cli` is cached so calling
`keepassxc` multiple times with the same *entry* will only invoke
`keepassxc-cli` once.
key-value pairs and cached so calling `keepassxc` multiple times with the same
*entry* will only invoke `keepassxc-cli` once.

#### `keepassxc` examples

username = {{ (keepassxc "example.com").UserName }}
password = {{ (keepassxc "example.com").Password }}

### `keepassxcAttribute` *entry* *attribute*

`keepassxcAttribute` returns the attribute *attribute* of *entry* using
`keepassxc-cli`, with any leading or trailing whitespace removed. It behaves
identically to the `keepassxc` function in terms of configuration, password
prompting, password storage, and result caching.

#### `keepassxcAttribute` examples

{{ keepassxcAttribute "SSH Key" "private-key" }}

### `keyring` *service* *user*

`keyring` retrieves the password associated with *service* and *user* from the
Expand Down

0 comments on commit 6ccd08c

Please sign in to comment.