github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/common/weak_passwords.go (about)

     1  package common
     2  
     3  import (
     4  	"errors"
     5  	"strconv"
     6  	"strings"
     7  	"unicode"
     8  )
     9  
    10  var weakPassStrings []string
    11  var weakPassLit = make(map[string]struct{})
    12  var ErrWeakPasswordNone = errors.New("You didn't put in a password.")
    13  var ErrWeakPasswordShort = errors.New("Your password needs to be at-least eight characters long")
    14  var ErrWeakPasswordNameInPass = errors.New("You can't use your name in your password.")
    15  var ErrWeakPasswordEmailInPass = errors.New("You can't use your email in your password.")
    16  var ErrWeakPasswordCommon = errors.New("You may not use a password that is in common use")
    17  var ErrWeakPasswordNoNumbers = errors.New("You don't have any numbers in your password")
    18  var ErrWeakPasswordNoUpper = errors.New("You don't have any uppercase characters in your password")
    19  var ErrWeakPasswordNoLower = errors.New("You don't have any lowercase characters in your password")
    20  var ErrWeakPasswordUniqueChars = errors.New("You don't have enough unique characters in your password")
    21  var ErrWeakPasswordContains error
    22  
    23  type weakpassHolder struct {
    24  	Contains []string `json:"contains"`
    25  	Literal  []string `json:"literal"`
    26  }
    27  
    28  func InitWeakPasswords() error {
    29  	var weakpass weakpassHolder
    30  	e := unmarshalJsonFile("./config/weakpass_default.json", &weakpass)
    31  	if e != nil {
    32  		return e
    33  	}
    34  
    35  	wcon := make(map[string]struct{})
    36  	for _, item := range weakpass.Contains {
    37  		wcon[item] = struct{}{}
    38  	}
    39  	for _, item := range weakpass.Literal {
    40  		weakPassLit[item] = struct{}{}
    41  	}
    42  
    43  	weakpass = weakpassHolder{}
    44  	e = unmarshalJsonFileIgnore404("./config/weakpass.json", &weakpass)
    45  	if e != nil {
    46  		return e
    47  	}
    48  
    49  	for _, item := range weakpass.Contains {
    50  		wcon[item] = struct{}{}
    51  	}
    52  	for _, item := range weakpass.Literal {
    53  		weakPassLit[item] = struct{}{}
    54  	}
    55  	weakPassStrings = make([]string, len(wcon))
    56  	var i int
    57  	for pattern, _ := range wcon {
    58  		weakPassStrings[i] = pattern
    59  		i++
    60  	}
    61  
    62  	s := "You may not have "
    63  	for i, passBit := range weakPassStrings {
    64  		if i > 0 {
    65  			if i == len(weakPassStrings)-1 {
    66  				s += " or "
    67  			} else {
    68  				s += ", "
    69  			}
    70  		}
    71  		s += "'" + passBit + "'"
    72  	}
    73  	ErrWeakPasswordContains = errors.New(s + " in your password")
    74  
    75  	return nil
    76  }
    77  
    78  func WeakPassword(password, username, email string) error {
    79  	lowPassword := strings.ToLower(password)
    80  	switch {
    81  	case password == "":
    82  		return ErrWeakPasswordNone
    83  	case len(password) < 8:
    84  		return ErrWeakPasswordShort
    85  	case len(username) > 3 && strings.Contains(lowPassword, strings.ToLower(username)):
    86  		return ErrWeakPasswordNameInPass
    87  	case len(email) > 2 && strings.Contains(lowPassword, strings.ToLower(email)):
    88  		return ErrWeakPasswordEmailInPass
    89  	}
    90  	if len(lowPassword) > 30 {
    91  		return nil
    92  	}
    93  
    94  	litPass := lowPassword
    95  	for i := 0; i < 10; i++ {
    96  		litPass = strings.TrimSuffix(litPass, strconv.Itoa(i))
    97  	}
    98  	_, ok := weakPassLit[litPass]
    99  	if ok {
   100  		return ErrWeakPasswordCommon
   101  	}
   102  	for _, passBit := range weakPassStrings {
   103  		if strings.Contains(lowPassword, passBit) {
   104  			return ErrWeakPasswordContains
   105  		}
   106  	}
   107  
   108  	charMap := make(map[rune]int)
   109  	var numbers, symbols, upper, lower int
   110  	for _, char := range password {
   111  		charItem, ok := charMap[char]
   112  		if ok {
   113  			charItem++
   114  		} else {
   115  			charItem = 1
   116  		}
   117  		charMap[char] = charItem
   118  
   119  		if unicode.IsLetter(char) {
   120  			if unicode.IsUpper(char) {
   121  				upper++
   122  			} else {
   123  				lower++
   124  			}
   125  		} else if unicode.IsNumber(char) {
   126  			numbers++
   127  		} else {
   128  			symbols++
   129  		}
   130  	}
   131  
   132  	if upper == 0 {
   133  		return ErrWeakPasswordNoUpper
   134  	}
   135  	if lower == 0 {
   136  		return ErrWeakPasswordNoLower
   137  	}
   138  	if len(password) < 18 {
   139  		if numbers == 0 {
   140  			return ErrWeakPasswordNoNumbers
   141  		}
   142  		if (len(password) / 2) > len(charMap) {
   143  			return ErrWeakPasswordUniqueChars
   144  		}
   145  	} else if (len(password) / 3) > len(charMap) {
   146  		// Be a little lenient on the number of unique characters for long passwords
   147  		return ErrWeakPasswordUniqueChars
   148  	}
   149  	return nil
   150  }