improve password validation, add charset generator
This commit is contained in:
parent
91476a29b2
commit
df1596312d
134
src/charsets/charsets.go
Normal file
134
src/charsets/charsets.go
Normal file
@ -0,0 +1,134 @@
|
||||
package charsets
|
||||
|
||||
import "strings"
|
||||
|
||||
type RandInt interface {
|
||||
Int() int
|
||||
}
|
||||
|
||||
type Charset interface {
|
||||
TestRune(char rune) bool
|
||||
RandomRune(r RandInt) rune
|
||||
RandomString(r RandInt, size int) string
|
||||
|
||||
String() string
|
||||
}
|
||||
|
||||
func NewCharsetFromASCII(offset, size int) Charset {
|
||||
return charsetASCII{offset: offset, size: size}
|
||||
}
|
||||
|
||||
type charsetASCII struct {
|
||||
offset int
|
||||
size int
|
||||
}
|
||||
|
||||
func (c charsetASCII) TestRune(char rune) bool {
|
||||
return int(char) >= c.offset && int(char) < c.offset+c.size
|
||||
}
|
||||
|
||||
func (c charsetASCII) RandomRune(r RandInt) rune {
|
||||
num := c.offset + r.Int()%(c.size-1)
|
||||
return rune(num)
|
||||
}
|
||||
|
||||
func (c charsetASCII) RandomString(r RandInt, size int) string {
|
||||
builder := strings.Builder{}
|
||||
for i := 0; i < size; i++ {
|
||||
builder.WriteRune(c.RandomRune(r))
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func (c charsetASCII) String() string {
|
||||
builder := strings.Builder{}
|
||||
for i := 0; i < c.size; i++ {
|
||||
builder.WriteRune(rune(c.offset + i))
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func NewCharsetFromString(s string) Charset {
|
||||
charsArray := make([]rune, len(s))
|
||||
charsMap := make(map[rune]bool, len(s))
|
||||
for i, v := range s {
|
||||
charsArray[i] = v
|
||||
charsMap[v] = true
|
||||
}
|
||||
|
||||
return charsetFromString{
|
||||
charsArray: charsArray,
|
||||
charsMap: charsMap,
|
||||
}
|
||||
}
|
||||
|
||||
type charsetFromString struct {
|
||||
charsMap map[rune]bool
|
||||
charsArray []rune
|
||||
}
|
||||
|
||||
func (c charsetFromString) TestRune(char rune) bool {
|
||||
return c.charsMap[char]
|
||||
}
|
||||
|
||||
func (c charsetFromString) RandomRune(r RandInt) rune {
|
||||
num := r.Int() % (len(c.charsArray) - 1)
|
||||
return c.charsArray[num]
|
||||
}
|
||||
|
||||
func (c charsetFromString) RandomString(r RandInt, size int) string {
|
||||
builder := strings.Builder{}
|
||||
for i := 0; i < size; i++ {
|
||||
builder.WriteRune(c.RandomRune(r))
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func (c charsetFromString) String() string {
|
||||
builder := strings.Builder{}
|
||||
for _, v := range c.charsArray {
|
||||
builder.WriteRune(v)
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func NewCharsetUnion(opts ...Charset) Charset {
|
||||
charsets := []Charset{}
|
||||
return charsetUnion{
|
||||
charsets: append(charsets, opts...),
|
||||
}
|
||||
}
|
||||
|
||||
type charsetUnion struct {
|
||||
charsets []Charset
|
||||
}
|
||||
|
||||
func (c charsetUnion) TestRune(char rune) bool {
|
||||
for _, charset := range c.charsets {
|
||||
if charset.TestRune(char) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c charsetUnion) RandomRune(r RandInt) rune {
|
||||
index := r.Int() % (len(c.charsets) - 1)
|
||||
charset := c.charsets[index]
|
||||
|
||||
return charset.RandomRune(r)
|
||||
}
|
||||
|
||||
func (c charsetUnion) RandomString(r RandInt, size int) string {
|
||||
builder := strings.Builder{}
|
||||
for i := 0; i < size; i++ {
|
||||
index := r.Int() % (len(c.charsets) - 1)
|
||||
charset := c.charsets[index]
|
||||
builder.WriteRune(charset.RandomRune(r))
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func (c charsetUnion) String() string {
|
||||
return ""
|
||||
}
|
||||
36
src/charsets/enum.go
Normal file
36
src/charsets/enum.go
Normal file
@ -0,0 +1,36 @@
|
||||
package charsets
|
||||
|
||||
type CharsetType int
|
||||
|
||||
const (
|
||||
CharsetTypeAll CharsetType = iota
|
||||
CharsetTypeLettersLower
|
||||
CharsetTypeLettersUpper
|
||||
CharsetTypeLetters
|
||||
CharsetTypeNumeric
|
||||
)
|
||||
|
||||
var (
|
||||
charsetNumeric = NewCharsetFromASCII(0x30, 10)
|
||||
charsetLettersLower = NewCharsetFromASCII(0x41, 26)
|
||||
charsetLettersUpper = NewCharsetFromASCII(0x61, 26)
|
||||
charsetLetters = NewCharsetUnion(charsetLettersLower, charsetLettersUpper)
|
||||
charsetAll = NewCharsetUnion(charsetNumeric, charsetLettersLower, charsetLettersUpper)
|
||||
)
|
||||
|
||||
func GetCharset(charsetType CharsetType) Charset {
|
||||
switch charsetType {
|
||||
case CharsetTypeNumeric:
|
||||
return charsetNumeric
|
||||
case CharsetTypeLettersLower:
|
||||
return charsetLettersLower
|
||||
case CharsetTypeLettersUpper:
|
||||
return charsetLettersLower
|
||||
case CharsetTypeLetters:
|
||||
return charsetLetters
|
||||
case CharsetTypeAll:
|
||||
return charsetAll
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"backend/src/charsets"
|
||||
"backend/src/core/repos"
|
||||
"backend/src/core/utils"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ShortlinkService interface {
|
||||
@ -18,18 +20,21 @@ type NewShortlinkServiceParams struct {
|
||||
|
||||
func NewShortlinkSevice(params NewShortlinkServiceParams) ShortlinkService {
|
||||
return &shortlinkService{
|
||||
randomUtil: *utils.NewRand(),
|
||||
cache: params.Cache,
|
||||
}
|
||||
}
|
||||
|
||||
type shortlinkService struct {
|
||||
randomUtil utils.RandomUtil
|
||||
cache repos.Cache[string, string]
|
||||
}
|
||||
|
||||
func (s *shortlinkService) CreateLink(in string) (string, error) {
|
||||
str := s.randomUtil.RandomID(10, utils.CharsetAll)
|
||||
charset := charsets.GetCharset(charsets.CharsetTypeAll)
|
||||
|
||||
src := rand.NewSource(time.Now().UnixMicro())
|
||||
randGen := rand.New(src)
|
||||
str := charset.RandomString(randGen, 10)
|
||||
|
||||
s.cache.Set(str, in, 7*24*60*60)
|
||||
return str, nil
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"backend/src/charsets"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
@ -13,10 +14,15 @@ type PasswordUtil interface {
|
||||
}
|
||||
|
||||
func NewPasswordUtil() PasswordUtil {
|
||||
return &passwordUtil{}
|
||||
specialChars := `!@#$%^&*()_-+={[}]|\:;"'<,>.?/`
|
||||
return &passwordUtil{
|
||||
charsetSpecialChars: charsets.NewCharsetFromString(specialChars),
|
||||
}
|
||||
}
|
||||
|
||||
type passwordUtil struct{}
|
||||
type passwordUtil struct {
|
||||
charsetSpecialChars charsets.Charset
|
||||
}
|
||||
|
||||
func (b *passwordUtil) Hash(password string) (string, error) {
|
||||
bytes, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
@ -31,5 +37,39 @@ func (b *passwordUtil) Validate(password string) error {
|
||||
if len(password) < 8 {
|
||||
return fmt.Errorf("password must contain 8 or more characters")
|
||||
}
|
||||
|
||||
charsetUpper := charsets.GetCharset(charsets.CharsetTypeLettersUpper)
|
||||
charsetLower := charsets.GetCharset(charsets.CharsetTypeLettersLower)
|
||||
|
||||
lowercaseLettersCount := 0
|
||||
uppercaseLettersCount := 0
|
||||
specialCharsCount := 0
|
||||
for _, v := range password {
|
||||
if b.charsetSpecialChars.TestRune(v) {
|
||||
specialCharsCount++
|
||||
continue
|
||||
}
|
||||
|
||||
if charsetUpper.TestRune(v) {
|
||||
uppercaseLettersCount++
|
||||
continue
|
||||
}
|
||||
|
||||
if charsetLower.TestRune(v) {
|
||||
lowercaseLettersCount++
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if lowercaseLettersCount == 0 {
|
||||
return fmt.Errorf("password must contain at least 1 lowercase letter")
|
||||
}
|
||||
if uppercaseLettersCount == 0 {
|
||||
return fmt.Errorf("password must contain at least 1 uppercase letter")
|
||||
}
|
||||
if specialCharsCount == 0 {
|
||||
return fmt.Errorf("password must contain at least 1 special character")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1,69 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Charset int
|
||||
|
||||
const (
|
||||
CharsetAll Charset = iota
|
||||
CharsetLettersLower
|
||||
CharsetLettersUpper
|
||||
CharsetLetters
|
||||
CharsetNumeric
|
||||
)
|
||||
|
||||
type charsetBlock struct {
|
||||
Offset int
|
||||
Size int
|
||||
}
|
||||
|
||||
func NewRand() *RandomUtil {
|
||||
charsetLettersLower := charsetBlock{
|
||||
Offset: 0x41,
|
||||
Size: 26,
|
||||
}
|
||||
|
||||
charsetLettersUpper := charsetBlock{
|
||||
Offset: 0x61,
|
||||
Size: 26,
|
||||
}
|
||||
|
||||
charsetNumeric := charsetBlock{
|
||||
Offset: 0x30,
|
||||
Size: 10,
|
||||
}
|
||||
|
||||
return &RandomUtil{
|
||||
charsets: map[Charset][]charsetBlock{
|
||||
CharsetNumeric: {charsetNumeric},
|
||||
CharsetLettersLower: {charsetLettersLower},
|
||||
CharsetLettersUpper: {charsetLettersUpper},
|
||||
CharsetLetters: {charsetLettersLower, charsetLettersUpper},
|
||||
CharsetAll: {charsetLettersLower, charsetLettersUpper, charsetNumeric},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type RandomUtil struct {
|
||||
charsets map[Charset][]charsetBlock
|
||||
}
|
||||
|
||||
func (r *RandomUtil) RandomID(outputLenght int, charset Charset) string {
|
||||
src := rand.NewSource(time.Now().UnixMicro())
|
||||
randGen := rand.New(src)
|
||||
|
||||
charsetBlocks := r.charsets[charset]
|
||||
|
||||
builder := strings.Builder{}
|
||||
for i := 0; i < outputLenght; i++ {
|
||||
charsetBlock := charsetBlocks[randGen.Int()%len(charsetBlocks)]
|
||||
|
||||
byte := charsetBlock.Offset + (randGen.Int() % charsetBlock.Size)
|
||||
builder.WriteRune(rune(byte))
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user