1
0
mirror of https://github.com/volatiletech/authboss.git synced 2025-01-10 04:17:59 +02:00
authboss/lock/lock.go

167 lines
3.5 KiB
Go
Raw Normal View History

// Package lock implements user locking after N bad sign-in attempts.
package lock
import (
"errors"
"time"
"gopkg.in/authboss.v0"
)
2015-03-16 23:42:45 +02:00
// Storage key constants
const (
StoreAttemptNumber = "attempt_number"
StoreAttemptTime = "attempt_time"
StoreLocked = "locked"
)
2015-02-22 22:55:09 +02:00
var (
errUserMissing = errors.New("lock: user not loaded in BeforeAuth callback")
)
func init() {
authboss.RegisterModule("lock", &Lock{})
}
2015-03-16 23:42:45 +02:00
// Lock module
type Lock struct {
2015-04-01 00:27:47 +02:00
*authboss.Authboss
}
2015-03-16 23:42:45 +02:00
// Initialize the module
2015-04-01 00:27:47 +02:00
func (l *Lock) Initialize(ab *authboss.Authboss) error {
l.Authboss = ab
if l.Storer == nil {
2015-03-16 23:42:45 +02:00
return errors.New("lock: Need a Storer")
}
// Events
l.Callbacks.After(authboss.EventGetUser, func(ctx *authboss.Context) error {
_, err := l.beforeAuth(ctx)
return err
})
2015-04-01 00:27:47 +02:00
l.Callbacks.Before(authboss.EventAuth, l.beforeAuth)
l.Callbacks.After(authboss.EventAuth, l.afterAuth)
l.Callbacks.After(authboss.EventAuthFail, l.afterAuthFail)
return nil
}
2015-03-16 23:42:45 +02:00
// Routes for the module
func (l *Lock) Routes() authboss.RouteTable {
return nil
}
2015-03-16 23:42:45 +02:00
// Storage requirements
func (l *Lock) Storage() authboss.StorageOptions {
return authboss.StorageOptions{
2015-04-01 00:27:47 +02:00
l.PrimaryID: authboss.String,
StoreAttemptNumber: authboss.Integer,
StoreAttemptTime: authboss.DateTime,
StoreLocked: authboss.DateTime,
}
}
2015-03-16 23:42:45 +02:00
// beforeAuth ensures the account is not locked.
func (l *Lock) beforeAuth(ctx *authboss.Context) (authboss.Interrupt, error) {
if ctx.User == nil {
2015-02-22 22:55:09 +02:00
return authboss.InterruptNone, errUserMissing
}
if locked, ok := ctx.User.DateTime(StoreLocked); ok && locked.After(time.Now().UTC()) {
2015-02-22 22:43:28 +02:00
return authboss.InterruptAccountLocked, nil
}
2015-02-22 22:43:28 +02:00
return authboss.InterruptNone, nil
}
2015-03-16 23:42:45 +02:00
// afterAuth resets the attempt number field.
func (l *Lock) afterAuth(ctx *authboss.Context) error {
if ctx.User == nil {
2015-02-22 22:55:09 +02:00
return errUserMissing
}
ctx.User[StoreAttemptNumber] = int64(0)
ctx.User[StoreAttemptTime] = time.Now().UTC()
if err := ctx.SaveUser(); err != nil {
2015-02-22 22:43:28 +02:00
return err
}
2015-02-22 22:43:28 +02:00
return nil
}
2015-03-16 23:42:45 +02:00
// afterAuthFail adjusts the attempt number and time.
func (l *Lock) afterAuthFail(ctx *authboss.Context) error {
if ctx.User == nil {
2015-02-22 22:55:09 +02:00
return errUserMissing
}
lastAttempt := time.Now().UTC()
if attemptTime, ok := ctx.User.DateTime(StoreAttemptTime); ok {
lastAttempt = attemptTime
}
var nAttempts int64
if attempts, ok := ctx.User.Int64(StoreAttemptNumber); ok {
nAttempts = attempts
}
nAttempts++
2015-04-01 00:27:47 +02:00
if time.Now().UTC().Sub(lastAttempt) <= l.LockWindow {
if nAttempts >= int64(l.LockAfter) {
ctx.User[StoreLocked] = time.Now().UTC().Add(l.LockDuration)
}
ctx.User[StoreAttemptNumber] = nAttempts
} else {
ctx.User[StoreAttemptNumber] = int64(0)
}
ctx.User[StoreAttemptTime] = time.Now().UTC()
if err := ctx.SaveUser(); err != nil {
2015-02-22 22:43:28 +02:00
return err
}
2015-02-22 22:43:28 +02:00
return nil
}
// Lock a user manually.
2015-02-22 22:43:28 +02:00
func (l *Lock) Lock(key string) error {
2015-04-01 00:27:47 +02:00
user, err := l.Storer.Get(key)
if err != nil {
return err
}
attr := authboss.Unbind(user)
if err != nil {
return err
}
2015-04-01 00:27:47 +02:00
attr[StoreLocked] = time.Now().UTC().Add(l.LockDuration)
2015-04-01 00:27:47 +02:00
return l.Storer.Put(key, attr)
}
// Unlock a user that was locked by this module.
2015-02-22 22:43:28 +02:00
func (l *Lock) Unlock(key string) error {
2015-04-01 00:27:47 +02:00
user, err := l.Storer.Get(key)
if err != nil {
return err
}
attr := authboss.Unbind(user)
if err != nil {
return err
}
// Set the last attempt to be -window*2 to avoid immediately
// giving another login failure.
2015-04-01 00:27:47 +02:00
attr[StoreAttemptTime] = time.Now().UTC().Add(-l.LockWindow * 2)
attr[StoreAttemptNumber] = int64(0)
2015-04-01 00:27:47 +02:00
attr[StoreLocked] = time.Now().UTC().Add(-l.LockDuration)
2015-04-01 00:27:47 +02:00
return l.Storer.Put(key, attr)
}