mirror of
https://github.com/volatiletech/authboss.git
synced 2024-11-28 08:58:38 +02:00
Implement auth logout
- Add del to client storer interface
This commit is contained in:
parent
14272c8164
commit
a0bde30e3d
28
auth/auth.go
28
auth/auth.go
@ -89,34 +89,41 @@ func (a *Auth) Storage() authboss.StorageOptions {
|
||||
return a.storageOptions
|
||||
}
|
||||
|
||||
func (a *Auth) loginHandlerFunc(c *authboss.Context, w http.ResponseWriter, r *http.Request) {
|
||||
func (a *Auth) loginHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case methodGET:
|
||||
if _, ok := ctx.SessionStorer.Get(authboss.SessionKey); ok {
|
||||
if halfAuthed, ok := ctx.SessionStorer.Get(authboss.HalfAuthKey); !ok || halfAuthed == "false" {
|
||||
http.Redirect(w, r, a.loginRedirect, http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
a.templates.ExecuteTemplate(w, pageLogin, AuthPage{ShowRemember: a.isRememberLoaded})
|
||||
case methodPOST:
|
||||
u, ok := c.FirstPostFormValue("username")
|
||||
u, ok := ctx.FirstPostFormValue("username")
|
||||
if !ok {
|
||||
fmt.Fprintln(a.logger, errors.New("auth: Expected postFormValue 'username' to be in the context"))
|
||||
}
|
||||
|
||||
if err := a.callbacks.FireBefore(authboss.EventAuth, c); err != nil {
|
||||
if err := a.callbacks.FireBefore(authboss.EventAuth, ctx); err != nil {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
a.templates.ExecuteTemplate(w, pageLogin, AuthPage{err.Error(), u, a.isRememberLoaded})
|
||||
}
|
||||
|
||||
p, ok := c.FirstPostFormValue("password")
|
||||
p, ok := ctx.FirstPostFormValue("password")
|
||||
if !ok {
|
||||
fmt.Fprintln(a.logger, errors.New("auth: Expected postFormValue 'password' to be in the context"))
|
||||
}
|
||||
|
||||
if err := a.authenticate(c, u, p); err != nil {
|
||||
if err := a.authenticate(ctx, u, p); err != nil {
|
||||
fmt.Fprintln(a.logger, err)
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
a.templates.ExecuteTemplate(w, pageLogin, AuthPage{"invalid username and/or password", u, a.isRememberLoaded})
|
||||
return
|
||||
}
|
||||
|
||||
a.callbacks.FireAfter(authboss.EventAuth, c)
|
||||
ctx.SessionStorer.Put(authboss.SessionKey, u)
|
||||
a.callbacks.FireAfter(authboss.EventAuth, ctx)
|
||||
|
||||
http.Redirect(w, r, a.loginRedirect, http.StatusFound)
|
||||
default:
|
||||
@ -124,16 +131,16 @@ func (a *Auth) loginHandlerFunc(c *authboss.Context, w http.ResponseWriter, r *h
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Auth) authenticate(c *authboss.Context, username, password string) error {
|
||||
func (a *Auth) authenticate(ctx *authboss.Context, username, password string) error {
|
||||
var userInter interface{}
|
||||
var err error
|
||||
if userInter, err = a.storer.Get(username, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.User = authboss.Unbind(userInter)
|
||||
ctx.User = authboss.Unbind(userInter)
|
||||
|
||||
pwdIntf, ok := c.User[attrPassword]
|
||||
pwdIntf, ok := ctx.User[attrPassword]
|
||||
if !ok {
|
||||
return errors.New("auth: User attributes did not include a password.")
|
||||
}
|
||||
@ -150,9 +157,10 @@ func (a *Auth) authenticate(c *authboss.Context, username, password string) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Auth) logoutHandlerFunc(c *authboss.Context, w http.ResponseWriter, r *http.Request) {
|
||||
func (a *Auth) logoutHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case methodGET:
|
||||
ctx.SessionStorer.Del(authboss.SessionKey)
|
||||
http.Redirect(w, r, a.logoutRedirect, http.StatusFound)
|
||||
default:
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
|
@ -115,7 +115,14 @@ func TestAuth_loginHandlerFunc_GET(t *testing.T) {
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
a.loginHandlerFunc(nil, w, r)
|
||||
ctx, err := authboss.ContextFromRequest(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("%d> Unexpected error '%s'", i, err)
|
||||
@ -135,12 +142,13 @@ func TestAuth_loginHandlerFunc_POST(t *testing.T) {
|
||||
tests := []struct {
|
||||
Username, Password string
|
||||
StatusCode int
|
||||
LoginSuccess bool
|
||||
Location string
|
||||
BodyData *AuthPage
|
||||
}{
|
||||
{"john", "1234", http.StatusFound, "/dashboard", nil},
|
||||
{"jane", "1234", http.StatusForbidden, "", &AuthPage{"invalid username and/or password", "jane", false}},
|
||||
{"mike", "", http.StatusForbidden, "", &AuthPage{"invalid username and/or password", "jane", false}},
|
||||
{"john", "1234", http.StatusFound, true, "/dashboard", nil},
|
||||
{"jane", "1234", http.StatusForbidden, false, "", &AuthPage{"invalid username and/or password", "jane", false}},
|
||||
{"mike", "", http.StatusForbidden, false, "", &AuthPage{"invalid username and/or password", "jane", false}},
|
||||
}
|
||||
|
||||
c := authboss.NewConfig()
|
||||
@ -171,9 +179,18 @@ func TestAuth_loginHandlerFunc_POST(t *testing.T) {
|
||||
t.Errorf("%d> Unexpected error '%s'", i, err)
|
||||
continue
|
||||
}
|
||||
ctx.SessionStorer = testClientStorer{}
|
||||
|
||||
a.loginHandlerFunc(ctx, w, r)
|
||||
|
||||
if test.LoginSuccess {
|
||||
if val, ok := ctx.SessionStorer.Get(authboss.SessionKey); !ok {
|
||||
t.Errorf("%d> Expected login to be present", i)
|
||||
} else if val != test.Username {
|
||||
t.Errorf("%d> Expected username %s, got %s", i, test.Username, val)
|
||||
}
|
||||
}
|
||||
|
||||
if test.StatusCode != w.Code {
|
||||
t.Errorf("%d> Expected status code %d, got %d", i, test.StatusCode, w.Code)
|
||||
continue
|
||||
@ -234,7 +251,16 @@ func TestAuth_logoutHandlerFunc_GET(t *testing.T) {
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
a.logoutHandlerFunc(nil, w, r)
|
||||
ctx, err := authboss.ContextFromRequest(r)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error '%s'", err)
|
||||
}
|
||||
ctx.SessionStorer = testClientStorer{authboss.SessionKey: "asdf"}
|
||||
a.logoutHandlerFunc(ctx, w, r)
|
||||
|
||||
if _, ok := ctx.SessionStorer.Get(authboss.SessionKey); ok {
|
||||
t.Errorf("Expected to be logged out")
|
||||
}
|
||||
|
||||
if http.StatusFound != w.Code {
|
||||
t.Errorf("Expected status code %d, got %d", http.StatusFound, w.Code)
|
||||
|
@ -42,3 +42,18 @@ func (s MockUserStorer) Get(key string, attrMeta authboss.AttributeMeta) (result
|
||||
|
||||
return nil, errors.New("User not found")
|
||||
}
|
||||
|
||||
type testClientStorer map[string]string
|
||||
|
||||
func (t testClientStorer) Put(key, value string) {
|
||||
t[key] = value
|
||||
}
|
||||
|
||||
func (t testClientStorer) Get(key string) (string, bool) {
|
||||
s, ok := t[key]
|
||||
return s, ok
|
||||
}
|
||||
|
||||
func (t testClientStorer) Del(key string) {
|
||||
delete(t, key)
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ type clientStoreMock struct{}
|
||||
|
||||
func (c clientStoreMock) Get(_ string) (string, bool) { return "", false }
|
||||
func (c clientStoreMock) Put(_, _ string) {}
|
||||
func (c clientStoreMock) Del(_ string) {}
|
||||
|
||||
func TestMain(main *testing.M) {
|
||||
RegisterModule("testmodule", testMod)
|
||||
|
@ -2,14 +2,21 @@ package authboss
|
||||
|
||||
import "net/http"
|
||||
|
||||
// SessionKey is the primarily used key by authboss.
|
||||
const SessionKey = "uid"
|
||||
const (
|
||||
// SessionKey is the primarily used key by authboss.
|
||||
SessionKey = "uid"
|
||||
// HalfAuthKey is used for sessions that have been authenticated by
|
||||
// the remember module. This serves as a way to force full authentication
|
||||
// by denying half-authed users acccess to sensitive areas.
|
||||
HalfAuthKey = "halfauth"
|
||||
)
|
||||
|
||||
// ClientStorer should be able to store values on the clients machine. Cookie and
|
||||
// Session storers are built with this interface.
|
||||
type ClientStorer interface {
|
||||
Put(key, value string)
|
||||
Get(key string) (string, bool)
|
||||
Del(key string)
|
||||
}
|
||||
|
||||
// CookieStoreMaker is used to create a cookie storer from an http request. Keep in mind
|
||||
|
@ -18,10 +18,6 @@ import (
|
||||
const (
|
||||
// ValueKey is used for cookies and form input names.
|
||||
ValueKey = "rm"
|
||||
// HalfAuthKey is used for sessions that have been authenticated by
|
||||
// the remember module. This serves as a way to force full authentication
|
||||
// by denying half-authed users acccess to sensitive areas.
|
||||
HalfAuthKey = "halfauth"
|
||||
)
|
||||
|
||||
const nRandBytes = 32
|
||||
@ -155,7 +151,7 @@ func (r *Remember) Auth(
|
||||
}
|
||||
|
||||
// Ensure a half-auth.
|
||||
sstorer.Put(HalfAuthKey, "true")
|
||||
sstorer.Put(authboss.HalfAuthKey, "true")
|
||||
// Log the user in.
|
||||
sstorer.Put(authboss.SessionKey, key)
|
||||
|
||||
|
@ -19,6 +19,10 @@ func (t testClientStorer) Get(key string) (string, bool) {
|
||||
return s, ok
|
||||
}
|
||||
|
||||
func (t testClientStorer) Del(key string) {
|
||||
delete(t, key)
|
||||
}
|
||||
|
||||
type testStorer struct {
|
||||
}
|
||||
|
||||
@ -150,7 +154,7 @@ func TestAuth(t *testing.T) {
|
||||
t.Error("Unexpected error:", err)
|
||||
}
|
||||
|
||||
if session[HalfAuthKey] != "true" {
|
||||
if session[authboss.HalfAuthKey] != "true" {
|
||||
t.Error("The user should have been half-authed.")
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user