Evidencija prijava — login_istorija tabela, logovanje svih pokušaja, stranica istorije po korisniku, WebAuthn shema

This commit is contained in:
2026-06-03 22:05:00 +02:00
parent ed7ae605b2
commit d68aaba787
11 changed files with 258 additions and 5 deletions
+44
View File
@@ -20,6 +20,12 @@ type podaciAdminKorisnici struct {
Sacuvano bool
}
type podaciLoginIstorija struct {
model.PodaciStranice
PrikazKorisnik model.Korisnik
Istorija []*model.LoginPokusaj
}
type podaciAdminProfil struct {
model.PodaciStranice
Greska string
@@ -333,6 +339,44 @@ func (h *Handler) AdminTotpDeaktivacija(w http.ResponseWriter, r *http.Request)
http.Redirect(w, r, "/admin/profil?sacuvano=totp_off", http.StatusSeeOther)
}
// AdminLoginIstorija prikazuje evidenciju prijava za datog korisnika
func (h *Handler) AdminLoginIstorija(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !middleware.JeAdmin(k) {
http.Error(w, "Pristup odbijen", http.StatusForbidden)
return
}
id, err := parseID(chi.URLParam(r, "id"))
if err != nil {
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
korisnik, err := h.KorisniciRepo.DohvatiPoID(r.Context(), id)
if err != nil {
http.Error(w, "Korisnik nije pronađen", http.StatusNotFound)
return
}
istorija, err := h.LoginIstorijsaRepo.ListaZaKorisnika(r.Context(), id, 50)
if err != nil {
http.Error(w, "Greška pri učitavanju istorije", http.StatusInternalServerError)
return
}
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "admin"
ps.NaslovStranice = "Istorija prijava — " + korisnik.KorisnickoIme
h.renderujTemplate(w, "admin_login_istorija", podaciLoginIstorija{
PodaciStranice: ps,
PrikazKorisnik: *korisnik,
Istorija: istorija,
})
}
// parseBoolForm čita boolean vrednost iz forme
func parseBoolForm(s string) bool {
b, _ := strconv.ParseBool(s)
+5 -3
View File
@@ -24,8 +24,9 @@ type Handler struct {
KorisniciRepo db.KorisniciRepository
SesijeRepo db.SesijeRepository
PodsetniciFRepo db.PodsetnikRepository
PokusajiRepo db.PokusajiPrijaveRepository
Verzija string
PokusajiRepo db.PokusajiPrijaveRepository
LoginIstorijsaRepo db.LoginIstorijsaRepository
Verzija string
Templates map[string]*template.Template
}
@@ -43,7 +44,8 @@ func Novi(baza *sql.DB) *Handler {
KorisniciRepo: sqlite.NoviKorisniciRepo(baza),
SesijeRepo: sqlite.NoviSesijeRepo(baza),
PodsetniciFRepo: sqlite.NoviPodsetnikRepo(baza),
PokusajiRepo: sqlite.NoviPokusajiPrijaveRepo(baza),
PokusajiRepo: sqlite.NoviPokusajiPrijaveRepo(baza),
LoginIstorijsaRepo: sqlite.NoviLoginIstorijsaRepo(baza),
}
}
+12 -1
View File
@@ -51,6 +51,7 @@ func (h *Handler) Prijava(w http.ResponseWriter, r *http.Request) {
if n >= maxNeuspehaPrijave {
if preostalo, zaklj := h.preostaloBruteforce(r.Context(), ip, od); zaklj {
auth.LogZaklucano(ip, korisnickoIme)
_ = h.LoginIstorijsaRepo.Zabeleži(r.Context(), nil, ip, r.UserAgent(), "ip_zaklucano", false)
h.renderujStandalone(w, "prijava", map[string]any{
"Greska": "zakljucano",
"Preostalo": preostalo,
@@ -62,21 +63,31 @@ func (h *Handler) Prijava(w http.ResponseWriter, r *http.Request) {
}
korisnik, err := h.KorisniciRepo.DohvatiPoImenu(r.Context(), korisnickoIme)
if err != nil || !korisnik.Aktivan {
if err != nil {
_ = h.PokusajiRepo.Zabeleži(r.Context(), ip, korisnickoIme, false)
_ = h.LoginIstorijsaRepo.Zabeleži(r.Context(), nil, ip, r.UserAgent(), "korisnik_ne_postoji", false)
auth.LogNeuspehPrijave(ip, korisnickoIme, "wrong_user")
http.Redirect(w, r, "/prijava?greska=1", http.StatusSeeOther)
return
}
if !korisnik.Aktivan {
_ = h.PokusajiRepo.Zabeleži(r.Context(), ip, korisnickoIme, false)
_ = h.LoginIstorijsaRepo.Zabeleži(r.Context(), &korisnik.ID, ip, r.UserAgent(), "nalog_neaktivan", false)
auth.LogNeuspehPrijave(ip, korisnickoIme, "nalog_neaktivan")
http.Redirect(w, r, "/prijava?greska=1", http.StatusSeeOther)
return
}
if !auth.ProveriLozinku(korisnik.LozinkaHash, lozinka) {
_ = h.PokusajiRepo.Zabeleži(r.Context(), ip, korisnickoIme, false)
_ = h.LoginIstorijsaRepo.Zabeleži(r.Context(), &korisnik.ID, ip, r.UserAgent(), "pogrešna_lozinka", false)
auth.LogNeuspehPrijave(ip, korisnickoIme, "wrong_password")
http.Redirect(w, r, "/prijava?greska=1", http.StatusSeeOther)
return
}
_ = h.PokusajiRepo.Zabeleži(r.Context(), ip, korisnickoIme, true)
_ = h.LoginIstorijsaRepo.Zabeleži(r.Context(), &korisnik.ID, ip, r.UserAgent(), "", true)
auth.LogUspehPrijave(ip, korisnickoIme)
token := auth.GenerisiToken()