172 lines
4.0 KiB
Go
172 lines
4.0 KiB
Go
package login
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"librapi/handlers/login/templates"
|
|
"librapi/services"
|
|
"net/http"
|
|
"os"
|
|
"sync"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
var (
|
|
adminUsername = sync.OnceValue[string](func() string {
|
|
return os.Getenv("API_ADMIN_USERNAME")
|
|
})
|
|
|
|
adminPassword = sync.OnceValue[string](func() string {
|
|
return os.Getenv("API_ADMIN_PASSWORD")
|
|
})
|
|
)
|
|
|
|
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 (lf *LoginForm) ValidCredentials() bool {
|
|
return lf.Username.Value == adminUsername() && lf.Password.Value == adminPassword()
|
|
}
|
|
|
|
func Handler(s *services.SessionStore) func(http.ResponseWriter, *http.Request) {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
getLogin(w, r, s)
|
|
case http.MethodPost:
|
|
postLogin(w, r, s)
|
|
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, s *services.SessionStore) {
|
|
loginForm := templates.GetLoginForm()
|
|
|
|
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
|
|
}
|
|
|
|
if ok := lf.ValidCredentials(); !ok {
|
|
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
|
|
}
|
|
|
|
session, err := s.NewSession()
|
|
if err != nil {
|
|
log.Err(err).Msg("unable to create a new session")
|
|
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
cookie := session.GenerateCookie()
|
|
http.SetCookie(w, cookie)
|
|
|
|
loginSuccess := templates.GetLoginSuccess()
|
|
fmt.Fprint(w, loginSuccess.Tree.Root.String())
|
|
}
|
|
|
|
func getLogin(w http.ResponseWriter, r *http.Request, s *services.SessionStore) {
|
|
loginForm := templates.GetLoginForm()
|
|
if loginForm == nil {
|
|
log.Error().Msg("unable to load login form")
|
|
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
if s.IsLogged(r) {
|
|
loginSuccess := templates.GetLoginSuccess()
|
|
fmt.Fprint(w, loginSuccess.Tree.Root.String())
|
|
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
|
|
}
|
|
|
|
if _, err := fmt.Fprint(w, buf); err != nil {
|
|
log.Err(err).Msg("unable to write to response")
|
|
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
|
|
}
|
|
}
|