lockbox

password manager
Log | Files | Refs | README | LICENSE

commit 2ab90802dd7820ff37da3205654ae6524ca655c9
parent c35e8d39967896cdb2756e6d492f05361a84d8b1
Author: Sean Enck <sean@ttypty.com>
Date:   Fri, 31 Mar 2023 18:20:10 -0400

split out some env functions

Diffstat:
Ainternal/inputs/coloring.go | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainternal/inputs/coloring_test.go | 40++++++++++++++++++++++++++++++++++++++++
Minternal/inputs/env.go | 81+------------------------------------------------------------------------------
Minternal/inputs/env_test.go | 54------------------------------------------------------
Ainternal/inputs/totp.go | 37+++++++++++++++++++++++++++++++++++++
Ainternal/inputs/totp_test.go | 29+++++++++++++++++++++++++++++
6 files changed, 175 insertions(+), 134 deletions(-)

diff --git a/internal/inputs/coloring.go b/internal/inputs/coloring.go @@ -0,0 +1,68 @@ +// Package inputs handles user inputs/UI elements. +package inputs + +import ( + "errors" + "fmt" + "strconv" + "strings" +) + +const ( + colorWindowDelimiter = "," + colorWindowSpan = ":" +) + +var ( + // TOTPDefaultColorWindow is the default coloring rules for totp + TOTPDefaultColorWindow = []ColorWindow{{Start: 0, End: 5}, {Start: 30, End: 35}} + // TOTPDefaultBetween is the default color window as a string + TOTPDefaultBetween = toString(TOTPDefaultColorWindow) +) + +type ( + // ColorWindow for handling terminal colors based on timing + ColorWindow struct { + Start int + End int + } +) + +func toString(windows []ColorWindow) string { + var results []string + for _, w := range windows { + results = append(results, fmt.Sprintf("%d%s%d", w.Start, colorWindowSpan, w.End)) + } + return strings.Join(results, colorWindowDelimiter) +} + +// ParseColorWindow will handle parsing a window of colors for TOTP operations +func ParseColorWindow(windowString string) ([]ColorWindow, error) { + var rules []ColorWindow + for _, item := range strings.Split(windowString, colorWindowDelimiter) { + line := strings.TrimSpace(item) + if line == "" { + continue + } + parts := strings.Split(line, colorWindowSpan) + if len(parts) != 2 { + return nil, fmt.Errorf("invalid colorization rule found: %s", line) + } + s, err := strconv.Atoi(parts[0]) + if err != nil { + return nil, err + } + e, err := strconv.Atoi(parts[1]) + if err != nil { + return nil, err + } + if s < 0 || e < 0 || e < s || s > 59 || e > 59 { + return nil, fmt.Errorf("invalid time found for colorization rule: %s", line) + } + rules = append(rules, ColorWindow{Start: s, End: e}) + } + if len(rules) == 0 { + return nil, errors.New("invalid colorization rules for totp, none found") + } + return rules, nil +} diff --git a/internal/inputs/coloring_test.go b/internal/inputs/coloring_test.go @@ -0,0 +1,40 @@ +package inputs_test + +import ( + "testing" + + "github.com/enckse/lockbox/internal/inputs" +) + +func TestParseWindows(t *testing.T) { + if _, err := inputs.ParseColorWindow(""); err.Error() != "invalid colorization rules for totp, none found" { + t.Errorf("invalid error: %v", err) + } + if _, err := inputs.ParseColorWindow(",2"); err.Error() != "invalid colorization rule found: 2" { + t.Errorf("invalid error: %v", err) + } + if _, err := inputs.ParseColorWindow(",1:200"); err.Error() != "invalid time found for colorization rule: 1:200" { + t.Errorf("invalid error: %v", err) + } + if _, err := inputs.ParseColorWindow(",1:-1"); err.Error() != "invalid time found for colorization rule: 1:-1" { + t.Errorf("invalid error: %v", err) + } + if _, err := inputs.ParseColorWindow(",200:1"); err.Error() != "invalid time found for colorization rule: 200:1" { + t.Errorf("invalid error: %v", err) + } + if _, err := inputs.ParseColorWindow(",-1:1"); err.Error() != "invalid time found for colorization rule: -1:1" { + t.Errorf("invalid error: %v", err) + } + if _, err := inputs.ParseColorWindow(",2:1"); err.Error() != "invalid time found for colorization rule: 2:1" { + t.Errorf("invalid error: %v", err) + } + if _, err := inputs.ParseColorWindow(",xxx:1"); err.Error() != "strconv.Atoi: parsing \"xxx\": invalid syntax" { + t.Errorf("invalid error: %v", err) + } + if _, err := inputs.ParseColorWindow(",1:xxx"); err.Error() != "strconv.Atoi: parsing \"xxx\": invalid syntax" { + t.Errorf("invalid error: %v", err) + } + if _, err := inputs.ParseColorWindow(",1:2,11:22"); err != nil { + t.Errorf("invalid error: %v", err) + } +} diff --git a/internal/inputs/env.go b/internal/inputs/env.go @@ -4,7 +4,6 @@ package inputs import ( "errors" "fmt" - "net/url" "os" "os/exec" "strconv" @@ -15,8 +14,6 @@ import ( ) const ( - otpAuth = "otpauth" - otpIssuer = "lbissuer" prefixKey = "LOCKBOX_" noClipEnv = prefixKey + "NOCLIP" noColorEnv = prefixKey + "NOCOLOR" @@ -58,8 +55,6 @@ const ( // WindowsLinuxPlatform for WSL subsystems WindowsLinuxPlatform = "wsl" defaultMaxClipboard = 45 - colorWindowDelimiter = "," - colorWindowSpan = ":" detectedValue = "(detected)" noTOTPEnv = prefixKey + "NOTOTP" // HookDirEnv represents a stored location for user hooks @@ -73,23 +68,12 @@ const ( MaxTOTPTimeDefault = "120" ) -var ( - isYesNoArgs = []string{isYes, isNo} - // TOTPDefaultColorWindow is the default coloring rules for totp - TOTPDefaultColorWindow = []ColorWindow{{Start: 0, End: 5}, {Start: 30, End: 35}} - // TOTPDefaultBetween is the default color window as a string - TOTPDefaultBetween = toString(TOTPDefaultColorWindow) -) +var isYesNoArgs = []string{isYes, isNo} type ( environmentOutput struct { showValues bool } - // ColorWindow for handling terminal colors based on timing - ColorWindow struct { - Start int - End int - } // SystemPlatform represents the platform lockbox is running on. SystemPlatform string ) @@ -121,45 +105,6 @@ func GetReKey() ([]string, error) { return out, nil } -func toString(windows []ColorWindow) string { - var results []string - for _, w := range windows { - results = append(results, fmt.Sprintf("%d%s%d", w.Start, colorWindowSpan, w.End)) - } - return strings.Join(results, colorWindowDelimiter) -} - -// ParseColorWindow will handle parsing a window of colors for TOTP operations -func ParseColorWindow(windowString string) ([]ColorWindow, error) { - var rules []ColorWindow - for _, item := range strings.Split(windowString, colorWindowDelimiter) { - line := strings.TrimSpace(item) - if line == "" { - continue - } - parts := strings.Split(line, colorWindowSpan) - if len(parts) != 2 { - return nil, fmt.Errorf("invalid colorization rule found: %s", line) - } - s, err := strconv.Atoi(parts[0]) - if err != nil { - return nil, err - } - e, err := strconv.Atoi(parts[1]) - if err != nil { - return nil, err - } - if s < 0 || e < 0 || e < s || s > 59 || e > 59 { - return nil, fmt.Errorf("invalid time found for colorization rule: %s", line) - } - rules = append(rules, ColorWindow{Start: s, End: e}) - } - if len(rules) == 0 { - return nil, errors.New("invalid colorization rules for totp, none found") - } - return rules, nil -} - // EnvOrDefault will get the environment value OR default if env is not set. func EnvOrDefault(envKey, defaultValue string) string { val := os.Getenv(envKey) @@ -282,30 +227,6 @@ func TOTPToken() string { return EnvOrDefault(fieldTOTPEnv, defaultTOTPField) } -// FormatTOTP will format a totp otpauth url -func FormatTOTP(value string) string { - if strings.HasPrefix(value, otpAuth) { - return value - } - override := EnvOrDefault(formatTOTPEnv, "") - if override != "" { - return fmt.Sprintf(override, value) - } - v := url.Values{} - v.Set("secret", value) - v.Set("issuer", otpIssuer) - v.Set("period", "30") - v.Set("algorithm", "SHA1") - v.Set("digits", "6") - u := url.URL{ - Scheme: otpAuth, - Host: "totp", - Path: "/" + otpIssuer + ":" + "lbaccount", - RawQuery: v.Encode(), - } - return u.String() -} - func (o environmentOutput) formatEnvironmentVariable(required bool, name, val, desc string, allowed []string) string { value := val if o.showValues { diff --git a/internal/inputs/env_test.go b/internal/inputs/env_test.go @@ -9,60 +9,6 @@ import ( "github.com/enckse/lockbox/internal/inputs" ) -func TestParseWindows(t *testing.T) { - if _, err := inputs.ParseColorWindow(""); err.Error() != "invalid colorization rules for totp, none found" { - t.Errorf("invalid error: %v", err) - } - if _, err := inputs.ParseColorWindow(",2"); err.Error() != "invalid colorization rule found: 2" { - t.Errorf("invalid error: %v", err) - } - if _, err := inputs.ParseColorWindow(",1:200"); err.Error() != "invalid time found for colorization rule: 1:200" { - t.Errorf("invalid error: %v", err) - } - if _, err := inputs.ParseColorWindow(",1:-1"); err.Error() != "invalid time found for colorization rule: 1:-1" { - t.Errorf("invalid error: %v", err) - } - if _, err := inputs.ParseColorWindow(",200:1"); err.Error() != "invalid time found for colorization rule: 200:1" { - t.Errorf("invalid error: %v", err) - } - if _, err := inputs.ParseColorWindow(",-1:1"); err.Error() != "invalid time found for colorization rule: -1:1" { - t.Errorf("invalid error: %v", err) - } - if _, err := inputs.ParseColorWindow(",2:1"); err.Error() != "invalid time found for colorization rule: 2:1" { - t.Errorf("invalid error: %v", err) - } - if _, err := inputs.ParseColorWindow(",xxx:1"); err.Error() != "strconv.Atoi: parsing \"xxx\": invalid syntax" { - t.Errorf("invalid error: %v", err) - } - if _, err := inputs.ParseColorWindow(",1:xxx"); err.Error() != "strconv.Atoi: parsing \"xxx\": invalid syntax" { - t.Errorf("invalid error: %v", err) - } - if _, err := inputs.ParseColorWindow(",1:2,11:22"); err != nil { - t.Errorf("invalid error: %v", err) - } -} - -func TestFormatTOTP(t *testing.T) { - otp := inputs.FormatTOTP("otpauth://abc") - if otp != "otpauth://abc" { - t.Errorf("invalid totp token: %s", otp) - } - otp = inputs.FormatTOTP("abc") - if otp != "otpauth://totp/lbissuer:lbaccount?algorithm=SHA1&digits=6&issuer=lbissuer&period=30&secret=abc" { - t.Errorf("invalid totp token: %s", otp) - } - os.Setenv("LOCKBOX_TOTP_FORMAT", "test/%s") - otp = inputs.FormatTOTP("abc") - if otp != "test/abc" { - t.Errorf("invalid totp token: %s", otp) - } - os.Setenv("LOCKBOX_TOTP_FORMAT", "") - otp = inputs.FormatTOTP("abc") - if otp != "otpauth://totp/lbissuer:lbaccount?algorithm=SHA1&digits=6&issuer=lbissuer&period=30&secret=abc" { - t.Errorf("invalid totp token: %s", otp) - } -} - func checkYesNo(key string, t *testing.T, cb func() (bool, error), onEmpty bool) { os.Setenv(key, "yes") c, err := cb() diff --git a/internal/inputs/totp.go b/internal/inputs/totp.go @@ -0,0 +1,37 @@ +// Package inputs handles user inputs/UI elements. +package inputs + +import ( + "fmt" + "net/url" + "strings" +) + +const ( + otpAuth = "otpauth" + otpIssuer = "lbissuer" +) + +// FormatTOTP will format a totp otpauth url +func FormatTOTP(value string) string { + if strings.HasPrefix(value, otpAuth) { + return value + } + override := EnvOrDefault(formatTOTPEnv, "") + if override != "" { + return fmt.Sprintf(override, value) + } + v := url.Values{} + v.Set("secret", value) + v.Set("issuer", otpIssuer) + v.Set("period", "30") + v.Set("algorithm", "SHA1") + v.Set("digits", "6") + u := url.URL{ + Scheme: otpAuth, + Host: "totp", + Path: "/" + otpIssuer + ":" + "lbaccount", + RawQuery: v.Encode(), + } + return u.String() +} diff --git a/internal/inputs/totp_test.go b/internal/inputs/totp_test.go @@ -0,0 +1,29 @@ +package inputs_test + +import ( + "os" + "testing" + + "github.com/enckse/lockbox/internal/inputs" +) + +func TestFormatTOTP(t *testing.T) { + otp := inputs.FormatTOTP("otpauth://abc") + if otp != "otpauth://abc" { + t.Errorf("invalid totp token: %s", otp) + } + otp = inputs.FormatTOTP("abc") + if otp != "otpauth://totp/lbissuer:lbaccount?algorithm=SHA1&digits=6&issuer=lbissuer&period=30&secret=abc" { + t.Errorf("invalid totp token: %s", otp) + } + os.Setenv("LOCKBOX_TOTP_FORMAT", "test/%s") + otp = inputs.FormatTOTP("abc") + if otp != "test/abc" { + t.Errorf("invalid totp token: %s", otp) + } + os.Setenv("LOCKBOX_TOTP_FORMAT", "") + otp = inputs.FormatTOTP("abc") + if otp != "otpauth://totp/lbissuer:lbaccount?algorithm=SHA1&digits=6&issuer=lbissuer&period=30&secret=abc" { + t.Errorf("invalid totp token: %s", otp) + } +}