1
0
mirror of https://github.com/volatiletech/authboss.git synced 2025-02-11 13:53:16 +02:00

Add beginnings of lock package.

- Adjust configuration for some lock variables and better XML Names.
This commit is contained in:
Aaron 2015-01-23 16:25:12 -08:00
parent 8f6f322c63
commit fe7b990095
3 changed files with 214 additions and 5 deletions

View File

@ -3,22 +3,29 @@ package authboss
import ( import (
"io" "io"
"io/ioutil" "io/ioutil"
"time"
) )
// Config holds all the configuration for both authboss and it's modules. // Config holds all the configuration for both authboss and it's modules.
type Config struct { type Config struct {
MountPath string `json:"mountPath" xml:"mountPath"` // MountPath is the path to mount the router at.
ViewsPath string `json:"viewsPath" xml:"viewsPath"` MountPath string `json:"mount_path" xml:"mountPath"`
// ViewsPath is the path to overiding view template files.
ViewsPath string `json:"views_path" xml:"viewsPath"`
AuthLogoutRoute string `json:"authLogoutRoute" xml:"authLogoutRoute"` AuthLogoutRoute string `json:"auth_logout_route" xml:"authLogoutRoute"`
AuthLoginSuccessRoute string `json:"authLoginSuccessRoute" xml:"authLoginSuccessRoute"` AuthLoginSuccessRoute string `json:"auth_login_success_route" xml:"authLoginSuccessRoute"`
RecoverFromEmail string `json:"recoverFromEmail" xml:"recoverFromEmail"` RecoverFromEmail string `json:"recover_from_email" xml:"recoverFromEmail"`
ValidateEmail Validator `json:"-" xml:"-"` ValidateEmail Validator `json:"-" xml:"-"`
ValidateUsername Validator `json:"-" xml:"-"` ValidateUsername Validator `json:"-" xml:"-"`
ValidatePassword Validator `json:"-" xml:"-"` ValidatePassword Validator `json:"-" xml:"-"`
LockAfter int `json:"lock_after" xml:"lockAfter"`
LockWindow time.Duration `json:"lock_window" xml:"lockWindow"`
LockDuration time.Duration `json:"lock_duration" xml:"lockDuration"`
Storer Storer `json:"-" xml:"-"` Storer Storer `json:"-" xml:"-"`
CookieStoreMaker CookieStoreMaker `json:"-" xml:"-"` CookieStoreMaker CookieStoreMaker `json:"-" xml:"-"`
SessionStoreMaker SessionStoreMaker `json:"-" xml:"-"` SessionStoreMaker SessionStoreMaker `json:"-" xml:"-"`

189
lock/lock.go Normal file
View File

@ -0,0 +1,189 @@
// Package lock implements user locking after N bad sign-in attempts.
package lock
import (
"errors"
"fmt"
"io"
"time"
"gopkg.in/authboss.v0"
)
const (
UserAttemptNumber = "attempt_number"
UserAttemptTime = "attempt_time"
UserLocked = "locked"
)
var (
ErrLocked = errors.New("Account is locked.")
)
// L is the singleton instance of the lock module which will have been
// configured and ready to use after authboss.Init()
var L *Lock
func init() {
L = &Lock{}
authboss.RegisterModule("lock", L)
}
type Lock struct {
storer authboss.TokenStorer
logger io.Writer
attempts int
window time.Duration
duration time.Duration
}
func (l *Lock) Initialize(config *authboss.Config) error {
if config.Storer == nil {
return errors.New("lock: Need a Storer.")
}
l.logger = config.LogWriter
l.attempts = config.LockAfter
l.window = config.LockWindow
l.duration = config.LockDuration
// Events
config.Callbacks.Before(authboss.EventGet, l.BeforeAuth)
config.Callbacks.Before(authboss.EventAuth, l.BeforeAuth)
config.Callbacks.After(authboss.EventAuth, l.AfterAuth)
config.Callbacks.After(authboss.EventAuthFail, l.AfterAuthFail)
return nil
}
func (l *Lock) Routes() authboss.RouteTable {
return nil
}
func (l *Lock) Storage() authboss.StorageOptions {
return authboss.StorageOptions{
UserAttemptNumber: authboss.Integer,
UserAttemptTime: authboss.DateTime,
}
}
// BeforeAuth ensures the account is not locked.
func (l *Lock) BeforeAuth(ctx *authboss.Context) error {
if ctx.User == nil {
return nil
}
if intf, ok := ctx.User[UserLocked]; ok {
if locked, ok := intf.(bool); ok && locked {
return ErrLocked
}
}
return nil
}
// AfterAuth resets the attempt number field.
func (l *Lock) AfterAuth(ctx *authboss.Context) {
if ctx.User == nil {
return
}
var username string
if intf, ok := ctx.User["username"]; !ok {
fmt.Fprintf(l.logger, "lock: username not present")
return
} else if username, ok = intf.(string); !ok {
fmt.Fprintf(l.logger, "lock: username wrong type")
return
}
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)
}
}
// AfterAuthFail adjusts the attempt number and time.
func (l *Lock) AfterAuthFail(ctx *authboss.Context) {
if ctx.User == nil {
return
}
var username string
if intf, ok := ctx.User["username"]; !ok {
fmt.Fprintf(l.logger, "lock: username not present")
return
} else if username, ok = intf.(string); !ok {
fmt.Fprintf(l.logger, "lock: username wrong type")
return
}
lastAttempt := time.Now().UTC()
if attemptTimeIntf, ok := ctx.User[UserAttemptTime]; ok {
if attemptTime, ok := attemptTimeIntf.(time.Time); ok {
lastAttempt = attemptTime
}
}
nAttempts := 0
if attemptsIntf, ok := ctx.User[UserAttemptNumber]; ok {
if attempts, ok := attemptsIntf.(int); ok {
nAttempts = attempts
}
}
nAttempts++
if time.Now().UTC().Sub(lastAttempt) <= l.window {
if nAttempts >= l.attempts {
ctx.User[UserLocked] = true
}
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)
}
}
// Lock a user manually.
func (l *Lock) Lock(key string, storer authboss.Storer) error {
user, err := storer.Get(key, authboss.ModuleAttrMeta)
if err != nil {
return err
}
attr := authboss.Unbind(user)
if err != nil {
return err
}
attr[UserLocked] = true
return storer.Put(key, attr)
}
// Unlock a user that was locked by this module.
func (l *Lock) Unlock(key string, storer authboss.Storer) error {
user, err := storer.Get(key, authboss.ModuleAttrMeta)
if err != nil {
return err
}
attr := authboss.Unbind(user)
if err != nil {
return err
}
// 10 Years ago should be sufficient, let's not deal with nulls.
attr[UserAttemptTime] = time.Now().UTC().AddDate(-10, 0, 0)
attr[UserAttemptNumber] = 0
attr[UserLocked] = false
return storer.Put(key, attr)
}

13
lock/lock_test.go Normal file
View File

@ -0,0 +1,13 @@
package lock
import "testing"
func TestStorage(t *testing.T) {
storage := L.Storage()
if _, ok := storage[UserAttemptNumber]; !ok {
t.Error("Expected attempt number storage option.")
}
if _, ok := storage[UserAttemptTime]; !ok {
t.Error("Expected attempt number time option.")
}
}