Files
spyda/internal/auth_handlers.go
2021-01-30 15:38:26 +10:00

100 lines
2.6 KiB
Go

package internal
import (
"net/http"
"time"
"github.com/julienschmidt/httprouter"
log "github.com/sirupsen/logrus"
"git.mills.io/prologic/spyda/internal/session"
)
// LoginHandler ...
func (s *Server) LoginHandler() httprouter.Handle {
// #239: Throttle failed login attempts and lock user account.
failures := NewTTLCache(5 * time.Minute)
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
ctx := NewContext(s.config, s.db, r)
if r.Method == "GET" {
s.render("login", w, ctx)
return
}
username := NormalizeUsername(r.FormValue("username"))
password := r.FormValue("password")
rememberme := r.FormValue("rememberme") == "on"
// Error: no username or password provided
if username == "" || password == "" {
log.Warn("no username or password provided")
http.Redirect(w, r, "/login", http.StatusFound)
return
}
// Lookup user
user, err := s.db.GetUser(username)
if err != nil {
ctx.Error = true
ctx.Message = "Invalid username! Hint: Register an account?"
s.render("error", w, ctx)
return
}
// #239: Throttle failed login attempts and lock user account.
if failures.Get(user.Username) > MaxFailedLogins {
ctx.Error = true
ctx.Message = "Too many failed login attempts. Account temporarily locked! Please try again later."
s.render("error", w, ctx)
return
}
// Validate cleartext password against KDF hash
err = s.pm.CheckPassword(user.Password, password)
if err != nil {
// #239: Throttle failed login attempts and lock user account.
failed := failures.Inc(user.Username)
time.Sleep(time.Duration(IntPow(2, failed)) * time.Second)
ctx.Error = true
ctx.Message = "Invalid password! Hint: Reset your password?"
s.render("error", w, ctx)
return
}
// #239: Throttle failed login attempts and lock user account.
failures.Reset(user.Username)
// Login successful
log.Infof("login successful: %s", username)
// Lookup session
sess := r.Context().Value(session.SessionKey)
if sess == nil {
log.Warn("no session found")
http.Redirect(w, r, "/login", http.StatusFound)
return
}
// Authorize session
_ = sess.(*session.Session).Set("username", username)
// Persist session?
if rememberme {
_ = sess.(*session.Session).Set("persist", "1")
}
http.Redirect(w, r, RedirectRefererURL(r, s.config, "/"), http.StatusFound)
}
}
// LogoutHandler ...
func (s *Server) LogoutHandler() httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
s.sm.Delete(w, r)
http.Redirect(w, r, "/", http.StatusFound)
}
}