From cbecd9caec7e77506120075a0fcd99f4bebd64d6 Mon Sep 17 00:00:00 2001 From: brent saner Date: Wed, 17 Jul 2024 04:03:31 -0400 Subject: [PATCH] v0.1.3 FIXED: * XML, YAML marshaling/unmarshaling of pwgenerator.CharSet. --- go.mod | 18 +- go.sum | 52 ++++-- pwgenerator/_testdata/test.json | 15 ++ pwgenerator/_testdata/test.toml | 13 ++ pwgenerator/_testdata/test.xml | 14 ++ pwgenerator/_testdata/test.yml | 13 ++ pwgenerator/funcs_charset.go | 60 +++++++ pwgenerator/funcs_genopts.go | 284 ++++++++++++++++---------------- pwgenerator/funcs_test.go | 148 +++++++++++++++++ pwgenerator/types.go | 26 +-- 10 files changed, 465 insertions(+), 178 deletions(-) create mode 100644 pwgenerator/_testdata/test.json create mode 100644 pwgenerator/_testdata/test.toml create mode 100644 pwgenerator/_testdata/test.xml create mode 100644 pwgenerator/_testdata/test.yml create mode 100644 pwgenerator/funcs_test.go diff --git a/go.mod b/go.mod index fb2c69d..88bae80 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,23 @@ module r00t2.io/pwgen -go 1.17 +go 1.22.4 require ( + github.com/BurntSushi/toml v1.4.0 + github.com/goccy/go-yaml v1.12.0 github.com/hlandau/passlib v1.0.11 - github.com/jessevdk/go-flags v1.5.0 - gopkg.in/yaml.v3 v3.0.0 - r00t2.io/goutils v1.3.1 + github.com/jessevdk/go-flags v1.6.1 + gopkg.in/yaml.v3 v3.0.1 + r00t2.io/goutils v1.7.0 ) require ( - golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 // indirect - golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect + github.com/fatih/color v1.17.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect gopkg.in/hlandau/easymetric.v1 v1.0.0 // indirect gopkg.in/hlandau/measurable.v1 v1.0.1 // indirect gopkg.in/hlandau/passlib.v1 v1.0.11 // indirect diff --git a/go.sum b/go.sum index ca20d18..72be47b 100644 --- a/go.sum +++ b/go.sum @@ -1,21 +1,39 @@ +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/goccy/go-yaml v1.12.0 h1:/1WHjnMsI1dlIBQutrvSMGZRQufVO3asrHfTwfACoPM= +github.com/goccy/go-yaml v1.12.0/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hlandau/passlib v1.0.11 h1:GNcnM0Iwqx5M4IDCdKi9pJI/jmf6Z4NooIh8ND7rRBg= github.com/hlandau/passlib v1.0.11/go.mod h1:77ovAz+VLR4VrRNrNhFTSSzYhZ4iUrGpXcBeC7cVRIU= -github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 h1:SLP7Q4Di66FONjDJbCYrCRrh97focO6sLogHO7/g8F0= -golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4= +github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 h1:LLhsEBxRTBLuKlQxFBYUOU8xyFgXv6cOTp2HASDlsDk= +golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/hlandau/easymetric.v1 v1.0.0 h1:ZbfbH7W3giuVDjWUoFhDOjjv20hiPr5HZ2yMV5f9IeE= @@ -24,8 +42,8 @@ gopkg.in/hlandau/measurable.v1 v1.0.1 h1:wH5UZKCRUnRr1iD+xIZfwhtxhmr+bprRJttqA1R gopkg.in/hlandau/measurable.v1 v1.0.1/go.mod h1:6N+SYJGMTmetsx7wskULP+juuO+++tsHJkAgzvzsbuM= gopkg.in/hlandau/passlib.v1 v1.0.11 h1:vKeHwGRdWBD9mm4bJ56GAAdBXpFUYvg/BYYkmphjnmA= gopkg.in/hlandau/passlib.v1 v1.0.11/go.mod h1:wxGAv2CtQHlzWY8NJp+p045yl4WHyX7v2T6XbOcmqjM= -gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -r00t2.io/goutils v1.3.1 h1:mk8B2v7fM3gYoX64CDV206+/j8Z5iSJcszcPFjuvf3o= -r00t2.io/goutils v1.3.1/go.mod h1:9ObJI9S71wDLTOahwoOPs19DhZVYrOh4LEHmQ8SW4Lk= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +r00t2.io/goutils v1.7.0 h1:iQluWlkOyBwOKaK94D5QSnSMYpGKtMb/5WjefmdfHgI= +r00t2.io/goutils v1.7.0/go.mod h1:9ObJI9S71wDLTOahwoOPs19DhZVYrOh4LEHmQ8SW4Lk= r00t2.io/sysutils v1.1.1/go.mod h1:Wlfi1rrJpoKBOjWiYM9rw2FaiZqraD6VpXyiHgoDo/o= diff --git a/pwgenerator/_testdata/test.json b/pwgenerator/_testdata/test.json new file mode 100644 index 0000000..1ba5907 --- /dev/null +++ b/pwgenerator/_testdata/test.json @@ -0,0 +1,15 @@ +{ + "do_alpha": true, + "do_numeric": true, + "do_symbols": true, + "do_extended": false, + "uppers": 0, + "lowers": 0, + "numbers": 0, + "symbols": 0, + "extended": 0, + "disabled_chars": "abc😊", + "length_min": 0, + "length_max": 0, + "count": 0 +} diff --git a/pwgenerator/_testdata/test.toml b/pwgenerator/_testdata/test.toml new file mode 100644 index 0000000..8082ab1 --- /dev/null +++ b/pwgenerator/_testdata/test.toml @@ -0,0 +1,13 @@ +Letters = true +Numbers = true +Symbols = true +ExtendedSymbols = false +CountUpper = 0 +CountLower = 0 +CountNumber = 0 +CountSymbol = 0 +CountExtended = 0 +DisabledCharSet = "abc😊" +MinLength = 0 +MaxLength = 0 +Count = 0 diff --git a/pwgenerator/_testdata/test.xml b/pwgenerator/_testdata/test.xml new file mode 100644 index 0000000..a332765 --- /dev/null +++ b/pwgenerator/_testdata/test.xml @@ -0,0 +1,14 @@ + diff --git a/pwgenerator/_testdata/test.yml b/pwgenerator/_testdata/test.yml new file mode 100644 index 0000000..db19510 --- /dev/null +++ b/pwgenerator/_testdata/test.yml @@ -0,0 +1,13 @@ +Letters: true +Numbers: true +Symbols: true +Extended Symbols: false +Number of Uppercase Letters: 0 +Number of Lowercase Letters: 0 +Number of Numeric Characters: 0 +Number of Symbols: 0 +Number of Extended Symbols: 0 +Disabled CharSet: abc😊 +Minimum Length: 0 +Maximum Length: 0 +Number of Passwords: 0 diff --git a/pwgenerator/funcs_charset.go b/pwgenerator/funcs_charset.go index 6e3747a..bea0ef3 100644 --- a/pwgenerator/funcs_charset.go +++ b/pwgenerator/funcs_charset.go @@ -55,6 +55,30 @@ func (c *CharSet) Less(i, j int) (isBefore bool) { return } +// MarshalText conforms to encoding/TextUnmarshaler. +func (c *CharSet) MarshalText() (text []byte, err error) { + + if c == nil { + return + } + + text = []byte(c.String()) + + return +} + +// MarshalYAML conforms to (github.com/goccy/go-yaml)yaml.BytesMarshaler. (For some reason, this only works as direct, not pointer.) +func (c CharSet) MarshalYAML() (b []byte, err error) { + + if c == nil { + return + } + + b = []byte(c.String()) + + return +} + // RandChar returns a random character from a CharSet. func (c *CharSet) RandChar() (char Char, err error) { @@ -86,3 +110,39 @@ func (c *CharSet) Swap(i, j int) { return } + +// UnmarshalText conforms to an encoding.TextUnmarshaler. +func (c *CharSet) UnmarshalText(text []byte) (err error) { + + var cs []Char + + if text == nil { + return + } + + for _, i := range string(text) { + cs = append(cs, Char(i)) + } + + *c = cs + + return +} + +// UnmarshalYAML conforms to (github.com/goccy/go-yaml)yaml.BytesUnmarshaler. +func (c *CharSet) UnmarshalYAML(text []byte) (err error) { + + var cs []Char + + if text == nil { + return + } + + for _, i := range string(text) { + cs = append(cs, Char(i)) + } + + *c = cs + + return +} diff --git a/pwgenerator/funcs_genopts.go b/pwgenerator/funcs_genopts.go index 470af19..610691d 100644 --- a/pwgenerator/funcs_genopts.go +++ b/pwgenerator/funcs_genopts.go @@ -1,13 +1,48 @@ package pwgenerator import ( - "encoding/xml" - "strings" - "time" + `encoding/xml` + `strings` + `time` "r00t2.io/goutils/multierr" ) +// Chars returns the list of evaluated characters that would be used in a GenOpts. +func (o *GenOpts) Chars() (chars CharSet, err error) { + + chars = make(CharSet, 0) + + if err = o.sanChk(); err != nil { + return + } + + if o.explicitCharset != nil && len(o.explicitCharset) != 0 { + chars = o.explicitCharset + return + } + + if o.Alpha { + chars = append(chars, alpha...) + } + if o.Numeric { + chars = append(chars, numeric...) + } + if o.Symbols { + chars = append(chars, symbols...) + } + if o.ExtendedSymbols { + chars = append(chars, extendedSymbols...) + } + + if chars == nil || len(chars) == 0 { + err = ErrEmptyCharsets + return + } + + return +} + // Generate generates a list of passwords for a GenOpts. func (o *GenOpts) Generate() (passwords []string, err error) { @@ -46,49 +81,6 @@ func (o *GenOpts) Generate() (passwords []string, err error) { return } -// MustGenerate is like Generate, but will panic on error instead of returning. -func (o *GenOpts) MustGenerate() (passwords []string) { - - var err error - - if passwords, err = o.Generate(); err != nil { - panic(err) - } - - return -} - -/* - GenOnce generates a *single* password from a GenOpts. - - This method is particularly useful/convenient if GenOpts.Count is 1 - and you don't want to have to assign to a slice and then get the first index. -*/ -func (o GenOpts) GenerateOnce() (password string, err error) { - - var passwds []string - - o.Count = 1 - if passwds, err = o.Generate(); err != nil { - return - } - password = passwds[0] - - return -} - -// MustGenerateOnce is like GenerateOnce, but will panic on error instead of returning. -func (o *GenOpts) MustGenerateOnce() (password string) { - - var err error - - if password, err = o.GenerateOnce(); err != nil { - panic(err) - } - - return -} - /* GenerateCollection returns a PwCollection instead of a slice of password text. @@ -99,8 +91,8 @@ func (o *GenOpts) MustGenerateOnce() (password string) { func (o *GenOpts) GenerateCollection(hashAlgos []pwHash) (collection *PwCollection, err error) { var charset CharSet - var errs *multierr.MultiError = multierr.NewMultiError(nil) var passwd string + var errs *multierr.MultiError = multierr.NewMultiError(nil) collection = &PwCollection{ XMLName: xml.Name{ @@ -144,6 +136,109 @@ func (o *GenOpts) GenerateCollection(hashAlgos []pwHash) (collection *PwCollecti return } +/* + GenOnce generates a *single* password from a GenOpts. + + This method is particularly useful/convenient if GenOpts.Count is 1 + and you don't want to have to assign to a slice and then get the first index. +*/ +func (o *GenOpts) GenerateOnce() (password string, err error) { + + var passwds []string + + o.Count = 1 + if passwds, err = o.Generate(); err != nil { + return + } + password = passwds[0] + + return +} + +// MustGenerate is like Generate, but will panic on error instead of returning. +func (o *GenOpts) MustGenerate() (passwords []string) { + + var err error + + if passwords, err = o.Generate(); err != nil { + panic(err) + } + + return +} + +// MustGenerateOnce is like GenerateOnce, but will panic on error instead of returning. +func (o *GenOpts) MustGenerateOnce() (password string) { + + var err error + + if password, err = o.GenerateOnce(); err != nil { + panic(err) + } + + return +} + +/* + SetExplicitCharset sets a GenOpts' charset to an explicit selection. It is deduplicated before being used unless inclDupes is true. + (Duplicate runes can be used to increase the likelihood of specific characters.) + + Note that if an explicit charset is defined, the following opts will have NO effect: + + Alpha + Numeric + Symbols + ExtendedSymbols + CountUpper + CountLower + CountSymbols + CountExtended + DisabledChars + + To have these fields be used again, call GenOpts.UnsetExplicitCharset. + + chars can be a CharSet, []rune, []byte, []string, or string +*/ +func (o *GenOpts) SetExplicitCharset(chars interface{}, inclDupes bool) (err error) { + + var charset CharSet + + switch t := chars.(type) { + case []rune: + charset = CharSet(string(t)) + case []byte: + charset = CharSet(string(t)) + case []string: + s := strings.Join(t, "") + charset = CharSet(s) + case string: + charset = CharSet(t) + default: + err = ErrBadType + return + } + + if !inclDupes { + sortDedupe(&charset) + } + + o.explicitCharset = charset + + return +} + +/* + UnsetExplicitCharset removes the explicit charset used for generation and instead uses the fields specified in the actual GenOpts. + + See GenOpts.SetExplicitCharset for more details. +*/ +func (o *GenOpts) UnsetExplicitCharset() { + + o.explicitCharset = nil + + return +} + // generatePassword generates a single password from CharSet c (plus any minimum requirements). func (o *GenOpts) generatePassword(c CharSet) (password string, err error) { @@ -281,101 +376,6 @@ func (o *GenOpts) generatePassword(c CharSet) (password string, err error) { return } -// Chars returns the list of evaluated characters that would be used in a GenOpts. -func (o *GenOpts) Chars() (chars CharSet, err error) { - - chars = make(CharSet, 0) - - if err = o.sanChk(); err != nil { - return - } - - if o.explicitCharset != nil && len(o.explicitCharset) != 0 { - chars = o.explicitCharset - return - } - - if o.Alpha { - chars = append(chars, alpha...) - } - if o.Numeric { - chars = append(chars, numeric...) - } - if o.Symbols { - chars = append(chars, symbols...) - } - if o.ExtendedSymbols { - chars = append(chars, extendedSymbols...) - } - - if chars == nil || len(chars) == 0 { - err = ErrEmptyCharsets - return - } - - return -} - -/* - SetExplicitCharset sets a GenOpts' charset to an explicit selection. It is deduplicated before being used unless inclDupes is true. - (Duplicate runes can be used to increase the likelihood of specific characters.) - - Note that if an explicit charset is defined, the following opts will have NO effect: - - Alpha - Numeric - Symbols - ExtendedSymbols - CountUpper - CountLower - CountSymbols - CountExtended - DisabledChars - - To have these fields be used again, call GenOpts.UnsetExplicitCharset. - - chars can be a CharSet, []rune, []byte, []string, or string -*/ -func (o *GenOpts) SetExplicitCharset(chars interface{}, inclDupes bool) (err error) { - - var charset CharSet - - switch t := chars.(type) { - case []rune: - charset = CharSet(string(t)) - case []byte: - charset = CharSet(string(t)) - case []string: - s := strings.Join(t, "") - charset = CharSet(s) - case string: - charset = CharSet(t) - default: - err = ErrBadType - return - } - - if !inclDupes { - sortDedupe(&charset) - } - - o.explicitCharset = charset - - return -} - -/* - UnsetExplicitCharset removes the explicit charset used for generation and instead uses the fields specified in the actual GenOpts. - - See GenOpts.SetExplicitCharset for more details. -*/ -func (o *GenOpts) UnsetExplicitCharset() { - - o.explicitCharset = nil - - return -} - // getFilter gets a filter counter. func (o *GenOpts) getFilter() (f *selectFilter) { diff --git a/pwgenerator/funcs_test.go b/pwgenerator/funcs_test.go new file mode 100644 index 0000000..5f97cd7 --- /dev/null +++ b/pwgenerator/funcs_test.go @@ -0,0 +1,148 @@ +package pwgenerator + +import ( + `bytes` + `embed` + `encoding/json` + `encoding/xml` + `fmt` + "testing" + + `github.com/BurntSushi/toml` + `github.com/goccy/go-yaml` +) + +var ( + //go:embed "_testdata" + testFiles embed.FS +) + +var ( + testGen *GenOpts = &GenOpts{ + Alpha: true, + Numeric: true, + Symbols: true, + ExtendedSymbols: false, + CountUpper: 0, + CountLower: 0, + CountNumbers: 0, + CountSymbols: 0, + CountExtended: 0, + DisabledChars: CharSet{ + Char('a'), + Char('b'), + Char('c'), + Char('😊'), + }, + LengthMin: 0, + LengthMax: 0, + Count: 0, + } + testGenNoDisabled *GenOpts = &GenOpts{ + Alpha: true, + Numeric: true, + Symbols: true, + ExtendedSymbols: false, + CountUpper: 0, + CountLower: 0, + CountNumbers: 0, + CountSymbols: 0, + CountExtended: 0, + DisabledChars: nil, + LengthMin: 0, + LengthMax: 0, + Count: 0, + } +) + +func TestParse(t *testing.T) { + + var cmpB []byte + var err error + var g *GenOpts + var mB map[string][]byte = make(map[string][]byte) + + if cmpB, err = json.Marshal(testGen); err != nil { + t.Fatal(err) + } + + for _, i := range []string{ + "json", + "xml", + "yml", + "toml", + } { + g = new(GenOpts) + + if mB[i], err = testFiles.ReadFile(fmt.Sprintf("_testdata/test.%s", i)); err != nil { + t.Fatal(err) + } + + switch i { + case "json": + if err = json.Unmarshal(mB[i], g); err != nil { + t.Fatal(err) + } + case "xml": + if err = xml.Unmarshal(mB[i], g); err != nil { + t.Fatal(err) + } + case "yml": + if err = yaml.Unmarshal(mB[i], g); err != nil { + t.Fatal(err) + } + case "toml": + if err = toml.Unmarshal(mB[i], g); err != nil { + t.Fatal(err) + } + } + if mB[i], err = json.Marshal(g); err != nil { + t.Fatal(err) + } + + if !bytes.Equal(mB[i], cmpB) { + t.Fatalf("Syntax/testfile %s failed comparison.", i) + } + } +} + +func TestRender(t *testing.T) { + + var b []byte + var err error + var buf *bytes.Buffer = new(bytes.Buffer) + + // JSON + if b, err = json.MarshalIndent(testGen, "", " "); err != nil { + t.Fatal(err) + } + buf.WriteString("# JSON\n") + buf.Write(b) + buf.WriteString("\n\n") + + // XML + if b, err = xml.MarshalIndent(testGen, "", " "); err != nil { + t.Fatal(err) + } + buf.WriteString("# XML\n") + buf.Write(b) + buf.WriteString("\n\n") + + // YAML + if b, err = yaml.Marshal(testGen); err != nil { + t.Fatal(err) + } + buf.WriteString("# YAML\n") + buf.Write(b) + buf.WriteString("\n\n") + + // TOML + if b, err = toml.Marshal(testGen); err != nil { + t.Fatal(err) + } + buf.WriteString("# TOML\n") + buf.Write(b) + buf.WriteString("\n\n") + + t.Log(buf.String()) +} diff --git a/pwgenerator/types.go b/pwgenerator/types.go index 470e8df..05635b3 100644 --- a/pwgenerator/types.go +++ b/pwgenerator/types.go @@ -32,34 +32,34 @@ type GenOpts struct { */ // HumanOnly bool `json:"do_human_readable" toml:"do_human_readable" yaml:"Human-Readable Only" xml:"doHumanReadable,attr"` // Alpha is true if letters (0x41 to 0x5a, 0x61 to 0x7a) should be included. - Alpha bool `json:"do_alpha" toml:"do_alpha" yaml:"Letters" xml:"doAlpha,attr"` + Alpha bool `json:"do_alpha" toml:"Letters" yaml:"Letters" xml:"doAlpha,attr"` // Numeric is true if numbers (0x30 to 0x39) should be included. - Numeric bool `json:"do_numeric" toml:"do_numeric" yaml:"Numbers" xml:"doNumeric,attr"` + Numeric bool `json:"do_numeric" toml:"Numbers" yaml:"Numbers" xml:"doNumeric,attr"` // Symbols is true if non-alphanumeric characters (between 0x21 and 0x7e) should be included. - Symbols bool `json:"do_symbols" toml:"do_symbols" yaml:"Symbols" xml:"doSymbols,attr"` + Symbols bool `json:"do_symbols" toml:"Symbols" yaml:"Symbols" xml:"doSymbols,attr"` // ExtendedSymbols is true if non-alphanumeric characters in the "extended ASCII" set (0x80 to 0xff) should be included. - ExtendedSymbols bool `json:"do_extended" toml:"do_extended" yaml:"Extended Symbols" xml:"doExtendedSymbols,attr"` + ExtendedSymbols bool `json:"do_extended" toml:"ExtendedSymbols" yaml:"Extended Symbols" xml:"doExtendedSymbols,attr"` // CountUpper specifies how many uppercase letters (0x41 to 0x5a) should be specified at a minimum. - CountUpper uint `json:"uppers" toml:"uppers" yaml:"Number of Uppercase Letters" xml:"numUppers,attr"` + CountUpper uint `json:"uppers" toml:"CountUpper" yaml:"Number of Uppercase Letters" xml:"numUppers,attr"` // CountLower specifies how many lowercase letters (0x61 to 0x7a) should be specified at a minimum. - CountLower uint `json:"lowers" toml:"lowers" yaml:"Number of Lowercase Letters" xml:"numLowers,attr"` + CountLower uint `json:"lowers" toml:"CountLower" yaml:"Number of Lowercase Letters" xml:"numLowers,attr"` // CountNumbers specifies how many numbers (0x30 to 0x39) should be specified at a minimum. - CountNumbers uint `json:"numbers" toml:"numbers" yaml:"Number of Numeric Characters" xml:"numNumeric,attr"` + CountNumbers uint `json:"numbers" toml:"CountNumber" yaml:"Number of Numeric Characters" xml:"numNumeric,attr"` // CountSymbols specifies how many symbols (0x21 to 0x7e) should be specified at a minimum. - CountSymbols uint `json:"symbols" toml:"symbols" yaml:"Number of Symbols" xml:"numSymbols,attr"` + CountSymbols uint `json:"symbols" toml:"CountSymbol" yaml:"Number of Symbols" xml:"numSymbols,attr"` // CountExtended specifies how many extended symbols (0x80 to 0xff) should be specified at a minimum. - CountExtended uint `json:"extended" toml:"extended" yaml:"Number of Extended Symbols" xml:"numXSymbols,attr"` + CountExtended uint `json:"extended" toml:"CountExtended" yaml:"Number of Extended Symbols" xml:"numXSymbols,attr"` // DisabledChars includes characters that should NOT be included from the above selection options. - DisabledChars CharSet `json:"disabled_chars,omitempty" toml:"disabled_chars,omitempty" yaml:"Disabled CharSet,omitempty" xml:"disabledChars,omitempty"` + DisabledChars CharSet `json:"disabled_chars,omitempty" toml:"DisabledCharSet,omitempty" yaml:"Disabled CharSet,omitempty" xml:"disabledChars,attr,omitempty"` // LengthMin specifies how long (in characters/bytes) each password should be at minimum. Use 0 for no minimum. - LengthMin uint `json:"length_min" toml:"length_min" yaml:"Minimum Length" xml:"minLen,attr"` + LengthMin uint `json:"length_min" toml:"MinLength" yaml:"Minimum Length" xml:"minLen,attr"` /* LengthMax specifies the maximum length for each password. Set to 0 for no limit (the language has a hard limit of 18446744073709551615; this is limited to 256 for performance reasons). */ - LengthMax uint `json:"length_max" toml:"length_max" yaml:"Maximum Length" xml:"maxLen,attr"` + LengthMax uint `json:"length_max" toml:"MaxLength" yaml:"Maximum Length" xml:"maxLen,attr"` // Count specifies how many passwords to generate. If 0, the default is 1. - Count uint `json:"count" toml:"count" yaml:"Number of Passwords" xml:"genCount,attr"` + Count uint `json:"count" toml:"Count" yaml:"Number of Passwords" xml:"genCount,attr"` // explicitCharset is the collection of acceptable characters as explicitly defined by the caller, if any. explicitCharset CharSet }