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:
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)
+ }
+}