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:
Vadim Filin 2020-03-25 17:30:20 +03:00 committed by GitHub
parent a2d70c8907
commit b8818c4dd2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 108 additions and 8 deletions

View File

@ -9,9 +9,18 @@ import (
"strings"
"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 (
userRegexp = regexp.MustCompile("^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+$")
@ -57,23 +66,43 @@ type UserRepository interface {
}
func isEmail(email string) bool {
if len(email) < 6 || len(email) > 254 {
if email == "" {
return false
}
at := strings.LastIndex(email, "@")
if at <= 0 || at > len(email)-3 {
es := strings.Split(email, atSeparator)
if len(es) != 2 {
return false
}
local, host := es[0], es[1]
if local == "" || len(local) > maxLocalLen {
return false
}
user := email[:at]
host := email[at+1:]
hs := strings.Split(host, dotSeparator)
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
}
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
}

View File

@ -5,6 +5,7 @@ package users_test
import (
"fmt"
"math/rand"
"testing"
"github.com/mainflux/mainflux/errors"
@ -16,8 +17,22 @@ const (
email = "user@example.com"
password = "password"
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) {
cases := map[string]struct {
user users.User
@ -51,6 +66,62 @@ func TestValidate(t *testing.T) {
},
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 {