add auth lib
This commit is contained in:
42
credentials/validation.go
Normal file
42
credentials/validation.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package credentials
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var emailRegex = regexp.MustCompile(`^[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}$`)
|
||||
var usernameAllowedRegex = regexp.MustCompile(`^[A-Za-z0-9._-]{3,20}$`)
|
||||
|
||||
const bcryptMaxPasswordBytes = 72
|
||||
|
||||
// At least one letter.
|
||||
var passwordLetterRegex = regexp.MustCompile(`[A-Za-z]`)
|
||||
|
||||
// At least one number or special symbol (non-letter).
|
||||
var passwordNonLetterRegex = regexp.MustCompile(`[^A-Za-z]`)
|
||||
|
||||
func IsValidEmail(email string) bool {
|
||||
return emailRegex.MatchString(email)
|
||||
}
|
||||
|
||||
func IsValidUsername(username string) bool {
|
||||
if !usernameAllowedRegex.MatchString(username) {
|
||||
return false
|
||||
}
|
||||
|
||||
return !strings.Contains(username, "..") &&
|
||||
!strings.Contains(username, "--") &&
|
||||
!strings.Contains(username, "__")
|
||||
}
|
||||
|
||||
func IsValidPassword(password string) bool {
|
||||
return len(password) >= 12 &&
|
||||
IsPasswordWithinBcryptLimit(password) &&
|
||||
passwordLetterRegex.MatchString(password) &&
|
||||
passwordNonLetterRegex.MatchString(password)
|
||||
}
|
||||
|
||||
func IsPasswordWithinBcryptLimit(password string) bool {
|
||||
return len([]byte(password)) <= bcryptMaxPasswordBytes
|
||||
}
|
||||
71
credentials/validation_test.go
Normal file
71
credentials/validation_test.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package credentials
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsValidEmail(t *testing.T) {
|
||||
tests := []struct {
|
||||
email string
|
||||
want bool
|
||||
}{
|
||||
{email: "a@example.com", want: true},
|
||||
{email: "user.name+tag@example.co.uk", want: true},
|
||||
{email: "", want: false},
|
||||
{email: "plainaddress", want: false},
|
||||
{email: "user@localhost", want: false},
|
||||
{email: "user@example", want: false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
if got := IsValidEmail(tc.email); got != tc.want {
|
||||
t.Fatalf("IsValidEmail(%q) = %v, want %v", tc.email, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidUsername(t *testing.T) {
|
||||
tests := []struct {
|
||||
username string
|
||||
want bool
|
||||
}{
|
||||
{username: "abc", want: true},
|
||||
{username: "user_name-1", want: true},
|
||||
{username: "ab", want: false},
|
||||
{username: "this_username_is_way_too_long_for_rules", want: false},
|
||||
{username: "user..name", want: false},
|
||||
{username: "user__name", want: false},
|
||||
{username: "user--name", want: false},
|
||||
{username: "user name", want: false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
if got := IsValidUsername(tc.username); got != tc.want {
|
||||
t.Fatalf("IsValidUsername(%q) = %v, want %v", tc.username, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidPassword(t *testing.T) {
|
||||
longerThan72 := "A1" + strings.Repeat("x", 71)
|
||||
exactly72 := "A1" + strings.Repeat("x", 70)
|
||||
tests := []struct {
|
||||
password string
|
||||
want bool
|
||||
}{
|
||||
{password: "LongPassword1", want: true},
|
||||
{password: "abcdefghijk1", want: true},
|
||||
{password: exactly72, want: true},
|
||||
{password: longerThan72, want: false},
|
||||
{password: "123456789012", want: false},
|
||||
{password: "OnlyLettersAB", want: false},
|
||||
{password: "short1A", want: false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
if got := IsValidPassword(tc.password); got != tc.want {
|
||||
t.Fatalf("IsValidPassword(%q) = %v, want %v", tc.password, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user