Merge pull request #6 from wneessen/dev

v0.2.7: Switched global config to it's own package
This commit is contained in:
Winni Neessen 2021-03-26 14:28:28 +01:00 committed by GitHub
commit 60346502fa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 219 additions and 200 deletions

83
apg.go
View file

@ -1,89 +1,30 @@
package main
import (
"flag"
"fmt"
"github.com/wneessen/apg.go/config"
"log"
"os"
)
// Constants
const DefaultPwLenght int = 20
const VersionString string = "0.2.6"
const PwLowerCharsHuman string = "abcdefghjkmnpqrstuvwxyz"
const PwUpperCharsHuman string = "ABCDEFGHJKMNPQRSTUVWXYZ"
const PwLowerChars string = "abcdefghijklmnopqrstuvwxyz"
const PwUpperChars string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
const PwSpecialCharsHuman string = "\"#%*+-/:;=\\_|~"
const PwSpecialChars string = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
const PwNumbersHuman string = "23456789"
const PwNumbers string = "1234567890"
type cliOpts 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 cliOpts
// Read flags
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)")
flag.Parse()
if config.showVersion {
_, _ = os.Stderr.WriteString("Winni's Advanced Password Generator Clone (apg.go) v" + VersionString + "\n")
_, _ = os.Stderr.WriteString("(C) 2021 by Winni Neessen\n")
os.Exit(0)
}
log.SetFlags(log.Ltime | log.Ldate | log.Lshortfile)
}
// Main function that generated the passwords and returns them
func main() {
parseParams()
pwLength := getPwLengthFromParams()
charRange := getCharRange()
// Log config
log.SetFlags(log.Ltime | log.Ldate | log.Lshortfile)
for i := 1; i <= config.numOfPass; i++ {
// Create config
conf := config.NewConfig()
conf.ParseParams()
pwLength := conf.GetPwLengthFromParams()
charRange := getCharRange(conf)
// Generate passwords
for i := 1; i <= conf.NumOfPass; i++ {
pwString, err := getRandChar(&charRange, pwLength)
if err != nil {
log.Fatalf("getRandChar returned an error: %q\n", err)
}
switch config.outputMode {
switch conf.OutputMode {
case 1:
{
spelledPw, err := spellPasswordString(pwString)

View file

@ -1,15 +1,10 @@
package main
import (
"github.com/wneessen/apg.go/config"
"testing"
)
// Make sure the flags are initalized
var _ = func() bool {
testing.Init()
return true
}()
// Test getRandNum with max 1000
func TestGetRandNum(t *testing.T) {
testTable := []struct {
@ -118,16 +113,19 @@ func TestGetCharRange(t *testing.T) {
{"special_only", specialBytes, false, false, false, true, false},
{"special_only_human", specialHumanBytes, false, false, false, true, true},
}
conf := config.NewConfig()
conf.ParseParams()
for _, testCase := range testTable {
t.Run(testCase.testName, func(t *testing.T) {
config.useLowerCase = testCase.useLowerCase
config.useUpperCase = testCase.useUpperCase
config.useNumber = testCase.useNumber
config.useSpecial = testCase.useSpecial
config.humanReadable = testCase.humanReadable
charRange := getCharRange()
conf.UseLowerCase = testCase.useLowerCase
conf.UseUpperCase = testCase.useUpperCase
conf.UseNumber = testCase.useNumber
conf.UseSpecial = testCase.useSpecial
conf.HumanReadable = testCase.humanReadable
charRange := getCharRange(conf)
for _, curChar := range charRange {
searchAllowedBytes := containsByte(testCase.allowedBytes, int(curChar))
searchAllowedBytes := containsByte(testCase.allowedBytes, int(curChar), t)
if !searchAllowedBytes {
t.Errorf("Character range returned invalid value: %v", string(curChar))
}
@ -149,6 +147,9 @@ func TestConvert(t *testing.T) {
{"convert_0_to_ZERO", '0', "ZERO", false},
{"convert_/_to_SLASH", '/', "SLASH", false},
}
conf := config.NewConfig()
conf.ParseParams()
for _, testCase := range testTable {
t.Run(testCase.testName, func(t *testing.T) {
charToString, err := convertCharToName(testCase.givenVal)
@ -170,12 +171,12 @@ func TestConvert(t *testing.T) {
}
t.Run("all_chars_must_return_a_conversion_string", func(t *testing.T) {
config.useUpperCase = true
config.useLowerCase = true
config.useNumber = true
config.useSpecial = true
config.humanReadable = false
charRange := getCharRange()
conf.UseUpperCase = true
conf.UseLowerCase = true
conf.UseNumber = true
conf.UseSpecial = true
conf.HumanReadable = false
charRange := getCharRange(conf)
for _, curChar := range charRange {
_, err := convertCharToName(byte(curChar))
if err != nil {
@ -214,12 +215,15 @@ func BenchmarkGetRandChar(b *testing.B) {
// Benchmark: Random char generation
func BenchmarkConvertChar(b *testing.B) {
config.useUpperCase = true
config.useLowerCase = true
config.useNumber = true
config.useSpecial = true
config.humanReadable = false
charRange := getCharRange()
conf := config.NewConfig()
conf.ParseParams()
conf.UseUpperCase = true
conf.UseLowerCase = true
conf.UseNumber = true
conf.UseSpecial = true
conf.HumanReadable = false
charRange := getCharRange(conf)
for i := 0; i < b.N; i++ {
charToConv, _ := getRandChar(&charRange, 1)
charBytes := []byte(charToConv)
@ -228,7 +232,9 @@ func BenchmarkConvertChar(b *testing.B) {
}
// Contains function to search a given slice for values
func containsByte(allowedBytes []int, currentChar int) bool {
func containsByte(allowedBytes []int, currentChar int, t *testing.T) bool {
t.Helper()
for _, charInt := range allowedBytes {
if charInt == currentChar {
return true

View file

@ -1,14 +1,26 @@
package main
import "regexp"
import (
"github.com/wneessen/apg.go/config"
"regexp"
)
const PwLowerCharsHuman string = "abcdefghjkmnpqrstuvwxyz"
const PwUpperCharsHuman string = "ABCDEFGHJKMNPQRSTUVWXYZ"
const PwLowerChars string = "abcdefghijklmnopqrstuvwxyz"
const PwUpperChars string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
const PwSpecialCharsHuman string = "\"#%*+-/:;=\\_|~"
const PwSpecialChars string = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
const PwNumbersHuman string = "23456789"
const PwNumbers string = "1234567890"
// Provide the range of available characters based on provided parameters
func getCharRange() string {
func getCharRange(config *config.Config) string {
pwUpperChars := PwUpperChars
pwLowerChars := PwLowerChars
pwNumbers := PwNumbers
pwSpecialChars := PwSpecialChars
if config.humanReadable {
if config.HumanReadable {
pwUpperChars = PwUpperCharsHuman
pwLowerChars = PwLowerCharsHuman
pwNumbers = PwNumbersHuman
@ -16,20 +28,20 @@ func getCharRange() string {
}
var charRange string
if config.useLowerCase {
if config.UseLowerCase {
charRange = charRange + pwLowerChars
}
if config.useUpperCase {
if config.UseUpperCase {
charRange = charRange + pwUpperChars
}
if config.useNumber {
if config.UseNumber {
charRange = charRange + pwNumbers
}
if config.useSpecial {
if config.UseSpecial {
charRange = charRange + pwSpecialChars
}
if config.excludeChars != "" {
regExp := regexp.MustCompile("[" + regexp.QuoteMeta(config.excludeChars) + "]")
if config.ExcludeChars != "" {
regExp := regexp.MustCompile("[" + regexp.QuoteMeta(config.ExcludeChars) + "]")
charRange = regExp.ReplaceAllLiteralString(charRange, "")
}

154
config/config.go Normal file
View file

@ -0,0 +1,154 @@
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))
}
}
}

View file

@ -1,94 +0,0 @@
package main
import (
"log"
)
// Parse the parameters and set the according config flags
func parseParams() {
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 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 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))
}
}
}