69 lines
2.6 KiB
Go
69 lines
2.6 KiB
Go
package auth
|
||
|
||
import (
|
||
"errors"
|
||
"unicode"
|
||
|
||
"golang.org/x/crypto/bcrypt"
|
||
)
|
||
|
||
// Политика установки пароля. Применяется только при создании/смене
|
||
// пароля: уже существующие аккаунты входят со своими старыми паролями.
|
||
const (
|
||
// MinPasswordLen — минимум символов (считаем руны, не байты:
|
||
// кириллический символ — уже два байта).
|
||
MinPasswordLen = 10
|
||
// MaxPasswordLen — потолок bcrypt: он режет вход длиннее 72 байт,
|
||
// пароль «длиннее максимума» молча стал бы короче.
|
||
MaxPasswordLen = 72
|
||
)
|
||
|
||
// Ошибки политики пароля. Тексты — короткие и англоязычные,
|
||
// как и все ошибки API.
|
||
var (
|
||
ErrPasswordShort = errors.New("password must be at least 10 characters")
|
||
ErrPasswordLong = errors.New("password must be at most 72 characters")
|
||
ErrPasswordWeak = errors.New("password must contain letters and digits")
|
||
)
|
||
|
||
// ValidatePassword проверяет пароль по политике: длина 10..72 и
|
||
// обязательные буквы И цифры. Спецсимволы не требуются — их принуждение
|
||
// чаще порождает «P@ssw0rd!», чем стойкость; зато цифры и буквы отсекают
|
||
// «password» и «1234567890».
|
||
func ValidatePassword(password string) error {
|
||
n := 0
|
||
var hasLetter, hasDigit bool
|
||
for _, r := range password {
|
||
n++
|
||
switch {
|
||
case unicode.IsLetter(r):
|
||
hasLetter = true
|
||
case unicode.IsDigit(r):
|
||
hasDigit = true
|
||
}
|
||
}
|
||
if n < MinPasswordLen {
|
||
return ErrPasswordShort
|
||
}
|
||
if len(password) > MaxPasswordLen {
|
||
return ErrPasswordLong
|
||
}
|
||
if !hasLetter || !hasDigit {
|
||
return ErrPasswordWeak
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// HashPassword считает bcrypt-хеш с cost 12. Ошибка возможна только
|
||
// на очень длинных паролях (bcrypt режет вход длиннее 72 байт).
|
||
func HashPassword(password string) (string, error) {
|
||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 12)
|
||
return string(bytes), err
|
||
}
|
||
|
||
// CheckPassword сверяет пароль с хешем. Сравнение за постоянное время —
|
||
// перебор по времени ответа не даёт выигрыша.
|
||
func CheckPassword(hash, password string) bool {
|
||
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil
|
||
}
|