1
0
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:
Kris Runzer 2015-02-26 23:09:37 -08:00
parent c8e2058440
commit a7a67981ce
12 changed files with 78 additions and 61 deletions

View File

@ -18,8 +18,7 @@ const (
)
func init() {
a := &Auth{}
authboss.RegisterModule("auth", a)
authboss.RegisterModule("auth", &Auth{})
}
type Auth struct {

View File

@ -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

View File

@ -109,6 +109,8 @@ func NewConfig() *Config {
StorePassword, ConfirmPrefix + StorePassword,
},
ExpireAfter: time.Duration(60) * time.Minute,
RecoverOKPath: "/",
RecoverTokenDuration: time.Duration(24) * time.Hour,

View File

@ -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 {

View File

@ -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{}

View File

@ -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 {

View File

@ -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)

View File

@ -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
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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