1
0
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:
Aaron 2015-01-24 22:19:22 -08:00
parent 65f7fad5fc
commit f0552cd8c6
3 changed files with 258 additions and 7 deletions

View File

@ -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
}

View File

@ -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

View File

@ -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.")
}
}