mirror of
https://github.com/volatiletech/authboss.git
synced 2024-11-28 08:58:38 +02:00
Move validation implementations to defaults
- Add more interfaces for validation
This commit is contained in:
parent
0e9761ddf5
commit
59b2874bcd
@ -70,7 +70,7 @@ func (a *Authboss) CurrentUserP(w http.ResponseWriter, r *http.Request) Storer {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
} else if i == nil {
|
||||
panic(ErrUserFound)
|
||||
panic(ErrUserNotFound)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package authboss
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"unicode"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/volatiletech/authboss"
|
||||
)
|
||||
|
||||
var blankRegex = regexp.MustCompile(`^\s*$`)
|
||||
@ -33,8 +34,8 @@ func (r Rules) Field() string {
|
||||
|
||||
// Errors returns an array of errors for each validation error that
|
||||
// is present in the given string. Returns nil if there are no errors.
|
||||
func (r Rules) Errors(toValidate string) ErrorList {
|
||||
errs := make(ErrorList, 0)
|
||||
func (r Rules) Errors(toValidate string) authboss.ErrorList {
|
||||
errs := make(authboss.ErrorList, 0)
|
||||
|
||||
ln := len(toValidate)
|
||||
if r.Required && (ln == 0 || blankRegex.MatchString(toValidate)) {
|
@ -1,4 +1,4 @@
|
||||
package authboss
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"regexp"
|
71
defaults/validation.go
Normal file
71
defaults/validation.go
Normal file
@ -0,0 +1,71 @@
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/volatiletech/authboss"
|
||||
)
|
||||
|
||||
// HTTPFormValidator validates HTTP post type inputs
|
||||
type HTTPFormValidator struct {
|
||||
Ruleset []authboss.FieldValidator
|
||||
ConfirmFields []string
|
||||
}
|
||||
|
||||
// Validate validates a request using the given ruleset.
|
||||
func (h HTTPFormValidator) Validate(r *http.Request) authboss.ErrorList {
|
||||
var errList authboss.ErrorList
|
||||
|
||||
for _, fieldValidator := range h.Ruleset {
|
||||
field := fieldValidator.Field()
|
||||
|
||||
val := r.FormValue(field)
|
||||
if errs := fieldValidator.Errors(val); len(errs) > 0 {
|
||||
errList = append(errList, errs...)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(h.ConfirmFields)-1; i += 2 {
|
||||
fmt.Println(h.ConfirmFields)
|
||||
main := r.FormValue(h.ConfirmFields[i])
|
||||
if len(main) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
confirm := r.FormValue(h.ConfirmFields[i+1])
|
||||
if len(confirm) == 0 || main != confirm {
|
||||
errList = append(errList, FieldError{h.ConfirmFields[i+1], fmt.Errorf("Does not match %s", h.ConfirmFields[i])})
|
||||
}
|
||||
}
|
||||
|
||||
return errList
|
||||
}
|
||||
|
||||
// FieldError represents an error that occurs during validation and is always
|
||||
// attached to field on a form.
|
||||
type FieldError struct {
|
||||
FieldName string
|
||||
FieldErr error
|
||||
}
|
||||
|
||||
// NewFieldError literally only exists because of poor name planning
|
||||
// where name and err can't be exported on the struct due to the method names
|
||||
func NewFieldError(name string, err error) FieldError {
|
||||
return FieldError{FieldName: name, FieldErr: err}
|
||||
}
|
||||
|
||||
// Name of the field the error is about
|
||||
func (f FieldError) Name() string {
|
||||
return f.FieldName
|
||||
}
|
||||
|
||||
// Err for the field
|
||||
func (f FieldError) Err() error {
|
||||
return f.FieldErr
|
||||
}
|
||||
|
||||
// Error in string form
|
||||
func (f FieldError) Error() string {
|
||||
return fmt.Sprintf("%s: %v", f.FieldName, f.FieldErr)
|
||||
}
|
74
defaults/validation_test.go
Normal file
74
defaults/validation_test.go
Normal file
@ -0,0 +1,74 @@
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/volatiletech/authboss"
|
||||
"github.com/volatiletech/authboss/internal/mocks"
|
||||
)
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req := mocks.Request("POST", "username", "john", "email", "john@john.com")
|
||||
|
||||
validator := HTTPFormValidator{
|
||||
Ruleset: []authboss.FieldValidator{
|
||||
mocks.FieldValidator{
|
||||
FieldName: "username",
|
||||
Errs: authboss.ErrorList{FieldError{"username", errors.New("must be longer than 4")}},
|
||||
},
|
||||
mocks.FieldValidator{
|
||||
FieldName: "missing_field",
|
||||
Errs: authboss.ErrorList{FieldError{"missing_field", errors.New("Expected field to exist")}},
|
||||
},
|
||||
mocks.FieldValidator{
|
||||
FieldName: "email", Errs: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
errList := validator.Validate(req)
|
||||
|
||||
errs := errList.Map()
|
||||
if errs["username"][0] != "must be longer than 4" {
|
||||
t.Error("Expected a different error for username:", errs["username"][0])
|
||||
}
|
||||
if errs["missing_field"][0] != "Expected field to exist" {
|
||||
t.Error("Expected a different error for missing_field:", errs["missing_field"][0])
|
||||
}
|
||||
if _, ok := errs["email"]; ok {
|
||||
t.Error("Expected no errors for email.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidate_Confirm(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
validator := HTTPFormValidator{
|
||||
ConfirmFields: []string{"username", "confirmUsername"},
|
||||
}
|
||||
|
||||
req := mocks.Request("POST", "username", "john", "confirmUsername", "johnny")
|
||||
errs := validator.Validate(req).Map()
|
||||
if errs["confirmUsername"][0] != "Does not match username" {
|
||||
t.Error("Expected a different error for confirmUsername:", errs["confirmUsername"][0])
|
||||
}
|
||||
|
||||
req = mocks.Request("POST", "username", "john", "confirmUsername", "john")
|
||||
errs = validator.Validate(req).Map()
|
||||
if len(errs) != 0 {
|
||||
t.Error("Expected no errors:", errs)
|
||||
}
|
||||
|
||||
validator = HTTPFormValidator{
|
||||
ConfirmFields: []string{"username"},
|
||||
}
|
||||
|
||||
req = mocks.Request("POST", "username", "john", "confirmUsername", "john")
|
||||
errs = validator.Validate(req).Map()
|
||||
if len(errs) != 0 {
|
||||
t.Error("Expected no errors:", errs)
|
||||
}
|
||||
}
|
@ -10,12 +10,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/volatiletech/authboss"
|
||||
)
|
||||
|
||||
// MockUser represents all possible fields a authboss User may have
|
||||
type MockUser struct {
|
||||
// User represents all possible fields a authboss User may have
|
||||
type User struct {
|
||||
Username string
|
||||
Email string
|
||||
Password string
|
||||
@ -31,96 +30,97 @@ type MockUser struct {
|
||||
OAuthExpiry time.Time
|
||||
}
|
||||
|
||||
func (m MockUser) GetUsername(context.Context) (string, error) { return m.Username, nil }
|
||||
func (m MockUser) GetEmail(context.Context) (string, error) { return m.Email, nil }
|
||||
func (m MockUser) GetPassword(context.Context) (string, error) { return m.Password, nil }
|
||||
func (m MockUser) GetRecoverToken(context.Context) (string, error) { return m.RecoverToken, nil }
|
||||
func (m MockUser) GetRecoverTokenExpiry(context.Context) (time.Time, error) {
|
||||
func (m User) GetUsername(context.Context) (string, error) { return m.Username, nil }
|
||||
func (m User) GetEmail(context.Context) (string, error) { return m.Email, nil }
|
||||
func (m User) GetPassword(context.Context) (string, error) { return m.Password, nil }
|
||||
func (m User) GetRecoverToken(context.Context) (string, error) { return m.RecoverToken, nil }
|
||||
func (m User) GetRecoverTokenExpiry(context.Context) (time.Time, error) {
|
||||
return m.RecoverTokenExpiry, nil
|
||||
}
|
||||
func (m MockUser) GetConfirmToken(context.Context) (string, error) { return m.ConfirmToken, nil }
|
||||
func (m MockUser) GetConfirmed(context.Context) (bool, error) { return m.Confirmed, nil }
|
||||
func (m MockUser) GetLocked(context.Context) (bool, error) { return m.Locked, nil }
|
||||
func (m MockUser) GetAttemptNumber(context.Context) (int, error) { return m.AttemptNumber, nil }
|
||||
func (m MockUser) GetAttemptTime(context.Context) (time.Time, error) {
|
||||
func (m User) GetConfirmToken(context.Context) (string, error) { return m.ConfirmToken, nil }
|
||||
func (m User) GetConfirmed(context.Context) (bool, error) { return m.Confirmed, nil }
|
||||
func (m User) GetLocked(context.Context) (bool, error) { return m.Locked, nil }
|
||||
func (m User) GetAttemptNumber(context.Context) (int, error) { return m.AttemptNumber, nil }
|
||||
func (m User) GetAttemptTime(context.Context) (time.Time, error) {
|
||||
return m.AttemptTime, nil
|
||||
}
|
||||
func (m MockUser) GetOAuthToken(context.Context) (string, error) { return m.OAuthToken, nil }
|
||||
func (m MockUser) GetOAuthRefresh(context.Context) (string, error) { return m.OAuthRefresh, nil }
|
||||
func (m MockUser) GetOAuthExpiry(context.Context) (time.Time, error) {
|
||||
func (m User) GetOAuthToken(context.Context) (string, error) { return m.OAuthToken, nil }
|
||||
func (m User) GetOAuthRefresh(context.Context) (string, error) { return m.OAuthRefresh, nil }
|
||||
func (m User) GetOAuthExpiry(context.Context) (time.Time, error) {
|
||||
return m.OAuthExpiry, nil
|
||||
}
|
||||
|
||||
func (m *MockUser) SetUsername(ctx context.Context, username string) error {
|
||||
func (m *User) SetUsername(ctx context.Context, username string) error {
|
||||
m.Username = username
|
||||
return nil
|
||||
}
|
||||
func (m *MockUser) SetEmail(ctx context.Context, email string) error {
|
||||
func (m *User) SetEmail(ctx context.Context, email string) error {
|
||||
m.Email = email
|
||||
return nil
|
||||
}
|
||||
func (m *MockUser) SetPassword(ctx context.Context, password string) error {
|
||||
func (m *User) SetPassword(ctx context.Context, password string) error {
|
||||
m.Password = password
|
||||
return nil
|
||||
}
|
||||
func (m *MockUser) SetRecoverToken(ctx context.Context, recoverToken string) error {
|
||||
func (m *User) SetRecoverToken(ctx context.Context, recoverToken string) error {
|
||||
m.RecoverToken = recoverToken
|
||||
return nil
|
||||
}
|
||||
func (m *MockUser) SetRecoverTokenExpiry(ctx context.Context, recoverTokenExpiry time.Time) error {
|
||||
func (m *User) SetRecoverTokenExpiry(ctx context.Context, recoverTokenExpiry time.Time) error {
|
||||
m.RecoverTokenExpiry = recoverTokenExpiry
|
||||
return nil
|
||||
}
|
||||
func (m *MockUser) SetConfirmToken(ctx context.Context, confirmToken string) error {
|
||||
func (m *User) SetConfirmToken(ctx context.Context, confirmToken string) error {
|
||||
m.ConfirmToken = confirmToken
|
||||
return nil
|
||||
}
|
||||
func (m *MockUser) SetConfirmed(ctx context.Context, confirmed bool) error {
|
||||
func (m *User) SetConfirmed(ctx context.Context, confirmed bool) error {
|
||||
m.Confirmed = confirmed
|
||||
return nil
|
||||
}
|
||||
func (m *MockUser) SetLocked(ctx context.Context, locked bool) error {
|
||||
func (m *User) SetLocked(ctx context.Context, locked bool) error {
|
||||
m.Locked = locked
|
||||
return nil
|
||||
}
|
||||
func (m *MockUser) SetAttemptNumber(ctx context.Context, attemptNumber int) error {
|
||||
func (m *User) SetAttemptNumber(ctx context.Context, attemptNumber int) error {
|
||||
m.AttemptNumber = attemptNumber
|
||||
return nil
|
||||
}
|
||||
func (m *MockUser) SetAttemptTime(ctx context.Context, attemptTime time.Time) error {
|
||||
func (m *User) SetAttemptTime(ctx context.Context, attemptTime time.Time) error {
|
||||
m.AttemptTime = attemptTime
|
||||
return nil
|
||||
}
|
||||
func (m *MockUser) SetOAuthToken(ctx context.Context, oAuthToken string) error {
|
||||
func (m *User) SetOAuthToken(ctx context.Context, oAuthToken string) error {
|
||||
m.OAuthToken = oAuthToken
|
||||
return nil
|
||||
}
|
||||
func (m *MockUser) SetOAuthRefresh(ctx context.Context, oAuthRefresh string) error {
|
||||
func (m *User) SetOAuthRefresh(ctx context.Context, oAuthRefresh string) error {
|
||||
m.OAuthRefresh = oAuthRefresh
|
||||
return nil
|
||||
}
|
||||
func (m *MockUser) SetOAuthExpiry(ctx context.Context, oAuthExpiry time.Time) error {
|
||||
func (m *User) SetOAuthExpiry(ctx context.Context, oAuthExpiry time.Time) error {
|
||||
m.OAuthExpiry = oAuthExpiry
|
||||
return nil
|
||||
}
|
||||
|
||||
// MockStorer should be valid for any module storer defined in authboss.
|
||||
type MockStoreLoader struct {
|
||||
Users map[string]*MockUser
|
||||
// StoreLoader should be valid for any module storer defined in authboss.
|
||||
type StoreLoader struct {
|
||||
Users map[string]*User
|
||||
RMTokens map[string][]string
|
||||
}
|
||||
|
||||
// NewMockStorer constructor
|
||||
func NewMockStoreLoader() *MockStoreLoader {
|
||||
return &MockStoreLoader{
|
||||
Users: make(map[string]*MockUser),
|
||||
// NewStoreLoader constructor
|
||||
func NewStoreLoader() *StoreLoader {
|
||||
return &StoreLoader{
|
||||
Users: make(map[string]*User),
|
||||
RMTokens: make(map[string][]string),
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO(aarondl): What is this?
|
||||
// AddToken for remember me
|
||||
func (m *MockStorer) AddToken(key, token string) error {
|
||||
func (m *Storer) AddToken(key, token string) error {
|
||||
if len(m.AddTokenErr) > 0 {
|
||||
return errors.New(m.AddTokenErr)
|
||||
}
|
||||
@ -131,7 +131,7 @@ func (m *MockStorer) AddToken(key, token string) error {
|
||||
}
|
||||
|
||||
// DelTokens for a user
|
||||
func (m *MockStorer) DelTokens(key string) error {
|
||||
func (m *Storer) DelTokens(key string) error {
|
||||
if len(m.DelTokensErr) > 0 {
|
||||
return errors.New(m.DelTokensErr)
|
||||
}
|
||||
@ -141,7 +141,7 @@ func (m *MockStorer) DelTokens(key string) error {
|
||||
}
|
||||
|
||||
// UseToken if it exists, deleting it in the process
|
||||
func (m *MockStorer) UseToken(givenKey, token string) (err error) {
|
||||
func (m *Storer) UseToken(givenKey, token string) (err error) {
|
||||
if len(m.UseTokenErr) > 0 {
|
||||
return errors.New(m.UseTokenErr)
|
||||
}
|
||||
@ -158,7 +158,7 @@ func (m *MockStorer) UseToken(givenKey, token string) (err error) {
|
||||
}
|
||||
|
||||
// RecoverUser by the token.
|
||||
func (m *MockStorer) RecoverUser(token string) (result interface{}, err error) {
|
||||
func (m *Storer) RecoverUser(token string) (result interface{}, err error) {
|
||||
if len(m.RecoverUserErr) > 0 {
|
||||
return nil, errors.New(m.RecoverUserErr)
|
||||
}
|
||||
@ -166,7 +166,7 @@ func (m *MockStorer) RecoverUser(token string) (result interface{}, err error) {
|
||||
for _, user := range m.Users {
|
||||
if user["recover_token"] == token {
|
||||
|
||||
u := &MockUser{}
|
||||
u := &User{}
|
||||
if err = user.Bind(u, false); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -179,7 +179,7 @@ func (m *MockStorer) RecoverUser(token string) (result interface{}, err error) {
|
||||
}
|
||||
|
||||
// ConfirmUser via their token
|
||||
func (m *MockStorer) ConfirmUser(confirmToken string) (result interface{}, err error) {
|
||||
func (m *Storer) ConfirmUser(confirmToken string) (result interface{}, err error) {
|
||||
if len(m.ConfirmUserErr) > 0 {
|
||||
return nil, errors.New(m.ConfirmUserErr)
|
||||
}
|
||||
@ -187,7 +187,7 @@ func (m *MockStorer) ConfirmUser(confirmToken string) (result interface{}, err e
|
||||
for _, user := range m.Users {
|
||||
if user["confirm_token"] == confirmToken {
|
||||
|
||||
u := &MockUser{}
|
||||
u := &User{}
|
||||
if err = user.Bind(u, false); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -200,34 +200,34 @@ func (m *MockStorer) ConfirmUser(confirmToken string) (result interface{}, err e
|
||||
}
|
||||
*/
|
||||
|
||||
// MockFailStorer is used for testing module initialize functions that recover more than the base storer
|
||||
type MockFailStorer struct {
|
||||
MockUser
|
||||
// FailStorer is used for testing module initialize functions that recover more than the base storer
|
||||
type FailStorer struct {
|
||||
User
|
||||
}
|
||||
|
||||
// Create fails
|
||||
func (_ MockFailStorer) Create(context.Context) error {
|
||||
func (FailStorer) Create(context.Context) error {
|
||||
return errors.New("fail storer: create")
|
||||
}
|
||||
|
||||
// Put fails
|
||||
func (_ MockFailStorer) Save(context.Context) error {
|
||||
// Save fails
|
||||
func (FailStorer) Save(context.Context) error {
|
||||
return errors.New("fail storer: put")
|
||||
}
|
||||
|
||||
// Get fails
|
||||
func (_ MockFailStorer) Load(context.Context) error {
|
||||
// Load fails
|
||||
func (FailStorer) Load(context.Context) error {
|
||||
return errors.New("fail storer: get")
|
||||
}
|
||||
|
||||
// MockClientStorer is used for testing the client stores on context
|
||||
type MockClientStorer struct {
|
||||
// ClientStorer is used for testing the client stores on context
|
||||
type ClientStorer struct {
|
||||
Values map[string]string
|
||||
GetShouldFail bool
|
||||
}
|
||||
|
||||
// NewMockClientStorer constructs a MockClientStorer
|
||||
func NewMockClientStorer(data ...string) *MockClientStorer {
|
||||
// NewClientStorer constructs a ClientStorer
|
||||
func NewClientStorer(data ...string) *ClientStorer {
|
||||
if len(data)%2 != 0 {
|
||||
panic("It should be a key value list of arguments.")
|
||||
}
|
||||
@ -238,11 +238,11 @@ func NewMockClientStorer(data ...string) *MockClientStorer {
|
||||
values[data[i]] = data[i+1]
|
||||
}
|
||||
|
||||
return &MockClientStorer{Values: values}
|
||||
return &ClientStorer{Values: values}
|
||||
}
|
||||
|
||||
// Get a key's value
|
||||
func (m *MockClientStorer) Get(key string) (string, bool) {
|
||||
func (m *ClientStorer) Get(key string) (string, bool) {
|
||||
if m.GetShouldFail {
|
||||
return "", false
|
||||
}
|
||||
@ -252,7 +252,7 @@ func (m *MockClientStorer) Get(key string) (string, bool) {
|
||||
}
|
||||
|
||||
// GetErr gets a key's value or err if not exist
|
||||
func (m *MockClientStorer) GetErr(key string) (string, error) {
|
||||
func (m *ClientStorer) GetErr(key string) (string, error) {
|
||||
if m.GetShouldFail {
|
||||
return "", authboss.ClientDataErr{Name: key}
|
||||
}
|
||||
@ -265,13 +265,13 @@ func (m *MockClientStorer) GetErr(key string) (string, error) {
|
||||
}
|
||||
|
||||
// Put a value
|
||||
func (m *MockClientStorer) Put(key, val string) { m.Values[key] = val }
|
||||
func (m *ClientStorer) Put(key, val string) { m.Values[key] = val }
|
||||
|
||||
// Del a key/value pair
|
||||
func (m *MockClientStorer) Del(key string) { delete(m.Values, key) }
|
||||
func (m *ClientStorer) Del(key string) { delete(m.Values, key) }
|
||||
|
||||
// MockRequest returns a new mock request with optional key-value body (form-post)
|
||||
func MockRequest(method string, postKeyValues ...string) *http.Request {
|
||||
// Request returns a new request with optional key-value body (form-post)
|
||||
func Request(method string, postKeyValues ...string) *http.Request {
|
||||
var body io.Reader
|
||||
location := "http://localhost"
|
||||
|
||||
@ -300,19 +300,19 @@ func MockRequest(method string, postKeyValues ...string) *http.Request {
|
||||
return req
|
||||
}
|
||||
|
||||
// MockMailer helps simplify mailer testing by storing the last sent email
|
||||
type MockMailer struct {
|
||||
// Mailer helps simplify mailer testing by storing the last sent email
|
||||
type Mailer struct {
|
||||
Last authboss.Email
|
||||
SendErr string
|
||||
}
|
||||
|
||||
// NewMockMailer constructs a mock mailer
|
||||
func NewMockMailer() *MockMailer {
|
||||
return &MockMailer{}
|
||||
// NewMailer constructs a mailer
|
||||
func NewMailer() *Mailer {
|
||||
return &Mailer{}
|
||||
}
|
||||
|
||||
// Send an e-mail
|
||||
func (m *MockMailer) Send(ctx context.Context, email authboss.Email) error {
|
||||
func (m *Mailer) Send(ctx context.Context, email authboss.Email) error {
|
||||
if len(m.SendErr) > 0 {
|
||||
return errors.New(m.SendErr)
|
||||
}
|
||||
@ -321,15 +321,15 @@ func (m *MockMailer) Send(ctx context.Context, email authboss.Email) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MockAfterCallback is a callback that knows if it was called
|
||||
type MockAfterCallback struct {
|
||||
// AfterCallback is a callback that knows if it was called
|
||||
type AfterCallback struct {
|
||||
HasBeenCalled bool
|
||||
Fn authboss.After
|
||||
}
|
||||
|
||||
// NewMockAfterCallback constructs a new mockaftercallback.
|
||||
func NewMockAfterCallback() *MockAfterCallback {
|
||||
m := MockAfterCallback{}
|
||||
// NewAfterCallback constructs a new aftercallback.
|
||||
func NewAfterCallback() *AfterCallback {
|
||||
m := AfterCallback{}
|
||||
|
||||
m.Fn = func(context.Context) error {
|
||||
m.HasBeenCalled = true
|
||||
@ -338,3 +338,25 @@ func NewMockAfterCallback() *MockAfterCallback {
|
||||
|
||||
return &m
|
||||
}
|
||||
|
||||
// FieldValidator mock
|
||||
type FieldValidator struct {
|
||||
FieldName string
|
||||
Errs []error
|
||||
Ruleset []string
|
||||
}
|
||||
|
||||
// Field being validated
|
||||
func (f FieldValidator) Field() string {
|
||||
return f.FieldName
|
||||
}
|
||||
|
||||
// Errors list
|
||||
func (f FieldValidator) Errors(in string) []error {
|
||||
return f.Errs
|
||||
}
|
||||
|
||||
// Rules present
|
||||
func (f FieldValidator) Rules() []string {
|
||||
return f.Ruleset
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
type DefaultLogger log.Logger
|
||||
|
||||
// LogWriteMaker is used to create a logger from an http request.
|
||||
// TODO(aarondl): decide what to do with this, should we keep it?
|
||||
type LogWriteMaker func(http.ResponseWriter, *http.Request) io.Writer
|
||||
|
||||
// NewDefaultLogger creates a logger to stdout.
|
||||
|
@ -168,24 +168,6 @@ func newMockAPIRequest(postKeyValues ...string) *http.Request {
|
||||
return req
|
||||
}
|
||||
|
||||
type mockValidator struct {
|
||||
FieldName string
|
||||
Errs ErrorList
|
||||
Ruleset []string
|
||||
}
|
||||
|
||||
func (m mockValidator) Field() string {
|
||||
return m.FieldName
|
||||
}
|
||||
|
||||
func (m mockValidator) Errors(in string) ErrorList {
|
||||
return m.Errs
|
||||
}
|
||||
|
||||
func (m mockValidator) Rules() []string {
|
||||
return m.Ruleset
|
||||
}
|
||||
|
||||
type mockRenderLoader struct{}
|
||||
|
||||
func (m mockRenderLoader) Init(names []string) (Renderer, error) {
|
||||
|
@ -2,8 +2,6 @@ package authboss
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -11,14 +9,27 @@ const (
|
||||
ConfirmPrefix = "confirm_"
|
||||
)
|
||||
|
||||
// Validator is anything that can validate a string and provide a list of errors
|
||||
// and describe its set of rules.
|
||||
// Validator takes a form name and a set of inputs and returns any validation errors
|
||||
// for the inputs.
|
||||
type Validator interface {
|
||||
// Validate inputs from the named form
|
||||
Validate(name string, fieldValues map[string]string) []error
|
||||
}
|
||||
|
||||
// FieldValidator is anything that can validate a string and provide a list of errors
|
||||
// and describe its set of rules.
|
||||
type FieldValidator interface {
|
||||
Field() string
|
||||
Errors(in string) ErrorList
|
||||
Errors(in string) []error
|
||||
Rules() []string
|
||||
}
|
||||
|
||||
// FieldError describes an error on a field
|
||||
type FieldError interface {
|
||||
Name() string
|
||||
Err() error
|
||||
}
|
||||
|
||||
// ErrorList is simply a slice of errors with helpers.
|
||||
type ErrorList []error
|
||||
|
||||
@ -46,64 +57,10 @@ func (e ErrorList) Map() map[string][]string {
|
||||
if !ok {
|
||||
m[""] = append(m[""], err.Error())
|
||||
} else {
|
||||
m[fieldErr.Name] = append(m[fieldErr.Name], fieldErr.Err.Error())
|
||||
name, err := fieldErr.Name(), fieldErr.Err()
|
||||
m[name] = append(m[name], err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// FieldError represents an error that occurs during validation and is always
|
||||
// attached to field on a form.
|
||||
type FieldError struct {
|
||||
Name string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (f FieldError) Error() string {
|
||||
return fmt.Sprintf("%s: %v", f.Name, f.Err)
|
||||
}
|
||||
|
||||
// Validate validates a request using the given ruleset.
|
||||
func Validate(r *http.Request, ruleset []Validator, confirmFields ...string) ErrorList {
|
||||
errList := make(ErrorList, 0)
|
||||
|
||||
for _, validator := range ruleset {
|
||||
field := validator.Field()
|
||||
|
||||
val := r.FormValue(field)
|
||||
if errs := validator.Errors(val); len(errs) > 0 {
|
||||
errList = append(errList, errs...)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(confirmFields)-1; i += 2 {
|
||||
main := r.FormValue(confirmFields[i])
|
||||
if len(main) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
confirm := r.FormValue(confirmFields[i+1])
|
||||
if len(confirm) == 0 || main != confirm {
|
||||
errList = append(errList, FieldError{confirmFields[i+1], fmt.Errorf("Does not match %s", confirmFields[i])})
|
||||
}
|
||||
}
|
||||
|
||||
return errList
|
||||
}
|
||||
|
||||
// FilterValidators returns a subset of registered validators
|
||||
func FilterValidators(validators []Validator, fields ...string) []Validator {
|
||||
var arr []Validator
|
||||
|
||||
for _, validator := range validators {
|
||||
fieldName := validator.Field()
|
||||
for _, field := range fields {
|
||||
if fieldName == field {
|
||||
arr = append(arr, validator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
|
@ -6,6 +6,23 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type mockFieldError struct {
|
||||
name string
|
||||
err error
|
||||
}
|
||||
|
||||
func (m mockFieldError) Name() string {
|
||||
return m.name
|
||||
}
|
||||
|
||||
func (m mockFieldError) Err() error {
|
||||
return m.err
|
||||
}
|
||||
|
||||
func (m mockFieldError) Error() string {
|
||||
return m.err.Error()
|
||||
}
|
||||
|
||||
func TestErrorList_Error(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@ -23,9 +40,9 @@ func TestErrorList_Map(t *testing.T) {
|
||||
errAsploded := "asploded"
|
||||
|
||||
errList := ErrorList{
|
||||
FieldError{StoreUsername, errors.New(errNotLong)},
|
||||
FieldError{StoreUsername, errors.New(errEmail)},
|
||||
FieldError{StorePassword, errors.New(errNotLong)},
|
||||
mockFieldError{"username", errors.New(errNotLong)},
|
||||
mockFieldError{"username", errors.New(errEmail)},
|
||||
mockFieldError{"password", errors.New(errNotLong)},
|
||||
errors.New(errAsploded),
|
||||
}
|
||||
|
||||
@ -34,7 +51,7 @@ func TestErrorList_Map(t *testing.T) {
|
||||
t.Error("Wrong number of fields:", len(m))
|
||||
}
|
||||
|
||||
usernameErrs := m[StoreUsername]
|
||||
usernameErrs := m["email"]
|
||||
if len(usernameErrs) != 2 {
|
||||
t.Error("Wrong number of username errors:", len(usernameErrs))
|
||||
}
|
||||
@ -45,7 +62,7 @@ func TestErrorList_Map(t *testing.T) {
|
||||
t.Error("Wrong username error at 1:", usernameErrs[1])
|
||||
}
|
||||
|
||||
passwordErrs := m[StorePassword]
|
||||
passwordErrs := m["password"]
|
||||
if len(passwordErrs) != 1 {
|
||||
t.Error("Wrong number of password errors:", len(passwordErrs))
|
||||
}
|
||||
@ -61,78 +78,3 @@ func TestErrorList_Map(t *testing.T) {
|
||||
t.Error("Wrong unkown error at 0:", unknownErrs[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req := newMockRequest(StoreUsername, "john", StoreEmail, "john@john.com")
|
||||
|
||||
errList := Validate(req, []Validator{
|
||||
mockValidator{
|
||||
FieldName: StoreUsername,
|
||||
Errs: ErrorList{FieldError{StoreUsername, errors.New("must be longer than 4")}},
|
||||
},
|
||||
mockValidator{
|
||||
FieldName: "missing_field",
|
||||
Errs: ErrorList{FieldError{"missing_field", errors.New("Expected field to exist.")}},
|
||||
},
|
||||
mockValidator{
|
||||
FieldName: StoreEmail, Errs: nil,
|
||||
},
|
||||
})
|
||||
|
||||
errs := errList.Map()
|
||||
if errs[StoreUsername][0] != "must be longer than 4" {
|
||||
t.Error("Expected a different error for username:", errs[StoreUsername][0])
|
||||
}
|
||||
if errs["missing_field"][0] != "Expected field to exist." {
|
||||
t.Error("Expected a different error for missing_field:", errs["missing_field"][0])
|
||||
}
|
||||
if _, ok := errs[StoreEmail]; ok {
|
||||
t.Error("Expected no errors for email.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidate_Confirm(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req := newMockRequest(StoreUsername, "john", "confirmUsername", "johnny")
|
||||
errs := Validate(req, nil, StoreUsername, "confirmUsername").Map()
|
||||
if errs["confirmUsername"][0] != "Does not match username" {
|
||||
t.Error("Expected a different error for confirmUsername:", errs["confirmUsername"][0])
|
||||
}
|
||||
|
||||
req = newMockRequest(StoreUsername, "john", "confirmUsername", "john")
|
||||
errs = Validate(req, nil, StoreUsername, "confirmUsername").Map()
|
||||
if len(errs) != 0 {
|
||||
t.Error("Expected no errors:", errs)
|
||||
}
|
||||
|
||||
req = newMockRequest(StoreUsername, "john", "confirmUsername", "john")
|
||||
errs = Validate(req, nil, StoreUsername).Map()
|
||||
if len(errs) != 0 {
|
||||
t.Error("Expected no errors:", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterValidators(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
validators := []Validator{
|
||||
mockValidator{
|
||||
FieldName: StoreUsername, Errs: ErrorList{FieldError{StoreUsername, errors.New("must be longer than 4")}},
|
||||
},
|
||||
mockValidator{
|
||||
FieldName: StorePassword, Errs: ErrorList{FieldError{StorePassword, errors.New("must be longer than 4")}},
|
||||
},
|
||||
}
|
||||
|
||||
validators = FilterValidators(validators, StoreUsername)
|
||||
|
||||
if len(validators) != 1 {
|
||||
t.Error("Expected length to be 1")
|
||||
}
|
||||
if validators[0].Field() != StoreUsername {
|
||||
t.Error("Expcted validator for field username", validators[0].Field())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user