Skip to content

Commit

Permalink
Add JSON output support.
Browse files Browse the repository at this point in the history
  • Loading branch information
marcopaganini committed Mar 18, 2024
1 parent 68f5fdb commit 28cf034
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 19 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ Uses [fzf](https://github.com/junegunn/fzf) to select the desired OTP. The `fzf`

Without any special options, **termotp** shows a formatted table of your TOTP providers and the calculated tokens. This option shows a simple TUI with a fuzzy selector. Hitting enter on an entry will print the otp to the standard output.

**--json**

Emits the output in JSON format.

**--plain**

Produces a plain listing of the vault.
Expand Down Expand Up @@ -112,7 +116,7 @@ Add support for other OTP programs, like AndOTP, 2FA, etc. I'll proceed to do th
## Thanks

* https://github.com/zalando/ for their Keyring manipulation library.
* https://github.com/sam-artuso for his ideas on using fzf (as an external program) and keyring support.
* https://github.com/sam-artuso and http://github.com/timkgh for their great feature requests.

## Author

Expand Down
6 changes: 3 additions & 3 deletions aegis.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ func filterAegisVault(plainJSON []byte, rematch *regexp.Regexp) ([]otpEntry, err
}
if rematch.MatchString(entry.Issuer) || rematch.MatchString(entry.Name) {
ret = append(ret, otpEntry{
issuer: entry.Issuer,
account: entry.Name,
token: token,
Issuer: entry.Issuer,
Account: entry.Name,
Token: token,
})
}
}
Expand Down
16 changes: 8 additions & 8 deletions fuzzy.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,27 @@ func maxlen(vault []otpEntry, f func(v otpEntry) string) int {
// fuzzyFind opens a fuzzy finder window and allows the user to select the
// desired issuer/account and returns the token.
func fuzzyFind(vault []otpEntry) (string, error) {
maxIssuerLen := maxlen(vault, func(otp otpEntry) string { return otp.issuer })
maxAccountLen := maxlen(vault, func(otp otpEntry) string { return otp.account })
maxTokenLen := maxlen(vault, func(otp otpEntry) string { return otp.token })
maxIssuerLen := maxlen(vault, func(otp otpEntry) string { return otp.Issuer })
maxAccountLen := maxlen(vault, func(otp otpEntry) string { return otp.Account })
maxTokenLen := maxlen(vault, func(otp otpEntry) string { return otp.Token })

idx, err := fuzzyfinder.Find(
vault,
func(i int) string {
// Default issuer name
issuer := defaultIssuerName
if vault[i].issuer != "" {
issuer = vault[i].issuer
if vault[i].Issuer != "" {
issuer = vault[i].Issuer
}

issuer = fmt.Sprintf("%-[1]*s", maxIssuerLen+fuzzyPadding, issuer)
account := fmt.Sprintf("%-[1]*s", maxAccountLen+fuzzyPadding, vault[i].account)
token := fmt.Sprintf("%-[1]*s", maxTokenLen+fuzzyPadding, vault[i].token)
account := fmt.Sprintf("%-[1]*s", maxAccountLen+fuzzyPadding, vault[i].Account)
token := fmt.Sprintf("%-[1]*s", maxTokenLen+fuzzyPadding, vault[i].Token)

return fmt.Sprintf("%s %s %s", issuer, account, token)
})
if err != nil {
return "", err
}
return vault[idx].token, nil
return vault[idx].Token, nil
}
32 changes: 25 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package main

import (
"encoding/json"
"errors"
"flag"
"fmt"
Expand All @@ -27,9 +28,9 @@ var BuildVersion string

// otpEntry holds the representation of the internal vault.
type otpEntry struct {
issuer string
account string
token string
Issuer string
Account string
Token string
}

// Keyring constants. User is not your user.
Expand All @@ -44,6 +45,7 @@ type cmdLineFlags struct {
fuzzy bool
fzf bool
plain bool
json bool
setkeyring bool
usekeyring bool
version bool
Expand Down Expand Up @@ -137,7 +139,7 @@ func outputTable(vault []otpEntry, flags cmdLineFlags) string {
}

for _, v := range vault {
tbl.AppendRow(table.Row{v.issuer, v.account, v.token})
tbl.AppendRow(table.Row{v.Issuer, v.Account, v.Token})
}

tbl.SortBy([]table.SortBy{
Expand All @@ -149,13 +151,23 @@ func outputTable(vault []otpEntry, flags cmdLineFlags) string {
return tbl.Render()
}

// outputJSON outputs a JSON representation of the decrypted vault.
func outputJSON(vault []otpEntry) (string, error) {
output, err := json.Marshal(vault)
if err != nil {
return "", err
}
return string(output), nil
}

// parseFlags parses the command line flags and returns a cmdLineFlag struct.
func parseFlags() (cmdLineFlags, error) {
flags := cmdLineFlags{}

flag.StringVar(&flags.input, "input", "", "Input (encrypted) JSON file glob.")
flag.BoolVar(&flags.fuzzy, "fuzzy", false, "Use interactive fuzzy finder.")
flag.BoolVar(&flags.fzf, "fzf", false, "Use fzf (needs external binary in path).")
flag.BoolVar(&flags.json, "json", false, "Use JSON output.")
flag.BoolVar(&flags.plain, "plain", false, "Use plain output (disables fuzzy finder and tabular output.)")
flag.BoolVar(&flags.version, "version", false, "Show program version and exit.")
flag.BoolVar(&flags.setkeyring, "set-keyring", false, "Set the keyring password and exit.")
Expand All @@ -180,7 +192,7 @@ func parseFlags() (cmdLineFlags, error) {

// Only one output format allowed.
n := 0
for _, v := range []bool{flags.plain, flags.fuzzy, flags.fzf} {
for _, v := range []bool{flags.fuzzy, flags.fzf, flags.json, flags.plain} {
if v {
n++
}
Expand Down Expand Up @@ -321,8 +333,8 @@ func main() {
os.Exit(1)
}
sort.Slice(vault, func(i, j int) bool {
key1 := vault[i].issuer + "/" + vault[i].account
key2 := vault[j].issuer + "/" + vault[j].account
key1 := vault[i].Issuer + "/" + vault[i].Account
key2 := vault[j].Issuer + "/" + vault[j].Account
return key1 > key2
})

Expand All @@ -342,6 +354,12 @@ func main() {
die(err)
}
fmt.Println(t)
case flags.json:
output, err := outputJSON(vault)
if err != nil {
die(err)
}
fmt.Println(output)
default:
fmt.Println(outputTable(vault, flags))
}
Expand Down

0 comments on commit 28cf034

Please sign in to comment.