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:
parent
8f6f322c63
commit
fe7b990095
17
config.go
17
config.go
@ -3,22 +3,29 @@ package authboss
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Config holds all the configuration for both authboss and it's modules.
|
||||
type Config struct {
|
||||
MountPath string `json:"mountPath" xml:"mountPath"`
|
||||
ViewsPath string `json:"viewsPath" xml:"viewsPath"`
|
||||
// MountPath is the path to mount the router at.
|
||||
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"`
|
||||
AuthLoginSuccessRoute string `json:"authLoginSuccessRoute" xml:"authLoginSuccessRoute"`
|
||||
AuthLogoutRoute string `json:"auth_logout_route" xml:"authLogoutRoute"`
|
||||
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:"-"`
|
||||
ValidateUsername 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:"-"`
|
||||
CookieStoreMaker CookieStoreMaker `json:"-" xml:"-"`
|
||||
SessionStoreMaker SessionStoreMaker `json:"-" xml:"-"`
|
||||
|
189
lock/lock.go
Normal file
189
lock/lock.go
Normal 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
13
lock/lock_test.go
Normal 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.")
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user