mirror of
https://github.com/volatiletech/authboss.git
synced 2024-11-24 08:42:17 +02:00
Move create from Storer to RegisterStorer
- Fixed the qq tests - Removed all module singletons
This commit is contained in:
parent
c8e2058440
commit
a7a67981ce
@ -18,8 +18,7 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
a := &Auth{}
|
||||
authboss.RegisterModule("auth", a)
|
||||
authboss.RegisterModule("auth", &Auth{})
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
|
@ -27,6 +27,7 @@ func Init() error {
|
||||
// CurrentUser retrieves the current user from the session and the database.
|
||||
func CurrentUser(w http.ResponseWriter, r *http.Request) (interface{}, error) {
|
||||
sessions := Cfg.SessionStoreMaker(w, r)
|
||||
cookies := Cfg.CookieStoreMaker(w, r)
|
||||
key, ok := sessions.Get(SessionKey)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
@ -37,6 +38,8 @@ func CurrentUser(w http.ResponseWriter, r *http.Request) (interface{}, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx.SessionStorer = clientStoreWrapper{sessions}
|
||||
ctx.CookieStorer = clientStoreWrapper{cookies}
|
||||
err = ctx.LoadUser(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -109,6 +109,8 @@ func NewConfig() *Config {
|
||||
StorePassword, ConfirmPrefix + StorePassword,
|
||||
},
|
||||
|
||||
ExpireAfter: time.Duration(60) * time.Minute,
|
||||
|
||||
RecoverOKPath: "/",
|
||||
RecoverTokenDuration: time.Duration(24) * time.Hour,
|
||||
|
||||
|
@ -39,13 +39,8 @@ type ConfirmStorer interface {
|
||||
ConfirmUser(confirmToken string) (interface{}, error)
|
||||
}
|
||||
|
||||
// C is the singleton instance of the confirm module which will have been
|
||||
// configured and ready to use after authboss.Init()
|
||||
var C *Confirm
|
||||
|
||||
func init() {
|
||||
C = &Confirm{}
|
||||
authboss.RegisterModule("confirm", C)
|
||||
authboss.RegisterModule("confirm", &Confirm{})
|
||||
}
|
||||
|
||||
type Confirm struct {
|
||||
|
@ -15,13 +15,8 @@ const (
|
||||
SessionLastAction = "last_action"
|
||||
)
|
||||
|
||||
// E is the singleton instance of the expire module which will have been
|
||||
// configured and ready to use after authboss.Init()
|
||||
var E *Expire
|
||||
|
||||
func init() {
|
||||
E = &Expire{}
|
||||
authboss.RegisterModule("expire", E)
|
||||
authboss.RegisterModule("expire", &Expire{})
|
||||
}
|
||||
|
||||
type Expire struct{}
|
||||
|
@ -18,13 +18,8 @@ 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)
|
||||
authboss.RegisterModule("lock", &Lock{})
|
||||
}
|
||||
|
||||
type Lock struct {
|
||||
|
@ -9,8 +9,9 @@ import (
|
||||
)
|
||||
|
||||
func TestStorage(t *testing.T) {
|
||||
l := &Lock{}
|
||||
authboss.NewConfig()
|
||||
storage := L.Storage()
|
||||
storage := l.Storage()
|
||||
if _, ok := storage[StoreAttemptNumber]; !ok {
|
||||
t.Error("Expected attempt number storage option.")
|
||||
}
|
||||
@ -20,10 +21,11 @@ func TestStorage(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBeforeAuth(t *testing.T) {
|
||||
l := &Lock{}
|
||||
authboss.NewConfig()
|
||||
ctx := authboss.NewContext()
|
||||
|
||||
if interrupt, err := L.BeforeAuth(ctx); err != errUserMissing {
|
||||
if interrupt, err := l.BeforeAuth(ctx); err != errUserMissing {
|
||||
t.Error("Expected an error because of missing user:", err)
|
||||
} else if interrupt != authboss.InterruptNone {
|
||||
t.Error("Interrupt should not be set:", interrupt)
|
||||
@ -31,7 +33,7 @@ func TestBeforeAuth(t *testing.T) {
|
||||
|
||||
ctx.User = authboss.Attributes{"locked": true}
|
||||
|
||||
if interrupt, err := L.BeforeAuth(ctx); err != nil {
|
||||
if interrupt, err := l.BeforeAuth(ctx); err != nil {
|
||||
t.Error(err)
|
||||
} else if interrupt != authboss.InterruptAccountLocked {
|
||||
t.Error("Expected a locked interrupt:", interrupt)
|
||||
|
@ -2,6 +2,7 @@
|
||||
package register
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
@ -13,13 +14,16 @@ const (
|
||||
tplRegister = "register.html.tpl"
|
||||
)
|
||||
|
||||
// R is the singleton instance of the register module which will have been
|
||||
// configured and ready to use after authboss.Init()
|
||||
var R *Register
|
||||
// RegisterStorer must be implemented in order to satisfy the register module's
|
||||
// storage requirments.
|
||||
type RegisterStorer interface {
|
||||
authboss.Storer
|
||||
// Create is the same as put, except it refers to a non-existent key.
|
||||
Create(key string, attr authboss.Attributes) error
|
||||
}
|
||||
|
||||
func init() {
|
||||
R = &Register{}
|
||||
authboss.RegisterModule("register", R)
|
||||
authboss.RegisterModule("register", &Register{})
|
||||
}
|
||||
|
||||
// Register module.
|
||||
@ -29,6 +33,14 @@ type Register struct {
|
||||
|
||||
// Initialize the module.
|
||||
func (r *Register) Initialize() (err error) {
|
||||
if authboss.Cfg.Storer == nil {
|
||||
return errors.New("register: Need a RegisterStorer.")
|
||||
}
|
||||
|
||||
if _, ok := authboss.Cfg.Storer.(RegisterStorer); !ok {
|
||||
return errors.New("register: RegisterStorer required for register functionality.")
|
||||
}
|
||||
|
||||
if r.templates, err = render.LoadTemplates(authboss.Cfg.Layout, authboss.Cfg.ViewsPath, tplRegister); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -96,7 +108,7 @@ func (reg *Register) registerPostHandler(ctx *authboss.Context, w http.ResponseW
|
||||
attr[authboss.StorePassword] = string(pass)
|
||||
ctx.User = attr
|
||||
|
||||
if err := authboss.Cfg.Storer.Create(key, attr); err != nil {
|
||||
if err := authboss.Cfg.Storer.(RegisterStorer).Create(key, attr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@ func setup() *Register {
|
||||
|
||||
func TestRegister(t *testing.T) {
|
||||
authboss.Cfg = authboss.NewConfig()
|
||||
authboss.Cfg.Storer = mocks.NewMockStorer()
|
||||
r := Register{}
|
||||
|
||||
if err := r.Initialize(); err != nil {
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"gopkg.in/authboss.v0"
|
||||
)
|
||||
@ -40,13 +41,8 @@ type TokenStorer interface {
|
||||
UseToken(givenKey, token string) (key string, err error)
|
||||
}
|
||||
|
||||
// R is the singleton instance of the remember module which will have been
|
||||
// configured and ready to use after authboss.Init()
|
||||
var R *Remember
|
||||
|
||||
func init() {
|
||||
R = &Remember{}
|
||||
authboss.RegisterModule("remember", R)
|
||||
authboss.RegisterModule("remember", &Remember{})
|
||||
}
|
||||
|
||||
type Remember struct{}
|
||||
@ -60,7 +56,8 @@ func (r *Remember) Initialize() error {
|
||||
return errors.New("remember: TokenStorer required for remember me functionality")
|
||||
}
|
||||
|
||||
authboss.Cfg.Callbacks.After(authboss.EventAuth, r.AfterAuth)
|
||||
authboss.Cfg.Callbacks.Before(authboss.EventGet, r.auth)
|
||||
authboss.Cfg.Callbacks.After(authboss.EventAuth, r.afterAuth)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -73,8 +70,8 @@ func (r *Remember) Storage() authboss.StorageOptions {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AfterAuth is called after authentication is successful.
|
||||
func (r *Remember) AfterAuth(ctx *authboss.Context) error {
|
||||
// afterAuth is called after authentication is successful.
|
||||
func (r *Remember) afterAuth(ctx *authboss.Context) error {
|
||||
if val, ok := ctx.FirstPostFormValue(RememberKey); !ok || val != "true" {
|
||||
return nil
|
||||
}
|
||||
@ -88,17 +85,17 @@ func (r *Remember) AfterAuth(ctx *authboss.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := r.New(ctx.CookieStorer, key); err != nil {
|
||||
if _, err := r.new(ctx.CookieStorer, key); err != nil {
|
||||
return fmt.Errorf("remember: Failed to create remember token: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// New generates a new remember token and stores it in the configured TokenStorer.
|
||||
// new generates a new remember token and stores it in the configured TokenStorer.
|
||||
// The return value is a token that should only be given to a user if the delivery
|
||||
// method is secure which means at least signed if not encrypted.
|
||||
func (r *Remember) New(cstorer authboss.ClientStorer, storageKey string) (string, error) {
|
||||
func (r *Remember) new(cstorer authboss.ClientStorer, storageKey string) (string, error) {
|
||||
token := make([]byte, nRandBytes+len(storageKey)+1)
|
||||
copy(token, []byte(storageKey))
|
||||
token[len(storageKey)] = ';'
|
||||
@ -122,42 +119,53 @@ func (r *Remember) New(cstorer authboss.ClientStorer, storageKey string) (string
|
||||
return finalToken, nil
|
||||
}
|
||||
|
||||
// Auth takes a token that was given to a user and checks to see if something
|
||||
// auth takes a token that was given to a user and checks to see if something
|
||||
// is matching in the database. If something is found the old token is deleted
|
||||
// and a new one should be generated. The return value is the key of the
|
||||
// record who owned this token.
|
||||
func (r *Remember) Auth(
|
||||
cstorer authboss.ClientStorer,
|
||||
sstorer authboss.ClientStorer,
|
||||
finalToken string) (string, error) {
|
||||
func (r *Remember) auth(ctx *authboss.Context) (authboss.Interrupt, error) {
|
||||
if val, ok := ctx.SessionStorer.Get(authboss.SessionKey); ok || len(val) > 0 {
|
||||
return authboss.InterruptNone, nil
|
||||
}
|
||||
|
||||
finalToken, ok := ctx.CookieStorer.Get(RememberKey)
|
||||
if !ok {
|
||||
return authboss.InterruptNone, nil
|
||||
}
|
||||
|
||||
log.Println("finalToken", finalToken)
|
||||
|
||||
token, err := base64.URLEncoding.DecodeString(finalToken)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return authboss.InterruptNone, err
|
||||
}
|
||||
|
||||
log.Println("token", token)
|
||||
|
||||
index := bytes.IndexByte(token, ';')
|
||||
if index < 0 {
|
||||
return "", errors.New("remember: Invalid remember me token.")
|
||||
return authboss.InterruptNone, errors.New("remember: Invalid remember me token.")
|
||||
}
|
||||
|
||||
// Get the key.
|
||||
givenKey := token[:index]
|
||||
log.Println("key", givenKey)
|
||||
|
||||
// Verify the tokens match.
|
||||
sum := md5.Sum(token)
|
||||
|
||||
key, err := authboss.Cfg.Storer.(TokenStorer).UseToken(string(givenKey), base64.StdEncoding.EncodeToString(sum[:]))
|
||||
log.Println("lookup", key, err)
|
||||
if err == authboss.ErrTokenNotFound {
|
||||
return "", nil
|
||||
return authboss.InterruptNone, nil
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
return authboss.InterruptNone, err
|
||||
}
|
||||
|
||||
// Ensure a half-auth.
|
||||
sstorer.Put(authboss.SessionHalfAuthKey, "true")
|
||||
ctx.SessionStorer.Put(authboss.SessionHalfAuthKey, "true")
|
||||
// Log the user in.
|
||||
sstorer.Put(authboss.SessionKey, key)
|
||||
ctx.SessionStorer.Put(authboss.SessionKey, string(givenKey))
|
||||
|
||||
return key, nil
|
||||
return authboss.InterruptNone, nil
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ func TestInitialize(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAfterAuth(t *testing.T) {
|
||||
r := Remember{}
|
||||
authboss.NewConfig()
|
||||
storer := mocks.NewMockStorer()
|
||||
authboss.Cfg.Storer = storer
|
||||
@ -54,7 +55,7 @@ func TestAfterAuth(t *testing.T) {
|
||||
ctx.CookieStorer = cookies
|
||||
ctx.User = authboss.Attributes{authboss.Cfg.PrimaryID: "test@email.com"}
|
||||
|
||||
if err := R.AfterAuth(ctx); err != nil {
|
||||
if err := r.afterAuth(ctx); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
@ -64,13 +65,14 @@ func TestAfterAuth(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
r := &Remember{}
|
||||
authboss.NewConfig()
|
||||
storer := mocks.NewMockStorer()
|
||||
authboss.Cfg.Storer = storer
|
||||
cookies := mocks.NewMockClientStorer()
|
||||
|
||||
key := "tester"
|
||||
token, err := R.New(cookies, key)
|
||||
token, err := r.new(cookies, key)
|
||||
|
||||
if err != nil {
|
||||
t.Error("Unexpected error:", err)
|
||||
@ -92,19 +94,24 @@ func TestNew(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuth(t *testing.T) {
|
||||
r := &Remember{}
|
||||
authboss.NewConfig()
|
||||
storer := mocks.NewMockStorer()
|
||||
authboss.Cfg.Storer = storer
|
||||
|
||||
cookies := mocks.NewMockClientStorer()
|
||||
session := mocks.NewMockClientStorer()
|
||||
ctx := authboss.NewContext()
|
||||
ctx.CookieStorer = cookies
|
||||
ctx.SessionStorer = session
|
||||
|
||||
key := "tester"
|
||||
token, err := R.New(cookies, key)
|
||||
_, err := r.new(cookies, key)
|
||||
if err != nil {
|
||||
t.Error("Unexpected error:", err)
|
||||
}
|
||||
|
||||
outKey, err := R.Auth(cookies, session, token)
|
||||
interrupt, err := r.auth(ctx)
|
||||
if err != nil {
|
||||
t.Error("Unexpected error:", err)
|
||||
}
|
||||
@ -117,7 +124,7 @@ func TestAuth(t *testing.T) {
|
||||
t.Error("The user should have been logged in.")
|
||||
}
|
||||
|
||||
if key != outKey {
|
||||
t.Error("Keys should have matched:", outKey)
|
||||
if authboss.InterruptNone != interrupt {
|
||||
t.Error("Keys should have matched:", interrupt)
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,6 @@ type StorageOptions map[string]DataType
|
||||
// The type of store is up to the developer implementing it, and all it has to
|
||||
// do is be able to store several simple types.
|
||||
type Storer interface {
|
||||
// Create is the same as put, except it refers to a non-existent key.
|
||||
Create(key string, attr Attributes) error
|
||||
// Put is for storing the attributes passed in. The type information can
|
||||
// help serialization without using type assertions.
|
||||
Put(key string, attr Attributes) error
|
||||
|
Loading…
Reference in New Issue
Block a user