mirror of
https://github.com/volatiletech/authboss.git
synced 2025-04-13 11:50:27 +02:00
144 lines
4.3 KiB
Go
144 lines
4.3 KiB
Go
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
|
|
XSRFName, XSRFToken string
|
|
}
|
|
|
|
func (m *RecoverModule) recoverCompleteHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case methodGET:
|
|
_, err := verifyToken(ctx, authboss.Cfg.Storer.(authboss.RecoverStorer))
|
|
if err != nil {
|
|
if err.Error() == errRecoveryTokenExpired.Error() {
|
|
fmt.Fprintln(authboss.Cfg.LogWriter, "recover [token expired]:", err)
|
|
ctx.SessionStorer.Put(authboss.FlashErrorKey, authboss.Cfg.RecoverTokenExpiredFlash)
|
|
http.Redirect(w, r, "/recover", http.StatusFound)
|
|
return
|
|
} else {
|
|
fmt.Fprintln(authboss.Cfg.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),
|
|
XSRFName: authboss.Cfg.XSRFName,
|
|
XSRFToken: authboss.Cfg.XSRFMaker(w, r),
|
|
}
|
|
m.execTpl(tplRecoverComplete, w, page)
|
|
case methodPOST:
|
|
errPage := m.recoverComplete(ctx, authboss.Cfg.XSRFName, authboss.Cfg.XSRFMaker(w, r))
|
|
if errPage != nil {
|
|
m.execTpl(tplRecoverComplete, w, *errPage)
|
|
return
|
|
}
|
|
|
|
http.Redirect(w, r, authboss.Cfg.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, xsrfName, xsrfToken string) (errPage *pageRecoverComplete) {
|
|
token, _ := ctx.FirstFormValue("token")
|
|
password, _ := ctx.FirstPostFormValue("password")
|
|
confirmPassword, _ := ctx.FirstPostFormValue("confirmPassword")
|
|
defaultErrPage := &pageRecoverComplete{
|
|
Token: token,
|
|
Password: password,
|
|
ConfirmPassword: confirmPassword,
|
|
FlashError: authboss.Cfg.RecoverFailedErrorFlash,
|
|
XSRFName: xsrfName,
|
|
XSRFToken: xsrfToken,
|
|
}
|
|
|
|
var err error
|
|
ctx.User, err = verifyToken(ctx, authboss.Cfg.Storer.(authboss.RecoverStorer))
|
|
if err != nil {
|
|
fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "failed to verify token", err)
|
|
return defaultErrPage
|
|
}
|
|
|
|
policies := authboss.FilterValidators(authboss.Cfg.Policies, "password")
|
|
if validationErrs := ctx.Validate(policies, authboss.Cfg.ConfirmFields...); len(validationErrs) > 0 {
|
|
fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "validation failed", validationErrs)
|
|
return &pageRecoverComplete{
|
|
Token: token,
|
|
Password: password,
|
|
ConfirmPassword: confirmPassword,
|
|
ErrMap: validationErrs.Map(),
|
|
XSRFName: xsrfName,
|
|
XSRFToken: xsrfToken,
|
|
}
|
|
}
|
|
|
|
encryptedPassword, err := bcrypt.GenerateFromPassword([]byte(password), authboss.Cfg.BCryptCost)
|
|
if err != nil {
|
|
fmt.Fprintf(authboss.Cfg.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(); err != nil {
|
|
fmt.Fprintf(authboss.Cfg.LogWriter, errFormat, "failed to save user", err)
|
|
return defaultErrPage
|
|
}
|
|
|
|
ctx.SessionStorer.Put(authboss.SessionKey, username)
|
|
return nil
|
|
}
|