Bezbednost: ispravke kontrole pristupa u admin i handler sloju

This commit is contained in:
2026-06-05 22:41:20 +02:00
parent ccc08aee08
commit 2b3636528f
44 changed files with 1310 additions and 480 deletions
+156 -5
View File
@@ -3,7 +3,6 @@ package handler
import (
"html/template"
"net/http"
"strconv"
"ntech/internal/auth"
"ntech/internal/db/sqlite"
@@ -51,6 +50,17 @@ func (h *Handler) AdminKorisnici(w http.ResponseWriter, r *http.Request) {
return
}
// admin ne sme da vidi superadmin naloge — filtriramo ih iz liste
if k.Uloga != "superadmin" {
filtrirano := lista[:0]
for _, kor := range lista {
if kor.Uloga != "superadmin" {
filtrirano = append(filtrirano, kor)
}
}
lista = filtrirano
}
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "admin"
ps.NaslovStranice = "Korisnici"
@@ -129,6 +139,12 @@ func (h *Handler) AdminToggleAktivan(w http.ResponseWriter, r *http.Request) {
return
}
// admin ne sme da menja status drugog admina ni superadmina
if korisnik.Uloga == "superadmin" || (korisnik.Uloga == "admin" && k.Uloga != "superadmin") {
http.Redirect(w, r, "/admin/korisnici?greska=3", http.StatusSeeOther)
return
}
if err := h.KorisniciRepo.AzurirajAktivan(r.Context(), id, !korisnik.Aktivan); err != nil {
http.Redirect(w, r, "/admin/korisnici?greska=2", http.StatusSeeOther)
return
@@ -413,6 +429,12 @@ func (h *Handler) AdminLoginIstorija(w http.ResponseWriter, r *http.Request) {
return
}
// admin ne sme da vidi istoriju superadmin naloga
if korisnik.Uloga == "superadmin" && k.Uloga != "superadmin" {
http.Error(w, "Pristup odbijen", http.StatusForbidden)
return
}
istorija, err := h.LoginIstorijsaRepo.ListaZaKorisnika(r.Context(), id, 50)
if err != nil {
http.Error(w, "Greška pri učitavanju istorije", http.StatusInternalServerError)
@@ -431,8 +453,137 @@ func (h *Handler) AdminLoginIstorija(w http.ResponseWriter, r *http.Request) {
})
}
// parseBoolForm čita boolean vrednost iz forme
func parseBoolForm(s string) bool {
b, _ := strconv.ParseBool(s)
return b
type podaciAdminDozvole struct {
model.PodaciStranice
Korisnici []model.Korisnik
TrenutniID int64
DozvoleRadnik map[string]bool
DozvoleAdmin map[string]bool
DozvoleSuperadmin map[string]bool
Greska string
Sacuvano bool
}
// AdminDozvole prikazuje stranicu za upravljanje ulogama i pregled dozvola
func (h *Handler) AdminDozvole(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !middleware.JeAdmin(k) {
http.Error(w, "Pristup odbijen", http.StatusForbidden)
return
}
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
lista, err := h.KorisniciRepo.Lista(r.Context())
if err != nil {
http.Error(w, "Greška pri učitavanju korisnika", http.StatusInternalServerError)
return
}
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "dozvole"
ps.NaslovStranice = "Dozvole"
h.renderujTemplate(w, "admin_dozvole", podaciAdminDozvole{
PodaciStranice: ps,
Korisnici: lista,
TrenutniID: k.ID,
DozvoleRadnik: h.DozvoleRepo.SveDozvole(r.Context(), "radnik"),
DozvoleAdmin: h.DozvoleRepo.SveDozvole(r.Context(), "admin"),
DozvoleSuperadmin: h.DozvoleRepo.SveDozvole(r.Context(), "superadmin"),
Greska: r.URL.Query().Get("greska"),
Sacuvano: r.URL.Query().Get("sacuvano") == "1",
})
}
// AdminDozvolePromeniUlogu menja ulogu korisnika sa stranice dozvola
func (h *Handler) AdminDozvolePromeniUlogu(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if k == nil || k.Uloga != "superadmin" {
http.Error(w, "Pristup odbijen", http.StatusForbidden)
return
}
id, err := parseID(chi.URLParam(r, "id"))
if err != nil {
http.Redirect(w, r, "/admin/dozvole?greska=1", http.StatusSeeOther)
return
}
if err := r.ParseForm(); err != nil {
http.Redirect(w, r, "/admin/dozvole?greska=1", http.StatusSeeOther)
return
}
// superadmin ne može menjati svoju vlastitu ulogu
if id == k.ID {
http.Redirect(w, r, "/admin/dozvole?greska=3", http.StatusSeeOther)
return
}
uloga := r.FormValue("uloga")
// superadmin uloga se ne može dodeliti kroz interfejs
validneUloge := map[string]bool{"admin": true, "radnik": true}
if !validneUloge[uloga] {
http.Redirect(w, r, "/admin/dozvole?greska=1", http.StatusSeeOther)
return
}
ciljni, err := h.KorisniciRepo.DohvatiPoID(r.Context(), id)
if err != nil {
http.Redirect(w, r, "/admin/dozvole?greska=2", http.StatusSeeOther)
return
}
// superadmin uloga se ne može menjati
if ciljni.Uloga == "superadmin" {
http.Redirect(w, r, "/admin/dozvole?greska=3", http.StatusSeeOther)
return
}
if err := h.KorisniciRepo.AzurirajUlogu(r.Context(), id, uloga); err != nil {
http.Redirect(w, r, "/admin/dozvole?greska=2", http.StatusSeeOther)
return
}
http.Redirect(w, r, "/admin/dozvole?sacuvano=1", http.StatusSeeOther)
}
// AdminDozvoleSacuvaj prima POST i čuva promene dozvola za radnik i admin uloge
func (h *Handler) AdminDozvoleSacuvaj(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !middleware.JeAdmin(k) {
http.Error(w, "Pristup odbijen", http.StatusForbidden)
return
}
if err := r.ParseForm(); err != nil {
http.Redirect(w, r, "/admin/dozvole?greska=1", http.StatusSeeOther)
return
}
// čuvamo dozvole samo za radnik i admin — superadmin uvek ima sve
for _, uloga := range []string{"radnik", "admin"} {
for _, akcija := range middleware.SveAkcije() {
kljuc := uloga + "__" + akcija
dozvoljeno := r.FormValue(kljuc) == "on"
if err := h.DozvoleRepo.Sacuvaj(r.Context(), uloga, akcija, dozvoljeno); err != nil {
http.Redirect(w, r, "/admin/dozvole?greska=2", http.StatusSeeOther)
return
}
}
}
http.Redirect(w, r, "/admin/dozvole?sacuvano=1", http.StatusSeeOther)
}
// AdminDozvoleReset vraća sve dozvole na podrazumevane vrednosti — samo superadmin
func (h *Handler) AdminDozvoleReset(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if k == nil || k.Uloga != "superadmin" {
http.Error(w, "Pristup odbijen", http.StatusForbidden)
return
}
if err := h.DozvoleRepo.Reset(r.Context()); err != nil {
http.Redirect(w, r, "/admin/dozvole?greska=2", http.StatusSeeOther)
return
}
http.Redirect(w, r, "/admin/dozvole?sacuvano=1", http.StatusSeeOther)
}
+18 -10
View File
@@ -13,6 +13,18 @@ import (
func (h *Handler) Dashboard(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// pročitaj i odmah obrišt flash poruku ako postoji
var flashGreska string
if kol, err := r.Cookie("ntech_flash_greska"); err == nil {
flashGreska = kol.Value
http.SetCookie(w, &http.Cookie{
Name: "ntech_flash_greska",
Value: "",
Path: "/",
MaxAge: -1,
})
}
podesavanja, err := sqlite.DohvatiSvaPodesavanja(ctx, h.DB)
if err != nil {
http.Error(w, "Greška pri učitavanju podešavanja", http.StatusInternalServerError)
@@ -126,17 +138,12 @@ func (h *Handler) Dashboard(w http.ResponseWriter, r *http.Request) {
}
}
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "dashboard"
ps.NaslovStranice = "Dashboard"
podaci := model.PodaciDashboarda{
PodaciStranice: model.PodaciStranice{
Stranica: "dashboard",
NaslovStranice: "Dashboard",
Tema: podesavanja["tema"],
NazivFirme: podesavanja["naziv_firme"],
Podnazlov: podesavanja["podnazlov"],
LogoTip: podesavanja["logo_tip"],
LogoPutanja: podesavanja["logo_putanja"],
Korisnik: "Admin",
},
PodaciStranice: ps,
BrojArtikala: brojArtikala,
AktivniServisi: aktivniServisi,
PrihodOvogMeseca: prihodOvogMeseca,
@@ -145,6 +152,7 @@ func (h *Handler) Dashboard(w http.ResponseWriter, r *http.Request) {
PoslednjiServisi: poslednjiServisi,
KriticneZalihe: kriticneZalihe,
PoslednjeProdaje: poslednjeProdaje,
FlashGreska: flashGreska,
}
h.renderujTemplate(w, "dashboard", podaci)
+16
View File
@@ -5,6 +5,7 @@ import (
"strings"
"ntech/internal/db/sqlite"
"ntech/internal/middleware"
"ntech/internal/model"
"github.com/go-chi/chi/v5"
@@ -76,6 +77,11 @@ func (h *Handler) NoviDobavljac(w http.ResponseWriter, r *http.Request) {
// SacuvajDobavljaca prima POST formu i upisuje novog dobavljača u bazu
func (h *Handler) SacuvajDobavljaca(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "dobavljac.dodaj") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, "Greška pri čitanju forme", http.StatusBadRequest)
return
@@ -136,6 +142,11 @@ func (h *Handler) IzmeniDobavljaca(w http.ResponseWriter, r *http.Request) {
// SacuvajIzmeneDobavljaca prima POST formu i ažurira postojećeg dobavljača u bazi
func (h *Handler) SacuvajIzmeneDobavljaca(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "dobavljac.izmeni") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
id, err := parseID(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Neispravan ID dobavljača", http.StatusBadRequest)
@@ -174,6 +185,11 @@ func (h *Handler) SacuvajIzmeneDobavljaca(w http.ResponseWriter, r *http.Request
// ObrisiDobavljaca prima POST zahtev i briše dobavljača po ID-u
func (h *Handler) ObrisiDobavljaca(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "dobavljac.obrisi") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
id, err := parseID(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Neispravan ID dobavljača", http.StatusBadRequest)
+4
View File
@@ -28,6 +28,7 @@ type Handler struct {
PodsetniciFRepo db.PodsetnikRepository
PokusajiRepo db.PokusajiPrijaveRepository
LoginIstorijsaRepo db.LoginIstorijsaRepository
DozvoleRepo db.DozvoleRepository
Verzija string
Templates map[string]*template.Template
TemplatesFS fs.FS
@@ -49,6 +50,7 @@ func Novi(baza *sql.DB) *Handler {
PodsetniciFRepo: sqlite.NoviPodsetnikRepo(baza),
PokusajiRepo: sqlite.NoviPokusajiPrijaveRepo(baza),
LoginIstorijsaRepo: sqlite.NoviLoginIstorijsaRepo(baza),
DozvoleRepo: sqlite.NoviDozvoleRepo(baza, middleware.ImaDozvolu, middleware.SveAkcije()),
}
}
@@ -67,6 +69,7 @@ func (h *Handler) reinicijalzijRepozitorijume(novaDB *sql.DB) {
h.PodsetniciFRepo = sqlite.NoviPodsetnikRepo(novaDB)
h.PokusajiRepo = sqlite.NoviPokusajiPrijaveRepo(novaDB)
h.LoginIstorijsaRepo = sqlite.NoviLoginIstorijsaRepo(novaDB)
h.DozvoleRepo = sqlite.NoviDozvoleRepo(novaDB, middleware.ImaDozvolu, middleware.SveAkcije())
}
// popuniPodaciStranice popunjava zajednička polja stranice uključujući prijavljenog korisnika
@@ -83,6 +86,7 @@ func (h *Handler) popuniPodaciStranice(r *http.Request, podesavanja map[string]s
ps.Korisnik = k.KorisnickoIme
ps.KorisnikIme = k.KorisnickoIme
ps.KorisnikUloga = k.Uloga
ps.Dozvole = h.DozvoleRepo.SveDozvole(r.Context(), k.Uloga)
}
ps.CsrfToken = middleware.CsrfToken(r.Context())
return ps
+11 -10
View File
@@ -9,6 +9,7 @@ import (
"time"
"ntech/internal/db/sqlite"
"ntech/internal/middleware"
"ntech/internal/model"
)
@@ -73,6 +74,11 @@ type TopKlijent struct {
// Izvestaji renderuje stranicu sa izveštajima
func (h *Handler) Izvestaji(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if k == nil || !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "izvestaj.pregled") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
ctx := r.Context()
podesavanja, err := sqlite.DohvatiSvaPodesavanja(ctx, h.DB)
@@ -239,17 +245,12 @@ func (h *Handler) Izvestaji(w http.ResponseWriter, r *http.Request) {
}
}
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "izvestaji"
ps.NaslovStranice = "Izveštaji"
podaci := PodaciIzvestaja{
PodaciStranice: model.PodaciStranice{
Stranica: "izvestaji",
NaslovStranice: "Izveštaji",
Tema: podesavanja["tema"],
NazivFirme: podesavanja["naziv_firme"],
Podnazlov: podesavanja["podnazlov"],
LogoTip: podesavanja["logo_tip"],
LogoPutanja: podesavanja["logo_putanja"],
Korisnik: "Admin",
},
PodaciStranice: ps,
MesecniPrihodi: mesecniPrihodi,
GrafikonJSON: template.JS(jsonBytes),
StariNalozi: stariNalozi,
+11
View File
@@ -5,6 +5,7 @@ import (
"strconv"
"ntech/internal/db/sqlite"
"ntech/internal/middleware"
"ntech/internal/model"
"github.com/go-chi/chi/v5"
@@ -47,6 +48,11 @@ func (h *Handler) Kategorije(w http.ResponseWriter, r *http.Request) {
// DodajKategoriju prima POST i čuva novu kategoriju
func (h *Handler) DodajKategoriju(w http.ResponseWriter, r *http.Request) {
kor := middleware.KorisnikIzKonteksta(r.Context())
if kor == nil || !h.DozvoleRepo.ImaDozvolu(r.Context(), kor.Uloga, "kategorija.dodaj") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, "Greška pri čitanju forme", http.StatusBadRequest)
return
@@ -73,6 +79,11 @@ func (h *Handler) DodajKategoriju(w http.ResponseWriter, r *http.Request) {
// ObrisiKategoriju briše kategoriju po ID-u
func (h *Handler) ObrisiKategoriju(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "kategorija.obrisi") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
idStr := chi.URLParam(r, "id")
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
+16
View File
@@ -6,6 +6,7 @@ import (
"strings"
"ntech/internal/db/sqlite"
"ntech/internal/middleware"
"ntech/internal/model"
"github.com/go-chi/chi/v5"
@@ -77,6 +78,11 @@ func (h *Handler) NoviKlijent(w http.ResponseWriter, r *http.Request) {
// SacuvajKlijenta prima POST formu i upisuje novog klijenta u bazu
func (h *Handler) SacuvajKlijenta(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "klijent.dodaj") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, "Greška pri čitanju forme", http.StatusBadRequest)
return
@@ -147,6 +153,11 @@ func (h *Handler) IzmeniKlijenta(w http.ResponseWriter, r *http.Request) {
// SacuvajIzmenuKlijenta prima POST formu i ažurira postojećeg klijenta u bazi
func (h *Handler) SacuvajIzmenuKlijenta(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "klijent.izmeni") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
id, err := parseID(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Neispravan ID klijenta", http.StatusBadRequest)
@@ -195,6 +206,11 @@ func (h *Handler) SacuvajIzmenuKlijenta(w http.ResponseWriter, r *http.Request)
// ObrisiKlijenta prima POST zahtev i briše klijenta po ID-u
func (h *Handler) ObrisiKlijenta(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "klijent.obrisi") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
id, err := parseID(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Neispravan ID klijenta", http.StatusBadRequest)
+6
View File
@@ -6,6 +6,7 @@ import (
"ntech/internal/db"
"ntech/internal/db/sqlite"
"ntech/internal/middleware"
"ntech/internal/model"
"github.com/go-chi/chi/v5"
@@ -74,6 +75,11 @@ func (h *Handler) Magacin(w http.ResponseWriter, r *http.Request) {
// ObrisiArtikal briše artikal po ID-u
func (h *Handler) ObrisiArtikal(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "artikal.obrisi") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
idStr := chi.URLParam(r, "id")
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
+11
View File
@@ -6,6 +6,7 @@ import (
"strconv"
"ntech/internal/db/sqlite"
"ntech/internal/middleware"
"ntech/internal/model"
"github.com/go-chi/chi/v5"
@@ -47,6 +48,11 @@ func (h *Handler) NoviArtikal(w http.ResponseWriter, r *http.Request) {
// SacuvajArtikal prima POST formu i čuva novi artikal
func (h *Handler) SacuvajArtikal(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "artikal.dodaj") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, "Greška pri čitanju forme", http.StatusBadRequest)
return
@@ -136,6 +142,11 @@ func (h *Handler) IzmeniArtikal(w http.ResponseWriter, r *http.Request) {
// SacuvajIzmenuArtikla prima POST formu i čuva izmenu artikla
func (h *Handler) SacuvajIzmenuArtikla(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "artikal.izmeni") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
idStr := chi.URLParam(r, "id")
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
+11
View File
@@ -9,6 +9,7 @@ import (
"ntech/internal/db"
"ntech/internal/db/sqlite"
"ntech/internal/middleware"
"ntech/internal/model"
"github.com/go-chi/chi/v5"
@@ -121,6 +122,11 @@ func (h *Handler) NovaNabavka(w http.ResponseWriter, r *http.Request) {
// SacuvajNabavku prima POST formu, parsira stavke i upisuje nabavku u bazu
func (h *Handler) SacuvajNabavku(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "nabavka.dodaj") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, "Greška pri čitanju forme", http.StatusBadRequest)
return
@@ -205,6 +211,11 @@ func (h *Handler) DetaljiNabavke(w http.ResponseWriter, r *http.Request) {
// ObrisiNabavku prima POST zahtev i briše nabavku po ID-u
func (h *Handler) ObrisiNabavku(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "nabavka.obrisi") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
id, err := parseID(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Neispravan ID nabavke", http.StatusBadRequest)
+26
View File
@@ -13,6 +13,7 @@ import (
"time"
ntechsqlite "ntech/internal/db/sqlite"
"ntech/internal/middleware"
"ntech/internal/model"
"github.com/go-chi/chi/v5"
@@ -48,6 +49,11 @@ var validnoImeBackupa = regexp.MustCompile(`^ntech_\d{8}_\d{6}\.db$`)
// Podesavanja renderuje stranicu podešavanja
func (h *Handler) Podesavanja(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if k == nil || !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "podesavanja.pregled") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
podesavanja, err := ntechsqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
if err != nil {
http.Error(w, "Greška pri učitavanju podešavanja", http.StatusInternalServerError)
@@ -107,6 +113,11 @@ func ucitajListuBackupa() []BackupInfo {
// VratiBackup zamenjuje trenutnu bazu sa izabranim backup fajlom
func (h *Handler) VratiBackup(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if k == nil || !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "backup.pokreni") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
if err := r.ParseForm(); err != nil {
http.Redirect(w, r, "/podesavanja?backup_greska=Greška+pri+čitanju+zahteva", http.StatusSeeOther)
return
@@ -187,6 +198,11 @@ func kopiraFajl(izvor, odrediste string) error {
// SacuvajPodesavanja prima POST i čuva podešavanja u bazu
func (h *Handler) SacuvajPodesavanja(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if k == nil || !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "podesavanja.izmeni") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, "Greška pri čitanju forme", http.StatusBadRequest)
return
@@ -217,6 +233,11 @@ func (h *Handler) SacuvajPodesavanja(w http.ResponseWriter, r *http.Request) {
// BackupBaze kreira konzistentnu kopiju baze i šalje je kao attachment
func (h *Handler) BackupBaze(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if k == nil || !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "backup.pregled") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
privremeni := fmt.Sprintf("%s/ntech_backup_%s.db", os.TempDir(), time.Now().Format("20060102_150405"))
if _, err := h.DB.ExecContext(r.Context(), "VACUUM INTO ?", privremeni); err != nil {
@@ -233,6 +254,11 @@ func (h *Handler) BackupBaze(w http.ResponseWriter, r *http.Request) {
// OtpremiLogo prima multipart upload slike loga i čuva je u web/static/uploads/
func (h *Handler) OtpremiLogo(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if k == nil || !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "podesavanja.izmeni") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
// ograničavamo telo zahteva na 2MB + malo za zaglavlja forme
r.Body = http.MaxBytesReader(w, r.Body, 2<<20+4096)
if err := r.ParseMultipartForm(2 << 20); err != nil {
+11
View File
@@ -12,6 +12,7 @@ import (
appdb "ntech/internal/db"
"ntech/internal/db/sqlite"
"ntech/internal/middleware"
"ntech/internal/model"
"github.com/go-chi/chi/v5"
@@ -135,6 +136,11 @@ func (h *Handler) NovaProdaja(w http.ResponseWriter, r *http.Request) {
// SacuvajProdaju prima POST formu, parsira stavke i upisuje prodajni nalog u bazu
func (h *Handler) SacuvajProdaju(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "prodaja.dodaj") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, "Greška pri čitanju forme", http.StatusBadRequest)
return
@@ -300,6 +306,11 @@ func (h *Handler) StampaProdaje(w http.ResponseWriter, r *http.Request) {
// ObrisiProdaju prima POST zahtev, vraća stanje na magacin i briše nalog
func (h *Handler) ObrisiProdaju(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "prodaja.obrisi") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
id, err := parseID(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Neispravan ID naloga", http.StatusBadRequest)
+16
View File
@@ -8,6 +8,7 @@ import (
"time"
"ntech/internal/db/sqlite"
"ntech/internal/middleware"
"ntech/internal/model"
"github.com/go-chi/chi/v5"
@@ -109,6 +110,11 @@ func (h *Handler) NoviNalog(w http.ResponseWriter, r *http.Request) {
// SacuvajNalog prima POST formu i upisuje novi servisni nalog u bazu
func (h *Handler) SacuvajNalog(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "servis.dodaj") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, "Greška pri čitanju forme", http.StatusBadRequest)
return
@@ -194,6 +200,11 @@ func (h *Handler) IzmeniNalog(w http.ResponseWriter, r *http.Request) {
// SacuvajIzmenaNaloga prima POST formu i ažurira postojeći servisni nalog
func (h *Handler) SacuvajIzmenaNaloga(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "servis.izmeni") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
id, err := parseID(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Neispravan ID naloga", http.StatusBadRequest)
@@ -248,6 +259,11 @@ func (h *Handler) SacuvajIzmenaNaloga(w http.ResponseWriter, r *http.Request) {
// ObrisiNalog prima POST zahtev i briše servisni nalog po ID-u
func (h *Handler) ObrisiNalog(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if !h.DozvoleRepo.ImaDozvolu(r.Context(), k.Uloga, "servis.obrisi") {
http.Error(w, "Nemate dozvolu za ovu akciju.", http.StatusForbidden)
return
}
id, err := parseID(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Neispravan ID naloga", http.StatusBadRequest)