Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions cmd/andcli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,17 @@ func open(cfg *config.Config) (vaults.Vault, error) {
}()

switch cfg.Type {
case vaults.TYPE_ANDOTP:
case vaults.ANDOTP:
return andotp.Open(cfg.File, pw)
case vaults.TYPE_AEGIS:
case vaults.AEGIS:
return aegis.Open(cfg.File, pw)
case vaults.TYPE_TWOFAS:
case vaults.TWOFAS:
return twofas.Open(cfg.File, pw)
case vaults.TYPE_STRATUM:
case vaults.STRATUM:
return stratum.Open(cfg.File, pw)
case vaults.TYPE_KEEPASS:
case vaults.KEEPASS:
return keepass.Open(cfg.File, pw)
case vaults.TYPE_PROTON:
case vaults.PROTON:
return protonpass.Open(cfg.File, pw)
}

Expand Down
56 changes: 28 additions & 28 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import (

"github.com/goccy/go-yaml"
"github.com/tjblackheart/andcli/v2/internal/buildinfo"
"github.com/tjblackheart/andcli/v2/internal/vaults"
)

type (
Config struct {
File string `yaml:"file"`
Type string `yaml:"type"`
ClipboardCmd string `yaml:"clipboard_cmd"`
Options *Opts `yaml:"options"`
File string `yaml:"file"`
Type vaults.Type `yaml:"type"`
ClipboardCmd string `yaml:"clipboard_cmd"`
Options *Opts `yaml:"options"`
//
path string
passwordFromStdin bool
Expand Down Expand Up @@ -70,81 +71,80 @@ func create(dir string) (*Config, error) {
}

// Writes the current configuration to a yaml file.
func (c Config) Persist() error {
b, err := yaml.Marshal(c)
func (cfg Config) Persist() error {
b, err := yaml.Marshal(cfg)
if err != nil {
return err
}

return os.WriteFile(c.path, b, 0600)
return os.WriteFile(cfg.path, b, 0o600)
}

// Returns true if the flag option "passwd-stdin" was set.
func (c Config) PasswdStdin() bool {
return c.passwordFromStdin
func (cfg Config) PasswdStdin() bool {
return cfg.passwordFromStdin
}

// Reads an possibly existing config file and merges the content
// into the current config.
func (c *Config) mergeExisting() error {
if _, err := os.Stat(c.path); os.IsNotExist(err) {
func (cfg *Config) mergeExisting() error {
if _, err := os.Stat(cfg.path); os.IsNotExist(err) {
return nil
}

b, err := os.ReadFile(c.path)
b, err := os.ReadFile(cfg.path)
if err != nil {
return err
}

var existing = new(Config)
existing := new(Config)
if err := yaml.Unmarshal(b, existing); err != nil {
return err
}

c.File = existing.File
c.Type = existing.Type
c.ClipboardCmd = existing.ClipboardCmd
cfg.File = existing.File
cfg.Type = existing.Type
cfg.ClipboardCmd = existing.ClipboardCmd

if existing.Options != nil {
c.Options = existing.Options
cfg.Options = existing.Options
}

return nil
}

// Validates the current configuration.
func (c *Config) validate() error {

if c.File == "" {
func (cfg *Config) validate() error {
if cfg.File == "" {
return errors.New("no vault file specified")
}

if c.Type == "" {
if cfg.Type == "" {
return errors.New("no vault type specified")
}

var err error
if c.File, err = filepath.Abs(c.File); err != nil {
return fmt.Errorf("%s: %s", c.File, err)
if cfg.File, err = filepath.Abs(cfg.File); err != nil {
return fmt.Errorf("%s: %s", cfg.File, err)
}

fi, err := os.Stat(c.File)
fi, err := os.Stat(cfg.File)
if err != nil {
return fmt.Errorf("%s: %s", c.File, err)
return fmt.Errorf("%s: %s", cfg.File, err)
}

if fi.IsDir() {
return fmt.Errorf("%s: is a directory, not a vault file", c.File)
return fmt.Errorf("%s: is a directory, not a vault file", cfg.File)
}

// if set, check if the basic clipboard cmd is available in system PATH.
// the option parsing is done at a later time.
if parts := strings.SplitN(c.ClipboardCmd, " ", 2); parts[0] != "" {
if parts := strings.SplitN(cfg.ClipboardCmd, " ", 2); parts[0] != "" {
path, err := exec.LookPath(parts[0])
if err != nil {
return fmt.Errorf("%s: %s", parts[0], err)
}
c.ClipboardCmd = strings.ReplaceAll(c.ClipboardCmd, parts[0], path)
cfg.ClipboardCmd = strings.ReplaceAll(cfg.ClipboardCmd, parts[0], path)
}

return nil
Expand Down
16 changes: 9 additions & 7 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/goccy/go-yaml"
"github.com/tjblackheart/andcli/v2/internal/buildinfo"
"github.com/tjblackheart/andcli/v2/internal/vaults"
)

func TestConfig_mergeExisting(t *testing.T) {
Expand Down Expand Up @@ -49,10 +50,11 @@ func TestConfig_mergeExisting(t *testing.T) {
},
{
"handles custom options",
&Config{Options: &Opts{
ShowUsernames: true,
ShowTokens: true,
},
&Config{
Options: &Opts{
ShowUsernames: true,
ShowTokens: true,
},
path: path,
},
&Config{
Expand All @@ -77,7 +79,7 @@ func TestConfig_mergeExisting(t *testing.T) {
t.Fatal(err)
}

if err := os.WriteFile(tt.want.path, b, 0644); err != nil {
if err := os.WriteFile(tt.want.path, b, 0o644); err != nil {
t.Fatal(err)
}
} else {
Expand All @@ -102,7 +104,7 @@ func TestConfig_validate(t *testing.T) {
path := filepath.Join(os.TempDir(), "andcli_test.json")
defer os.Remove(path)

os.WriteFile(path, nil, 0644)
os.WriteFile(path, nil, 0o644)

tests := []struct {
name string
Expand Down Expand Up @@ -210,7 +212,7 @@ func Test_create(t *testing.T) {
// default config
want := &Config{
File: abs,
Type: *vtype,
Type: vaults.Type(*vtype),
ClipboardCmd: "",
Options: &Opts{
ShowUsernames: true,
Expand Down
9 changes: 2 additions & 7 deletions internal/config/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,16 @@ import (
"log"
"os"
"path/filepath"
"strings"

flag "github.com/spf13/pflag"
"github.com/tjblackheart/andcli/v2/internal/buildinfo"
"github.com/tjblackheart/andcli/v2/internal/vaults"
)

var (
availableVaults = strings.Join(vaults.Types(), ", ")

set = flag.NewFlagSet("default", flag.ExitOnError)
vfile = set.StringP("file", "f", "", "Path to the encrypted vault (deprecated: Pass the filename directly)")
vtype = set.StringP("type", "t", "", fmt.Sprintf("Vault type (%s)", availableVaults))
vtype = set.StringP("type", "t", "", fmt.Sprintf("Vault type (%s)", vaults.StrTypes()))
cmd = set.StringP("clipboard-cmd", "c", "", "A custom clipboard command, including args (xclip, wl-copy, pbcopy etc.)")
pwstdin = set.Bool("passwd-stdin", false, "Read the vault password from stdin. If set, skips the password input.")
version = set.BoolP("version", "v", false, "Prints version info and exits")
Expand All @@ -26,7 +23,6 @@ var (

// Parses given flags into the existing config.
func (cfg *Config) parseFlags() error {

set.Usage = func() { usage(true) }

if err := set.Parse(os.Args[1:]); err != nil {
Expand Down Expand Up @@ -54,7 +50,7 @@ func (cfg *Config) parseFlags() error {
}

if *vtype != "" {
cfg.Type = *vtype
cfg.Type = vaults.Type(*vtype)
}

if *cmd != "" {
Expand All @@ -78,7 +74,6 @@ func (cfg *Config) parseFlags() error {

// prints custom formatted usage information
func usage(includeDescription bool) {

if includeDescription {
fmt.Println("andcli - A 2FA TUI for your shell")
}
Expand Down
27 changes: 13 additions & 14 deletions internal/vaults/aegis/aegis.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ import (
"golang.org/x/crypto/scrypt"
)

const t = vaults.TYPE_AEGIS
const vaultType = vaults.AEGIS

var _ vaults.Vault = &aegis{}

type (
vault struct {
aegis struct {
Version int
Header struct {
Slots []struct {
Expand Down Expand Up @@ -57,37 +59,35 @@ type (
)

func Open(filename string, pass []byte) (vaults.Vault, error) {

var v vault
var v aegis

b, err := os.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("%s: %w", t, err)
return nil, fmt.Errorf("%s: %w", vaultType, err)
}

if err := json.Unmarshal(b, &v); err != nil {
return nil, fmt.Errorf("%s: %w", t, err)
return nil, fmt.Errorf("%s: %w", vaultType, err)
}

key, err := v.masterKeyFromPass(pass)
if err != nil {
return nil, fmt.Errorf("%s: %w", t, err)
return nil, fmt.Errorf("%s: %w", vaultType, err)
}

b, err = v.decryptDB(key)
if err != nil {
return nil, fmt.Errorf("%s: %w", t, err)
return nil, fmt.Errorf("%s: %w", vaultType, err)
}

if err := json.Unmarshal(b, &v.db); err != nil {
return nil, fmt.Errorf("%s: %w", t, err)
return nil, fmt.Errorf("%s: %w", vaultType, err)
}

return v, nil
}

func (v vault) Entries() []vaults.Entry {

func (v aegis) Entries() []vaults.Entry {
entries := make([]vaults.Entry, 0)

for _, e := range v.db.Entries {
Expand All @@ -109,7 +109,7 @@ func (v vault) Entries() []vaults.Entry {
return entries
}

func (v vault) masterKeyFromPass(password []byte) ([]byte, error) {
func (v aegis) masterKeyFromPass(password []byte) ([]byte, error) {
var salt, keyNonce, keyTag, key, derivedKey []byte
var err error

Expand Down Expand Up @@ -166,8 +166,7 @@ func (v vault) masterKeyFromPass(password []byte) ([]byte, error) {
return masterKey, nil
}

func (v vault) decryptDB(key []byte) ([]byte, error) {

func (v aegis) decryptDB(key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion internal/vaults/aegis/aegis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func TestEntries(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
entries := (&vault{db: db{Entries: tt.input}}).Entries()
entries := (&aegis{db: db{Entries: tt.input}}).Entries()
if !reflect.DeepEqual(entries, tt.want) {
t.Fatalf("Entries(): want %#v\nhave %#v", tt.want, entries)
}
Expand Down
25 changes: 11 additions & 14 deletions internal/vaults/andotp/andotp.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import (
"os"
"strings"

"github.com/grijul/go-andotp/andotp"
gao "github.com/grijul/go-andotp/andotp"
"github.com/tjblackheart/andcli/v2/internal/vaults"
)

const t = vaults.TYPE_ANDOTP
const vaultType = vaults.ANDOTP

var _ vaults.Vault = &andotp{}

type (
vault struct {
entries []entry
}
andotp struct{ entries []entry }

entry struct {
Secret string
Expand All @@ -33,28 +33,25 @@ type (
)

func Open(filename string, pass []byte) (vaults.Vault, error) {

b, err := os.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("%s: %w", t, err)
return nil, fmt.Errorf("%s: %w", vaultType, err)
}

b, err = andotp.Decrypt(b, string(pass))
b, err = gao.Decrypt(b, string(pass))
if err != nil {
return nil, fmt.Errorf("%s: %w", t, err)
return nil, fmt.Errorf("%s: %w", vaultType, err)
}

entries := make([]entry, 0)
if err := json.Unmarshal(b, &entries); err != nil {
return nil, fmt.Errorf("%s: %w", t, err)
return nil, fmt.Errorf("%s: %w", vaultType, err)
}

return &vault{entries}, nil

return &andotp{entries}, nil
}

func (v vault) Entries() []vaults.Entry {

func (v andotp) Entries() []vaults.Entry {
entries := make([]vaults.Entry, 0)

for _, e := range v.entries {
Expand Down
2 changes: 1 addition & 1 deletion internal/vaults/andotp/andotp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func TestEntries(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
entries := (&vault{entries: tt.input}).Entries()
entries := (&andotp{entries: tt.input}).Entries()
if !reflect.DeepEqual(entries, tt.want) {
t.Fatalf("Entries(): want %#v\nhave %#v", tt.want, entries)
}
Expand Down
Loading