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
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))
|
|
|