mirror of
https://github.com/volatiletech/authboss.git
synced 2024-11-28 08:58:38 +02:00
Add lock tests.
- Fix some logic errors in lock module. - Add some more power to the mocks.
This commit is contained in:
parent
65f7fad5fc
commit
f0552cd8c6
@ -1,6 +1,12 @@
|
||||
package mocks
|
||||
|
||||
import "gopkg.in/authboss.v0"
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"gopkg.in/authboss.v0"
|
||||
)
|
||||
|
||||
type MockUser struct {
|
||||
Email string
|
||||
@ -25,13 +31,19 @@ func (m *MockStorer) Create(key string, attr authboss.Attributes) error {
|
||||
}
|
||||
|
||||
func (m *MockStorer) Put(key string, attr authboss.Attributes) error {
|
||||
m.Users[key] = attr
|
||||
if _, ok := m.Users[key]; !ok {
|
||||
m.Users[key] = attr
|
||||
return nil
|
||||
}
|
||||
for k, v := range attr {
|
||||
m.Users[key][k] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockStorer) Get(key string, attrMeta authboss.AttributeMeta) (result interface{}, err error) {
|
||||
return &MockUser{
|
||||
m.Users[key]["email"].(string), m.Users[key]["password"].(string),
|
||||
m.Users[key]["username"].(string), m.Users[key]["password"].(string),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -66,3 +78,26 @@ func (m MockClientStorer) Get(key string) (string, bool) {
|
||||
}
|
||||
func (m MockClientStorer) Put(key, val string) { m[key] = val }
|
||||
func (m MockClientStorer) Del(key string) { delete(m, key) }
|
||||
|
||||
func MockRequestContext(postKeyValues ...string) *authboss.Context {
|
||||
keyValues := &bytes.Buffer{}
|
||||
for i := 0; i < len(postKeyValues); i += 2 {
|
||||
if i != 0 {
|
||||
keyValues.WriteByte('&')
|
||||
}
|
||||
fmt.Fprintf(keyValues, "%s=%s", postKeyValues[i], postKeyValues[i+1])
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", "http://localhost", keyValues)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
ctx, err := authboss.ContextFromRequest(req)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
10
lock/lock.go
10
lock/lock.go
@ -143,8 +143,11 @@ func (l *Lock) AfterAuthFail(ctx *authboss.Context) {
|
||||
ctx.User[UserLocked] = true
|
||||
}
|
||||
|
||||
ctx.User[UserAttemptTime] = time.Now().UTC()
|
||||
ctx.User[UserAttemptNumber] = nAttempts
|
||||
} else {
|
||||
ctx.User[UserAttemptNumber] = 0
|
||||
}
|
||||
ctx.User[UserAttemptTime] = time.Now().UTC()
|
||||
|
||||
if err := ctx.SaveUser(username, l.storer); err != nil {
|
||||
fmt.Fprintf(l.logger, "lock: saving user failed %v", err)
|
||||
@ -180,8 +183,9 @@ func (l *Lock) Unlock(key string, storer authboss.Storer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 10 Years ago should be sufficient, let's not deal with nulls.
|
||||
attr[UserAttemptTime] = time.Now().UTC().AddDate(-10, 0, 0)
|
||||
// Set the last attempt to be -window*2 to avoid immediately
|
||||
// giving another login failure.
|
||||
attr[UserAttemptTime] = time.Now().UTC().Add(-l.window * 2)
|
||||
attr[UserAttemptNumber] = 0
|
||||
attr[UserLocked] = false
|
||||
|
||||
|
@ -1,6 +1,13 @@
|
||||
package lock
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gopkg.in/authboss.v0"
|
||||
"gopkg.in/authboss.v0/internal/mocks"
|
||||
)
|
||||
|
||||
func TestStorage(t *testing.T) {
|
||||
storage := L.Storage()
|
||||
@ -11,3 +18,208 @@ func TestStorage(t *testing.T) {
|
||||
t.Error("Expected attempt number time option.")
|
||||
}
|
||||
}
|
||||
|
||||
func setup(keyValuePairs interface{}) {
|
||||
storer := mocks.NewMockStorer()
|
||||
L.storer = storer
|
||||
_ = make(mocks.MockClientStorer)
|
||||
_ = make(mocks.MockClientStorer)
|
||||
}
|
||||
|
||||
func TestBeforeAuth(t *testing.T) {
|
||||
ctx := authboss.NewContext()
|
||||
|
||||
if nil != L.BeforeAuth(ctx) {
|
||||
t.Error("Expected it to break early.")
|
||||
}
|
||||
|
||||
if err := L.BeforeAuth(ctx); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
ctx.User = authboss.Attributes{"locked": true}
|
||||
|
||||
if err := L.BeforeAuth(ctx); err != ErrLocked {
|
||||
t.Error("Expected an ErrLocked:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAfterAuth(t *testing.T) {
|
||||
lock := Lock{}
|
||||
lock.logger = ioutil.Discard
|
||||
ctx := authboss.NewContext()
|
||||
|
||||
lock.AfterAuth(ctx)
|
||||
if _, ok := ctx.User[UserAttemptNumber]; ok {
|
||||
t.Error("Expected nothing to be set, missing user.")
|
||||
}
|
||||
|
||||
ctx.User = map[string]interface{}{"otherattribute": "somevalue"}
|
||||
lock.AfterAuth(ctx)
|
||||
if _, ok := ctx.User[UserAttemptNumber]; ok {
|
||||
t.Error("Expected username not present to stop this assignment.")
|
||||
}
|
||||
|
||||
ctx.User["username"] = 5
|
||||
lock.AfterAuth(ctx)
|
||||
if _, ok := ctx.User[UserAttemptNumber]; ok {
|
||||
t.Error("Expected username wrong type stop this assignment.")
|
||||
}
|
||||
|
||||
storer := mocks.NewMockStorer()
|
||||
lock.storer = storer
|
||||
ctx.User["username"] = "username"
|
||||
lock.AfterAuth(ctx)
|
||||
|
||||
if storer.Users["username"][UserAttemptNumber].(int) != 0 {
|
||||
t.Error("UserAttemptNumber set incorrectly.")
|
||||
}
|
||||
if _, ok := storer.Users["username"][UserAttemptTime].(time.Time); !ok {
|
||||
t.Error("UserAttemptTime not set.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAfterAuthFail_Lock(t *testing.T) {
|
||||
var old, current time.Time
|
||||
var ok bool
|
||||
|
||||
ctx := authboss.NewContext()
|
||||
storer := mocks.NewMockStorer()
|
||||
lock := Lock{}
|
||||
lock.logger = ioutil.Discard
|
||||
lock.storer = storer
|
||||
lock.window = 30 * time.Minute
|
||||
lock.attempts = 3
|
||||
|
||||
ctx.User = map[string]interface{}{"username": "username"}
|
||||
|
||||
old = time.Now().UTC().Add(-1 * time.Hour)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
if lockedIntf, ok := storer.Users["username"][UserLocked]; ok && lockedIntf.(bool) {
|
||||
t.Errorf("%d: User should not be locked.", i)
|
||||
}
|
||||
|
||||
lock.AfterAuthFail(ctx)
|
||||
if val := storer.Users["username"][UserAttemptNumber].(int); val != i+1 {
|
||||
t.Errorf("%d: UserAttemptNumber set incorrectly: %v", i, val)
|
||||
}
|
||||
if current, ok = storer.Users["username"][UserAttemptTime].(time.Time); !ok || old.After(current) {
|
||||
t.Error("%d: UserAttemptTime not set correctly: %v", i, current)
|
||||
}
|
||||
|
||||
current = old
|
||||
}
|
||||
|
||||
if !storer.Users["username"][UserLocked].(bool) {
|
||||
t.Error("User should be locked.")
|
||||
}
|
||||
if val := storer.Users["username"][UserAttemptNumber].(int); val != 3 {
|
||||
t.Error("UserAttemptNumber set incorrectly:", val)
|
||||
}
|
||||
if _, ok = storer.Users["username"][UserAttemptTime].(time.Time); !ok {
|
||||
t.Error("UserAttemptTime not set correctly.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAfterAuthFail_Reset(t *testing.T) {
|
||||
var old, current time.Time
|
||||
var ok bool
|
||||
|
||||
ctx := authboss.NewContext()
|
||||
storer := mocks.NewMockStorer()
|
||||
lock := Lock{}
|
||||
lock.window = 30 * time.Minute
|
||||
lock.logger = ioutil.Discard
|
||||
lock.storer = storer
|
||||
|
||||
old = time.Now().UTC().Add(-time.Hour)
|
||||
|
||||
ctx.User = map[string]interface{}{
|
||||
"username": "username",
|
||||
UserAttemptNumber: 2,
|
||||
UserAttemptTime: old,
|
||||
UserLocked: false,
|
||||
}
|
||||
|
||||
lock.AfterAuthFail(ctx)
|
||||
if val := storer.Users["username"][UserAttemptNumber].(int); val != 0 {
|
||||
t.Error("UserAttemptNumber set incorrectly:", val)
|
||||
}
|
||||
if current, ok = storer.Users["username"][UserAttemptTime].(time.Time); !ok || current.Before(old) {
|
||||
t.Error("UserAttemptTime not set correctly.")
|
||||
}
|
||||
if locked := storer.Users["username"][UserLocked].(bool); locked {
|
||||
t.Error("UserLocked not set correctly:", locked)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAfterAuthFail_Errors(t *testing.T) {
|
||||
lock := Lock{}
|
||||
lock.logger = ioutil.Discard
|
||||
ctx := authboss.NewContext()
|
||||
|
||||
lock.AfterAuthFail(ctx)
|
||||
if _, ok := ctx.User[UserAttemptNumber]; ok {
|
||||
t.Error("Expected nothing to be set, missing user.")
|
||||
}
|
||||
|
||||
ctx.User = map[string]interface{}{"otherattribute": "somevalue"}
|
||||
lock.AfterAuthFail(ctx)
|
||||
if _, ok := ctx.User[UserAttemptNumber]; ok {
|
||||
t.Error("Expected username not present to stop this assignment.")
|
||||
}
|
||||
|
||||
ctx.User["username"] = 5
|
||||
lock.AfterAuthFail(ctx)
|
||||
if _, ok := ctx.User[UserAttemptNumber]; ok {
|
||||
t.Error("Expected username wrong type stop this assignment.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLock(t *testing.T) {
|
||||
storer := mocks.NewMockStorer()
|
||||
lock := Lock{}
|
||||
|
||||
storer.Users["username"] = map[string]interface{}{
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
}
|
||||
|
||||
err := lock.Lock("username", storer)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if locked := storer.Users["username"][UserLocked].(bool); !locked {
|
||||
t.Error("User should be locked.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnlock(t *testing.T) {
|
||||
storer := mocks.NewMockStorer()
|
||||
lock := Lock{}
|
||||
lock.window = 1 * time.Hour
|
||||
|
||||
storer.Users["username"] = map[string]interface{}{
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"locked": true,
|
||||
}
|
||||
|
||||
err := lock.Unlock("username", storer)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
attemptTime := storer.Users["username"][UserAttemptTime].(time.Time)
|
||||
if attemptTime.After(time.Now().UTC().Add(-lock.window)) {
|
||||
t.Error("UserLocked not set correctly:", attemptTime)
|
||||
}
|
||||
if number := storer.Users["username"][UserAttemptNumber].(int); number != 0 {
|
||||
t.Error("UserLocked not set correctly:", number)
|
||||
}
|
||||
if locked := storer.Users["username"][UserLocked].(bool); locked {
|
||||
t.Error("User should not be locked.")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user