mirror of
https://github.com/mainflux/mainflux.git
synced 2025-05-04 22:17:59 +08:00
MF-798 - Add utf8 support for email validation (#1082)
* Add utf8 support for email validation Signed-off-by: Vadim Filin <phil192@yandex.ru> * Fix review comments Signed-off-by: Vadim Filin <phil192@yandex.ru> * Refactor Signed-off-by: Vadim Filin <phil192@yandex.ru> Co-authored-by: Drasko DRASKOVIC <drasko.draskovic@gmail.com>
This commit is contained in:
parent
a2d70c8907
commit
b8818c4dd2
@ -9,9 +9,18 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/mainflux/mainflux/errors"
|
"github.com/mainflux/mainflux/errors"
|
||||||
|
"golang.org/x/net/idna"
|
||||||
)
|
)
|
||||||
|
|
||||||
const minPassLen = 8
|
const (
|
||||||
|
minPassLen = 8
|
||||||
|
maxLocalLen = 64
|
||||||
|
maxDomainLen = 255
|
||||||
|
maxTLDLen = 24 // longest TLD currently in existence
|
||||||
|
|
||||||
|
atSeparator = "@"
|
||||||
|
dotSeparator = "."
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
userRegexp = regexp.MustCompile("^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+$")
|
userRegexp = regexp.MustCompile("^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+$")
|
||||||
@ -57,23 +66,43 @@ type UserRepository interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isEmail(email string) bool {
|
func isEmail(email string) bool {
|
||||||
if len(email) < 6 || len(email) > 254 {
|
if email == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
at := strings.LastIndex(email, "@")
|
es := strings.Split(email, atSeparator)
|
||||||
if at <= 0 || at > len(email)-3 {
|
if len(es) != 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
local, host := es[0], es[1]
|
||||||
|
|
||||||
|
if local == "" || len(local) > maxLocalLen {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
user := email[:at]
|
hs := strings.Split(host, dotSeparator)
|
||||||
host := email[at+1:]
|
if len(hs) != 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
domain, ext := hs[0], hs[1]
|
||||||
|
|
||||||
if len(user) > 64 {
|
if domain == "" || len(domain) > maxDomainLen {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ext == "" || len(ext) > maxTLDLen {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if userDotRegexp.MatchString(user) || !userRegexp.MatchString(user) || !hostRegexp.MatchString(host) {
|
punyLocal, err := idna.ToASCII(local)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
punyHost, err := idna.ToASCII(host)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if userDotRegexp.MatchString(punyLocal) || !userRegexp.MatchString(punyLocal) || !hostRegexp.MatchString(punyHost) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ package users_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mainflux/mainflux/errors"
|
"github.com/mainflux/mainflux/errors"
|
||||||
@ -16,8 +17,22 @@ const (
|
|||||||
email = "user@example.com"
|
email = "user@example.com"
|
||||||
password = "password"
|
password = "password"
|
||||||
metadata = `{"role":"manager"}`
|
metadata = `{"role":"manager"}`
|
||||||
|
|
||||||
|
maxLocalLen = 64
|
||||||
|
maxDomainLen = 255
|
||||||
|
maxTLDLen = 24
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var letters = "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
|
||||||
|
func randomString(n int) string {
|
||||||
|
b := make([]byte, n)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = letters[rand.Intn(len(letters))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidate(t *testing.T) {
|
func TestValidate(t *testing.T) {
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
user users.User
|
user users.User
|
||||||
@ -51,6 +66,62 @@ func TestValidate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
err: users.ErrMalformedEntity,
|
err: users.ErrMalformedEntity,
|
||||||
},
|
},
|
||||||
|
"validate user with utf8 email (cyrillic)": {
|
||||||
|
user: users.User{
|
||||||
|
Email: "почта@кино-россия.рф",
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
"validate user with utf8 email (hieroglyph)": {
|
||||||
|
user: users.User{
|
||||||
|
Email: "艾付忧西开@艾付忧西开.再得",
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
"validate user with no email tld": {
|
||||||
|
user: users.User{
|
||||||
|
Email: "user@example.",
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
err: users.ErrMalformedEntity,
|
||||||
|
},
|
||||||
|
"validate user with too long email tld": {
|
||||||
|
user: users.User{
|
||||||
|
Email: "user@example." + randomString(maxTLDLen+1),
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
err: users.ErrMalformedEntity,
|
||||||
|
},
|
||||||
|
"validate user with no email domain": {
|
||||||
|
user: users.User{
|
||||||
|
Email: "user@.com",
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
err: users.ErrMalformedEntity,
|
||||||
|
},
|
||||||
|
"validate user with too long email domain": {
|
||||||
|
user: users.User{
|
||||||
|
Email: "user@" + randomString(maxDomainLen+1) + ".com",
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
err: users.ErrMalformedEntity,
|
||||||
|
},
|
||||||
|
"validate user with no email local": {
|
||||||
|
user: users.User{
|
||||||
|
Email: "@example.com",
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
err: users.ErrMalformedEntity,
|
||||||
|
},
|
||||||
|
"validate user with too long email local": {
|
||||||
|
user: users.User{
|
||||||
|
Email: randomString(maxLocalLen+1) + "@example.com",
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
err: users.ErrMalformedEntity,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for desc, tc := range cases {
|
for desc, tc := range cases {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user