1
0
mirror of https://github.com/volatiletech/authboss.git synced 2025-01-26 05:27:33 +02:00
authboss/recover/recover.go

207 lines
5.5 KiB
Go
Raw Normal View History

2015-01-12 22:28:42 -08:00
package recover
import (
"bytes"
"crypto/md5"
2015-01-16 21:49:23 -08:00
"crypto/rand"
"encoding/base64"
2015-01-12 22:28:42 -08:00
"errors"
2015-01-16 21:49:23 -08:00
"fmt"
2015-01-12 22:28:42 -08:00
"html/template"
"net/http"
2015-01-16 21:49:23 -08:00
"io"
2015-01-12 22:28:42 -08:00
"gopkg.in/authboss.v0"
2015-01-18 14:24:20 -08:00
"gopkg.in/authboss.v0/internal/views"
2015-01-12 22:28:42 -08:00
)
const (
methodGET = "GET"
methodPOST = "POST"
tplLogin = "login.tpl"
2015-01-18 14:24:20 -08:00
tplRecover = "recover.tpl"
tplRecoverComplete = "recover-complete.tpl"
tplInitEmail = "recover-init.email"
2015-01-12 22:28:42 -08:00
attrUsername = "username"
attrRecoverToken = "recover_token"
attrEmail = "email"
2015-01-12 22:28:42 -08:00
)
func init() {
m := &RecoverModule{}
authboss.RegisterModule("recover", m)
}
type RecoverPage struct {
Username, ConfirmUsername string
ErrMap map[string][]string
2015-01-12 22:28:42 -08:00
}
type RecoverModule struct {
templates *template.Template
routes authboss.RouteTable
storageOptions authboss.StorageOptions
storer authboss.RecoverStorer
logger io.Writer
policies []authboss.Validator
confirmFields []string
hostName string
recoverInitiateRedirect string
recoverInitiateSuccessFlash string
fromEmail string
2015-01-18 14:24:20 -08:00
}
2015-01-12 22:28:42 -08:00
2015-01-18 14:24:20 -08:00
func (m *RecoverModule) Initialize(config *authboss.Config) (err error) {
if config.Storer == nil {
return errors.New("recover: Need a RecoverStorer.")
}
if storer, ok := config.Storer.(authboss.RecoverStorer); !ok {
return errors.New("recover: RecoverStorer required for recover functionality.")
} else {
m.storer = storer
}
2015-01-18 14:24:20 -08:00
if m.templates, err = views.Get(config.ViewsPath, tplRecover, tplRecoverComplete, tplInitEmail); err != nil {
return err
2015-01-12 22:28:42 -08:00
}
m.routes = authboss.RouteTable{
"recover": m.recoverHandlerFunc,
//"recover/complete": m.recoverCompleteHandlerFunc,
2015-01-12 22:28:42 -08:00
}
m.storageOptions = authboss.StorageOptions{
attrUsername: authboss.String,
attrRecoverToken: authboss.String,
attrEmail: authboss.String,
2015-01-12 22:28:42 -08:00
}
2015-01-18 14:24:20 -08:00
m.logger = config.LogWriter
m.fromEmail = config.RecoverFromEmail
m.hostName = config.HostName
m.recoverInitiateRedirect = config.RecoverInitiateRedirect
m.recoverInitiateSuccessFlash = config.RecoverInitiateSuccessFlash
m.policies = config.Policies
m.confirmFields = config.ConfirmFields
2015-01-12 22:28:42 -08:00
return nil
}
func (m *RecoverModule) Routes() authboss.RouteTable { return m.routes }
func (m *RecoverModule) Storage() authboss.StorageOptions { return m.storageOptions }
2015-01-12 22:28:42 -08:00
2015-01-16 21:49:23 -08:00
func (m *RecoverModule) recoverHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) {
2015-01-12 22:28:42 -08:00
switch r.Method {
case methodGET:
2015-01-18 14:24:20 -08:00
m.templates.ExecuteTemplate(w, tplRecover, nil)
2015-01-12 22:28:42 -08:00
case methodPOST:
username, _ := ctx.FirstPostFormValue("username")
confirmUsername, _ := ctx.FirstPostFormValue("confirmUsername")
policies := authboss.FilterValidators(m.policies, "username")
if validationErrs := ctx.Validate(policies, m.confirmFields...); len(validationErrs) > 0 {
err := m.templates.ExecuteTemplate(w, tplRecover, RecoverPage{username, confirmUsername, validationErrs.Map()})
if err != nil {
fmt.Fprintln(m.logger, "recover:", err)
}
return
2015-01-16 21:49:23 -08:00
}
2015-01-12 22:28:42 -08:00
if err := m.initiateRecover(ctx, username, confirmUsername); err != nil {
fmt.Fprintln(m.logger, fmt.Sprintf("recover: %s", err.Error()))
2015-01-16 21:49:23 -08:00
}
ctx.SessionStorer.Put(authboss.FlashSuccessKey, m.recoverInitiateSuccessFlash)
http.Redirect(w, r, m.recoverInitiateRedirect, http.StatusFound)
2015-01-12 22:28:42 -08:00
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
func (m *RecoverModule) initiateRecover(ctx *authboss.Context, username, confirmUsername string) (err error) {
2015-01-16 21:49:23 -08:00
if err := ctx.LoadUser(username, m.storer); err != nil {
return err
}
email, ok := ctx.User.String(attrEmail)
2015-01-18 14:24:20 -08:00
if !ok {
return fmt.Errorf("missing attr: %s", email)
2015-01-18 14:24:20 -08:00
}
token := make([]byte, 32)
if _, err := rand.Read(token); err != nil {
return err
2015-01-18 14:24:20 -08:00
}
sum := md5.Sum(token)
ctx.User[attrRecoverToken] = base64.StdEncoding.EncodeToString(sum[:])
2015-01-18 14:24:20 -08:00
if err := ctx.SaveUser(username, m.storer); err != nil {
return err
}
emailBody := &bytes.Buffer{}
if err := m.templates.ExecuteTemplate(emailBody, tplInitEmail, struct{ Link string }{
fmt.Sprintf("%s/recover/complete?token=%s", m.hostName, base64.URLEncoding.EncodeToString(sum[:])),
2015-01-18 14:24:20 -08:00
}); err != nil {
return err
}
return authboss.SendEmail(email, m.fromEmail, emailBody.Bytes())
2015-01-12 22:28:42 -08:00
}
2015-01-18 14:24:20 -08:00
/*func (m *RecoverModule) recoverCompleteHandlerFunc(ctx *authboss.Context, w http.ResponseWriter, r *http.Request) {
2015-01-18 14:24:20 -08:00
switch r.Method {
case methodGET:
token, ok := ctx.FirstFormValue("token")
if !ok {
fmt.Fprintln(m.logger, "recover: expected value token")
//http.Redirect(w, r, "/", http.StatusFound)
return
}
userAttrs, err := m.verifyToken(token);
if err != nil {
fmt.Fprintf(m.logger, "recover: %s", err)
//http.Redirect(w, r, urlStr, code)
return
}
2015-01-18 14:24:20 -08:00
m.templates.ExecuteTemplate(w, tplRecoverComplete, nil)
case methodPOST:
//if err := completeRecover(ctx); err :=
2015-01-18 14:24:20 -08:00
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
func (m *RecoverModule) verifyToken(token) (attrs authboss.Attributes, err) {
decodedToken, err := base64.URLEncoding.DecodeString(token)
if err != nil {
return nil, err
}
sum := md5.Sum(decodedToken)
userInter, err := m.storer.RecoverUser(base64.StdEncoding.EncodeToString(sum[:]))
if err != nil {
return nil, err
}
return authboss.Unbind(userInter), nil
}
func (m *RecoverModule) completeRecover(ctx *authboss.Context, password, confirmPassword string) error {
if password == confirmPassword {
return errors.New("Passwords do not match")
}
return nil
}
*/