librapi/handlers/login/handler.go
2025-01-03 16:18:39 +01:00

177 lines
4.0 KiB
Go

package login
import (
"bytes"
"errors"
"fmt"
"net/http"
"github.com/rs/zerolog/log"
"librapi/services"
"librapi/templates"
)
const URL = "/login"
var (
ErrInvalidUsername = errors.New("username must not be empty")
ErrInvalidPassword = errors.New("password must not be empty")
ErrInvalidCredentials = errors.New("bad credentials")
)
type LoginField struct {
Name string
Value string
Err string
}
type LoginForm struct {
Username LoginField
Password LoginField
Error error
Method string
}
func NewLoginForm() LoginForm {
return LoginForm{
Username: LoginField{
Name: "username",
},
Password: LoginField{
Name: "password",
},
Method: http.MethodPost,
}
}
func (lf *LoginForm) HasErrors() bool {
return lf.Username.Err != "" || lf.Password.Err != ""
}
func (lf *LoginForm) IsSuccess() bool {
return lf.Method == http.MethodPost && lf.Error != nil && !lf.HasErrors()
}
func Handler(a services.IAuthenticate) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
getLogin(w, r, a)
case http.MethodPost:
postLogin(w, r, a)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
}
}
func extractLoginForm(r *http.Request) LoginForm {
lf := NewLoginForm()
username := r.FormValue(lf.Username.Name)
if username == "" {
lf.Username.Err = ErrInvalidUsername.Error()
}
lf.Username.Value = username
password := r.FormValue(lf.Password.Name)
if password == "" {
lf.Password.Err = ErrInvalidPassword.Error()
}
lf.Password.Value = password
return lf
}
func postLogin(w http.ResponseWriter, r *http.Request, a services.IAuthenticate) {
loginForm := templates.GetLoginForm()
loginSuccess := templates.GetLoginSuccess()
if a.IsLogged(r) {
buf := bytes.NewBufferString("")
if err := loginSuccess.Execute(buf, nil); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
return
}
fmt.Fprint(w, buf)
return
}
lf := extractLoginForm(r)
if lf.HasErrors() {
buf := bytes.NewBufferString("")
if err := loginForm.Execute(buf, &lf); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
}
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, buf.String())
return
}
session, err := a.Authenticate(lf.Username.Value, lf.Password.Value)
if err != nil {
if errors.Is(err, services.ErrUnauthorized) {
log.Warn().Str("username", lf.Username.Value).Msg("bad credentials")
lf.Error = ErrInvalidCredentials
buf := bytes.NewBufferString("")
if err := loginForm.Execute(buf, &lf); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprint(w, buf.String())
return
}
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
return
}
cookie := session.GenerateCookie()
http.SetCookie(w, cookie)
buf := bytes.NewBufferString("")
if err := loginSuccess.Execute(buf, nil); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
return
}
fmt.Fprint(w, buf)
}
func getLogin(w http.ResponseWriter, r *http.Request, a services.IAuthenticate) {
loginForm := templates.GetLoginForm()
if a.IsLogged(r) {
loginSuccess := templates.GetLoginSuccess()
buf := bytes.NewBufferString("")
if err := loginSuccess.Execute(buf, nil); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
return
}
fmt.Fprint(w, buf)
return
}
buf := bytes.NewBufferString("")
if err := loginForm.Execute(buf, &LoginForm{}); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
return
}
fmt.Fprint(w, buf)
}