mirror of
https://github.com/volatiletech/authboss.git
synced 2025-01-08 04:03:53 +02:00
161 lines
3.4 KiB
Go
161 lines
3.4 KiB
Go
// Package lock implements user locking after N bad sign-in attempts.
|
|
package lock
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"gopkg.in/authboss.v0"
|
|
)
|
|
|
|
const (
|
|
StoreAttemptNumber = "attempt_number"
|
|
StoreAttemptTime = "attempt_time"
|
|
StoreLocked = "locked"
|
|
)
|
|
|
|
var (
|
|
errUserMissing = errors.New("lock: user not loaded in BeforeAuth callback")
|
|
)
|
|
|
|
// 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 {
|
|
}
|
|
|
|
func (l *Lock) Initialize() error {
|
|
if authboss.Cfg.Storer == nil {
|
|
return errors.New("lock: Need a Storer.")
|
|
}
|
|
|
|
// Events
|
|
authboss.Cfg.Callbacks.Before(authboss.EventGet, l.BeforeAuth)
|
|
authboss.Cfg.Callbacks.Before(authboss.EventAuth, l.BeforeAuth)
|
|
authboss.Cfg.Callbacks.After(authboss.EventAuth, l.AfterAuth)
|
|
authboss.Cfg.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{
|
|
StoreAttemptNumber: authboss.Integer,
|
|
StoreAttemptTime: authboss.DateTime,
|
|
StoreLocked: authboss.Bool,
|
|
}
|
|
}
|
|
|
|
// BeforeAuth ensures the account is not locked.
|
|
func (l *Lock) BeforeAuth(ctx *authboss.Context) (authboss.Interrupt, error) {
|
|
if ctx.User == nil {
|
|
return authboss.InterruptNone, errUserMissing
|
|
}
|
|
|
|
if locked, ok := ctx.User.Bool(StoreLocked); ok && locked {
|
|
return authboss.InterruptAccountLocked, nil
|
|
}
|
|
|
|
return authboss.InterruptNone, nil
|
|
}
|
|
|
|
// AfterAuth resets the attempt number field.
|
|
func (l *Lock) AfterAuth(ctx *authboss.Context) error {
|
|
if ctx.User == nil {
|
|
return errUserMissing
|
|
}
|
|
|
|
ctx.User[StoreAttemptNumber] = 0
|
|
ctx.User[StoreAttemptTime] = time.Now().UTC()
|
|
|
|
if err := ctx.SaveUser(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AfterAuthFail adjusts the attempt number and time.
|
|
func (l *Lock) AfterAuthFail(ctx *authboss.Context) error {
|
|
if ctx.User == nil {
|
|
return errUserMissing
|
|
}
|
|
|
|
lastAttempt := time.Now().UTC()
|
|
if attemptTime, ok := ctx.User.DateTime(StoreAttemptTime); ok {
|
|
lastAttempt = attemptTime
|
|
}
|
|
|
|
nAttempts := 0
|
|
if attempts, ok := ctx.User.Int(StoreAttemptNumber); ok {
|
|
nAttempts = attempts
|
|
}
|
|
|
|
nAttempts++
|
|
|
|
if time.Now().UTC().Sub(lastAttempt) <= authboss.Cfg.LockWindow {
|
|
if nAttempts >= authboss.Cfg.LockAfter {
|
|
ctx.User[StoreLocked] = true
|
|
}
|
|
|
|
ctx.User[StoreAttemptNumber] = nAttempts
|
|
} else {
|
|
ctx.User[StoreAttemptNumber] = 0
|
|
}
|
|
ctx.User[StoreAttemptTime] = time.Now().UTC()
|
|
|
|
if err := ctx.SaveUser(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Lock a user manually.
|
|
func (l *Lock) Lock(key string) error {
|
|
user, err := authboss.Cfg.Storer.Get(key, authboss.ModuleAttrMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
attr := authboss.Unbind(user)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
attr[StoreLocked] = true
|
|
|
|
return authboss.Cfg.Storer.Put(key, attr)
|
|
}
|
|
|
|
// Unlock a user that was locked by this module.
|
|
func (l *Lock) Unlock(key string) error {
|
|
user, err := authboss.Cfg.Storer.Get(key, authboss.ModuleAttrMeta)
|
|
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.
|
|
attr[StoreAttemptTime] = time.Now().UTC().Add(-authboss.Cfg.LockWindow * 2)
|
|
attr[StoreAttemptNumber] = 0
|
|
attr[StoreLocked] = false
|
|
|
|
return authboss.Cfg.Storer.Put(key, attr)
|
|
}
|