1
0
mirror of https://github.com/volatiletech/authboss.git synced 2024-11-28 08:58:38 +02:00

Change config to be global. Updated most modules and tests.

This commit is contained in:
Kris Runzer 2015-02-15 20:07:36 -08:00
parent 1aa0da808c
commit bab1475b72
25 changed files with 284 additions and 421 deletions

View File

@ -3,16 +3,13 @@ package auth
import ( import (
"errors" "errors"
"fmt" "fmt"
"html/template"
"net/http" "net/http"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"gopkg.in/authboss.v0" "gopkg.in/authboss.v0"
"gopkg.in/authboss.v0/internal/views" "gopkg.in/authboss.v0/internal/views"
"html/template"
"io"
) )
const ( const (
@ -44,19 +41,14 @@ type AuthPage struct {
type Auth struct { type Auth struct {
routes authboss.RouteTable routes authboss.RouteTable
storageOptions authboss.StorageOptions storageOptions authboss.StorageOptions
storer authboss.Storer
logoutRedirect string
loginRedirect string
logger io.Writer
templates map[string]*template.Template templates map[string]*template.Template
callbacks *authboss.Callbacks
isRememberLoaded bool isRememberLoaded bool
isRecoverLoaded bool isRecoverLoaded bool
} }
func (a *Auth) Initialize(config *authboss.Config) (err error) { func (a *Auth) Initialize() (err error) {
if a.templates, err = views.Get(config.Layout, config.ViewsPath, pageLogin); err != nil { if a.templates, err = views.Get(authboss.Cfg.Layout, authboss.Cfg.ViewsPath, pageLogin); err != nil {
return err return err
} }
@ -68,11 +60,6 @@ func (a *Auth) Initialize(config *authboss.Config) (err error) {
attrUsername: authboss.String, attrUsername: authboss.String,
attrPassword: authboss.String, attrPassword: authboss.String,
} }
a.storer = config.Storer
a.logoutRedirect = config.AuthLogoutRoute
a.loginRedirect = config.AuthLoginSuccessRoute
a.logger = config.LogWriter
a.callbacks = config.Callbacks
a.isRememberLoaded = authboss.IsLoaded("remember") a.isRememberLoaded = authboss.IsLoaded("remember")
a.isRecoverLoaded = authboss.IsLoaded("recover") a.isRecoverLoaded = authboss.IsLoaded("recover")
@ -93,7 +80,7 @@ func (a *Auth) loginHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r
case methodGET: case methodGET:
if _, ok := ctx.SessionStorer.Get(authboss.SessionKey); ok { if _, ok := ctx.SessionStorer.Get(authboss.SessionKey); ok {
if halfAuthed, ok := ctx.SessionStorer.Get(authboss.HalfAuthKey); !ok || halfAuthed == "false" { if halfAuthed, ok := ctx.SessionStorer.Get(authboss.HalfAuthKey); !ok || halfAuthed == "false" {
http.Redirect(w, r, a.loginRedirect, http.StatusFound) http.Redirect(w, r, authboss.Cfg.AuthLoginSuccessRoute, http.StatusFound)
} }
} }
@ -106,14 +93,13 @@ func (a *Auth) loginHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r
tpl := a.templates[pageLogin] tpl := a.templates[pageLogin]
tpl.Execute(w, page) tpl.Execute(w, page)
// tpl.ExecuteTemplate(w, tpl.Name(), page)
case methodPOST: case methodPOST:
u, ok := ctx.FirstPostFormValue("username") u, ok := ctx.FirstPostFormValue("username")
if !ok { if !ok {
fmt.Fprintln(a.logger, errors.New("auth: Expected postFormValue 'username' to be in the context")) fmt.Fprintln(authboss.Cfg.LogWriter, errors.New("auth: Expected postFormValue 'username' to be in the context"))
} }
if err := a.callbacks.FireBefore(authboss.EventAuth, ctx); err != nil { if err := authboss.Cfg.Callbacks.FireBefore(authboss.EventAuth, ctx); err != nil {
w.WriteHeader(http.StatusForbidden) w.WriteHeader(http.StatusForbidden)
tpl := a.templates[pageLogin] tpl := a.templates[pageLogin]
@ -122,11 +108,11 @@ func (a *Auth) loginHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r
p, ok := ctx.FirstPostFormValue("password") p, ok := ctx.FirstPostFormValue("password")
if !ok { if !ok {
fmt.Fprintln(a.logger, errors.New("auth: Expected postFormValue 'password' to be in the context")) fmt.Fprintln(authboss.Cfg.LogWriter, errors.New("auth: Expected postFormValue 'password' to be in the context"))
} }
if err := a.authenticate(ctx, u, p); err != nil { if err := a.authenticate(ctx, u, p); err != nil {
fmt.Fprintln(a.logger, err) fmt.Fprintln(authboss.Cfg.LogWriter, err)
w.WriteHeader(http.StatusForbidden) w.WriteHeader(http.StatusForbidden)
tpl := a.templates[pageLogin] tpl := a.templates[pageLogin]
tpl.ExecuteTemplate(w, tpl.Name(), AuthPage{"invalid username and/or password", u, a.isRememberLoaded, a.isRecoverLoaded, "", ""}) tpl.ExecuteTemplate(w, tpl.Name(), AuthPage{"invalid username and/or password", u, a.isRememberLoaded, a.isRecoverLoaded, "", ""})
@ -134,9 +120,9 @@ func (a *Auth) loginHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r
} }
ctx.SessionStorer.Put(authboss.SessionKey, u) ctx.SessionStorer.Put(authboss.SessionKey, u)
a.callbacks.FireAfter(authboss.EventAuth, ctx) authboss.Cfg.Callbacks.FireAfter(authboss.EventAuth, ctx)
http.Redirect(w, r, a.loginRedirect, http.StatusFound) http.Redirect(w, r, authboss.Cfg.AuthLoginSuccessRoute, http.StatusFound)
default: default:
w.WriteHeader(http.StatusMethodNotAllowed) w.WriteHeader(http.StatusMethodNotAllowed)
} }
@ -145,7 +131,7 @@ func (a *Auth) loginHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r
func (a *Auth) authenticate(ctx *authboss.Context, username, password string) error { func (a *Auth) authenticate(ctx *authboss.Context, username, password string) error {
var userInter interface{} var userInter interface{}
var err error var err error
if userInter, err = a.storer.Get(username, nil); err != nil { if userInter, err = authboss.Cfg.Storer.Get(username, nil); err != nil {
return err return err
} }
@ -172,7 +158,7 @@ func (a *Auth) logoutHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r
switch r.Method { switch r.Method {
case methodGET: case methodGET:
ctx.SessionStorer.Del(authboss.SessionKey) ctx.SessionStorer.Del(authboss.SessionKey)
http.Redirect(w, r, a.logoutRedirect, http.StatusFound) http.Redirect(w, r, authboss.Cfg.AuthLogoutRoute, http.StatusFound)
default: default:
w.WriteHeader(http.StatusMethodNotAllowed) w.WriteHeader(http.StatusMethodNotAllowed)
} }

View File

@ -1,6 +1,6 @@
package auth package auth
import ( /*import (
"bytes" "bytes"
"html/template" "html/template"
"io/ioutil" "io/ioutil"
@ -33,10 +33,8 @@ func getCompiledTemplate(path string, data interface{}) (b *bytes.Buffer, err er
} }
func TestAuth_Storage(t *testing.T) { func TestAuth_Storage(t *testing.T) {
t.Parallel()
a := &Auth{} a := &Auth{}
if err := a.Initialize(authboss.NewConfig()); err != nil { if err := a.Initialize(); err != nil {
t.Errorf("Unexpected config error: %v", err) t.Errorf("Unexpected config error: %v", err)
} }
options := a.Storage() options := a.Storage()
@ -61,10 +59,8 @@ func TestAuth_Storage(t *testing.T) {
} }
func TestAuth_Routes(t *testing.T) { func TestAuth_Routes(t *testing.T) {
t.Parallel()
a := &Auth{} a := &Auth{}
if err := a.Initialize(authboss.NewConfig()); err != nil { if err := a.Initialize(); err != nil {
t.Errorf("Unexpected config error: %v", err) t.Errorf("Unexpected config error: %v", err)
} }
routes := a.Routes() routes := a.Routes()
@ -86,53 +82,35 @@ func TestAuth_Routes(t *testing.T) {
} }
func TestAuth_loginHandlerFunc_GET(t *testing.T) { func TestAuth_loginHandlerFunc_GET(t *testing.T) {
t.Parallel() a := &Auth{}
if err := a.Initialize(); err != nil {
tests := []struct { t.Errorf("Unexpected config error: %v", err)
Config *authboss.Config
}{
{authboss.NewConfig()},
{&authboss.Config{}},
{&authboss.Config{ViewsPath: "views"}},
} }
for i, test := range tests { r, err := http.NewRequest("GET", "/login", nil)
a := &Auth{} if err != nil {
if err := a.Initialize(test.Config); err != nil { t.Errorf("Unexpected error '%s'", err)
t.Errorf("%d> Unexpected config error: %v", i, err) }
continue w := httptest.NewRecorder()
}
r, err := http.NewRequest("GET", "/login", nil) ctx, err := authboss.ContextFromRequest(r)
if err != nil { if err != nil {
t.Errorf("Unexpected error '%s'", err) t.Errorf("Unexpected error '%s'", err)
} }
w := httptest.NewRecorder() ctx.SessionStorer = testClientStorer{}
ctx, err := authboss.ContextFromRequest(r) a.loginHandlerFunc(ctx, w, r)
if err != nil {
t.Errorf("%d> Unexpected error '%s'", i, err)
continue
}
ctx.SessionStorer = testClientStorer{}
a.loginHandlerFunc(ctx, w, r) if tpl, err := getCompiledTemplate("views/login.tpl", nil); err != nil {
t.Errorf("Unexpected error '%s'", err)
if tpl, err := getCompiledTemplate("views/login.tpl", nil); err != nil { } else {
t.Errorf("%d> Unexpected error '%s'", i, err) if !bytes.Equal(tpl.Bytes(), w.Body.Bytes()) {
continue t.Errorf("Expected '%s', got '%s'", tpl.Bytes(), w.Body.Bytes())
} else {
if !bytes.Equal(tpl.Bytes(), w.Body.Bytes()) {
t.Errorf("%d> Expected '%s', got '%s'", i, tpl.Bytes(), w.Body.Bytes())
continue
}
} }
} }
} }
func TestAuth_loginHandlerFunc_POST(t *testing.T) { func TestAuth_loginHandlerFunc_POST(t *testing.T) {
t.Parallel()
tests := []struct { tests := []struct {
Username, Password string Username, Password string
StatusCode int StatusCode int
@ -141,17 +119,16 @@ func TestAuth_loginHandlerFunc_POST(t *testing.T) {
BodyData *AuthPage BodyData *AuthPage
}{ }{
{"john", "1234", http.StatusFound, true, "/dashboard", nil}, {"john", "1234", http.StatusFound, true, "/dashboard", nil},
{"jane", "1234", http.StatusForbidden, false, "", &AuthPage{"invalid username and/or password", "jane", false, false}}, {"jane", "1234", http.StatusForbidden, false, "", &AuthPage{"invalid username and/or password", "jane", false, false, "", ""}},
{"mike", "", http.StatusForbidden, false, "", &AuthPage{"invalid username and/or password", "jane", false, false}}, {"mike", "", http.StatusForbidden, false, "", &AuthPage{"invalid username and/or password", "jane", false, false, "", ""}},
} }
c := authboss.NewConfig() authboss.Cfg.Storer = NewMockUserStorer()
c.Storer = NewMockUserStorer() authboss.Cfg.AuthLoginSuccessRoute = "/dashboard"
c.AuthLoginSuccessRoute = "/dashboard"
for i, test := range tests { for i, test := range tests {
a := &Auth{} a := &Auth{}
if err := a.Initialize(c); err != nil { if err := a.Initialize(); err != nil {
t.Errorf("%d> Unexpected config error: %v", i, err) t.Errorf("%d> Unexpected config error: %v", i, err)
continue continue
} }
@ -211,8 +188,6 @@ func TestAuth_loginHandlerFunc_POST(t *testing.T) {
} }
func TestAuth_loginHandlerFunc_OtherMethods(t *testing.T) { func TestAuth_loginHandlerFunc_OtherMethods(t *testing.T) {
t.Parallel()
a := Auth{} a := Auth{}
methods := []string{"HEAD", "PUT", "DELETE", "TRACE", "CONNECT"} methods := []string{"HEAD", "PUT", "DELETE", "TRACE", "CONNECT"}
@ -233,10 +208,9 @@ func TestAuth_loginHandlerFunc_OtherMethods(t *testing.T) {
} }
func TestAuth_logoutHandlerFunc_GET(t *testing.T) { func TestAuth_logoutHandlerFunc_GET(t *testing.T) {
t.Parallel() authboss.Cfg.AuthLogoutRoute = "/dashboard"
a := Auth{} a := Auth{}
if err := a.Initialize(&authboss.Config{AuthLogoutRoute: "/dashboard"}); err != nil { if err := a.Initialize(); err != nil {
t.Errorf("Unexpeced config error '%s'", err) t.Errorf("Unexpeced config error '%s'", err)
} }
r, err := http.NewRequest("GET", "/logout", nil) r, err := http.NewRequest("GET", "/logout", nil)
@ -267,8 +241,6 @@ func TestAuth_logoutHandlerFunc_GET(t *testing.T) {
} }
func TestAuth_logoutHandlerFunc_OtherMethods(t *testing.T) { func TestAuth_logoutHandlerFunc_OtherMethods(t *testing.T) {
t.Parallel()
a := Auth{} a := Auth{}
methods := []string{"HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT"} methods := []string{"HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT"}
@ -287,3 +259,4 @@ func TestAuth_logoutHandlerFunc_OtherMethods(t *testing.T) {
} }
} }
} }
*/

View File

@ -8,26 +8,15 @@ Remember Me tokens, or passwords.
package authboss // import "gopkg.in/authboss.v0" package authboss // import "gopkg.in/authboss.v0"
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
) )
var ( // Init authboss and it's loaded modules.
cfg *Config func Init() error {
)
// Init authboss and it's loaded modules with a configuration.
func Init(config *Config) error {
if config.Storer == nil {
return errors.New("configuration must provide a storer.")
}
cfg = config
for name, mod := range modules { for name, mod := range modules {
fmt.Fprintf(cfg.LogWriter, "%-10s Initializing\n", "["+name+"]") fmt.Fprintf(Cfg.LogWriter, "%-10s Initializing\n", "["+name+"]")
if err := mod.Initialize(config); err != nil { if err := mod.Initialize(); err != nil {
return fmt.Errorf("[%s] Error Initializing: %v", name, err) return fmt.Errorf("[%s] Error Initializing: %v", name, err)
} }
} }
@ -37,7 +26,7 @@ func Init(config *Config) error {
// CurrentUser retrieves the current user from the session and the database. // CurrentUser retrieves the current user from the session and the database.
func CurrentUser(w http.ResponseWriter, r *http.Request) (interface{}, error) { func CurrentUser(w http.ResponseWriter, r *http.Request) (interface{}, error) {
sessions := cfg.SessionStoreMaker(w, r) sessions := Cfg.SessionStoreMaker(w, r)
key, ok := sessions.Get(SessionKey) key, ok := sessions.Get(SessionKey)
if !ok { if !ok {
return nil, nil return nil, nil
@ -48,17 +37,17 @@ func CurrentUser(w http.ResponseWriter, r *http.Request) (interface{}, error) {
return nil, err return nil, err
} }
err = ctx.LoadUser(key, cfg.Storer) err = ctx.LoadUser(key, Cfg.Storer)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = cfg.Callbacks.FireBefore(EventGet, ctx) err = Cfg.Callbacks.FireBefore(EventGet, ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return cfg.Storer.Get(key, ModuleAttrMeta) return Cfg.Storer.Get(key, ModuleAttrMeta)
} }
// CurrentUserP retrieves the current user but panics if it's not available for // CurrentUserP retrieves the current user but panics if it's not available for

View File

@ -4,45 +4,34 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
"strings"
"testing" "testing"
) )
func TestMain(main *testing.M) { func TestMain(main *testing.M) {
RegisterModule("testmodule", testMod) RegisterModule("testmodule", testMod)
Init(NewConfig()) Init()
code := main.Run() code := main.Run()
os.Exit(code) os.Exit(code)
} }
func TestAuthBossInit(t *testing.T) { func TestAuthBossInit(t *testing.T) {
c := NewConfig() NewConfig()
err := Init()
err := Init(c)
if err == nil || !strings.Contains(err.Error(), "storer") {
t.Error("Expected error about a storer, got:", err)
}
c.Storer = mockStorer{}
err = Init(c)
if err != nil { if err != nil {
t.Error("Unexpected error:", err) t.Error("Unexpected error:", err)
} }
if testMod.c == nil {
t.Error("Expected the modules to be passed the config.")
}
} }
func TestAuthBossRouter(t *testing.T) { func TestAuthBossRouter(t *testing.T) {
c := NewConfig() NewConfig()
c.Storer = mockStorer{} Cfg.Storer = mockStorer{}
c.CookieStoreMaker = func(_ http.ResponseWriter, _ *http.Request) ClientStorer { Cfg.CookieStoreMaker = func(_ http.ResponseWriter, _ *http.Request) ClientStorer {
return mockClientStore{} return mockClientStore{}
} }
c.SessionStoreMaker = SessionStoreMaker(c.CookieStoreMaker) Cfg.SessionStoreMaker = SessionStoreMaker(Cfg.CookieStoreMaker)
c.MountPath = "/candycanes" Cfg.MountPath = "/candycanes"
if err := Init(c); err != nil { if err := Init(); err != nil {
t.Error("Unexpected error:", err) t.Error("Unexpected error:", err)
} }
router := NewRouter() router := NewRouter()
@ -58,13 +47,13 @@ func TestAuthBossRouter(t *testing.T) {
} }
func TestAuthBossCurrentUser(t *testing.T) { func TestAuthBossCurrentUser(t *testing.T) {
c := NewConfig() NewConfig()
c.Storer = mockStorer{"joe": Attributes{"email": "john@john.com", "password": "lies"}} Cfg.Storer = mockStorer{"joe": Attributes{"email": "john@john.com", "password": "lies"}}
c.SessionStoreMaker = func(_ http.ResponseWriter, _ *http.Request) ClientStorer { Cfg.SessionStoreMaker = func(_ http.ResponseWriter, _ *http.Request) ClientStorer {
return mockClientStore{SessionKey: "joe"} return mockClientStore{SessionKey: "joe"}
} }
if err := Init(c); err != nil { if err := Init(); err != nil {
t.Error("Unexpected error:", err) t.Error("Unexpected error:", err)
} }

View File

@ -16,6 +16,9 @@ const (
layoutEmailTpl = "layoutEmail.tpl" layoutEmailTpl = "layoutEmail.tpl"
) )
// Cfg is the singleton instance of Config
var Cfg *Config = NewConfig()
// Config holds all the configuration for both authboss and it's modules. // Config holds all the configuration for both authboss and it's modules.
type Config struct { type Config struct {
// MountPath is the path to mount the router at. // MountPath is the path to mount the router at.
@ -64,7 +67,6 @@ type Config struct {
Mailer Mailer Mailer Mailer
} }
// NewConfig creates a new config full of default values ready to override.
func NewConfig() *Config { func NewConfig() *Config {
layout, err := views.AssetToTemplate(layoutTpl) layout, err := views.AssetToTemplate(layoutTpl)
if err != nil { if err != nil {

View File

@ -29,14 +29,10 @@ func init() {
authboss.RegisterModule("expire", E) authboss.RegisterModule("expire", E)
} }
type Expire struct { type Expire struct{}
window time.Duration
}
func (e *Expire) Initialize(config *authboss.Config) error { func (e *Expire) Initialize() error {
e.window = config.ExpireAfter authboss.Cfg.Callbacks.Before(authboss.EventGet, e.BeforeAuth)
config.Callbacks.Before(authboss.EventGet, e.BeforeAuth)
return nil return nil
} }
@ -54,7 +50,7 @@ func (e *Expire) BeforeAuth(ctx *authboss.Context) error {
if ok { if ok {
if date, err := time.Parse(time.RFC3339, dateStr); err != nil { if date, err := time.Parse(time.RFC3339, dateStr); err != nil {
Touch(ctx.SessionStorer) Touch(ctx.SessionStorer)
} else if time.Now().UTC().After(date.Add(e.window)) { } else if time.Now().UTC().After(date.Add(authboss.Cfg.ExpireAfter)) {
ctx.SessionStorer.Del(authboss.SessionKey) ctx.SessionStorer.Del(authboss.SessionKey)
return ErrExpired return ErrExpired
} }

View File

@ -9,21 +9,8 @@ import (
"gopkg.in/authboss.v0/internal/mocks" "gopkg.in/authboss.v0/internal/mocks"
) )
func TestExpire(t *testing.T) {
t.Parallel()
config := authboss.NewConfig()
config.ExpireAfter = time.Hour
E.Initialize(config)
if E.window != time.Hour {
t.Error("Config not loaded properly:", E.window)
}
}
func TestExpire_Touch(t *testing.T) { func TestExpire_Touch(t *testing.T) {
t.Parallel() authboss.NewConfig()
session := mocks.NewMockClientStorer() session := mocks.NewMockClientStorer()
if _, ok := session.Get(UserLastAction); ok { if _, ok := session.Get(UserLastAction); ok {
@ -40,9 +27,9 @@ func TestExpire_Touch(t *testing.T) {
} }
func TestExpire_BeforeAuth(t *testing.T) { func TestExpire_BeforeAuth(t *testing.T) {
t.Parallel() authboss.NewConfig()
authboss.Cfg.ExpireAfter = time.Hour
expire := &Expire{window: time.Hour} expire := &Expire{}
session := mocks.NewMockClientStorer() session := mocks.NewMockClientStorer()
ctx := mocks.MockRequestContext() ctx := mocks.MockRequestContext()
@ -82,6 +69,7 @@ func (t *testHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
func TestExpire_Middleware(t *testing.T) { func TestExpire_Middleware(t *testing.T) {
authboss.NewConfig()
session := mocks.NewMockClientStorer() session := mocks.NewMockClientStorer()
session.Values = map[string]string{ session.Values = map[string]string{
authboss.SessionKey: "username", authboss.SessionKey: "username",

View File

@ -4,7 +4,6 @@ package lock
import ( import (
"errors" "errors"
"fmt" "fmt"
"io"
"time" "time"
"gopkg.in/authboss.v0" "gopkg.in/authboss.v0"
@ -30,30 +29,18 @@ func init() {
} }
type Lock struct { type Lock struct {
storer authboss.Storer
logger io.Writer
attempts int
window time.Duration
duration time.Duration
} }
func (l *Lock) Initialize(config *authboss.Config) error { func (l *Lock) Initialize() error {
if config.Storer == nil { if authboss.Cfg.Storer == nil {
return errors.New("lock: Need a Storer.") return errors.New("lock: Need a Storer.")
} }
l.logger = config.LogWriter
l.attempts = config.LockAfter
l.window = config.LockWindow
l.duration = config.LockDuration
// Events // Events
config.Callbacks.Before(authboss.EventGet, l.BeforeAuth) authboss.Cfg.Callbacks.Before(authboss.EventGet, l.BeforeAuth)
config.Callbacks.Before(authboss.EventAuth, l.BeforeAuth) authboss.Cfg.Callbacks.Before(authboss.EventAuth, l.BeforeAuth)
config.Callbacks.After(authboss.EventAuth, l.AfterAuth) authboss.Cfg.Callbacks.After(authboss.EventAuth, l.AfterAuth)
config.Callbacks.After(authboss.EventAuthFail, l.AfterAuthFail) authboss.Cfg.Callbacks.After(authboss.EventAuthFail, l.AfterAuthFail)
return nil return nil
} }
@ -88,23 +75,23 @@ func (l *Lock) BeforeAuth(ctx *authboss.Context) error {
// AfterAuth resets the attempt number field. // AfterAuth resets the attempt number field.
func (l *Lock) AfterAuth(ctx *authboss.Context) { func (l *Lock) AfterAuth(ctx *authboss.Context) {
if ctx.User == nil { if ctx.User == nil {
fmt.Fprintln(l.logger, "lock: user not loaded in after auth callback") fmt.Fprintln(authboss.Cfg.LogWriter, "lock: user not loaded in after auth callback")
} }
var username string var username string
if intf, ok := ctx.User["username"]; !ok { if intf, ok := ctx.User["username"]; !ok {
fmt.Fprintf(l.logger, "lock: username not present") fmt.Fprintf(authboss.Cfg.LogWriter, "lock: username not present")
return return
} else if username, ok = intf.(string); !ok { } else if username, ok = intf.(string); !ok {
fmt.Fprintf(l.logger, "lock: username wrong type") fmt.Fprintf(authboss.Cfg.LogWriter, "lock: username wrong type")
return return
} }
ctx.User[UserAttemptNumber] = 0 ctx.User[UserAttemptNumber] = 0
ctx.User[UserAttemptTime] = time.Now().UTC() ctx.User[UserAttemptTime] = time.Now().UTC()
if err := ctx.SaveUser(username, l.storer); err != nil { if err := ctx.SaveUser(username, authboss.Cfg.Storer); err != nil {
fmt.Fprintf(l.logger, "lock: saving user failed %v", err) fmt.Fprintf(authboss.Cfg.LogWriter, "lock: saving user failed %v", err)
} }
} }
@ -116,10 +103,10 @@ func (l *Lock) AfterAuthFail(ctx *authboss.Context) {
var username string var username string
if intf, ok := ctx.User["username"]; !ok { if intf, ok := ctx.User["username"]; !ok {
fmt.Fprintf(l.logger, "lock: username not present") fmt.Fprintf(authboss.Cfg.LogWriter, "lock: username not present")
return return
} else if username, ok = intf.(string); !ok { } else if username, ok = intf.(string); !ok {
fmt.Fprintf(l.logger, "lock: username wrong type") fmt.Fprintf(authboss.Cfg.LogWriter, "lock: username wrong type")
return return
} }
@ -139,8 +126,8 @@ func (l *Lock) AfterAuthFail(ctx *authboss.Context) {
nAttempts++ nAttempts++
if time.Now().UTC().Sub(lastAttempt) <= l.window { if time.Now().UTC().Sub(lastAttempt) <= authboss.Cfg.LockWindow {
if nAttempts >= l.attempts { if nAttempts >= authboss.Cfg.LockAfter {
ctx.User[UserLocked] = true ctx.User[UserLocked] = true
} }
@ -150,8 +137,8 @@ func (l *Lock) AfterAuthFail(ctx *authboss.Context) {
} }
ctx.User[UserAttemptTime] = time.Now().UTC() ctx.User[UserAttemptTime] = time.Now().UTC()
if err := ctx.SaveUser(username, l.storer); err != nil { if err := ctx.SaveUser(username, authboss.Cfg.Storer); err != nil {
fmt.Fprintf(l.logger, "lock: saving user failed %v", err) fmt.Fprintf(authboss.Cfg.LogWriter, "lock: saving user failed %v", err)
} }
} }
@ -186,7 +173,7 @@ func (l *Lock) Unlock(key string, storer authboss.Storer) error {
// Set the last attempt to be -window*2 to avoid immediately // Set the last attempt to be -window*2 to avoid immediately
// giving another login failure. // giving another login failure.
attr[UserAttemptTime] = time.Now().UTC().Add(-l.window * 2) attr[UserAttemptTime] = time.Now().UTC().Add(-authboss.Cfg.LockWindow * 2)
attr[UserAttemptNumber] = 0 attr[UserAttemptNumber] = 0
attr[UserLocked] = false attr[UserLocked] = false

View File

@ -1,7 +1,6 @@
package lock package lock
import ( import (
"io/ioutil"
"testing" "testing"
"time" "time"
@ -10,6 +9,7 @@ import (
) )
func TestStorage(t *testing.T) { func TestStorage(t *testing.T) {
authboss.NewConfig()
storage := L.Storage() storage := L.Storage()
if _, ok := storage[UserAttemptNumber]; !ok { if _, ok := storage[UserAttemptNumber]; !ok {
t.Error("Expected attempt number storage option.") t.Error("Expected attempt number storage option.")
@ -20,8 +20,8 @@ func TestStorage(t *testing.T) {
} }
func TestBeforeAuth(t *testing.T) { func TestBeforeAuth(t *testing.T) {
authboss.NewConfig()
ctx := authboss.NewContext() ctx := authboss.NewContext()
L.logger = ioutil.Discard
if nil != L.BeforeAuth(ctx) { if nil != L.BeforeAuth(ctx) {
t.Error("Expected it to break early.") t.Error("Expected it to break early.")
@ -39,8 +39,8 @@ func TestBeforeAuth(t *testing.T) {
} }
func TestAfterAuth(t *testing.T) { func TestAfterAuth(t *testing.T) {
authboss.NewConfig()
lock := Lock{} lock := Lock{}
lock.logger = ioutil.Discard
ctx := authboss.NewContext() ctx := authboss.NewContext()
lock.AfterAuth(ctx) lock.AfterAuth(ctx)
@ -61,7 +61,7 @@ func TestAfterAuth(t *testing.T) {
} }
storer := mocks.NewMockStorer() storer := mocks.NewMockStorer()
lock.storer = storer authboss.Cfg.Storer = storer
ctx.User["username"] = "username" ctx.User["username"] = "username"
lock.AfterAuth(ctx) lock.AfterAuth(ctx)
@ -74,16 +74,16 @@ func TestAfterAuth(t *testing.T) {
} }
func TestAfterAuthFail_Lock(t *testing.T) { func TestAfterAuthFail_Lock(t *testing.T) {
authboss.NewConfig()
var old, current time.Time var old, current time.Time
var ok bool var ok bool
ctx := authboss.NewContext() ctx := authboss.NewContext()
storer := mocks.NewMockStorer() storer := mocks.NewMockStorer()
authboss.Cfg.Storer = storer
lock := Lock{} lock := Lock{}
lock.logger = ioutil.Discard authboss.Cfg.LockWindow = 30 * time.Minute
lock.storer = storer authboss.Cfg.LockAfter = 3
lock.window = 30 * time.Minute
lock.attempts = 3
ctx.User = map[string]interface{}{"username": "username"} ctx.User = map[string]interface{}{"username": "username"}
@ -117,15 +117,15 @@ func TestAfterAuthFail_Lock(t *testing.T) {
} }
func TestAfterAuthFail_Reset(t *testing.T) { func TestAfterAuthFail_Reset(t *testing.T) {
authboss.NewConfig()
var old, current time.Time var old, current time.Time
var ok bool var ok bool
ctx := authboss.NewContext() ctx := authboss.NewContext()
storer := mocks.NewMockStorer() storer := mocks.NewMockStorer()
lock := Lock{} lock := Lock{}
lock.window = 30 * time.Minute authboss.Cfg.LockWindow = 30 * time.Minute
lock.logger = ioutil.Discard authboss.Cfg.Storer = storer
lock.storer = storer
old = time.Now().UTC().Add(-time.Hour) old = time.Now().UTC().Add(-time.Hour)
@ -149,8 +149,8 @@ func TestAfterAuthFail_Reset(t *testing.T) {
} }
func TestAfterAuthFail_Errors(t *testing.T) { func TestAfterAuthFail_Errors(t *testing.T) {
authboss.NewConfig()
lock := Lock{} lock := Lock{}
lock.logger = ioutil.Discard
ctx := authboss.NewContext() ctx := authboss.NewContext()
lock.AfterAuthFail(ctx) lock.AfterAuthFail(ctx)
@ -172,6 +172,7 @@ func TestAfterAuthFail_Errors(t *testing.T) {
} }
func TestLock(t *testing.T) { func TestLock(t *testing.T) {
authboss.NewConfig()
storer := mocks.NewMockStorer() storer := mocks.NewMockStorer()
lock := Lock{} lock := Lock{}
@ -191,9 +192,10 @@ func TestLock(t *testing.T) {
} }
func TestUnlock(t *testing.T) { func TestUnlock(t *testing.T) {
authboss.NewConfig()
storer := mocks.NewMockStorer() storer := mocks.NewMockStorer()
lock := Lock{} lock := Lock{}
lock.window = 1 * time.Hour authboss.Cfg.LockWindow = 1 * time.Hour
storer.Users["username"] = map[string]interface{}{ storer.Users["username"] = map[string]interface{}{
"username": "username", "username": "username",
@ -207,7 +209,7 @@ func TestUnlock(t *testing.T) {
} }
attemptTime := storer.Users["username"][UserAttemptTime].(time.Time) attemptTime := storer.Users["username"][UserAttemptTime].(time.Time)
if attemptTime.After(time.Now().UTC().Add(-lock.window)) { if attemptTime.After(time.Now().UTC().Add(-authboss.Cfg.LockWindow)) {
t.Error("UserLocked not set correctly:", attemptTime) t.Error("UserLocked not set correctly:", attemptTime)
} }
if number := storer.Users["username"][UserAttemptNumber].(int); number != 0 { if number := storer.Users["username"][UserAttemptNumber].(int); number != 0 {

View File

@ -11,7 +11,7 @@ import (
// SendMail uses the currently configured mailer to deliver e-mails. // SendMail uses the currently configured mailer to deliver e-mails.
func SendMail(data Email) error { func SendMail(data Email) error {
return cfg.Mailer.Send(data) return Cfg.Mailer.Send(data)
} }
// Mailer is a type that is capable of sending an e-mail. // Mailer is a type that is capable of sending an e-mail.

View File

@ -7,12 +7,12 @@ import (
) )
func TestMailer(t *testing.T) { func TestMailer(t *testing.T) {
NewConfig()
mailServer := &bytes.Buffer{} mailServer := &bytes.Buffer{}
config := NewConfig() Cfg.Mailer = LogMailer(mailServer)
config.Mailer = LogMailer(mailServer) Cfg.Storer = mockStorer{}
config.Storer = mockStorer{} Init()
Init(config)
err := SendMail(Email{ err := SendMail(Email{
To: []string{"some@email.com", "a@a.com"}, To: []string{"some@email.com", "a@a.com"},

View File

@ -6,7 +6,7 @@ var ModuleAttrMeta = make(AttributeMeta)
// Modularizer should be implemented by all the authboss modules. // Modularizer should be implemented by all the authboss modules.
type Modularizer interface { type Modularizer interface {
Initialize(*Config) error Initialize() error
Routes() RouteTable Routes() RouteTable
Storage() StorageOptions Storage() StorageOptions
} }

View File

@ -8,7 +8,6 @@ import (
const testModName = "testmodule" const testModName = "testmodule"
type testModule struct { type testModule struct {
c *Config
s StorageOptions s StorageOptions
r RouteTable r RouteTable
} }
@ -23,8 +22,7 @@ func testHandler(ctx *Context, w http.ResponseWriter, r *http.Request) {
w.Header().Set("testhandler", "test") w.Header().Set("testhandler", "test")
} }
func (t *testModule) Initialize(c *Config) error { func (t *testModule) Initialize() error {
t.c = c
return nil return nil
} }

View File

@ -24,15 +24,15 @@ type pageRecoverComplete struct {
func (m *RecoverModule) recoverCompleteHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) { func (m *RecoverModule) recoverCompleteHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) {
switch r.Method { switch r.Method {
case methodGET: case methodGET:
_, err := verifyToken(ctx, m.config.Storer.(authboss.RecoverStorer)) _, err := verifyToken(ctx, authboss.Cfg.Storer.(authboss.RecoverStorer))
if err != nil { if err != nil {
if err.Error() == errRecoveryTokenExpired.Error() { if err.Error() == errRecoveryTokenExpired.Error() {
fmt.Fprintln(m.config.LogWriter, "recover [token expired]:", err) fmt.Fprintln(authboss.Cfg.LogWriter, "recover [token expired]:", err)
ctx.SessionStorer.Put(authboss.FlashErrorKey, m.config.RecoverTokenExpiredFlash) ctx.SessionStorer.Put(authboss.FlashErrorKey, authboss.Cfg.RecoverTokenExpiredFlash)
http.Redirect(w, r, "/recover", http.StatusFound) http.Redirect(w, r, "/recover", http.StatusFound)
return return
} else { } else {
fmt.Fprintln(m.config.LogWriter, "recover:", err) fmt.Fprintln(authboss.Cfg.LogWriter, "recover:", err)
http.Redirect(w, r, "/", http.StatusFound) http.Redirect(w, r, "/", http.StatusFound)
return return
} }
@ -52,7 +52,7 @@ func (m *RecoverModule) recoverCompleteHandlerFunc(ctx *authboss.Context, w http
return return
} }
http.Redirect(w, r, m.config.AuthLoginSuccessRoute, http.StatusFound) http.Redirect(w, r, authboss.Cfg.AuthLoginSuccessRoute, http.StatusFound)
default: default:
w.WriteHeader(http.StatusMethodNotAllowed) w.WriteHeader(http.StatusMethodNotAllowed)
} }
@ -90,24 +90,24 @@ func (m *RecoverModule) recoverComplete(ctx *authboss.Context) (errPage *pageRec
token, _ := ctx.FirstFormValue("token") token, _ := ctx.FirstFormValue("token")
password, _ := ctx.FirstPostFormValue("password") password, _ := ctx.FirstPostFormValue("password")
confirmPassword, _ := ctx.FirstPostFormValue("confirmPassword") confirmPassword, _ := ctx.FirstPostFormValue("confirmPassword")
defaultErrPage := &pageRecoverComplete{token, password, confirmPassword, nil, "", m.config.RecoverFailedErrorFlash} defaultErrPage := &pageRecoverComplete{token, password, confirmPassword, nil, "", authboss.Cfg.RecoverFailedErrorFlash}
var err error var err error
ctx.User, err = verifyToken(ctx, m.config.Storer.(authboss.RecoverStorer)) ctx.User, err = verifyToken(ctx, authboss.Cfg.Storer.(authboss.RecoverStorer))
if err != nil { if err != nil {
fmt.Fprintf(m.config.LogWriter, errFormat, "failed to verify token", err) fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "failed to verify token", err)
return defaultErrPage return defaultErrPage
} }
policies := authboss.FilterValidators(m.config.Policies, "password") policies := authboss.FilterValidators(authboss.Cfg.Policies, "password")
if validationErrs := ctx.Validate(policies, m.config.ConfirmFields...); len(validationErrs) > 0 { if validationErrs := ctx.Validate(policies, authboss.Cfg.ConfirmFields...); len(validationErrs) > 0 {
fmt.Fprintf(m.config.LogWriter, errFormat, "validation failed", validationErrs) fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "validation failed", validationErrs)
return &pageRecoverComplete{token, password, confirmPassword, validationErrs.Map(), "", ""} return &pageRecoverComplete{token, password, confirmPassword, validationErrs.Map(), "", ""}
} }
encryptedPassword, err := bcrypt.GenerateFromPassword([]byte(password), m.config.BCryptCost) encryptedPassword, err := bcrypt.GenerateFromPassword([]byte(password), authboss.Cfg.BCryptCost)
if err != nil { if err != nil {
fmt.Fprintf(m.config.LogWriter, errFormat, "failed to encrypt password", err) fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "failed to encrypt password", err)
return defaultErrPage return defaultErrPage
} }
ctx.User[attrPassword] = string(encryptedPassword) ctx.User[attrPassword] = string(encryptedPassword)
@ -116,8 +116,8 @@ func (m *RecoverModule) recoverComplete(ctx *authboss.Context) (errPage *pageRec
ctx.User[attrRecoverTokenExpiry] = nullTime ctx.User[attrRecoverTokenExpiry] = nullTime
username, _ := ctx.User.String(attrUsername) username, _ := ctx.User.String(attrUsername)
if err := ctx.SaveUser(username, m.config.Storer); err != nil { if err := ctx.SaveUser(username, authboss.Cfg.Storer); err != nil {
fmt.Fprintf(m.config.LogWriter, errFormat, "failed to save user", err) fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "failed to save user", err)
return defaultErrPage return defaultErrPage
} }

View File

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log"
"net/http" "net/http"
"net/url" "net/url"
"reflect" "reflect"
@ -22,11 +21,9 @@ const (
) )
func Test_recoverCompleteHandlerFunc_GET_TokenExpired(t *testing.T) { func Test_recoverCompleteHandlerFunc_GET_TokenExpired(t *testing.T) {
t.Parallel()
m, logger := testValidRecoverModule() m, logger := testValidRecoverModule()
storer, ok := m.config.Storer.(*mocks.MockStorer) storer, ok := authboss.Cfg.Storer.(*mocks.MockStorer)
if !ok { if !ok {
panic("Failed to get storer") panic("Failed to get storer")
} }
@ -50,7 +47,7 @@ func Test_recoverCompleteHandlerFunc_GET_TokenExpired(t *testing.T) {
t.Error("Expected logs to start with:", "recover [token expired]:") t.Error("Expected logs to start with:", "recover [token expired]:")
} }
if flash := clientStorer.Values[authboss.FlashErrorKey]; flash != m.config.RecoverTokenExpiredFlash { if flash := clientStorer.Values[authboss.FlashErrorKey]; flash != authboss.Cfg.RecoverTokenExpiredFlash {
t.Error("Unexpected error flash:", flash) t.Error("Unexpected error flash:", flash)
} }
@ -60,8 +57,6 @@ func Test_recoverCompleteHandlerFunc_GET_TokenExpired(t *testing.T) {
} }
func Test_recoverCompleteHandlerFunc_GET_OtherErrors(t *testing.T) { func Test_recoverCompleteHandlerFunc_GET_OtherErrors(t *testing.T) {
t.Parallel()
m, logger := testValidRecoverModule() m, logger := testValidRecoverModule()
w, r, ctx := testHttpRequest("GET", "/recover/complete?token=asdf", nil) w, r, ctx := testHttpRequest("GET", "/recover/complete?token=asdf", nil)
@ -81,11 +76,9 @@ func Test_recoverCompleteHandlerFunc_GET_OtherErrors(t *testing.T) {
} }
func Test_recoverCompleteHandlerFunc_GET(t *testing.T) { func Test_recoverCompleteHandlerFunc_GET(t *testing.T) {
t.Parallel()
m, _ := testValidRecoverModule() m, _ := testValidRecoverModule()
storer, ok := m.config.Storer.(*mocks.MockStorer) storer, ok := authboss.Cfg.Storer.(*mocks.MockStorer)
if !ok { if !ok {
panic("Failed to get storer") panic("Failed to get storer")
} }
@ -117,8 +110,6 @@ func Test_recoverCompleteHandlerFunc_GET(t *testing.T) {
} }
func Test_recoverCompleteHandlerFunc_POST_RecoveryCompleteFailed(t *testing.T) { func Test_recoverCompleteHandlerFunc_POST_RecoveryCompleteFailed(t *testing.T) {
t.Parallel()
m, _ := testValidRecoverModule() m, _ := testValidRecoverModule()
w, r, ctx := testHttpRequest( w, r, ctx := testHttpRequest(
"POST", "POST",
@ -132,7 +123,7 @@ func Test_recoverCompleteHandlerFunc_POST_RecoveryCompleteFailed(t *testing.T) {
Token: testUrlBase64Token, Token: testUrlBase64Token,
Password: "a", Password: "a",
ConfirmPassword: "a", ConfirmPassword: "a",
FlashError: m.config.RecoverFailedErrorFlash, FlashError: authboss.Cfg.RecoverFailedErrorFlash,
}); err != nil { }); err != nil {
panic(err) panic(err)
} }
@ -140,9 +131,6 @@ func Test_recoverCompleteHandlerFunc_POST_RecoveryCompleteFailed(t *testing.T) {
// missing storer will cause this to fail // missing storer will cause this to fail
m.recoverCompleteHandlerFunc(ctx, w, r) m.recoverCompleteHandlerFunc(ctx, w, r)
// spew.Dump(expectedBody.Bytes())
// spew.Dump(w.Body.Bytes())
if w.Code != http.StatusOK { if w.Code != http.StatusOK {
t.Error("Unexpected code:", w.Code) t.Error("Unexpected code:", w.Code)
} }
@ -153,16 +141,14 @@ func Test_recoverCompleteHandlerFunc_POST_RecoveryCompleteFailed(t *testing.T) {
} }
func Test_recoverCompleteHandlerFunc_POST(t *testing.T) { func Test_recoverCompleteHandlerFunc_POST(t *testing.T) {
t.Parallel() m, _ := testValidRecoverModule()
m, logger := testValidRecoverModule()
w, r, ctx := testHttpRequest( w, r, ctx := testHttpRequest(
"POST", "POST",
fmt.Sprintf("/recover/complete?token=%s", testUrlBase64Token), fmt.Sprintf("/recover/complete?token=%s", testUrlBase64Token),
url.Values{"password": []string{"a"}, "confirmPassword": []string{"a"}}, url.Values{"password": []string{"a"}, "confirmPassword": []string{"a"}},
) )
storer, ok := m.config.Storer.(*mocks.MockStorer) storer, ok := authboss.Cfg.Storer.(*mocks.MockStorer)
if !ok { if !ok {
panic("Failed to get storer") panic("Failed to get storer")
} }
@ -174,8 +160,6 @@ func Test_recoverCompleteHandlerFunc_POST(t *testing.T) {
m.recoverCompleteHandlerFunc(ctx, w, r) m.recoverCompleteHandlerFunc(ctx, w, r)
log.Println(logger)
if w.Code != http.StatusFound { if w.Code != http.StatusFound {
t.Error("Unexpected code:", w.Code) t.Error("Unexpected code:", w.Code)
} }
@ -187,8 +171,7 @@ func Test_recoverCompleteHandlerFunc_POST(t *testing.T) {
} }
func Test_verifyToken_MissingToken(t *testing.T) { func Test_verifyToken_MissingToken(t *testing.T) {
t.Parallel() authboss.NewConfig()
ctx := mocks.MockRequestContext() ctx := mocks.MockRequestContext()
_, err := verifyToken(ctx, nil) _, err := verifyToken(ctx, nil)
@ -198,10 +181,9 @@ func Test_verifyToken_MissingToken(t *testing.T) {
} }
func Test_verifyToken_InvalidToken(t *testing.T) { func Test_verifyToken_InvalidToken(t *testing.T) {
t.Parallel() authboss.NewConfig()
testValidTestConfig()
config := testValidTestConfig() storer, ok := authboss.Cfg.Storer.(*mocks.MockStorer)
storer, ok := config.Storer.(*mocks.MockStorer)
if !ok { if !ok {
panic("Failed to get storer") panic("Failed to get storer")
} }
@ -218,10 +200,8 @@ func Test_verifyToken_InvalidToken(t *testing.T) {
} }
func Test_verifyToken_ExpiredToken(t *testing.T) { func Test_verifyToken_ExpiredToken(t *testing.T) {
t.Parallel() testValidTestConfig()
storer, ok := authboss.Cfg.Storer.(*mocks.MockStorer)
config := testValidTestConfig()
storer, ok := config.Storer.(*mocks.MockStorer)
if !ok { if !ok {
panic("Failed to get storer") panic("Failed to get storer")
} }
@ -239,10 +219,9 @@ func Test_verifyToken_ExpiredToken(t *testing.T) {
} }
func Test_verifyToken(t *testing.T) { func Test_verifyToken(t *testing.T) {
t.Parallel() testValidTestConfig()
config := testValidTestConfig()
storer, ok := config.Storer.(*mocks.MockStorer) storer, ok := authboss.Cfg.Storer.(*mocks.MockStorer)
if !ok { if !ok {
panic("Failed to get storer") panic("Failed to get storer")
} }
@ -263,8 +242,6 @@ func Test_verifyToken(t *testing.T) {
} }
func Test_recoverComplete_TokenVerificationFails(t *testing.T) { func Test_recoverComplete_TokenVerificationFails(t *testing.T) {
t.Parallel()
m, logger := testValidRecoverModule() m, logger := testValidRecoverModule()
ctx := mocks.MockRequestContext() ctx := mocks.MockRequestContext()
@ -272,7 +249,7 @@ func Test_recoverComplete_TokenVerificationFails(t *testing.T) {
if errPage == nil { if errPage == nil {
t.Error("Expected err page") t.Error("Expected err page")
} }
if !reflect.DeepEqual(*errPage, pageRecoverComplete{FlashError: m.config.RecoverFailedErrorFlash}) { if !reflect.DeepEqual(*errPage, pageRecoverComplete{FlashError: authboss.Cfg.RecoverFailedErrorFlash}) {
t.Error("Unexpected err page:", errPage) t.Error("Unexpected err page:", errPage)
} }
@ -286,12 +263,10 @@ func Test_recoverComplete_TokenVerificationFails(t *testing.T) {
} }
func Test_recoverComplete_ValidationFails(t *testing.T) { func Test_recoverComplete_ValidationFails(t *testing.T) {
t.Parallel()
m, logger := testValidRecoverModule() m, logger := testValidRecoverModule()
ctx := mocks.MockRequestContext("token", testUrlBase64Token, "password", "a", "confirmPassword", "b") ctx := mocks.MockRequestContext("token", testUrlBase64Token, "password", "a", "confirmPassword", "b")
storer, ok := m.config.Storer.(*mocks.MockStorer) storer, ok := authboss.Cfg.Storer.(*mocks.MockStorer)
if !ok { if !ok {
panic("Failed to get storer") panic("Failed to get storer")
} }
@ -326,15 +301,13 @@ func Test_recoverComplete_ValidationFails(t *testing.T) {
} }
func Test_recoverComplete(t *testing.T) { func Test_recoverComplete(t *testing.T) {
t.Parallel()
m, _ := testValidRecoverModule() m, _ := testValidRecoverModule()
ctx := mocks.MockRequestContext("token", testUrlBase64Token, "password", "a", "confirmPassword", "a") ctx := mocks.MockRequestContext("token", testUrlBase64Token, "password", "a", "confirmPassword", "a")
clientStorer := mocks.NewMockClientStorer() clientStorer := mocks.NewMockClientStorer()
ctx.SessionStorer = clientStorer ctx.SessionStorer = clientStorer
storer, ok := m.config.Storer.(*mocks.MockStorer) storer, ok := authboss.Cfg.Storer.(*mocks.MockStorer)
if !ok { if !ok {
panic("Failed to get storer") panic("Failed to get storer")
} }
@ -375,8 +348,6 @@ func Test_recoverComplete(t *testing.T) {
} }
func Test_recoverCompleteHandlerFunc_OtherMethods(t *testing.T) { func Test_recoverCompleteHandlerFunc_OtherMethods(t *testing.T) {
t.Parallel()
m, _ := testValidRecoverModule() m, _ := testValidRecoverModule()
for i, method := range []string{"HEAD", "PUT", "DELETE", "TRACE", "CONNECT"} { for i, method := range []string{"HEAD", "PUT", "DELETE", "TRACE", "CONNECT"} {

View File

@ -35,8 +35,8 @@ func (m *RecoverModule) recoverHandlerFunc(ctx *authboss.Context, w http.Respons
return return
} }
ctx.SessionStorer.Put(authboss.FlashSuccessKey, m.config.RecoverInitiateSuccessFlash) ctx.SessionStorer.Put(authboss.FlashSuccessKey, authboss.Cfg.RecoverInitiateSuccessFlash)
http.Redirect(w, r, m.config.RecoverRedirect, http.StatusFound) http.Redirect(w, r, authboss.Cfg.RecoverRedirect, http.StatusFound)
default: default:
w.WriteHeader(http.StatusMethodNotAllowed) w.WriteHeader(http.StatusMethodNotAllowed)
} }
@ -46,23 +46,23 @@ func (m *RecoverModule) recover(ctx *authboss.Context) (errPage *pageRecover, em
username, _ := ctx.FirstPostFormValue("username") username, _ := ctx.FirstPostFormValue("username")
confirmUsername, _ := ctx.FirstPostFormValue("confirmUsername") confirmUsername, _ := ctx.FirstPostFormValue("confirmUsername")
policies := authboss.FilterValidators(m.config.Policies, "username") policies := authboss.FilterValidators(authboss.Cfg.Policies, "username")
if validationErrs := ctx.Validate(policies, m.config.ConfirmFields...); len(validationErrs) > 0 { if validationErrs := ctx.Validate(policies, authboss.Cfg.ConfirmFields...); len(validationErrs) > 0 {
fmt.Fprintf(m.config.LogWriter, errFormat, "validation failed", validationErrs) fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "validation failed", validationErrs)
return &pageRecover{username, confirmUsername, validationErrs.Map(), "", ""}, nil return &pageRecover{username, confirmUsername, validationErrs.Map(), "", ""}, nil
} }
err, emailSent := m.makeAndSendToken(ctx, username) err, emailSent := m.makeAndSendToken(ctx, username)
if err != nil { if err != nil {
fmt.Fprintf(m.config.LogWriter, errFormat, "failed to recover", err) fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "failed to recover", err)
return &pageRecover{username, confirmUsername, nil, "", m.config.RecoverFailedErrorFlash}, nil return &pageRecover{username, confirmUsername, nil, "", authboss.Cfg.RecoverFailedErrorFlash}, nil
} }
return nil, emailSent return nil, emailSent
} }
func (m *RecoverModule) makeAndSendToken(ctx *authboss.Context, username string) (err error, emailSent <-chan struct{}) { func (m *RecoverModule) makeAndSendToken(ctx *authboss.Context, username string) (err error, emailSent <-chan struct{}) {
if err = ctx.LoadUser(username, m.config.Storer); err != nil { if err = ctx.LoadUser(username, authboss.Cfg.Storer); err != nil {
return err, nil return err, nil
} }
@ -78,9 +78,9 @@ func (m *RecoverModule) makeAndSendToken(ctx *authboss.Context, username string)
sum := md5.Sum(token) sum := md5.Sum(token)
ctx.User[attrRecoverToken] = base64.StdEncoding.EncodeToString(sum[:]) ctx.User[attrRecoverToken] = base64.StdEncoding.EncodeToString(sum[:])
ctx.User[attrRecoverTokenExpiry] = time.Now().Add(m.config.RecoverTokenDuration) ctx.User[attrRecoverTokenExpiry] = time.Now().Add(authboss.Cfg.RecoverTokenDuration)
if err = ctx.SaveUser(username, m.config.Storer); err != nil { if err = ctx.SaveUser(username, authboss.Cfg.Storer); err != nil {
return err, nil return err, nil
} }
@ -91,31 +91,31 @@ func (m *RecoverModule) sendRecoverEmail(to string, token []byte) <-chan struct{
emailSent := make(chan struct{}, 1) emailSent := make(chan struct{}, 1)
go func() { go func() {
data := struct{ Link string }{fmt.Sprintf("%s/recover/complete?token=%s", m.config.HostName, base64.URLEncoding.EncodeToString(token))} data := struct{ Link string }{fmt.Sprintf("%s/recover/complete?token=%s", authboss.Cfg.HostName, base64.URLEncoding.EncodeToString(token))}
htmlEmailBody, err := m.emailTemplates.ExecuteTemplate(tplInitHTMLEmail, data) htmlEmailBody, err := m.emailTemplates.ExecuteTemplate(tplInitHTMLEmail, data)
if err != nil { if err != nil {
fmt.Fprintf(m.config.LogWriter, errFormat, "failed to build html email", err) fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "failed to build html email", err)
close(emailSent) close(emailSent)
return return
} }
textEmaiLBody, err := m.emailTemplates.ExecuteTemplate(tplInitTextEmail, data) textEmaiLBody, err := m.emailTemplates.ExecuteTemplate(tplInitTextEmail, data)
if err != nil { if err != nil {
fmt.Fprintf(m.config.LogWriter, errFormat, "failed to build plaintext email", err) fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "failed to build plaintext email", err)
close(emailSent) close(emailSent)
return return
} }
if err := m.config.Mailer.Send(authboss.Email{ if err := authboss.Cfg.Mailer.Send(authboss.Email{
To: []string{to}, To: []string{to},
ToNames: []string{""}, ToNames: []string{""},
From: m.config.EmailFrom, From: authboss.Cfg.EmailFrom,
Subject: m.config.EmailSubjectPrefix + "Password Reset", Subject: authboss.Cfg.EmailSubjectPrefix + "Password Reset",
TextBody: textEmaiLBody.String(), TextBody: textEmaiLBody.String(),
HTMLBody: htmlEmailBody.String(), HTMLBody: htmlEmailBody.String(),
}); err != nil { }); err != nil {
fmt.Fprintf(m.config.LogWriter, errFormat, "failed to send email", err) fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "failed to send email", err)
close(emailSent) close(emailSent)
return return
} }

View File

@ -15,8 +15,6 @@ import (
) )
func Test_recoverHandlerFunc_GET(t *testing.T) { func Test_recoverHandlerFunc_GET(t *testing.T) {
t.Parallel()
m, _ := testValidRecoverModule() m, _ := testValidRecoverModule()
w, r, ctx := testHttpRequest("GET", "/recover", nil) w, r, ctx := testHttpRequest("GET", "/recover", nil)
@ -36,8 +34,6 @@ func Test_recoverHandlerFunc_GET(t *testing.T) {
} }
func Test_recoverHandlerFunc_POST_RecoveryFailed(t *testing.T) { func Test_recoverHandlerFunc_POST_RecoveryFailed(t *testing.T) {
t.Parallel()
m, _ := testValidRecoverModule() m, _ := testValidRecoverModule()
w, r, ctx := testHttpRequest("POST", "/login", url.Values{"username": []string{"a"}, "confirmUsername": []string{"a"}}) w, r, ctx := testHttpRequest("POST", "/login", url.Values{"username": []string{"a"}, "confirmUsername": []string{"a"}})
@ -46,7 +42,7 @@ func Test_recoverHandlerFunc_POST_RecoveryFailed(t *testing.T) {
if err := tpl.Execute(expectedBody, pageRecover{ if err := tpl.Execute(expectedBody, pageRecover{
Username: "a", Username: "a",
ConfirmUsername: "a", ConfirmUsername: "a",
FlashError: m.config.RecoverFailedErrorFlash, FlashError: authboss.Cfg.RecoverFailedErrorFlash,
}); err != nil { }); err != nil {
panic(err) panic(err)
} }
@ -64,12 +60,10 @@ func Test_recoverHandlerFunc_POST_RecoveryFailed(t *testing.T) {
} }
func Test_recoverHandlerFunc_POST(t *testing.T) { func Test_recoverHandlerFunc_POST(t *testing.T) {
t.Parallel()
m, _ := testValidRecoverModule() m, _ := testValidRecoverModule()
w, r, ctx := testHttpRequest("POST", "/login", url.Values{"username": []string{"a"}, "confirmUsername": []string{"a"}}) w, r, ctx := testHttpRequest("POST", "/login", url.Values{"username": []string{"a"}, "confirmUsername": []string{"a"}})
storer, ok := m.config.Storer.(*mocks.MockStorer) storer, ok := authboss.Cfg.Storer.(*mocks.MockStorer)
if !ok { if !ok {
panic("Failed to get storer") panic("Failed to get storer")
} }
@ -87,14 +81,12 @@ func Test_recoverHandlerFunc_POST(t *testing.T) {
} }
successFlash := ctx.SessionStorer.(*mocks.MockClientStorer).Values[authboss.FlashSuccessKey] successFlash := ctx.SessionStorer.(*mocks.MockClientStorer).Values[authboss.FlashSuccessKey]
if successFlash != m.config.RecoverInitiateSuccessFlash { if successFlash != authboss.Cfg.RecoverInitiateSuccessFlash {
t.Error("Unexpected success flash message:", successFlash) t.Error("Unexpected success flash message:", successFlash)
} }
} }
func Test_recover_UsernameValidationFail(t *testing.T) { func Test_recover_UsernameValidationFail(t *testing.T) {
t.Parallel()
m, logger := testValidRecoverModule() m, logger := testValidRecoverModule()
ctx := mocks.MockRequestContext() ctx := mocks.MockRequestContext()
@ -119,8 +111,6 @@ func Test_recover_UsernameValidationFail(t *testing.T) {
} }
func Test_recover_ConfirmUsernameCheckFail(t *testing.T) { func Test_recover_ConfirmUsernameCheckFail(t *testing.T) {
t.Parallel()
m, logger := testValidRecoverModule() m, logger := testValidRecoverModule()
ctx := mocks.MockRequestContext("username", "a", "confirmUsername", "b") ctx := mocks.MockRequestContext("username", "a", "confirmUsername", "b")
@ -148,8 +138,6 @@ func Test_recover_ConfirmUsernameCheckFail(t *testing.T) {
} }
func Test_recover_InvalidUser(t *testing.T) { func Test_recover_InvalidUser(t *testing.T) {
t.Parallel()
m, logger := testValidRecoverModule() m, logger := testValidRecoverModule()
ctx := mocks.MockRequestContext("username", "a", "confirmUsername", "a") ctx := mocks.MockRequestContext("username", "a", "confirmUsername", "a")
@ -157,8 +145,8 @@ func Test_recover_InvalidUser(t *testing.T) {
if page.ErrMap != nil { if page.ErrMap != nil {
t.Error("Exepted no validation errors") t.Error("Exepted no validation errors")
} }
if page.FlashError != m.config.RecoverFailedErrorFlash { if page.FlashError != authboss.Cfg.RecoverFailedErrorFlash {
t.Error("Expected flash error:", m.config.RecoverFailedErrorFlash) t.Error("Expected flash error:", authboss.Cfg.RecoverFailedErrorFlash)
} }
actualLog, err := ioutil.ReadAll(logger) actualLog, err := ioutil.ReadAll(logger)
@ -174,11 +162,9 @@ func Test_recover_InvalidUser(t *testing.T) {
} }
func Test_recover(t *testing.T) { func Test_recover(t *testing.T) {
t.Parallel()
m, _ := testValidRecoverModule() m, _ := testValidRecoverModule()
storer, ok := m.config.Storer.(*mocks.MockStorer) storer, ok := authboss.Cfg.Storer.(*mocks.MockStorer)
if !ok { if !ok {
panic("Failed to get storer") panic("Failed to get storer")
} }
@ -196,8 +182,6 @@ func Test_recover(t *testing.T) {
} }
func Test_makeAndSendToken_MissingStorer(t *testing.T) { func Test_makeAndSendToken_MissingStorer(t *testing.T) {
t.Parallel()
m, _ := testValidRecoverModule() m, _ := testValidRecoverModule()
ctx := mocks.MockRequestContext() ctx := mocks.MockRequestContext()
@ -211,12 +195,10 @@ func Test_makeAndSendToken_MissingStorer(t *testing.T) {
} }
func Test_makeAndSendToken_CheckEmail(t *testing.T) { func Test_makeAndSendToken_CheckEmail(t *testing.T) {
t.Parallel()
m, _ := testValidRecoverModule() m, _ := testValidRecoverModule()
ctx := mocks.MockRequestContext() ctx := mocks.MockRequestContext()
storer, ok := m.config.Storer.(*mocks.MockStorer) storer, ok := authboss.Cfg.Storer.(*mocks.MockStorer)
if !ok { if !ok {
panic("Failed to get storer") panic("Failed to get storer")
} }
@ -244,12 +226,10 @@ func Test_makeAndSendToken_CheckEmail(t *testing.T) {
} }
func Test_makeAndSendToken(t *testing.T) { func Test_makeAndSendToken(t *testing.T) {
t.Parallel()
m, _ := testValidRecoverModule() m, _ := testValidRecoverModule()
ctx := mocks.MockRequestContext() ctx := mocks.MockRequestContext()
storer, ok := m.config.Storer.(*mocks.MockStorer) storer, ok := authboss.Cfg.Storer.(*mocks.MockStorer)
if !ok { if !ok {
panic("Failed to get storer") panic("Failed to get storer")
} }
@ -276,7 +256,6 @@ func Test_makeAndSendToken(t *testing.T) {
} }
func Test_sendRecoverEmail_InvalidTemplates(t *testing.T) { func Test_sendRecoverEmail_InvalidTemplates(t *testing.T) {
t.Parallel()
m, logger := testValidRecoverModule() m, logger := testValidRecoverModule()
failTpl, err := template.New("").Parse("{{.Fail}}") failTpl, err := template.New("").Parse("{{.Fail}}")
@ -316,12 +295,11 @@ func Test_sendRecoverEmail_InvalidTemplates(t *testing.T) {
} }
func Test_sendRecoverEmail_FailToSend(t *testing.T) { func Test_sendRecoverEmail_FailToSend(t *testing.T) {
t.Parallel()
m, logger := testValidRecoverModule() m, logger := testValidRecoverModule()
mailer := mocks.NewMockMailer() mailer := mocks.NewMockMailer()
mailer.SendErr = "explode" mailer.SendErr = "explode"
m.config.Mailer = mailer authboss.Cfg.Mailer = mailer
<-m.sendRecoverEmail("a@b.c", []byte("abc123")) <-m.sendRecoverEmail("a@b.c", []byte("abc123"))
actualLog, err := ioutil.ReadAll(logger) actualLog, err := ioutil.ReadAll(logger)
@ -335,12 +313,11 @@ func Test_sendRecoverEmail_FailToSend(t *testing.T) {
} }
func Test_sendRecoverEmail(t *testing.T) { func Test_sendRecoverEmail(t *testing.T) {
t.Parallel()
m, _ := testValidRecoverModule() m, _ := testValidRecoverModule()
<-m.sendRecoverEmail("a@b.c", []byte("abc123")) <-m.sendRecoverEmail("a@b.c", []byte("abc123"))
mailer, ok := m.config.Mailer.(*mocks.MockMailer) mailer, ok := authboss.Cfg.Mailer.(*mocks.MockMailer)
if !ok { if !ok {
panic("Failed to assert mock mailer") panic("Failed to assert mock mailer")
} }
@ -353,7 +330,7 @@ func Test_sendRecoverEmail(t *testing.T) {
t.Error("Unexpected to email:", sent.To[0]) t.Error("Unexpected to email:", sent.To[0])
} }
if sent.From != m.config.EmailFrom { if sent.From != authboss.Cfg.EmailFrom {
t.Error("Unexpected from email:", sent.From) t.Error("Unexpected from email:", sent.From)
} }
@ -365,7 +342,7 @@ func Test_sendRecoverEmail(t *testing.T) {
Link string Link string
}{ }{
fmt.Sprintf("%s/recover/complete?token=%s", fmt.Sprintf("%s/recover/complete?token=%s",
m.config.HostName, authboss.Cfg.HostName,
base64.URLEncoding.EncodeToString([]byte("abc123")), base64.URLEncoding.EncodeToString([]byte("abc123")),
), ),
} }
@ -387,8 +364,6 @@ func Test_sendRecoverEmail(t *testing.T) {
} }
func Test_recoverHandlerFunc_OtherMethods(t *testing.T) { func Test_recoverHandlerFunc_OtherMethods(t *testing.T) {
t.Parallel()
m, _ := testValidRecoverModule() m, _ := testValidRecoverModule()
for i, method := range []string{"HEAD", "PUT", "DELETE", "TRACE", "CONNECT"} { for i, method := range []string{"HEAD", "PUT", "DELETE", "TRACE", "CONNECT"} {

View File

@ -37,34 +37,31 @@ func init() {
type RecoverModule struct { type RecoverModule struct {
templates views.Templates templates views.Templates
emailTemplates views.Templates emailTemplates views.Templates
config *authboss.Config
} }
func (m *RecoverModule) Initialize(config *authboss.Config) (err error) { func (m *RecoverModule) Initialize() (err error) {
if config.Storer == nil { if authboss.Cfg.Storer == nil {
return errors.New("recover: Need a RecoverStorer.") return errors.New("recover: Need a RecoverStorer.")
} }
if _, ok := config.Storer.(authboss.RecoverStorer); !ok { if _, ok := authboss.Cfg.Storer.(authboss.RecoverStorer); !ok {
return errors.New("recover: RecoverStorer required for recover functionality.") return errors.New("recover: RecoverStorer required for recover functionality.")
} }
if config.Layout == nil { if authboss.Cfg.Layout == nil {
return errors.New("recover: Layout required for Recover functionallity.") return errors.New("recover: Layout required for Recover functionallity.")
} }
if m.templates, err = views.Get(config.Layout, config.ViewsPath, tplRecover, tplRecoverComplete); err != nil { if m.templates, err = views.Get(authboss.Cfg.Layout, authboss.Cfg.ViewsPath, tplRecover, tplRecoverComplete); err != nil {
return err return err
} }
if config.LayoutEmail == nil { if authboss.Cfg.LayoutEmail == nil {
return errors.New("recover: LayoutEmail required for Recover functionallity.") return errors.New("recover: LayoutEmail required for Recover functionallity.")
} }
if m.emailTemplates, err = views.Get(config.LayoutEmail, config.ViewsPath, tplInitHTMLEmail, tplInitTextEmail); err != nil { if m.emailTemplates, err = views.Get(authboss.Cfg.LayoutEmail, authboss.Cfg.ViewsPath, tplInitHTMLEmail, tplInitTextEmail); err != nil {
return err return err
} }
m.config = config
return nil return nil
} }
@ -87,7 +84,7 @@ func (m *RecoverModule) Storage() authboss.StorageOptions {
func (m *RecoverModule) execTpl(tpl string, w http.ResponseWriter, data interface{}) { func (m *RecoverModule) execTpl(tpl string, w http.ResponseWriter, data interface{}) {
buffer, err := m.templates.ExecuteTemplate(tpl, data) buffer, err := m.templates.ExecuteTemplate(tpl, data)
if err != nil { if err != nil {
fmt.Fprintf(m.config.LogWriter, errFormat, "unable to execute template", err) fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "unable to execute template", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return return
} }

View File

@ -9,7 +9,6 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
"os"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -20,73 +19,73 @@ import (
) )
func Test_Initialize(t *testing.T) { func Test_Initialize(t *testing.T) {
t.Parallel() authboss.NewConfig()
config := &authboss.Config{ViewsPath: os.TempDir()}
m := &RecoverModule{} m := &RecoverModule{}
authboss.Cfg.Storer = nil
if err := m.Initialize(config); err == nil { if err := m.Initialize(); err == nil {
t.Error("Expected error") t.Error("Expected error")
} else if err.Error() != "recover: Need a RecoverStorer." { } else if err.Error() != "recover: Need a RecoverStorer." {
t.Error("Got error but wrong reason:", err) t.Error("Got error but wrong reason:", err)
} }
config.Storer = mocks.MockFailStorer{} authboss.Cfg.Storer = mocks.MockFailStorer{}
if err := m.Initialize(config); err == nil { if err := m.Initialize(); err == nil {
t.Error("Expected error") t.Error("Expected error")
} else if err.Error() != "recover: RecoverStorer required for recover functionality." { } else if err.Error() != "recover: RecoverStorer required for recover functionality." {
t.Error("Got error but wrong reason:", err) t.Error("Got error but wrong reason:", err)
} }
config.Storer = mocks.NewMockStorer() authboss.Cfg.Storer = mocks.NewMockStorer()
authboss.Cfg.Layout = nil
if err := m.Initialize(config); err == nil { if err := m.Initialize(); err == nil {
t.Error("Expected error") t.Error("Expected error")
} else if err.Error() != "recover: Layout required for Recover functionallity." { } else if err.Error() != "recover: Layout required for Recover functionallity." {
t.Error("Got error but wrong reason:", err) t.Error("Got error but wrong reason:", err)
} }
var err error var err error
config.Layout, err = template.New("").Parse(`{{template "authboss" .}}`) authboss.Cfg.Layout, err = template.New("").Parse(`{{template "authboss" .}}`)
if err != nil { if err != nil {
t.Fatal("Unexpected error:", err) t.Fatal("Unexpected error:", err)
} }
authboss.Cfg.LayoutEmail = nil
if err := m.Initialize(config); err == nil { if err := m.Initialize(); err == nil {
t.Error("Expected error:", err) t.Error("Expected error:", err)
} else if err.Error() != "recover: LayoutEmail required for Recover functionallity." { } else if err.Error() != "recover: LayoutEmail required for Recover functionallity." {
t.Error("Got error but wrong reason:", err) t.Error("Got error but wrong reason:", err)
} }
config.LayoutEmail, err = template.New("").Parse(`{{template "authboss" .}}`) authboss.Cfg.LayoutEmail, err = template.New("").Parse(`{{template "authboss" .}}`)
if err != nil { if err != nil {
t.Fatal("Unexpected error:", err) t.Fatal("Unexpected error:", err)
} }
if err := m.Initialize(config); err != nil { if err := m.Initialize(); err != nil {
t.Error("Unexpected error:", err) t.Error("Unexpected error:", err)
} }
} }
func testValidTestConfig() *authboss.Config { func testValidTestConfig() {
config := &authboss.Config{} authboss.NewConfig()
authboss.Cfg.Storer = mocks.NewMockStorer()
config.Storer = mocks.NewMockStorer() authboss.Cfg.EmailFrom = "auth@boss.com"
config.EmailFrom = "auth@boss.com"
var err error var err error
if config.Layout, err = views.AssetToTemplate("layout.tpl"); err != nil { if authboss.Cfg.Layout, err = views.AssetToTemplate("layout.tpl"); err != nil {
panic(err) panic(err)
} }
if config.LayoutEmail, err = views.AssetToTemplate("layoutEmail.tpl"); err != nil { if authboss.Cfg.LayoutEmail, err = views.AssetToTemplate("layoutEmail.tpl"); err != nil {
panic(err) panic(err)
} }
config.RecoverRedirect = "/login" authboss.Cfg.RecoverRedirect = "/login"
config.RecoverInitiateSuccessFlash = "sf" authboss.Cfg.RecoverInitiateSuccessFlash = "sf"
config.RecoverTokenExpiredFlash = "exf" authboss.Cfg.RecoverTokenExpiredFlash = "exf"
config.RecoverFailedErrorFlash = "errf" authboss.Cfg.RecoverFailedErrorFlash = "errf"
config.Policies = []authboss.Validator{ authboss.Cfg.Policies = []authboss.Validator{
authboss.Rules{ authboss.Rules{
FieldName: "username", FieldName: "username",
Required: true, Required: true,
@ -96,31 +95,29 @@ func testValidTestConfig() *authboss.Config {
Required: true, Required: true,
}, },
} }
config.ConfirmFields = []string{"username", "confirmUsername", "password", "confirmPassword"} authboss.Cfg.ConfirmFields = []string{"username", "confirmUsername", "password", "confirmPassword"}
config.LogWriter = &bytes.Buffer{} authboss.Cfg.LogWriter = &bytes.Buffer{}
config.Mailer = &mocks.MockMailer{} authboss.Cfg.Mailer = &mocks.MockMailer{}
config.EmailFrom = "auth@boss.com" authboss.Cfg.EmailFrom = "auth@boss.com"
config.HostName = "localhost" authboss.Cfg.HostName = "localhost"
config.RecoverTokenDuration = time.Duration(24) * time.Hour authboss.Cfg.RecoverTokenDuration = time.Duration(24) * time.Hour
config.BCryptCost = 4 authboss.Cfg.BCryptCost = 4
config.AuthLoginSuccessRoute = "/login" authboss.Cfg.AuthLoginSuccessRoute = "/login"
return config
} }
func testValidRecoverModule() (*RecoverModule, *bytes.Buffer) { func testValidRecoverModule() (*RecoverModule, *bytes.Buffer) {
c := testValidTestConfig() testValidTestConfig()
m := &RecoverModule{} m := &RecoverModule{}
if err := m.Initialize(c); err != nil { if err := m.Initialize(); err != nil {
panic(err) panic(err)
} }
return m, c.LogWriter.(*bytes.Buffer) return m, authboss.Cfg.LogWriter.(*bytes.Buffer)
} }
func Test_Routes(t *testing.T) { func Test_Routes(t *testing.T) {
t.Parallel() testValidTestConfig()
m, _ := testValidRecoverModule() m, _ := testValidRecoverModule()
@ -136,7 +133,7 @@ func Test_Routes(t *testing.T) {
} }
func Test_Storage(t *testing.T) { func Test_Storage(t *testing.T) {
t.Parallel() testValidTestConfig()
m, _ := testValidRecoverModule() m, _ := testValidRecoverModule()
@ -186,7 +183,7 @@ func testHttpRequest(method, url string, data url.Values) (*httptest.ResponseRec
} }
func Test_execTpl_TemplateExectionFail(t *testing.T) { func Test_execTpl_TemplateExectionFail(t *testing.T) {
t.Parallel() testValidTestConfig()
m, logger := testValidRecoverModule() m, logger := testValidRecoverModule()
w := httptest.NewRecorder() w := httptest.NewRecorder()
@ -213,12 +210,12 @@ func Test_execTpl_TemplateExectionFail(t *testing.T) {
} }
func Test_execTpl(t *testing.T) { func Test_execTpl(t *testing.T) {
t.Parallel() testValidTestConfig()
m, _ := testValidRecoverModule() m, _ := testValidRecoverModule()
w := httptest.NewRecorder() w := httptest.NewRecorder()
page := pageRecover{"bobby", "bob", nil, "", m.config.RecoverFailedErrorFlash} page := pageRecover{"bobby", "bob", nil, "", authboss.Cfg.RecoverFailedErrorFlash}
m.execTpl(tplRecover, w, page) m.execTpl(tplRecover, w, page)
tpl := m.templates[tplRecover] tpl := m.templates[tplRecover]

View File

@ -10,7 +10,6 @@ import (
"encoding/base64" "encoding/base64"
"errors" "errors"
"fmt" "fmt"
"io"
"gopkg.in/authboss.v0" "gopkg.in/authboss.v0"
) )
@ -31,26 +30,18 @@ func init() {
authboss.RegisterModule("remember", R) authboss.RegisterModule("remember", R)
} }
type Remember struct { type Remember struct{}
storer authboss.TokenStorer
cookieStorer authboss.ClientStorer
sessionStorer authboss.ClientStorer
logger io.Writer
}
func (r *Remember) Initialize(config *authboss.Config) error { func (r *Remember) Initialize() error {
if config.Storer == nil { if authboss.Cfg.Storer == nil {
return errors.New("remember: Need a TokenStorer.") return errors.New("remember: Need a TokenStorer.")
} }
if storer, ok := config.Storer.(authboss.TokenStorer); !ok { if _, ok := authboss.Cfg.Storer.(authboss.TokenStorer); !ok {
return errors.New("remember: TokenStorer required for remember me functionality.") return errors.New("remember: TokenStorer required for remember me functionality.")
} else {
r.storer = storer
} }
r.logger = config.LogWriter authboss.Cfg.Callbacks.After(authboss.EventAuth, r.AfterAuth)
config.Callbacks.After(authboss.EventAuth, r.AfterAuth)
return nil return nil
} }
@ -70,24 +61,24 @@ func (r *Remember) AfterAuth(ctx *authboss.Context) {
} }
if ctx.User == nil { if ctx.User == nil {
fmt.Fprintf(r.logger, "remember: AfterAuth no user loaded") fmt.Fprintf(authboss.Cfg.LogWriter, "remember: AfterAuth no user loaded")
return return
} }
keyIntf, ok := ctx.User["username"] keyIntf, ok := ctx.User["username"]
if !ok { if !ok {
fmt.Fprintf(r.logger, "remember: username not present") fmt.Fprintf(authboss.Cfg.LogWriter, "remember: username not present")
return return
} }
key, ok := keyIntf.(string) key, ok := keyIntf.(string)
if !ok { if !ok {
fmt.Fprintf(r.logger, "remember: username not a string") fmt.Fprintf(authboss.Cfg.LogWriter, "remember: username not a string")
return return
} }
if _, err := r.New(ctx.CookieStorer, key); err != nil { if _, err := r.New(ctx.CookieStorer, key); err != nil {
fmt.Fprintf(r.logger, "remember: Failed to create remember token: %v", err) fmt.Fprintf(authboss.Cfg.LogWriter, "remember: Failed to create remember token: %v", err)
} }
} }
@ -108,7 +99,7 @@ func (r *Remember) New(cstorer authboss.ClientStorer, storageKey string) (string
storageToken := base64.StdEncoding.EncodeToString(sum[:]) storageToken := base64.StdEncoding.EncodeToString(sum[:])
// Save the token in the DB // Save the token in the DB
if err := r.storer.AddToken(storageKey, storageToken); err != nil { if err := authboss.Cfg.Storer.(authboss.TokenStorer).AddToken(storageKey, storageToken); err != nil {
return "", err return "", err
} }
@ -143,7 +134,7 @@ func (r *Remember) Auth(
// Verify the tokens match. // Verify the tokens match.
sum := md5.Sum(token) sum := md5.Sum(token)
key, err := r.storer.UseToken(string(givenKey), base64.StdEncoding.EncodeToString(sum[:])) key, err := authboss.Cfg.Storer.(authboss.TokenStorer).UseToken(string(givenKey), base64.StdEncoding.EncodeToString(sum[:]))
if err == authboss.ErrTokenNotFound { if err == authboss.ErrTokenNotFound {
return "", nil return "", nil
} else if err != nil { } else if err != nil {

View File

@ -10,30 +10,32 @@ import (
) )
func TestInitialize(t *testing.T) { func TestInitialize(t *testing.T) {
testConfig := authboss.NewConfig() authboss.NewConfig()
r := &Remember{} r := &Remember{}
err := r.Initialize(testConfig) err := r.Initialize()
if err == nil { if err == nil {
t.Error("Expected error about token storers.") t.Error("Expected error about token storers.")
} }
testConfig.Storer = mocks.MockFailStorer{} authboss.Cfg.Storer = mocks.MockFailStorer{}
err = r.Initialize(testConfig) err = r.Initialize()
if err == nil { if err == nil {
t.Error("Expected error about token storers.") t.Error("Expected error about token storers.")
} }
testConfig.Storer = mocks.NewMockStorer() authboss.Cfg.Storer = mocks.NewMockStorer()
err = r.Initialize(testConfig) err = r.Initialize()
if err != nil { if err != nil {
t.Error("Unexpected error:", err) t.Error("Unexpected error:", err)
} }
} }
func TestAfterAuth(t *testing.T) { func TestAfterAuth(t *testing.T) {
authboss.NewConfig()
storer := mocks.NewMockStorer() storer := mocks.NewMockStorer()
R.storer = storer authboss.Cfg.Storer = storer
cookies := mocks.NewMockClientStorer() cookies := mocks.NewMockClientStorer()
session := mocks.NewMockClientStorer() session := mocks.NewMockClientStorer()
@ -60,8 +62,9 @@ func TestAfterAuth(t *testing.T) {
} }
func TestNew(t *testing.T) { func TestNew(t *testing.T) {
authboss.NewConfig()
storer := mocks.NewMockStorer() storer := mocks.NewMockStorer()
R.storer = storer authboss.Cfg.Storer = storer
cookies := mocks.NewMockClientStorer() cookies := mocks.NewMockClientStorer()
key := "tester" key := "tester"
@ -87,8 +90,9 @@ func TestNew(t *testing.T) {
} }
func TestAuth(t *testing.T) { func TestAuth(t *testing.T) {
authboss.NewConfig()
storer := mocks.NewMockStorer() storer := mocks.NewMockStorer()
R.storer = storer authboss.Cfg.Storer = storer
cookies := mocks.NewMockClientStorer() cookies := mocks.NewMockClientStorer()
session := mocks.NewMockClientStorer() session := mocks.NewMockClientStorer()

View File

@ -18,8 +18,8 @@ func NewRouter() http.Handler {
for name, mod := range modules { for name, mod := range modules {
for route, handler := range mod.Routes() { for route, handler := range mod.Routes() {
fmt.Fprintf(cfg.LogWriter, "%-10s Register Route: %s\n", "["+name+"]", route) fmt.Fprintf(Cfg.LogWriter, "%-10s Register Route: %s\n", "["+name+"]", route)
mux.Handle(path.Join(cfg.MountPath, route), contextRoute{handler}) mux.Handle(path.Join(Cfg.MountPath, route), contextRoute{handler})
} }
} }
@ -33,12 +33,12 @@ type contextRoute struct {
func (c contextRoute) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (c contextRoute) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx, err := ContextFromRequest(r) ctx, err := ContextFromRequest(r)
if err != nil { if err != nil {
fmt.Fprintf(cfg.LogWriter, "route: Malformed request, could not create context: %v", err) fmt.Fprintf(Cfg.LogWriter, "route: Malformed request, could not create context: %v", err)
return return
} }
ctx.CookieStorer = cfg.CookieStoreMaker(w, r) ctx.CookieStorer = Cfg.CookieStoreMaker(w, r)
ctx.SessionStorer = cfg.SessionStoreMaker(w, r) ctx.SessionStorer = Cfg.SessionStoreMaker(w, r)
c.fn(ctx, w, r) c.fn(ctx, w, r)
} }

View File

@ -7,6 +7,8 @@ import (
) )
func TestAttributes_Names(t *testing.T) { func TestAttributes_Names(t *testing.T) {
t.Parallel()
attr := Attributes{ attr := Attributes{
"integer": 5, "integer": 5,
"string": "string", "string": "string",
@ -28,6 +30,8 @@ func TestAttributes_Names(t *testing.T) {
} }
func TestAttributeMeta_Names(t *testing.T) { func TestAttributeMeta_Names(t *testing.T) {
t.Parallel()
meta := AttributeMeta{ meta := AttributeMeta{
"integer": Integer, "integer": Integer,
"string": String, "string": String,
@ -49,6 +53,8 @@ func TestAttributeMeta_Names(t *testing.T) {
} }
func TestDataType_String(t *testing.T) { func TestDataType_String(t *testing.T) {
t.Parallel()
if Integer.String() != "Integer" { if Integer.String() != "Integer" {
t.Error("Expected Integer:", Integer) t.Error("Expected Integer:", Integer)
} }
@ -64,6 +70,8 @@ func TestDataType_String(t *testing.T) {
} }
func TestAttributes_Bind(t *testing.T) { func TestAttributes_Bind(t *testing.T) {
t.Parallel()
anInteger := 5 anInteger := 5
aString := "string" aString := "string"
aBool := true aBool := true
@ -102,6 +110,8 @@ func TestAttributes_Bind(t *testing.T) {
} }
func TestAttributes_BindNoPtr(t *testing.T) { func TestAttributes_BindNoPtr(t *testing.T) {
t.Parallel()
data := Attributes{} data := Attributes{}
s := struct{}{} s := struct{}{}
@ -113,6 +123,8 @@ func TestAttributes_BindNoPtr(t *testing.T) {
} }
func TestAttributes_BindMissingField(t *testing.T) { func TestAttributes_BindMissingField(t *testing.T) {
t.Parallel()
data := Attributes{"Integer": 5} data := Attributes{"Integer": 5}
s := struct{}{} s := struct{}{}
@ -124,6 +136,8 @@ func TestAttributes_BindMissingField(t *testing.T) {
} }
func TestAttributes_BindTypeFail(t *testing.T) { func TestAttributes_BindTypeFail(t *testing.T) {
t.Parallel()
tests := []struct { tests := []struct {
Attr Attributes Attr Attributes
Err string Err string
@ -170,6 +184,8 @@ func TestAttributes_BindTypeFail(t *testing.T) {
} }
func TestAttributes_Unbind(t *testing.T) { func TestAttributes_Unbind(t *testing.T) {
t.Parallel()
s1 := struct { s1 := struct {
Integer int Integer int
String string String string
@ -221,6 +237,8 @@ func TestAttributes_Unbind(t *testing.T) {
} }
func TestCasingStyleConversions(t *testing.T) { func TestCasingStyleConversions(t *testing.T) {
t.Parallel()
camel := "SomethingInCamel" camel := "SomethingInCamel"
got := camelToUnder(camel) got := camelToUnder(camel)

View File

@ -26,11 +26,11 @@ type Validate struct {
Password authboss.Validator Password authboss.Validator
} }
func (v *Validate) Initialize(config *authboss.Config) error { func (v *Validate) Initialize() error {
policies := authboss.FilterValidators(config.Policies, policyEmail, policyUsername, policyPassword) policies := authboss.FilterValidators(authboss.Cfg.Policies, policyEmail, policyUsername, policyPassword)
if v.Email = policies[0]; v.Email.Field() != policyEmail { if v.Email = policies[0]; v.Email.Field() != policyEmail {
return fmt.Errorf("validate: missing policy: %s", policyEmail) return fmt.Errorf("validate: missin g policy: %s", policyEmail)
} }
if v.Username = policies[1]; v.Username.Field() != policyUsername { if v.Username = policies[1]; v.Username.Field() != policyUsername {
@ -41,9 +41,9 @@ func (v *Validate) Initialize(config *authboss.Config) error {
return fmt.Errorf("validate: missing policy: %s", policyPassword) return fmt.Errorf("validate: missing policy: %s", policyPassword)
} }
config.Callbacks.Before(authboss.EventRegister, v.BeforeRegister) authboss.Cfg.Callbacks.Before(authboss.EventRegister, v.BeforeRegister)
config.Callbacks.Before(authboss.EventRecoverStart, v.BeforeRegister) authboss.Cfg.Callbacks.Before(authboss.EventRecoverStart, v.BeforeRegister)
config.Callbacks.Before(authboss.EventRecoverEnd, v.BeforeRegister) authboss.Cfg.Callbacks.Before(authboss.EventRecoverEnd, v.BeforeRegister)
return nil return nil
} }

View File

@ -9,14 +9,14 @@ import (
) )
func TestValidate_Initialiaze(t *testing.T) { func TestValidate_Initialiaze(t *testing.T) {
cfg := authboss.NewConfig() authboss.NewConfig()
cfg.Policies = []authboss.Validator{ authboss.Cfg.Policies = []authboss.Validator{
authboss.Rules{FieldName: policyEmail}, authboss.Rules{FieldName: policyEmail},
authboss.Rules{FieldName: policyUsername}, authboss.Rules{FieldName: policyUsername},
authboss.Rules{FieldName: policyPassword}, authboss.Rules{FieldName: policyPassword},
} }
err := V.Initialize(cfg) err := V.Initialize()
if err != nil { if err != nil {
t.Error("Unexpected error:", err) t.Error("Unexpected error:", err)
} }
@ -33,14 +33,14 @@ func TestValidate_Initialiaze(t *testing.T) {
} }
func TestValidate_BeforeRegister(t *testing.T) { func TestValidate_BeforeRegister(t *testing.T) {
cfg := authboss.NewConfig() authboss.NewConfig()
cfg.Policies = []authboss.Validator{ authboss.Cfg.Policies = []authboss.Validator{
authboss.Rules{FieldName: policyEmail, MinLength: 15}, authboss.Rules{FieldName: policyEmail, MinLength: 15},
authboss.Rules{FieldName: policyUsername, MaxLength: 1}, authboss.Rules{FieldName: policyUsername, MaxLength: 1},
authboss.Rules{FieldName: policyPassword, MinLength: 8}, authboss.Rules{FieldName: policyPassword, MinLength: 8},
} }
err := V.Initialize(cfg) err := V.Initialize()
if err != nil { if err != nil {
t.Error("Unexpected error:", err) t.Error("Unexpected error:", err)
} }