You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

122 lines
3.2 KiB

package auth
import (
"errors"
"html/template"
"net/http"
"strconv"
"time"
webtemplates "github.com/domagojzecevic/cammonitor/internal/web/templates"
"github.com/go-chi/chi/v5"
)
type Handler struct {
store *Store
sessionTTL time.Duration
}
func NewHandler(store *Store, sessionTTL time.Duration) *Handler {
return &Handler{store: store, sessionTTL: sessionTTL}
}
func (h *Handler) LoginPage(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_ = loginTemplate.ExecuteTemplate(w, webtemplates.Login, nil)
}
func (h *Handler) Login(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
http.Error(w, "invalid form", http.StatusBadRequest)
return
}
user, err := h.store.Authenticate(r.FormValue("username"), r.FormValue("password"))
if errors.Is(err, ErrInvalidCredentials) {
http.Error(w, "invalid credentials", http.StatusUnauthorized)
return
}
if err != nil {
http.Error(w, "login failed", http.StatusInternalServerError)
return
}
token, err := h.store.CreateSession(user.ID, h.sessionTTL)
if err != nil {
http.Error(w, "create session failed", http.StatusInternalServerError)
return
}
http.SetCookie(w, &http.Cookie{
Name: sessionCookieName,
Value: token,
Path: "/",
Expires: time.Now().UTC().Add(h.sessionTTL),
HttpOnly: true,
SameSite: http.SameSiteLaxMode,
})
http.Redirect(w, r, "/", http.StatusSeeOther)
}
func (h *Handler) Logout(w http.ResponseWriter, r *http.Request) {
if cookie, err := r.Cookie(sessionCookieName); err == nil {
_ = h.store.DeleteSession(cookie.Value)
}
http.SetCookie(w, &http.Cookie{
Name: sessionCookieName,
Value: "",
Path: "/",
Expires: time.Unix(0, 0),
MaxAge: -1,
HttpOnly: true,
SameSite: http.SameSiteLaxMode,
})
http.Redirect(w, r, "/login", http.StatusSeeOther)
}
func (h *Handler) UsersPage(w http.ResponseWriter, _ *http.Request) {
users, err := h.store.ListUsers()
if err != nil {
http.Error(w, "list users failed", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_ = adminUsersTemplate.ExecuteTemplate(w, webtemplates.AdminUsers, struct {
Users []User
}{Users: users})
}
func (h *Handler) CreateUser(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
http.Error(w, "invalid form", http.StatusBadRequest)
return
}
if err := h.store.CreateUser(r.FormValue("username"), r.FormValue("password"), r.FormValue("is_admin") == "on"); err != nil {
http.Error(w, "create user failed", http.StatusBadRequest)
return
}
http.Redirect(w, r, "/admin/users", http.StatusSeeOther)
}
func (h *Handler) DeleteUser(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
if err != nil {
http.Error(w, "invalid user id", http.StatusBadRequest)
return
}
if err := h.store.DeleteUser(id); err != nil {
http.Error(w, "delete user failed", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/admin/users", http.StatusSeeOther)
}
var loginTemplate = template.Must(template.ParseFS(webtemplates.FS, webtemplates.Login))
var adminUsersTemplate = template.Must(template.ParseFS(webtemplates.FS, webtemplates.AdminUsers))