mirror of
https://github.com/volatiletech/authboss.git
synced 2025-02-03 13:21:22 +02:00
Finish tests for confirm.
- Rename UserXXX to StoreXXX in confirm/authboss.
This commit is contained in:
parent
c74bb311b3
commit
dee6a42a68
@ -16,9 +16,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
UserConfirmToken = "confirmToken"
|
StoreConfirmToken = "confirm_token"
|
||||||
UserConfirmed = "confirmed"
|
StoreConfirmed = "confirmed"
|
||||||
UserEmail = "email"
|
|
||||||
|
|
||||||
FormValueConfirm = "cnf"
|
FormValueConfirm = "cnf"
|
||||||
|
|
||||||
@ -71,25 +70,26 @@ func (c *Confirm) Routes() authboss.RouteTable {
|
|||||||
|
|
||||||
func (c *Confirm) Storage() authboss.StorageOptions {
|
func (c *Confirm) Storage() authboss.StorageOptions {
|
||||||
return authboss.StorageOptions{
|
return authboss.StorageOptions{
|
||||||
UserConfirmToken: authboss.String,
|
StoreConfirmToken: authboss.String,
|
||||||
UserConfirmed: authboss.Bool,
|
StoreConfirmed: authboss.Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Confirm) BeforeGet(ctx *authboss.Context) error {
|
func (c *Confirm) BeforeGet(ctx *authboss.Context) error {
|
||||||
if intf, ok := ctx.User[UserConfirmed]; ok {
|
if intf, ok := ctx.User[StoreConfirmed]; ok {
|
||||||
if confirmed, ok := intf.(bool); !ok && !confirmed {
|
if confirmed, ok := intf.(bool); ok && confirmed {
|
||||||
return ErrNotConfirmed
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return ErrNotConfirmed
|
||||||
}
|
}
|
||||||
|
|
||||||
// AfterRegister ensures the account is not activated.
|
// AfterRegister ensures the account is not activated.
|
||||||
func (c *Confirm) AfterRegister(ctx *authboss.Context) {
|
func (c *Confirm) AfterRegister(ctx *authboss.Context) {
|
||||||
if ctx.User == nil {
|
if ctx.User == nil {
|
||||||
fmt.Fprintln(authboss.Cfg.LogWriter, "confirm: user not loaded in AfterRegister callback")
|
fmt.Fprintln(authboss.Cfg.LogWriter, "confirm: user not loaded in AfterRegister callback")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
token := make([]byte, 32)
|
token := make([]byte, 32)
|
||||||
@ -98,16 +98,19 @@ func (c *Confirm) AfterRegister(ctx *authboss.Context) {
|
|||||||
}
|
}
|
||||||
sum := md5.Sum(token)
|
sum := md5.Sum(token)
|
||||||
|
|
||||||
ctx.User[UserConfirmToken] = base64.StdEncoding.EncodeToString(sum[:])
|
ctx.User[StoreConfirmToken] = base64.StdEncoding.EncodeToString(sum[:])
|
||||||
|
|
||||||
username, _ := ctx.User.String(authboss.UserName)
|
username, ok := ctx.User.String(authboss.StoreUsername)
|
||||||
|
if !ok {
|
||||||
|
fmt.Fprintln(authboss.Cfg.LogWriter, "confirm: failed to save confirm token, username doesn't exist")
|
||||||
|
}
|
||||||
|
|
||||||
if err := ctx.SaveUser(username, authboss.Cfg.Storer); err != nil {
|
if err := ctx.SaveUser(username, authboss.Cfg.Storer); err != nil {
|
||||||
fmt.Fprintln(authboss.Cfg.LogWriter, "confirm: failed to save user's token:", err)
|
fmt.Fprintln(authboss.Cfg.LogWriter, "confirm: failed to save user's token:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if email, ok := ctx.User.String(UserEmail); !ok {
|
if email, ok := ctx.User.String(authboss.StoreEmail); !ok {
|
||||||
fmt.Fprintln(authboss.Cfg.LogWriter, "confirm: user has no e-mail address to send to, could not send confirm e-mail")
|
fmt.Fprintln(authboss.Cfg.LogWriter, "confirm: user has no e-mail address to send to, could not send confirm e-mail")
|
||||||
} else {
|
} else {
|
||||||
goConfirmEmail(c, email, base64.URLEncoding.EncodeToString(sum[:]))
|
goConfirmEmail(c, email, base64.URLEncoding.EncodeToString(sum[:]))
|
||||||
@ -149,22 +152,24 @@ func (c *Confirm) confirmHandler(ctx *authboss.Context, w http.ResponseWriter, r
|
|||||||
token, ok := ctx.FirstFormValue(FormValueConfirm)
|
token, ok := ctx.FirstFormValue(FormValueConfirm)
|
||||||
if len(token) == 0 || !ok {
|
if len(token) == 0 || !ok {
|
||||||
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||||
fmt.Fprintln(authboss.Cfg.LogWriter, "confirm: no confirm token found in get")
|
fmt.Fprintln(authboss.Cfg.LogWriter, "confirm: no confirm token found in request")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tok, err := base64.URLEncoding.DecodeString(token)
|
toHash, err := base64.URLEncoding.DecodeString(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||||
fmt.Fprintf(authboss.Cfg.LogWriter, "confirm: confirm token failed to decode %q => %v\n", token, err)
|
fmt.Fprintf(authboss.Cfg.LogWriter, "confirm: confirm token failed to decode %q => %v\n", token, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dbTok := base64.StdEncoding.EncodeToString(tok)
|
sum := md5.Sum(toHash)
|
||||||
|
|
||||||
|
dbTok := base64.StdEncoding.EncodeToString(sum[:])
|
||||||
user, err := authboss.Cfg.Storer.(authboss.ConfirmStorer).ConfirmUser(dbTok)
|
user, err := authboss.Cfg.Storer.(authboss.ConfirmStorer).ConfirmUser(dbTok)
|
||||||
if err == authboss.ErrUserNotFound {
|
if err == authboss.ErrUserNotFound {
|
||||||
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||||
fmt.Fprintln(authboss.Cfg.LogWriter, "confirm: token not found", err)
|
fmt.Fprintln(authboss.Cfg.LogWriter, "confirm: token not found:", err)
|
||||||
return
|
return
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
@ -174,16 +179,10 @@ func (c *Confirm) confirmHandler(ctx *authboss.Context, w http.ResponseWriter, r
|
|||||||
|
|
||||||
ctx.User = authboss.Unbind(user)
|
ctx.User = authboss.Unbind(user)
|
||||||
|
|
||||||
ctx.User[UserConfirmToken] = ""
|
ctx.User[StoreConfirmToken] = ""
|
||||||
ctx.User[UserConfirmed] = true
|
ctx.User[StoreConfirmed] = true
|
||||||
|
|
||||||
key, ok := ctx.User.String(authboss.UserName)
|
|
||||||
if !ok {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
fmt.Fprintln(authboss.Cfg.LogWriter, "confirm: user had no key field")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
key, _ := ctx.User.String(authboss.StoreUsername)
|
||||||
ctx.SessionStorer.Put(authboss.SessionKey, key)
|
ctx.SessionStorer.Put(authboss.SessionKey, key)
|
||||||
ctx.SessionStorer.Put(authboss.FlashSuccessKey, "Successfully confirmed your account.")
|
ctx.SessionStorer.Put(authboss.FlashSuccessKey, "Successfully confirmed your account.")
|
||||||
|
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
package confirm
|
package confirm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/base64"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gopkg.in/authboss.v0"
|
"gopkg.in/authboss.v0"
|
||||||
@ -33,3 +39,193 @@ func TestConfirm_Initialize(t *testing.T) {
|
|||||||
t.Error("Missing email templates")
|
t.Error("Missing email templates")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfirm_Routes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
c := &Confirm{}
|
||||||
|
if c.Routes()["/confirm"] == nil {
|
||||||
|
t.Error("Expected confirm route.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfirm_Storage(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
c := &Confirm{}
|
||||||
|
storage := c.Storage()
|
||||||
|
|
||||||
|
if authboss.String != storage[StoreConfirmToken] {
|
||||||
|
t.Error("Expect StoreConfirmToken to be a string.")
|
||||||
|
}
|
||||||
|
if authboss.Bool != storage[StoreConfirmed] {
|
||||||
|
t.Error("Expect StoreConfirmed to be a bool.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfirm_BeforeGet(t *testing.T) {
|
||||||
|
c := setup()
|
||||||
|
ctx := authboss.NewContext()
|
||||||
|
|
||||||
|
if err := c.BeforeGet(ctx); err == nil {
|
||||||
|
t.Error("Should stop the get due to non-confirm.")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.User = authboss.Attributes{
|
||||||
|
StoreConfirmed: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.BeforeGet(ctx); err == nil {
|
||||||
|
t.Error("Should stop the get due to non-confirm.")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.User = authboss.Attributes{
|
||||||
|
StoreConfirmed: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.BeforeGet(ctx); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfirm_AfterRegister(t *testing.T) {
|
||||||
|
c := setup()
|
||||||
|
ctx := authboss.NewContext()
|
||||||
|
log := &bytes.Buffer{}
|
||||||
|
authboss.Cfg.LogWriter = log
|
||||||
|
authboss.Cfg.Mailer = authboss.LogMailer(log)
|
||||||
|
|
||||||
|
sentEmail := false
|
||||||
|
|
||||||
|
goConfirmEmail = func(c *Confirm, to, token string) {
|
||||||
|
c.confirmEmail(to, token)
|
||||||
|
sentEmail = true
|
||||||
|
}
|
||||||
|
|
||||||
|
c.AfterRegister(ctx)
|
||||||
|
if str := log.String(); !strings.Contains(str, "user not loaded") {
|
||||||
|
t.Error("Expected it to die with loading error:", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.User = authboss.Attributes{}
|
||||||
|
log.Reset()
|
||||||
|
c.AfterRegister(ctx)
|
||||||
|
if str := log.String(); !strings.Contains(str, "username doesn't exist") {
|
||||||
|
t.Error("Expected it to die with username error:", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.User[authboss.StoreUsername] = "uname"
|
||||||
|
log.Reset()
|
||||||
|
c.AfterRegister(ctx)
|
||||||
|
if str := log.String(); !strings.Contains(str, "no e-mail address to send to") {
|
||||||
|
t.Error("Expected it to die with e-mail address error:", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.User[authboss.StoreEmail] = "a@a.com"
|
||||||
|
log.Reset()
|
||||||
|
c.AfterRegister(ctx)
|
||||||
|
if str := log.String(); !strings.Contains(str, "Subject: Confirm New Account") {
|
||||||
|
t.Error("Expected it to send an e-mail:", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !sentEmail {
|
||||||
|
t.Error("Expected it to send an e-mail.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfirm_ConfirmHandlerErrors(t *testing.T) {
|
||||||
|
c := setup()
|
||||||
|
log := &bytes.Buffer{}
|
||||||
|
authboss.Cfg.LogWriter = log
|
||||||
|
authboss.Cfg.Mailer = authboss.LogMailer(log)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
URL string
|
||||||
|
Confirmed bool
|
||||||
|
Redirect bool
|
||||||
|
Error string
|
||||||
|
}{
|
||||||
|
{"http://localhost", false, true, "no confirm token found in request"},
|
||||||
|
{"http://localhost?cnf=c$ats", false, true, "confirm token failed to decode"},
|
||||||
|
{"http://localhost?cnf=SGVsbG8sIHBsYXlncm91bmQ=", false, true, "token not found"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
r, _ := http.NewRequest("GET", test.URL, nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
ctx, _ := authboss.ContextFromRequest(r)
|
||||||
|
|
||||||
|
log.Reset()
|
||||||
|
c.confirmHandler(ctx, w, r)
|
||||||
|
|
||||||
|
if len(test.Error) != 0 {
|
||||||
|
if str := log.String(); !strings.Contains(str, test.Error) {
|
||||||
|
t.Errorf("%d) Expected: %q, got: %q", i, test.Error, str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is, ok := ctx.User.Bool(StoreConfirmed)
|
||||||
|
if ok && is {
|
||||||
|
t.Error("The user should not be confirmed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.Redirect && w.Code != http.StatusTemporaryRedirect {
|
||||||
|
t.Error("Expected a redirect, got:", w.Header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfirm_Confirm(t *testing.T) {
|
||||||
|
c := setup()
|
||||||
|
ctx := authboss.NewContext()
|
||||||
|
log := &bytes.Buffer{}
|
||||||
|
authboss.Cfg.LogWriter = log
|
||||||
|
authboss.Cfg.Mailer = authboss.LogMailer(log)
|
||||||
|
|
||||||
|
// Create a token
|
||||||
|
token := []byte("hi")
|
||||||
|
sum := md5.Sum(token)
|
||||||
|
|
||||||
|
// Create the "database"
|
||||||
|
storer := mocks.NewMockStorer()
|
||||||
|
authboss.Cfg.Storer = storer
|
||||||
|
user := authboss.Attributes{
|
||||||
|
authboss.StoreUsername: "usern",
|
||||||
|
StoreConfirmToken: base64.StdEncoding.EncodeToString(sum[:]),
|
||||||
|
}
|
||||||
|
storer.Users["usern"] = user
|
||||||
|
|
||||||
|
// Make a request with session and context support.
|
||||||
|
r, _ := http.NewRequest("GET", "http://localhost?cnf="+base64.URLEncoding.EncodeToString(token), nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
ctx, _ = authboss.ContextFromRequest(r)
|
||||||
|
session := mocks.NewMockClientStorer()
|
||||||
|
ctx.User = user
|
||||||
|
ctx.SessionStorer = session
|
||||||
|
|
||||||
|
c.confirmHandler(ctx, w, r)
|
||||||
|
if w.Code != http.StatusTemporaryRedirect {
|
||||||
|
t.Error("Expected a redirect after success:", w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
if log.Len() != 0 {
|
||||||
|
t.Error("Expected a clean log on success:", log.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
is, ok := ctx.User.Bool(StoreConfirmed)
|
||||||
|
if !ok || !is {
|
||||||
|
t.Error("The user should be confirmed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
tok, ok := ctx.User.String(StoreConfirmToken)
|
||||||
|
if ok && len(tok) != 0 {
|
||||||
|
t.Error("Confirm token should have been wiped out.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if key, ok := ctx.SessionStorer.Get(authboss.SessionKey); !ok || len(key) == 0 {
|
||||||
|
t.Error("Should have logged the user in.")
|
||||||
|
}
|
||||||
|
if success, ok := ctx.SessionStorer.Get(authboss.FlashSuccessKey); !ok || len(success) == 0 {
|
||||||
|
t.Error("Should have left a nice message.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -18,6 +18,8 @@ type MockUser struct {
|
|||||||
Password string
|
Password string
|
||||||
RecoverToken string
|
RecoverToken string
|
||||||
RecoverTokenExpiry time.Time
|
RecoverTokenExpiry time.Time
|
||||||
|
ConfirmToken string
|
||||||
|
Confirmed bool
|
||||||
Locked bool
|
Locked bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,11 +11,11 @@ import (
|
|||||||
|
|
||||||
// Data store constants for attribute names.
|
// Data store constants for attribute names.
|
||||||
const (
|
const (
|
||||||
UserEmail = "email"
|
StoreEmail = "email"
|
||||||
UserName = "username"
|
StoreUsername = "username"
|
||||||
UserPassword = "password"
|
StorePassword = "password"
|
||||||
// UserKey is used to uniquely identify the user.
|
// UserKey is used to uniquely identify the user.
|
||||||
UserKey = UserEmail
|
StoreKey = StoreEmail
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user