195 lines
4.6 KiB
Go
195 lines
4.6 KiB
Go
package internal
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/dgrijalva/jwt-go"
|
|
"github.com/julienschmidt/httprouter"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// ResetPasswordHandler ...
|
|
func (s *Server) ResetPasswordHandler() httprouter.Handle {
|
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
|
ctx := NewContext(s.config, s.db, r)
|
|
|
|
if r.Method == "GET" {
|
|
ctx.Title = "Reset password"
|
|
s.render("resetPassword", w, ctx)
|
|
return
|
|
}
|
|
|
|
username := NormalizeUsername(r.FormValue("username"))
|
|
email := strings.TrimSpace(r.FormValue("email"))
|
|
recovery := fmt.Sprintf("email:%s", FastHash(email))
|
|
|
|
// Check if user exist
|
|
if !s.db.HasUser(username) {
|
|
ctx.Error = true
|
|
ctx.Message = "User not found!"
|
|
s.render("error", w, ctx)
|
|
return
|
|
}
|
|
|
|
// Get user object from DB
|
|
user, err := s.db.GetUser(username)
|
|
if err != nil {
|
|
ctx.Error = true
|
|
ctx.Message = "Error loading user"
|
|
s.render("error", w, ctx)
|
|
return
|
|
}
|
|
|
|
if recovery != user.Recovery {
|
|
ctx.Error = true
|
|
ctx.Message = "Error! The email address you supplied does not match what you registered with :/"
|
|
s.render("error", w, ctx)
|
|
return
|
|
}
|
|
|
|
// Create magic link expiry time
|
|
now := time.Now()
|
|
secs := now.Unix()
|
|
expiresAfterSeconds := int64(600) // Link expires after 10 minutes
|
|
|
|
expiryTime := secs + expiresAfterSeconds
|
|
|
|
// Create magic link
|
|
token := jwt.NewWithClaims(
|
|
jwt.SigningMethodHS256,
|
|
jwt.MapClaims{"username": username, "expiresAt": expiryTime},
|
|
)
|
|
tokenString, err := token.SignedString([]byte(s.config.MagicLinkSecret))
|
|
if err != nil {
|
|
ctx.Error = true
|
|
ctx.Message = err.Error()
|
|
s.render("error", w, ctx)
|
|
return
|
|
}
|
|
|
|
if err := SendPasswordResetEmail(s.config, user, email, tokenString); err != nil {
|
|
log.WithError(err).Errorf("unable to send reset password email to %s", user.Username)
|
|
ctx.Error = true
|
|
ctx.Message = err.Error()
|
|
s.render("error", w, ctx)
|
|
return
|
|
}
|
|
|
|
log.Infof("reset password email sent for %s", user.Username)
|
|
|
|
// Show success msg
|
|
ctx.Error = false
|
|
ctx.Message = "Password request request sent! Please check your email and follow the instructions"
|
|
s.render("error", w, ctx)
|
|
}
|
|
}
|
|
|
|
// ResetPasswordMagicLinkHandler ...
|
|
func (s *Server) ResetPasswordMagicLinkHandler() httprouter.Handle {
|
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
|
ctx := NewContext(s.config, s.db, r)
|
|
|
|
// Get token from query string
|
|
tokens, ok := r.URL.Query()["token"]
|
|
|
|
// Check if valid token
|
|
if !ok || len(tokens[0]) < 1 {
|
|
ctx.Error = true
|
|
ctx.Message = "Invalid token"
|
|
s.render("error", w, ctx)
|
|
return
|
|
}
|
|
|
|
tokenEmail := tokens[0]
|
|
ctx.PasswordResetToken = tokenEmail
|
|
|
|
// Show newPassword page
|
|
s.render("newPassword", w, ctx)
|
|
}
|
|
}
|
|
|
|
// NewPasswordHandler ...
|
|
func (s *Server) NewPasswordHandler() httprouter.Handle {
|
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
|
ctx := NewContext(s.config, s.db, r)
|
|
|
|
if r.Method == "GET" {
|
|
return
|
|
}
|
|
|
|
password := r.FormValue("password")
|
|
tokenEmail := r.FormValue("token")
|
|
|
|
// Check if token is valid
|
|
token, err := jwt.Parse(tokenEmail, func(token *jwt.Token) (interface{}, error) {
|
|
|
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
|
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
|
|
}
|
|
|
|
return []byte(s.config.MagicLinkSecret), nil
|
|
})
|
|
|
|
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
|
|
|
|
var username = fmt.Sprintf("%v", claims["username"])
|
|
var expiresAt int = int(claims["expiresAt"].(float64))
|
|
|
|
now := time.Now()
|
|
secs := now.Unix()
|
|
|
|
// Check token expiry
|
|
if secs > int64(expiresAt) {
|
|
ctx.Error = true
|
|
ctx.Message = "Token expires"
|
|
s.render("error", w, ctx)
|
|
return
|
|
}
|
|
|
|
user, err := s.db.GetUser(username)
|
|
if err != nil {
|
|
ctx.Error = true
|
|
ctx.Message = "Error loading user"
|
|
s.render("error", w, ctx)
|
|
return
|
|
}
|
|
|
|
// Reset password
|
|
if password != "" {
|
|
hash, err := s.pm.CreatePassword(password)
|
|
if err != nil {
|
|
ctx.Error = true
|
|
ctx.Message = "Error loading user"
|
|
s.render("error", w, ctx)
|
|
return
|
|
}
|
|
|
|
user.Password = hash
|
|
|
|
// Save user
|
|
if err := s.db.SetUser(username, user); err != nil {
|
|
ctx.Error = true
|
|
ctx.Message = "Error loading user"
|
|
s.render("error", w, ctx)
|
|
return
|
|
}
|
|
}
|
|
|
|
log.Infof("password changed: %v", user)
|
|
|
|
// Show success msg
|
|
ctx.Error = false
|
|
ctx.Message = "Password reset successfully."
|
|
s.render("error", w, ctx)
|
|
} else {
|
|
ctx.Error = true
|
|
ctx.Message = err.Error()
|
|
s.render("error", w, ctx)
|
|
return
|
|
}
|
|
}
|
|
}
|