1
0
mirror of https://github.com/volatiletech/authboss.git synced 2025-01-10 04:17:59 +02:00
authboss/defaults/rules.go

203 lines
4.7 KiB
Go
Raw Normal View History

package defaults
2015-01-18 09:37:05 +02:00
import (
"fmt"
"regexp"
"unicode"
"github.com/pkg/errors"
"github.com/volatiletech/authboss"
2015-01-18 09:37:05 +02:00
)
var blankRegex = regexp.MustCompile(`^\s*$`)
2015-01-18 09:37:05 +02:00
// Rules defines a ruleset by which a string can be validated.
2018-05-01 03:18:03 +02:00
// The errors it produces are english only, with some basic pluralization.
2015-01-18 09:37:05 +02:00
type Rules struct {
2015-01-25 02:07:41 +02:00
// FieldName is the name of the field this is intended to validate.
FieldName string
2015-01-18 09:37:05 +02:00
// MatchError describes the MustMatch regexp to a user.
Required bool
2015-01-18 09:37:05 +02:00
MatchError string
MustMatch *regexp.Regexp
MinLength, MaxLength int
MinLetters int
2015-02-24 21:13:46 +02:00
MinLower, MinUpper int
2015-01-18 09:37:05 +02:00
MinNumeric int
MinSymbols int
AllowWhitespace bool
}
// Errors returns an array of errors for each validation error that
// is present in the given string. Returns nil if there are no errors.
func (r Rules) Errors(toValidate string) authboss.ErrorList {
errs := make(authboss.ErrorList, 0)
2015-01-18 09:37:05 +02:00
ln := len(toValidate)
if r.Required && (ln == 0 || blankRegex.MatchString(toValidate)) {
return append(errs, FieldError{r.FieldName, errors.New("Cannot be blank")})
2015-01-18 09:37:05 +02:00
}
if r.MustMatch != nil {
if !r.MustMatch.MatchString(toValidate) {
2015-01-25 02:07:41 +02:00
errs = append(errs, FieldError{r.FieldName, errors.New(r.MatchError)})
2015-01-18 09:37:05 +02:00
}
}
if (r.MinLength > 0 && ln < r.MinLength) || (r.MaxLength > 0 && ln > r.MaxLength) {
2015-01-25 02:07:41 +02:00
errs = append(errs, FieldError{r.FieldName, errors.New(r.lengthErr())})
2015-01-18 09:37:05 +02:00
}
2015-02-24 21:13:46 +02:00
upper, lower, numeric, symbols, whitespace := tallyCharacters(toValidate)
if upper+lower < r.MinLetters {
2015-01-25 02:07:41 +02:00
errs = append(errs, FieldError{r.FieldName, errors.New(r.charErr())})
2015-01-18 09:37:05 +02:00
}
2015-02-24 21:13:46 +02:00
if upper < r.MinUpper {
errs = append(errs, FieldError{r.FieldName, errors.New(r.upperErr())})
}
2018-11-10 12:36:44 +02:00
if lower < r.MinLower {
2015-02-24 21:13:46 +02:00
errs = append(errs, FieldError{r.FieldName, errors.New(r.lowerErr())})
}
2015-01-18 09:37:05 +02:00
if numeric < r.MinNumeric {
2015-01-25 02:07:41 +02:00
errs = append(errs, FieldError{r.FieldName, errors.New(r.numericErr())})
2015-01-18 09:37:05 +02:00
}
if symbols < r.MinSymbols {
2015-01-25 02:07:41 +02:00
errs = append(errs, FieldError{r.FieldName, errors.New(r.symbolErr())})
2015-01-18 09:37:05 +02:00
}
if !r.AllowWhitespace && whitespace > 0 {
2015-01-25 02:07:41 +02:00
errs = append(errs, FieldError{r.FieldName, errors.New("No whitespace permitted")})
2015-01-18 09:37:05 +02:00
}
if len(errs) == 0 {
return nil
}
2015-01-18 09:37:05 +02:00
return errs
}
// IsValid checks toValidate to make sure it's valid according to the rules.
func (r Rules) IsValid(toValidate string) bool {
return nil == r.Errors(toValidate)
}
// Rules returns an array of strings describing the rules.
func (r Rules) Rules() []string {
2015-03-16 23:42:45 +02:00
var rules []string
2015-01-18 09:37:05 +02:00
if r.MustMatch != nil {
rules = append(rules, r.MatchError)
}
if e := r.lengthErr(); len(e) > 0 {
rules = append(rules, e)
}
if e := r.charErr(); len(e) > 0 {
rules = append(rules, e)
}
2015-02-24 21:13:46 +02:00
if e := r.upperErr(); len(e) > 0 {
rules = append(rules, e)
}
if e := r.lowerErr(); len(e) > 0 {
rules = append(rules, e)
}
2015-01-18 09:37:05 +02:00
if e := r.numericErr(); len(e) > 0 {
rules = append(rules, e)
}
if e := r.symbolErr(); len(e) > 0 {
rules = append(rules, e)
}
return rules
}
func (r Rules) lengthErr() (err string) {
switch {
case r.MinLength > 0 && r.MaxLength > 0:
err = fmt.Sprintf("Must be between %d and %d characters", r.MinLength, r.MaxLength)
case r.MinLength > 0:
2018-05-01 03:18:03 +02:00
err = fmt.Sprintf("Must be at least %d character", r.MinLength)
if r.MinLength > 1 {
err += "s"
}
2015-01-18 09:37:05 +02:00
case r.MaxLength > 0:
2018-05-01 03:18:03 +02:00
err = fmt.Sprintf("Must be at most %d character", r.MaxLength)
if r.MaxLength > 1 {
err += "s"
}
2015-01-18 09:37:05 +02:00
}
return err
}
func (r Rules) charErr() (err string) {
if r.MinLetters > 0 {
2018-05-01 03:18:03 +02:00
err = fmt.Sprintf("Must contain at least %d letter", r.MinLetters)
if r.MinLetters > 1 {
err += "s"
}
2015-01-18 09:37:05 +02:00
}
return err
}
2015-02-24 21:13:46 +02:00
func (r Rules) upperErr() (err string) {
if r.MinUpper > 0 {
2018-05-01 03:18:03 +02:00
err = fmt.Sprintf("Must contain at least %d uppercase letter", r.MinUpper)
if r.MinUpper > 1 {
err += "s"
}
2015-02-24 21:13:46 +02:00
}
return err
}
func (r Rules) lowerErr() (err string) {
if r.MinLower > 0 {
2018-05-01 03:18:03 +02:00
err = fmt.Sprintf("Must contain at least %d lowercase letter", r.MinLower)
if r.MinLower > 1 {
err += "s"
}
2015-02-24 21:13:46 +02:00
}
return err
}
2015-01-18 09:37:05 +02:00
func (r Rules) numericErr() (err string) {
if r.MinNumeric > 0 {
2018-05-01 03:18:03 +02:00
err = fmt.Sprintf("Must contain at least %d number", r.MinNumeric)
if r.MinNumeric > 1 {
err += "s"
}
2015-01-18 09:37:05 +02:00
}
return err
}
func (r Rules) symbolErr() (err string) {
if r.MinSymbols > 0 {
2018-05-01 03:18:03 +02:00
err = fmt.Sprintf("Must contain at least %d symbol", r.MinSymbols)
if r.MinSymbols > 1 {
err += "s"
}
2015-01-18 09:37:05 +02:00
}
return err
}
2015-02-24 21:13:46 +02:00
func tallyCharacters(s string) (upper, lower, numeric, symbols, whitespace int) {
2015-01-18 09:37:05 +02:00
for _, c := range s {
switch {
case unicode.IsLetter(c):
2015-02-24 21:13:46 +02:00
if unicode.IsUpper(c) {
upper++
} else {
lower++
}
2015-01-18 09:37:05 +02:00
case unicode.IsDigit(c):
numeric++
case unicode.IsSpace(c):
whitespace++
default:
symbols++
}
}
2015-02-24 21:13:46 +02:00
return upper, lower, numeric, symbols, whitespace
2015-01-18 09:37:05 +02:00
}