Skip to content

Commit f12f10f

Browse files
committed
Stop reliance on global scope.
- This change was necessary because multi-tenancy sites could not use authboss properly.
1 parent bd0d3c5 commit f12f10f

40 files changed

Lines changed: 560 additions & 499 deletions

auth/auth.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,19 @@ type Auth struct {
2929

3030
// Initialize module
3131
func (a *Auth) Initialize() (err error) {
32-
if authboss.Cfg.Storer == nil {
32+
if authboss.a.Storer == nil {
3333
return errors.New("auth: Need a Storer")
3434
}
3535

36-
if len(authboss.Cfg.XSRFName) == 0 {
36+
if len(authboss.a.XSRFName) == 0 {
3737
return errors.New("auth: XSRFName must be set")
3838
}
3939

40-
if authboss.Cfg.XSRFMaker == nil {
40+
if authboss.a.XSRFMaker == nil {
4141
return errors.New("auth: XSRFMaker must be defined")
4242
}
4343

44-
a.templates, err = response.LoadTemplates(authboss.Cfg.Layout, authboss.Cfg.ViewsPath, tplLogin)
44+
a.templates, err = response.LoadTemplates(authboss.a.Layout, authboss.a.ViewsPath, tplLogin)
4545
if err != nil {
4646
return err
4747
}
@@ -60,7 +60,7 @@ func (a *Auth) Routes() authboss.RouteTable {
6060
// Storage requirements
6161
func (a *Auth) Storage() authboss.StorageOptions {
6262
return authboss.StorageOptions{
63-
authboss.Cfg.PrimaryID: authboss.String,
63+
authboss.a.PrimaryID: authboss.String,
6464
authboss.StorePassword: authboss.String,
6565
}
6666
}
@@ -70,32 +70,32 @@ func (a *Auth) loginHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r
7070
case methodGET:
7171
if _, ok := ctx.SessionStorer.Get(authboss.SessionKey); ok {
7272
if halfAuthed, ok := ctx.SessionStorer.Get(authboss.SessionHalfAuthKey); !ok || halfAuthed == "false" {
73-
//http.Redirect(w, r, authboss.Cfg.AuthLoginOKPath, http.StatusFound, true)
74-
response.Redirect(ctx, w, r, authboss.Cfg.AuthLoginOKPath, "", "", true)
73+
//http.Redirect(w, r, authboss.a.AuthLoginOKPath, http.StatusFound, true)
74+
response.Redirect(ctx, w, r, authboss.a.AuthLoginOKPath, "", "", true)
7575
return nil
7676
}
7777
}
7878

7979
data := authboss.NewHTMLData(
8080
"showRemember", authboss.IsLoaded("remember"),
8181
"showRecover", authboss.IsLoaded("recover"),
82-
"primaryID", authboss.Cfg.PrimaryID,
82+
"primaryID", authboss.a.PrimaryID,
8383
"primaryIDValue", "",
8484
)
8585
return a.templates.Render(ctx, w, r, tplLogin, data)
8686
case methodPOST:
87-
key, _ := ctx.FirstPostFormValue(authboss.Cfg.PrimaryID)
87+
key, _ := ctx.FirstPostFormValue(authboss.a.PrimaryID)
8888
password, _ := ctx.FirstPostFormValue("password")
8989

9090
errData := authboss.NewHTMLData(
91-
"error", fmt.Sprintf("invalid %s and/or password", authboss.Cfg.PrimaryID),
92-
"primaryID", authboss.Cfg.PrimaryID,
91+
"error", fmt.Sprintf("invalid %s and/or password", authboss.a.PrimaryID),
92+
"primaryID", authboss.a.PrimaryID,
9393
"primaryIDValue", key,
9494
"showRemember", authboss.IsLoaded("remember"),
9595
"showRecover", authboss.IsLoaded("recover"),
9696
)
9797

98-
policies := authboss.FilterValidators(authboss.Cfg.Policies, authboss.Cfg.PrimaryID, authboss.StorePassword)
98+
policies := authboss.FilterValidators(authboss.a.Policies, authboss.a.PrimaryID, authboss.StorePassword)
9999
if validationErrs := ctx.Validate(policies); len(validationErrs) > 0 {
100100
return a.templates.Render(ctx, w, r, tplLogin, errData)
101101
}
@@ -104,7 +104,7 @@ func (a *Auth) loginHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r
104104
return a.templates.Render(ctx, w, r, tplLogin, errData)
105105
}
106106

107-
interrupted, err := authboss.Cfg.Callbacks.FireBefore(authboss.EventAuth, ctx)
107+
interrupted, err := authboss.a.Callbacks.FireBefore(authboss.EventAuth, ctx)
108108
if err != nil {
109109
return err
110110
} else if interrupted != authboss.InterruptNone {
@@ -115,17 +115,17 @@ func (a *Auth) loginHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r
115115
case authboss.InterruptAccountNotConfirmed:
116116
reason = "Your account has not been confirmed."
117117
}
118-
response.Redirect(ctx, w, r, authboss.Cfg.AuthLoginFailPath, "", reason, false)
118+
response.Redirect(ctx, w, r, authboss.a.AuthLoginFailPath, "", reason, false)
119119
return nil
120120
}
121121

122122
ctx.SessionStorer.Put(authboss.SessionKey, key)
123123
ctx.SessionStorer.Del(authboss.SessionHalfAuthKey)
124124

125-
if err := authboss.Cfg.Callbacks.FireAfter(authboss.EventAuth, ctx); err != nil {
125+
if err := authboss.a.Callbacks.FireAfter(authboss.EventAuth, ctx); err != nil {
126126
return err
127127
}
128-
response.Redirect(ctx, w, r, authboss.Cfg.AuthLoginOKPath, "", "", true)
128+
response.Redirect(ctx, w, r, authboss.a.AuthLoginOKPath, "", "", true)
129129
default:
130130
w.WriteHeader(http.StatusMethodNotAllowed)
131131
}
@@ -157,7 +157,7 @@ func (a *Auth) logoutHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r
157157
ctx.CookieStorer.Del(authboss.CookieRemember)
158158
ctx.SessionStorer.Del(authboss.SessionLastAction)
159159

160-
response.Redirect(ctx, w, r, authboss.Cfg.AuthLogoutOKPath, "You have logged out", "", true)
160+
response.Redirect(ctx, w, r, authboss.a.AuthLogoutOKPath, "You have logged out", "", true)
161161
default:
162162
w.WriteHeader(http.StatusMethodNotAllowed)
163163
}

auth/auth_test.go

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ func testSetup() (a *Auth, s *mocks.MockStorer) {
1717
s = mocks.NewMockStorer()
1818

1919
authboss.Cfg = authboss.NewConfig()
20-
authboss.Cfg.LogWriter = ioutil.Discard
21-
authboss.Cfg.Layout = template.Must(template.New("").Parse(`{{template "authboss" .}}`))
22-
authboss.Cfg.Storer = s
23-
authboss.Cfg.XSRFName = "xsrf"
24-
authboss.Cfg.XSRFMaker = func(_ http.ResponseWriter, _ *http.Request) string {
20+
authboss.a.LogWriter = ioutil.Discard
21+
authboss.a.Layout = template.Must(template.New("").Parse(`{{template "authboss" .}}`))
22+
authboss.a.Storer = s
23+
authboss.a.XSRFName = "xsrf"
24+
authboss.a.XSRFMaker = func(_ http.ResponseWriter, _ *http.Request) string {
2525
return "xsrfvalue"
2626
}
27-
authboss.Cfg.PrimaryID = authboss.StoreUsername
27+
authboss.a.PrimaryID = authboss.StoreUsername
2828

2929
a = &Auth{}
3030
if err := a.Initialize(); err != nil {
@@ -51,8 +51,8 @@ func TestAuth(t *testing.T) {
5151
a, _ := testSetup()
5252

5353
storage := a.Storage()
54-
if storage[authboss.Cfg.PrimaryID] != authboss.String {
55-
t.Error("Expected storage KV:", authboss.Cfg.PrimaryID, authboss.String)
54+
if storage[authboss.a.PrimaryID] != authboss.String {
55+
t.Error("Expected storage KV:", authboss.a.PrimaryID, authboss.String)
5656
}
5757
if storage[authboss.StorePassword] != authboss.String {
5858
t.Error("Expected storage KV:", authboss.StorePassword, authboss.String)
@@ -74,7 +74,7 @@ func TestAuth_loginHandlerFunc_GET_RedirectsWhenHalfAuthed(t *testing.T) {
7474
sessionStore.Put(authboss.SessionKey, "a")
7575
sessionStore.Put(authboss.SessionHalfAuthKey, "false")
7676

77-
authboss.Cfg.AuthLoginOKPath = "/dashboard"
77+
authboss.a.AuthLoginOKPath = "/dashboard"
7878

7979
if err := a.loginHandlerFunc(ctx, w, r); err != nil {
8080
t.Error("Unexpeced error:", err)
@@ -85,7 +85,7 @@ func TestAuth_loginHandlerFunc_GET_RedirectsWhenHalfAuthed(t *testing.T) {
8585
}
8686

8787
loc := w.Header().Get("Location")
88-
if loc != authboss.Cfg.AuthLoginOKPath {
88+
if loc != authboss.a.AuthLoginOKPath {
8989
t.Error("Unexpected redirect:", loc)
9090
}
9191
}
@@ -106,7 +106,7 @@ func TestAuth_loginHandlerFunc_GET(t *testing.T) {
106106
if !strings.Contains(body, "<form") {
107107
t.Error("Should have rendered a form")
108108
}
109-
if !strings.Contains(body, `name="`+authboss.Cfg.PrimaryID) {
109+
if !strings.Contains(body, `name="`+authboss.a.PrimaryID) {
110110
t.Error("Form should contain the primary ID field:", body)
111111
}
112112
if !strings.Contains(body, `name="password"`) {
@@ -118,8 +118,8 @@ func TestAuth_loginHandlerFunc_POST_ReturnsErrorOnCallbackFailure(t *testing.T)
118118
a, storer := testSetup()
119119
storer.Users["john"] = authboss.Attributes{"password": "$2a$10$B7aydtqVF9V8RSNx3lCKB.l09jqLV/aMiVqQHajtL7sWGhCS9jlOu"}
120120

121-
authboss.Cfg.Callbacks = authboss.NewCallbacks()
122-
authboss.Cfg.Callbacks.Before(authboss.EventAuth, func(_ *authboss.Context) (authboss.Interrupt, error) {
121+
authboss.a.Callbacks = authboss.NewCallbacks()
122+
authboss.a.Callbacks.Before(authboss.EventAuth, func(_ *authboss.Context) (authboss.Interrupt, error) {
123123
return authboss.InterruptNone, errors.New("explode")
124124
})
125125

@@ -134,8 +134,8 @@ func TestAuth_loginHandlerFunc_POST_RedirectsWhenInterrupted(t *testing.T) {
134134
a, storer := testSetup()
135135
storer.Users["john"] = authboss.Attributes{"password": "$2a$10$B7aydtqVF9V8RSNx3lCKB.l09jqLV/aMiVqQHajtL7sWGhCS9jlOu"}
136136

137-
authboss.Cfg.Callbacks = authboss.NewCallbacks()
138-
authboss.Cfg.Callbacks.Before(authboss.EventAuth, func(_ *authboss.Context) (authboss.Interrupt, error) {
137+
authboss.a.Callbacks = authboss.NewCallbacks()
138+
authboss.a.Callbacks.Before(authboss.EventAuth, func(_ *authboss.Context) (authboss.Interrupt, error) {
139139
return authboss.InterruptAccountLocked, nil
140140
})
141141

@@ -150,7 +150,7 @@ func TestAuth_loginHandlerFunc_POST_RedirectsWhenInterrupted(t *testing.T) {
150150
}
151151

152152
loc := w.Header().Get("Location")
153-
if loc != authboss.Cfg.AuthLoginFailPath {
153+
if loc != authboss.a.AuthLoginFailPath {
154154
t.Error("Unexpeced location:", loc)
155155
}
156156

@@ -159,8 +159,8 @@ func TestAuth_loginHandlerFunc_POST_RedirectsWhenInterrupted(t *testing.T) {
159159
t.Error("Expected error flash message:", expectedMsg)
160160
}
161161

162-
authboss.Cfg.Callbacks = authboss.NewCallbacks()
163-
authboss.Cfg.Callbacks.Before(authboss.EventAuth, func(_ *authboss.Context) (authboss.Interrupt, error) {
162+
authboss.a.Callbacks = authboss.NewCallbacks()
163+
authboss.a.Callbacks.Before(authboss.EventAuth, func(_ *authboss.Context) (authboss.Interrupt, error) {
164164
return authboss.InterruptAccountNotConfirmed, nil
165165
})
166166

@@ -173,7 +173,7 @@ func TestAuth_loginHandlerFunc_POST_RedirectsWhenInterrupted(t *testing.T) {
173173
}
174174

175175
loc = w.Header().Get("Location")
176-
if loc != authboss.Cfg.AuthLoginFailPath {
176+
if loc != authboss.a.AuthLoginFailPath {
177177
t.Error("Unexpeced location:", loc)
178178
}
179179

@@ -224,9 +224,9 @@ func TestAuth_loginHandlerFunc_POST(t *testing.T) {
224224
ctx, w, r, _ := testRequest("POST", "username", "john", "password", "1234")
225225
cb := mocks.NewMockAfterCallback()
226226

227-
authboss.Cfg.Callbacks = authboss.NewCallbacks()
228-
authboss.Cfg.Callbacks.After(authboss.EventAuth, cb.Fn)
229-
authboss.Cfg.AuthLoginOKPath = "/dashboard"
227+
authboss.a.Callbacks = authboss.NewCallbacks()
228+
authboss.a.Callbacks.After(authboss.EventAuth, cb.Fn)
229+
authboss.a.AuthLoginOKPath = "/dashboard"
230230

231231
sessions := mocks.NewMockClientStorer()
232232
ctx.SessionStorer = sessions
@@ -244,7 +244,7 @@ func TestAuth_loginHandlerFunc_POST(t *testing.T) {
244244
}
245245

246246
loc := w.Header().Get("Location")
247-
if loc != authboss.Cfg.AuthLoginOKPath {
247+
if loc != authboss.a.AuthLoginOKPath {
248248
t.Error("Unexpeced location:", loc)
249249
}
250250

@@ -283,7 +283,7 @@ func TestAuth_validateCredentials(t *testing.T) {
283283

284284
storer := mocks.NewMockStorer()
285285
storer.GetErr = "Failed to load user"
286-
authboss.Cfg.Storer = storer
286+
authboss.a.Storer = storer
287287

288288
ctx := authboss.Context{}
289289

@@ -305,7 +305,7 @@ func TestAuth_validateCredentials(t *testing.T) {
305305
func TestAuth_logoutHandlerFunc_GET(t *testing.T) {
306306
a, _ := testSetup()
307307

308-
authboss.Cfg.AuthLogoutOKPath = "/dashboard"
308+
authboss.a.AuthLogoutOKPath = "/dashboard"
309309

310310
ctx, w, r, sessionStorer := testRequest("GET")
311311
sessionStorer.Put(authboss.SessionKey, "asdf")

authboss.go

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,24 @@ import (
1717
"golang.org/x/crypto/bcrypt"
1818
)
1919

20+
// Authboss contains a configuration and other details for running.
21+
type Authboss struct {
22+
Config
23+
}
24+
25+
// New makes a new instance of authboss with a default
26+
// configuration.
27+
func New() *Authboss {
28+
ab := &Authboss{}
29+
ab.Defaults()
30+
return ab
31+
}
32+
2033
// Init authboss and it's loaded modules.
21-
func Init() error {
34+
func (a *Authboss) Init() error {
2235
for name, mod := range modules {
23-
fmt.Fprintf(Cfg.LogWriter, "%-10s Initializing\n", "["+name+"]")
24-
if err := mod.Initialize(); err != nil {
36+
fmt.Fprintf(a.LogWriter, "%-10s Initializing\n", "["+name+"]")
37+
if err := mod.Initialize(a); err != nil {
2538
return fmt.Errorf("[%s] Error Initializing: %v", name, err)
2639
}
2740
}
@@ -30,16 +43,16 @@ func Init() error {
3043
}
3144

3245
// CurrentUser retrieves the current user from the session and the database.
33-
func CurrentUser(w http.ResponseWriter, r *http.Request) (interface{}, error) {
34-
ctx, err := ContextFromRequest(r)
46+
func (a *Authboss) CurrentUser(w http.ResponseWriter, r *http.Request) (interface{}, error) {
47+
ctx, err := a.ContextFromRequest(r)
3548
if err != nil {
3649
return nil, err
3750
}
3851

39-
ctx.SessionStorer = clientStoreWrapper{Cfg.SessionStoreMaker(w, r)}
40-
ctx.CookieStorer = clientStoreWrapper{Cfg.CookieStoreMaker(w, r)}
52+
ctx.SessionStorer = clientStoreWrapper{a.SessionStoreMaker(w, r)}
53+
ctx.CookieStorer = clientStoreWrapper{a.CookieStoreMaker(w, r)}
4154

42-
_, err = Cfg.Callbacks.FireBefore(EventGetUserSession, ctx)
55+
_, err = a.Callbacks.FireBefore(EventGetUserSession, ctx)
4356
if err != nil {
4457
return nil, err
4558
}
@@ -54,22 +67,22 @@ func CurrentUser(w http.ResponseWriter, r *http.Request) (interface{}, error) {
5467
return nil, err
5568
}
5669

57-
_, err = Cfg.Callbacks.FireBefore(EventGet, ctx)
70+
_, err = a.Callbacks.FireBefore(EventGet, ctx)
5871
if err != nil {
5972
return nil, err
6073
}
6174

6275
if index := strings.IndexByte(key, ';'); index > 0 {
63-
return Cfg.OAuth2Storer.GetOAuth(key[:index], key[index+1:])
76+
return a.OAuth2Storer.GetOAuth(key[:index], key[index+1:])
6477
}
6578

66-
return Cfg.Storer.Get(key)
79+
return a.Storer.Get(key)
6780
}
6881

6982
// CurrentUserP retrieves the current user but panics if it's not available for
7083
// any reason.
71-
func CurrentUserP(w http.ResponseWriter, r *http.Request) interface{} {
72-
i, err := CurrentUser(w, r)
84+
func (a *Authboss) CurrentUserP(w http.ResponseWriter, r *http.Request) interface{} {
85+
i, err := a.CurrentUser(w, r)
7386
if err != nil {
7487
panic(err.Error())
7588
}
@@ -96,13 +109,13 @@ will be returned.
96109
The error returned is returned either from the updater if that produced an error
97110
or from the cleanup routines.
98111
*/
99-
func UpdatePassword(w http.ResponseWriter, r *http.Request,
112+
func (a *Authboss) UpdatePassword(w http.ResponseWriter, r *http.Request,
100113
ptPassword string, user interface{}, updater func() error) error {
101114

102115
updatePwd := len(ptPassword) > 0
103116

104117
if updatePwd {
105-
pass, err := bcrypt.GenerateFromPassword([]byte(ptPassword), Cfg.BCryptCost)
118+
pass, err := bcrypt.GenerateFromPassword([]byte(ptPassword), a.BCryptCost)
106119
if err != nil {
107120
return err
108121
}
@@ -131,11 +144,11 @@ func UpdatePassword(w http.ResponseWriter, r *http.Request,
131144
return nil
132145
}
133146

134-
ctx, err := ContextFromRequest(r)
147+
ctx, err := a.ContextFromRequest(r)
135148
if err != nil {
136149
return err
137150
}
138-
ctx.SessionStorer = clientStoreWrapper{Cfg.SessionStoreMaker(w, r)}
139-
ctx.CookieStorer = clientStoreWrapper{Cfg.CookieStoreMaker(w, r)}
140-
return Cfg.Callbacks.FireAfter(EventPasswordReset, ctx)
151+
ctx.SessionStorer = clientStoreWrapper{a.SessionStoreMaker(w, r)}
152+
ctx.CookieStorer = clientStoreWrapper{a.CookieStoreMaker(w, r)}
153+
return a.Callbacks.FireAfter(EventPasswordReset, ctx)
141154
}

0 commit comments

Comments
 (0)