1
0
mirror of https://github.com/volatiletech/authboss.git synced 2025-04-23 12:18:58 +02:00
authboss/recover/complete.go

127 lines
3.8 KiB
Go
Raw Normal View History

2015-02-11 13:44:36 -08:00
package recover
import (
"crypto/md5"
"encoding/base64"
"errors"
"fmt"
"net/http"
"time"
"golang.org/x/crypto/bcrypt"
"gopkg.in/authboss.v0"
"gopkg.in/authboss.v0/internal/flashutil"
)
var errRecoveryTokenExpired = errors.New("recovery token expired")
type pageRecoverComplete struct {
Token, Password, ConfirmPassword string
ErrMap map[string][]string
FlashSuccess, FlashError string
}
func (m *RecoverModule) recoverCompleteHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) {
switch r.Method {
case methodGET:
_, err := verifyToken(ctx, m.config.Storer.(authboss.RecoverStorer))
if err != nil {
if err.Error() == errRecoveryTokenExpired.Error() {
fmt.Fprintln(m.config.LogWriter, "recover [token expired]:", err)
ctx.SessionStorer.Put(authboss.FlashErrorKey, m.config.RecoverTokenExpiredFlash)
http.Redirect(w, r, "/recover", http.StatusFound)
return
} else {
fmt.Fprintln(m.config.LogWriter, "recover:", err)
http.Redirect(w, r, "/", http.StatusFound)
return
}
}
token, _ := ctx.FirstFormValue("token")
page := pageRecoverComplete{
Token: token,
FlashError: flashutil.Pull(ctx.SessionStorer, authboss.FlashErrorKey),
}
m.execTpl(tplRecoverComplete, w, page)
case methodPOST:
errPage := m.recoverComplete(ctx)
if errPage != nil {
m.execTpl(tplRecoverComplete, w, *errPage)
return
}
http.Redirect(w, r, m.config.AuthLoginSuccessRoute, http.StatusFound)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
// verifyToken expects a base64.URLEncoded token.
func verifyToken(ctx *authboss.Context, storer authboss.RecoverStorer) (attrs authboss.Attributes, err error) {
token, ok := ctx.FirstFormValue("token")
if !ok {
return nil, errors.New("missing form value: token")
}
decoded, err := base64.URLEncoding.DecodeString(token)
if err != nil {
return nil, err
}
sum := md5.Sum(decoded)
userInter, err := storer.RecoverUser(base64.StdEncoding.EncodeToString(sum[:]))
if err != nil {
return nil, err
}
attrs = authboss.Unbind(userInter)
expiry, ok := attrs.DateTime(attrRecoverTokenExpiry)
if !ok || time.Now().After(expiry) {
return nil, errRecoveryTokenExpired
}
return attrs, nil
}
func (m *RecoverModule) recoverComplete(ctx *authboss.Context) (errPage *pageRecoverComplete) {
token, _ := ctx.FirstFormValue("token")
password, _ := ctx.FirstPostFormValue("password")
confirmPassword, _ := ctx.FirstPostFormValue("confirmPassword")
defaultErrPage := &pageRecoverComplete{token, password, confirmPassword, nil, "", m.config.RecoverFailedErrorFlash}
var err error
ctx.User, err = verifyToken(ctx, m.config.Storer.(authboss.RecoverStorer))
if err != nil {
fmt.Fprintf(m.config.LogWriter, errFormat, "failed to verify token", err)
return defaultErrPage
}
policies := authboss.FilterValidators(m.config.Policies, "password")
if validationErrs := ctx.Validate(policies, m.config.ConfirmFields...); len(validationErrs) > 0 {
fmt.Fprintf(m.config.LogWriter, errFormat, "validation failed", validationErrs)
return &pageRecoverComplete{token, password, confirmPassword, validationErrs.Map(), "", ""}
}
encryptedPassword, err := bcrypt.GenerateFromPassword([]byte(password), m.config.BCryptCost)
if err != nil {
fmt.Fprintf(m.config.LogWriter, errFormat, "failed to encrypt password", err)
return defaultErrPage
}
ctx.User[attrPassword] = string(encryptedPassword)
ctx.User[attrRecoverToken] = ""
var nullTime time.Time
ctx.User[attrRecoverTokenExpiry] = nullTime
username, _ := ctx.User.String(attrUsername)
if err := ctx.SaveUser(username, m.config.Storer); err != nil {
fmt.Fprintf(m.config.LogWriter, errFormat, "failed to save user", err)
return defaultErrPage
}
ctx.SessionStorer.Put(authboss.SessionKey, username)
return nil
}