v0.2.8: De-cluttered the config flags stuff

This commit is contained in:
Winni Neessen 2021-03-27 17:03:19 +01:00
parent dd1a6b2b62
commit 1130a698f6
Signed by: wneessen
GPG key ID: 385AC9889632126E
5 changed files with 196 additions and 199 deletions

46
apg.go
View file

@ -2,29 +2,59 @@ package main
import ( import (
"fmt" "fmt"
"github.com/wneessen/apg.go/config"
"log" "log"
"os"
) )
// Constants
const DefaultPwLenght int = 20
const VersionString string = "0.2.8"
type Config struct {
minPassLen int
maxPassLen int
numOfPass int
useComplex bool
useLowerCase bool
useUpperCase bool
useNumber bool
useSpecial bool
humanReadable bool
excludeChars string
newStyleModes string
spellPassword bool
ShowHelp bool
showVersion bool
outputMode int
}
// Main function that generated the passwords and returns them // Main function that generated the passwords and returns them
func main() { func main() {
// Log config // Log config
log.SetFlags(log.Ltime | log.Ldate | log.Lshortfile) log.SetFlags(log.Ltime | log.Ldate | log.Lshortfile)
// Create config // Read and parse flags
conf := config.NewConfig() var config = parseFlags()
conf.ParseParams()
pwLength := conf.GetPwLengthFromParams() // Show version and exit
charRange := getCharRange(conf) if config.showVersion {
_, _ = os.Stderr.WriteString("Advanced Password Generator Clone (apg.go) v" + VersionString + "\n")
_, _ = os.Stderr.WriteString("(C) 2021 by Winni Neessen\n")
os.Exit(0)
}
// Set PW length and available characterset
pwLength := getPwLengthFromParams(&config)
charRange := getCharRange(&config)
// Generate passwords // Generate passwords
for i := 1; i <= conf.NumOfPass; i++ { for i := 1; i <= config.numOfPass; i++ {
pwString, err := getRandChar(&charRange, pwLength) pwString, err := getRandChar(&charRange, pwLength)
if err != nil { if err != nil {
log.Fatalf("getRandChar returned an error: %q\n", err) log.Fatalf("getRandChar returned an error: %q\n", err)
} }
switch conf.OutputMode { switch config.outputMode {
case 1: case 1:
{ {
spelledPw, err := spellPasswordString(pwString) spelledPw, err := spellPasswordString(pwString)

View file

@ -1,9 +1,15 @@
package main package main
import ( import "testing"
"github.com/wneessen/apg.go/config"
"testing" var config Config
)
// Make sure the flags are initalized
var _ = func() bool {
testing.Init()
config = parseFlags()
return true
}()
// Test getRandNum with max 1000 // Test getRandNum with max 1000
func TestGetRandNum(t *testing.T) { func TestGetRandNum(t *testing.T) {
@ -114,16 +120,14 @@ func TestGetCharRange(t *testing.T) {
{"special_only_human", specialHumanBytes, false, false, false, true, true}, {"special_only_human", specialHumanBytes, false, false, false, true, true},
} }
conf := config.NewConfig()
conf.ParseParams()
for _, testCase := range testTable { for _, testCase := range testTable {
t.Run(testCase.testName, func(t *testing.T) { t.Run(testCase.testName, func(t *testing.T) {
conf.UseLowerCase = testCase.useLowerCase config.useLowerCase = testCase.useLowerCase
conf.UseUpperCase = testCase.useUpperCase config.useUpperCase = testCase.useUpperCase
conf.UseNumber = testCase.useNumber config.useNumber = testCase.useNumber
conf.UseSpecial = testCase.useSpecial config.useSpecial = testCase.useSpecial
conf.HumanReadable = testCase.humanReadable config.humanReadable = testCase.humanReadable
charRange := getCharRange(conf) charRange := getCharRange(&config)
for _, curChar := range charRange { for _, curChar := range charRange {
searchAllowedBytes := containsByte(testCase.allowedBytes, int(curChar), t) searchAllowedBytes := containsByte(testCase.allowedBytes, int(curChar), t)
if !searchAllowedBytes { if !searchAllowedBytes {
@ -147,8 +151,6 @@ func TestConvert(t *testing.T) {
{"convert_0_to_ZERO", '0', "ZERO", false}, {"convert_0_to_ZERO", '0', "ZERO", false},
{"convert_/_to_SLASH", '/', "SLASH", false}, {"convert_/_to_SLASH", '/', "SLASH", false},
} }
conf := config.NewConfig()
conf.ParseParams()
for _, testCase := range testTable { for _, testCase := range testTable {
t.Run(testCase.testName, func(t *testing.T) { t.Run(testCase.testName, func(t *testing.T) {
@ -171,12 +173,12 @@ func TestConvert(t *testing.T) {
} }
t.Run("all_chars_must_return_a_conversion_string", func(t *testing.T) { t.Run("all_chars_must_return_a_conversion_string", func(t *testing.T) {
conf.UseUpperCase = true config.useUpperCase = true
conf.UseLowerCase = true config.useLowerCase = true
conf.UseNumber = true config.useNumber = true
conf.UseSpecial = true config.useSpecial = true
conf.HumanReadable = false config.humanReadable = false
charRange := getCharRange(conf) charRange := getCharRange(&config)
for _, curChar := range charRange { for _, curChar := range charRange {
_, err := convertCharToName(byte(curChar)) _, err := convertCharToName(byte(curChar))
if err != nil { if err != nil {
@ -215,15 +217,13 @@ func BenchmarkGetRandChar(b *testing.B) {
// Benchmark: Random char generation // Benchmark: Random char generation
func BenchmarkConvertChar(b *testing.B) { func BenchmarkConvertChar(b *testing.B) {
conf := config.NewConfig()
conf.ParseParams()
conf.UseUpperCase = true config.useUpperCase = true
conf.UseLowerCase = true config.useLowerCase = true
conf.UseNumber = true config.useNumber = true
conf.UseSpecial = true config.useSpecial = true
conf.HumanReadable = false config.humanReadable = false
charRange := getCharRange(conf) charRange := getCharRange(&config)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
charToConv, _ := getRandChar(&charRange, 1) charToConv, _ := getRandChar(&charRange, 1)
charBytes := []byte(charToConv) charBytes := []byte(charToConv)

View file

@ -1,7 +1,6 @@
package main package main
import ( import (
"github.com/wneessen/apg.go/config"
"regexp" "regexp"
) )
@ -15,12 +14,12 @@ const PwNumbersHuman string = "23456789"
const PwNumbers string = "1234567890" const PwNumbers string = "1234567890"
// Provide the range of available characters based on provided parameters // Provide the range of available characters based on provided parameters
func getCharRange(config *config.Config) string { func getCharRange(config *Config) string {
pwUpperChars := PwUpperChars pwUpperChars := PwUpperChars
pwLowerChars := PwLowerChars pwLowerChars := PwLowerChars
pwNumbers := PwNumbers pwNumbers := PwNumbers
pwSpecialChars := PwSpecialChars pwSpecialChars := PwSpecialChars
if config.HumanReadable { if config.humanReadable {
pwUpperChars = PwUpperCharsHuman pwUpperChars = PwUpperCharsHuman
pwLowerChars = PwLowerCharsHuman pwLowerChars = PwLowerCharsHuman
pwNumbers = PwNumbersHuman pwNumbers = PwNumbersHuman
@ -28,20 +27,20 @@ func getCharRange(config *config.Config) string {
} }
var charRange string var charRange string
if config.UseLowerCase { if config.useLowerCase {
charRange = charRange + pwLowerChars charRange = charRange + pwLowerChars
} }
if config.UseUpperCase { if config.useUpperCase {
charRange = charRange + pwUpperChars charRange = charRange + pwUpperChars
} }
if config.UseNumber { if config.useNumber {
charRange = charRange + pwNumbers charRange = charRange + pwNumbers
} }
if config.UseSpecial { if config.useSpecial {
charRange = charRange + pwSpecialChars charRange = charRange + pwSpecialChars
} }
if config.ExcludeChars != "" { if config.excludeChars != "" {
regExp := regexp.MustCompile("[" + regexp.QuoteMeta(config.ExcludeChars) + "]") regExp := regexp.MustCompile("[" + regexp.QuoteMeta(config.excludeChars) + "]")
charRange = regExp.ReplaceAllLiteralString(charRange, "") charRange = regExp.ReplaceAllLiteralString(charRange, "")
} }

122
config.go Normal file
View file

@ -0,0 +1,122 @@
package main
import (
"flag"
"log"
)
// Parse the CLI flags
func parseFlags() Config {
var config Config
// Read and set all flags
flag.BoolVar(&config.useLowerCase, "L", true, "Use lower case characters in passwords")
flag.BoolVar(&config.useUpperCase, "U", true, "Use upper case characters in passwords")
flag.BoolVar(&config.useNumber, "N", true, "Use numbers in passwords")
flag.BoolVar(&config.useSpecial, "S", false, "Use special characters in passwords")
flag.BoolVar(&config.useComplex, "C", false, "Generate complex passwords (implies -L -U -N -S, disables -H)")
flag.BoolVar(&config.spellPassword, "l", false, "Spell generated password")
flag.BoolVar(&config.humanReadable, "H", false, "Generate human-readable passwords")
flag.BoolVar(&config.showVersion, "v", false, "Show version")
flag.IntVar(&config.minPassLen, "m", DefaultPwLenght, "Minimum password length")
flag.IntVar(&config.maxPassLen, "x", DefaultPwLenght, "Maxiumum password length")
flag.IntVar(&config.numOfPass, "n", 1, "Number of passwords to generate")
flag.StringVar(&config.excludeChars, "E", "", "Exclude list of characters from generated password")
flag.StringVar(&config.newStyleModes, "M", "",
"New style password parameters (higher priority than single parameters)")
flag.Parse()
// Parse additional parameters
parseParams(&config)
return config
}
// Parse the parameters and set the according config flags
func parseParams(config *Config) {
parseNewStyleParams(config)
// Complex overrides everything
if config.useComplex {
config.useUpperCase = true
config.useLowerCase = true
config.useSpecial = true
config.useNumber = true
config.humanReadable = false
}
if config.useUpperCase == false &&
config.useLowerCase == false &&
config.useNumber == false &&
config.useSpecial == false {
log.Fatalf("No password mode set. Cannot generate password from empty character set.")
}
// Set output mode
if config.spellPassword {
config.outputMode = 1
}
}
// Get the password length from the given cli flags
func getPwLengthFromParams(config *Config) int {
pwLength := config.minPassLen
if pwLength < config.minPassLen {
pwLength = config.minPassLen
}
if pwLength > config.maxPassLen {
pwLength = config.maxPassLen
}
return pwLength
}
// Parse the new style parameters
func parseNewStyleParams(config *Config) {
if config.newStyleModes == "" {
return
}
for _, curParam := range config.newStyleModes {
switch curParam {
case 'S':
config.useSpecial = true
break
case 's':
config.useSpecial = false
break
case 'N':
config.useNumber = true
break
case 'n':
config.useNumber = false
break
case 'L':
config.useLowerCase = true
break
case 'l':
config.useLowerCase = false
break
case 'U':
config.useUpperCase = true
break
case 'u':
config.useUpperCase = false
break
case 'H':
config.humanReadable = true
break
case 'h':
config.humanReadable = false
break
case 'C':
config.useComplex = true
break
case 'c':
config.useComplex = false
break
default:
log.Fatalf("Unknown password style parameter: %q\n", string(curParam))
}
}
}

View file

@ -1,154 +0,0 @@
package config
import (
"flag"
"log"
"os"
)
// Constants
const DefaultPwLenght int = 20
const VersionString string = "0.2.7"
type Config struct {
MinPassLen int
MaxPassLen int
NumOfPass int
UseComplex bool
UseLowerCase bool
UseUpperCase bool
UseNumber bool
UseSpecial bool
HumanReadable bool
ExcludeChars string
NewStyleModes string
SpellPassword bool
ShowHelp bool
showVersion bool
OutputMode int
}
var config Config
func init() {
// Bool flags
flag.BoolVar(&config.UseLowerCase, "L", true, "Use lower case characters in passwords")
flag.BoolVar(&config.UseUpperCase, "U", true, "Use upper case characters in passwords")
flag.BoolVar(&config.UseNumber, "N", true, "Use numbers in passwords")
flag.BoolVar(&config.UseSpecial, "S", false, "Use special characters in passwords")
flag.BoolVar(&config.UseComplex, "C", false, "Generate complex passwords (implies -L -U -N -S, disables -H)")
flag.BoolVar(&config.SpellPassword, "l", false, "Spell generated password")
flag.BoolVar(&config.HumanReadable, "H", false, "Generate human-readable passwords")
flag.BoolVar(&config.showVersion, "v", false, "Show version")
// Int flags
flag.IntVar(&config.MinPassLen, "m", DefaultPwLenght, "Minimum password length")
flag.IntVar(&config.MaxPassLen, "x", DefaultPwLenght, "Maxiumum password length")
flag.IntVar(&config.NumOfPass, "n", 1, "Number of passwords to generate")
// String flags
flag.StringVar(&config.ExcludeChars, "E", "", "Exclude list of characters from generated password")
flag.StringVar(&config.NewStyleModes, "M", "",
"New style password parameters (higher priority than single parameters)")
}
func NewConfig() *Config {
flag.Parse()
if config.showVersion {
_, _ = os.Stderr.WriteString("Advanced Password Generator Clone (apg.go) v" + VersionString + "\n")
_, _ = os.Stderr.WriteString("(C) 2021 by Winni Neessen\n")
os.Exit(0)
}
return &config
}
// Parse the parameters and set the according config flags
func (config *Config) ParseParams() {
config.parseNewStyleParams()
// Complex overrides everything
if config.UseComplex {
config.UseUpperCase = true
config.UseLowerCase = true
config.UseSpecial = true
config.UseNumber = true
config.HumanReadable = false
}
if config.UseUpperCase == false &&
config.UseLowerCase == false &&
config.UseNumber == false &&
config.UseSpecial == false {
log.Fatalf("No password mode set. Cannot generate password from empty character set.")
}
// Set output mode
if config.SpellPassword {
config.OutputMode = 1
}
}
// Get the password length from the given cli flags
func (config *Config) GetPwLengthFromParams() int {
pwLength := config.MinPassLen
if pwLength < config.MinPassLen {
pwLength = config.MinPassLen
}
if pwLength > config.MaxPassLen {
pwLength = config.MaxPassLen
}
return pwLength
}
// Parse the new style parameters
func (config *Config) parseNewStyleParams() {
if config.NewStyleModes == "" {
return
}
for _, curParam := range config.NewStyleModes {
switch curParam {
case 'S':
config.UseSpecial = true
break
case 's':
config.UseSpecial = false
break
case 'N':
config.UseNumber = true
break
case 'n':
config.UseNumber = false
break
case 'L':
config.UseLowerCase = true
break
case 'l':
config.UseLowerCase = false
break
case 'U':
config.UseUpperCase = true
break
case 'u':
config.UseUpperCase = false
break
case 'H':
config.HumanReadable = true
break
case 'h':
config.HumanReadable = false
break
case 'C':
config.UseComplex = true
break
case 'c':
config.UseComplex = false
break
default:
log.Fatalf("Unknown password style parameter: %q\n", string(curParam))
}
}
}