213 lines
6.2 KiB
Go
213 lines
6.2 KiB
Go
package handler
|
|
|
|
import (
|
|
"html/template"
|
|
"net/http"
|
|
"time"
|
|
|
|
"ntech/internal/auth"
|
|
)
|
|
|
|
const imeKolacica = "ntech_sesija"
|
|
const trajanjeSeije = 7 * 24 * time.Hour
|
|
const trajanjePredSeije = 5 * time.Minute
|
|
|
|
// PrikazPrijave renderuje formu za prijavu
|
|
func (h *Handler) PrikazPrijave(w http.ResponseWriter, r *http.Request) {
|
|
// ako nema korisnika, preusmeri na setup wizard
|
|
postoji, err := h.KorisniciRepo.PostojiIjedan(r.Context())
|
|
if err == nil && !postoji {
|
|
http.Redirect(w, r, "/setup", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
greska := r.URL.Query().Get("greska")
|
|
renderujStandaloneTemplate(w, "web/templates/stranice/prijava.html", map[string]any{
|
|
"Greska": greska,
|
|
})
|
|
}
|
|
|
|
// Prijava obrađuje POST formu za prijavu
|
|
func (h *Handler) Prijava(w http.ResponseWriter, r *http.Request) {
|
|
if err := r.ParseForm(); err != nil {
|
|
http.Redirect(w, r, "/prijava?greska=1", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
korisnickoIme := r.FormValue("korisnicko_ime")
|
|
lozinka := r.FormValue("lozinka")
|
|
|
|
korisnik, err := h.KorisniciRepo.DohvatiPoImenu(r.Context(), korisnickoIme)
|
|
if err != nil || !korisnik.Aktivan {
|
|
http.Redirect(w, r, "/prijava?greska=1", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
if !auth.ProveriLozinku(korisnik.LozinkaHash, lozinka) {
|
|
http.Redirect(w, r, "/prijava?greska=1", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
token := auth.GenerisiToken()
|
|
|
|
if korisnik.TotpTajna != "" {
|
|
// kreira privremenu sesiju koja čeka TOTP verifikaciju
|
|
if err := h.SesijeRepo.Kreiraj(r.Context(), korisnik.ID, token, time.Now().Add(trajanjePredSeije), false); err != nil {
|
|
http.Redirect(w, r, "/prijava?greska=2", http.StatusSeeOther)
|
|
return
|
|
}
|
|
http.SetCookie(w, napraviKolacic(token, time.Now().Add(trajanjePredSeije)))
|
|
http.Redirect(w, r, "/prijava/totp", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
// direktna sesija bez TOTP
|
|
if err := h.SesijeRepo.Kreiraj(r.Context(), korisnik.ID, token, time.Now().Add(trajanjeSeije), true); err != nil {
|
|
http.Redirect(w, r, "/prijava?greska=2", http.StatusSeeOther)
|
|
return
|
|
}
|
|
http.SetCookie(w, napraviKolacic(token, time.Now().Add(trajanjeSeije)))
|
|
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
|
}
|
|
|
|
// PrikazTotp renderuje formu za unos TOTP koda
|
|
func (h *Handler) PrikazTotp(w http.ResponseWriter, r *http.Request) {
|
|
kolacic, err := r.Cookie(imeKolacica)
|
|
if err != nil {
|
|
http.Redirect(w, r, "/prijava", http.StatusSeeOther)
|
|
return
|
|
}
|
|
sesija, err := h.SesijeRepo.DohvatiPoTokenu(r.Context(), kolacic.Value)
|
|
if err != nil || sesija.TotpPotvrdjeno || time.Now().After(sesija.DatumIsteka) {
|
|
http.Redirect(w, r, "/prijava", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
greska := r.URL.Query().Get("greska")
|
|
renderujStandaloneTemplate(w, "web/templates/stranice/totp_provera.html", map[string]any{
|
|
"Greska": greska,
|
|
})
|
|
}
|
|
|
|
// VerifikujTotp obrađuje POST formu za TOTP verifikaciju
|
|
func (h *Handler) VerifikujTotp(w http.ResponseWriter, r *http.Request) {
|
|
kolacic, err := r.Cookie(imeKolacica)
|
|
if err != nil {
|
|
http.Redirect(w, r, "/prijava", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
sesija, err := h.SesijeRepo.DohvatiPoTokenu(r.Context(), kolacic.Value)
|
|
if err != nil || sesija.TotpPotvrdjeno || time.Now().After(sesija.DatumIsteka) {
|
|
http.Redirect(w, r, "/prijava", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
korisnik, err := h.KorisniciRepo.DohvatiPoID(r.Context(), sesija.KorisnikID)
|
|
if err != nil {
|
|
http.Redirect(w, r, "/prijava", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
kod := r.FormValue("kod")
|
|
if !auth.VerifikujTotpKod(kod, korisnik.TotpTajna) {
|
|
http.Redirect(w, r, "/prijava/totp?greska=1", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
novoIstice := time.Now().Add(trajanjeSeije)
|
|
if err := h.SesijeRepo.PotvrdiTotp(r.Context(), kolacic.Value, novoIstice); err != nil {
|
|
http.Redirect(w, r, "/prijava?greska=2", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
http.SetCookie(w, napraviKolacic(kolacic.Value, novoIstice))
|
|
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
|
}
|
|
|
|
// PrikazSetupa renderuje setup wizard za prvog korisnika
|
|
func (h *Handler) PrikazSetupa(w http.ResponseWriter, r *http.Request) {
|
|
postoji, err := h.KorisniciRepo.PostojiIjedan(r.Context())
|
|
if err == nil && postoji {
|
|
http.Redirect(w, r, "/prijava", http.StatusSeeOther)
|
|
return
|
|
}
|
|
greska := r.URL.Query().Get("greska")
|
|
renderujStandaloneTemplate(w, "web/templates/stranice/setup.html", map[string]any{
|
|
"Greska": greska,
|
|
})
|
|
}
|
|
|
|
// SacuvajSetup kreira prvog superadmin korisnika
|
|
func (h *Handler) SacuvajSetup(w http.ResponseWriter, r *http.Request) {
|
|
postoji, err := h.KorisniciRepo.PostojiIjedan(r.Context())
|
|
if err == nil && postoji {
|
|
http.Redirect(w, r, "/prijava", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
if err := r.ParseForm(); err != nil {
|
|
http.Redirect(w, r, "/setup?greska=1", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
ime := r.FormValue("korisnicko_ime")
|
|
lozinka := r.FormValue("lozinka")
|
|
potvrda := r.FormValue("lozinka_potvrda")
|
|
|
|
if len(ime) < 3 || len(lozinka) < 8 || lozinka != potvrda {
|
|
http.Redirect(w, r, "/setup?greska=1", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
hash, err := auth.HashujLozinku(lozinka)
|
|
if err != nil {
|
|
http.Redirect(w, r, "/setup?greska=2", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
if _, err := h.KorisniciRepo.Kreiraj(r.Context(), ime, hash, "superadmin"); err != nil {
|
|
http.Redirect(w, r, "/setup?greska=2", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
http.Redirect(w, r, "/prijava?sacuvano=1", http.StatusSeeOther)
|
|
}
|
|
|
|
// Odjava briše sesiju i kolačić
|
|
func (h *Handler) Odjava(w http.ResponseWriter, r *http.Request) {
|
|
if kolacic, err := r.Cookie(imeKolacica); err == nil {
|
|
_ = h.SesijeRepo.Obrisi(r.Context(), kolacic.Value)
|
|
}
|
|
http.SetCookie(w, &http.Cookie{
|
|
Name: imeKolacica,
|
|
Value: "",
|
|
Path: "/",
|
|
Expires: time.Unix(0, 0),
|
|
MaxAge: -1,
|
|
})
|
|
http.Redirect(w, r, "/prijava", http.StatusSeeOther)
|
|
}
|
|
|
|
func napraviKolacic(token string, istice time.Time) *http.Cookie {
|
|
return &http.Cookie{
|
|
Name: imeKolacica,
|
|
Value: token,
|
|
Path: "/",
|
|
Expires: istice,
|
|
HttpOnly: true,
|
|
SameSite: http.SameSiteStrictMode,
|
|
}
|
|
}
|
|
|
|
func renderujStandaloneTemplate(w http.ResponseWriter, putanja string, podaci any) {
|
|
tmpl, err := template.ParseFiles(putanja)
|
|
if err != nil {
|
|
http.Error(w, "Greška pri učitavanju stranice", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if err := tmpl.Execute(w, podaci); err != nil {
|
|
http.Error(w, "Greška pri prikazu stranice", http.StatusInternalServerError)
|
|
}
|
|
}
|