Bezbednost: superadmin može da odblokira IP adrese zaključane bruteforce zaštitom
Nova stranica /admin/blokirane-ip — lista IP adresa sa previše neuspelih pokušaja prijave (broj neuspeha, poslednji pokušaj, preostalo vreme zaključavanja) i dugme za ručno odblokiranje. Nova dozvola 'bezbednost.odblokiraj', namerno rezervisana isključivo za ulogu superadmin (admin je nema — odblokiranje zaobilazi bruteforce zaštitu). Repo metode PokusajiPrijaveRepo.ListaBlokiranih/Odblokiraj.
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
ntechsqlite "ntech/internal/db/sqlite"
|
||||
"ntech/internal/middleware"
|
||||
"ntech/internal/model"
|
||||
)
|
||||
|
||||
// stavkaBlokiraneIP je jedan red na stranici blokiranih IP adresa, sa
|
||||
// unapred izračunatim preostalim vremenom zaključavanja (ili "" ako je isteklo).
|
||||
type stavkaBlokiraneIP struct {
|
||||
model.BlokiranaIP
|
||||
Preostalo string
|
||||
}
|
||||
|
||||
type podaciBlokiraneIP struct {
|
||||
model.PodaciStranice
|
||||
Lista []stavkaBlokiraneIP
|
||||
}
|
||||
|
||||
// AdminBlokiraneIP prikazuje IP adrese trenutno zaključane zbog previše neuspelih
|
||||
// pokušaja prijave, sa mogućnošću ručnog odblokiranja (superadmin).
|
||||
func (h *Handler) AdminBlokiraneIP(w http.ResponseWriter, r *http.Request) {
|
||||
if _, ok := h.zahtevajDozvolu(w, r, "bezbednost.odblokiraj"); !ok {
|
||||
return
|
||||
}
|
||||
podesavanja, err := ntechsqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
|
||||
if err != nil {
|
||||
http.Error(w, "Greška pri učitavanju podešavanja", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
ps := h.popuniPodaciStranice(r, podesavanja)
|
||||
ps.Stranica = "blokirane-ip"
|
||||
ps.NaslovStranice = "Blokirane IP adrese"
|
||||
|
||||
od := time.Now().Add(-prozorPrijave)
|
||||
blokirane, err := h.PokusajiRepo.ListaBlokiranih(r.Context(), od, maxNeuspehaPrijave)
|
||||
if err != nil {
|
||||
http.Error(w, "Greška pri učitavanju blokiranih IP adresa", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
lista := make([]stavkaBlokiraneIP, 0, len(blokirane))
|
||||
for _, b := range blokirane {
|
||||
preostalo, zakljucano := h.preostaloBruteforce(r.Context(), b.IP, od)
|
||||
if !zakljucano {
|
||||
continue
|
||||
}
|
||||
lista = append(lista, stavkaBlokiraneIP{BlokiranaIP: b, Preostalo: preostalo})
|
||||
}
|
||||
|
||||
h.renderujTemplate(w, "blokirane_ip", podaciBlokiraneIP{PodaciStranice: ps, Lista: lista})
|
||||
}
|
||||
|
||||
// AdminOdblokirajIP briše neuspele pokušaje prijave za datu IP adresu, čime se ona
|
||||
// odmah oslobađa bruteforce blokade.
|
||||
func (h *Handler) AdminOdblokirajIP(w http.ResponseWriter, r *http.Request) {
|
||||
if _, ok := h.zahtevajDozvolu(w, r, "bezbednost.odblokiraj"); !ok {
|
||||
return
|
||||
}
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "Greška pri čitanju forme", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
ip := strings.TrimSpace(r.FormValue("ip"))
|
||||
if ip == "" {
|
||||
http.Redirect(w, r, "/admin/blokirane-ip", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
if err := h.PokusajiRepo.Odblokiraj(r.Context(), ip); err != nil {
|
||||
middleware.SetFlash(w, r, h.DB, "greska", "Odblokiranje nije uspelo.")
|
||||
http.Redirect(w, r, "/admin/blokirane-ip", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
middleware.SetFlash(w, r, h.DB, "uspeh", "IP adresa "+ip+" je odblokirana.")
|
||||
http.Redirect(w, r, "/admin/blokirane-ip", http.StatusSeeOther)
|
||||
}
|
||||
Reference in New Issue
Block a user