Files
Dasko 4047f035da Admin: ispravke AJAX toast-a za sve akcije korisnika i dozvola
- base.html: toast pri učitavanju stranice ako URL sadrži ?sacuvano=1 (pokriven i cross-page redirect)
- admin.go: svih 9 SetFlash(uspeh) zamenjeno sa redirect ?sacuvano=1 (korisnici, profil, dozvole)
2026-06-20 13:15:54 +02:00

724 lines
24 KiB
Go

package handler
import (
"html/template"
"net/http"
"ntech/internal/auth"
"ntech/internal/db/sqlite"
"ntech/internal/middleware"
"ntech/internal/model"
"github.com/go-chi/chi/v5"
)
type podaciAdminKorisnici struct {
model.PodaciStranice
Korisnici []model.Korisnik
}
type podaciLoginIstorija struct {
model.PodaciStranice
PrikazKorisnik model.Korisnik
Istorija []*model.LoginPokusaj
}
type podaciAdminProfil struct {
model.PodaciStranice
// Greska se koristi samo za inline prikaz greške pri TOTP aktivaciji (bez redirecta)
Greska string
TotpURI string
TotpTajna string
TotpQR template.URL
TotpAktivan bool
RezervniKodovi []string // jednokratni prikaz novih kodova (posle aktivacije/regeneracije)
BrojRezervnih int // koliko neiskorišćenih rezervnih kodova je preostalo
LokalnaTema string
KoristiLokalnuTemu bool
JelDemo bool
}
type podaciProfilTema struct {
model.PodaciStranice
LokalnaTema string
KoristiLokalnuTemu bool
LokalnaPozadina string
LokalnaPozadinaOpacity string
LokalnaPozadinaBlur string
LokalnaPozadinaBlurPozadine string
LokalnaPozadinaGlassOpacity string
LokalnaAnimacija string
LokalniHover string
}
// AdminKorisnici prikazuje listu korisnika
func (h *Handler) AdminKorisnici(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
}
// 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"
h.renderujTemplate(w, "admin_korisnici", podaciAdminKorisnici{
PodaciStranice: ps,
Korisnici: lista,
})
}
// AdminSacuvajKorisnika kreira novog korisnika
func (h *Handler) AdminSacuvajKorisnika(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 {
middleware.SetFlash(w, r, h.DB, "greska", "Proverite unete podatke.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
ime := r.FormValue("korisnicko_ime")
lozinka := r.FormValue("lozinka")
uloga := r.FormValue("uloga")
// superadmin uloga se ne može kreirati kroz interfejs — jedini superadmin postoji od setup-a
validneUloge := map[string]bool{"admin": true, "radnik": true}
if len(ime) < 3 || len(lozinka) < 8 || !validneUloge[uloga] {
middleware.SetFlash(w, r, h.DB, "greska", "Proverite unete podatke.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
hash, err := auth.HashujLozinku(lozinka)
if err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
if _, err := h.KorisniciRepo.Kreiraj(r.Context(), ime, hash, uloga); err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
http.Redirect(w, r, "/admin/korisnici?sacuvano=1", http.StatusSeeOther)
}
// AdminToggleAktivan menja aktivan status korisnika
func (h *Handler) AdminToggleAktivan(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 {
middleware.SetFlash(w, r, h.DB, "greska", "Proverite unete podatke.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
// ne sme deaktivirati sam sebe
if id == k.ID {
middleware.SetFlash(w, r, h.DB, "greska", "Ne možete promeniti status sopstvenog naloga.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
korisnik, err := h.KorisniciRepo.DohvatiPoID(r.Context(), id)
if err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Proverite unete podatke.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
// admin ne sme da menja status drugog admina ni superadmina
if korisnik.Uloga == "superadmin" || (korisnik.Uloga == "admin" && k.Uloga != "superadmin") {
middleware.SetFlash(w, r, h.DB, "greska", "Ova radnja nije dozvoljena.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
if err := h.KorisniciRepo.AzurirajAktivan(r.Context(), id, !korisnik.Aktivan); err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
http.Redirect(w, r, "/admin/korisnici?sacuvano=1", http.StatusSeeOther)
}
// AdminPromeniUlogu menja ulogu korisnika
func (h *Handler) AdminPromeniUlogu(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 {
middleware.SetFlash(w, r, h.DB, "greska", "Proverite unete podatke.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
if err := r.ParseForm(); err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Proverite unete podatke.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
// superadmin ne može menjati svoju vlastitu ulogu
if id == k.ID {
middleware.SetFlash(w, r, h.DB, "greska", "Ova radnja nije dozvoljena.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
uloga := r.FormValue("uloga")
// dozvoljena je samo promena između admin i radnik; superadmin uloga se ne može dodeliti ni ukloniti
validneUloge := map[string]bool{"admin": true, "radnik": true}
if !validneUloge[uloga] {
middleware.SetFlash(w, r, h.DB, "greska", "Proverite unete podatke.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
// dohvati korisnika da proverimo njegovu trenutnu ulogu
ciljniKorisnik, err := h.KorisniciRepo.DohvatiPoID(r.Context(), id)
if err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
// superadmin uloga se ne može menjati
if ciljniKorisnik.Uloga == "superadmin" {
middleware.SetFlash(w, r, h.DB, "greska", "Ova radnja nije dozvoljena.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
if err := h.KorisniciRepo.AzurirajUlogu(r.Context(), id, uloga); err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
http.Redirect(w, r, "/admin/korisnici?sacuvano=1", http.StatusSeeOther)
}
// AdminObrisiKorisnika briše korisnika sa ulogom radnik
func (h *Handler) AdminObrisiKorisnika(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 {
middleware.SetFlash(w, r, h.DB, "greska", "Proverite unete podatke.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
if id == k.ID {
middleware.SetFlash(w, r, h.DB, "greska", "Ova radnja nije dozvoljena.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
ciljni, err := h.KorisniciRepo.DohvatiPoID(r.Context(), id)
if err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
// dozvoljeno je brisanje samo radnika
if ciljni.Uloga != "radnik" {
middleware.SetFlash(w, r, h.DB, "greska", "Ova radnja nije dozvoljena.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
if err := h.KorisniciRepo.Obrisi(r.Context(), id); err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/korisnici", http.StatusSeeOther)
return
}
http.Redirect(w, r, "/admin/korisnici?sacuvano=1", http.StatusSeeOther)
}
// AdminProfil prikazuje stranicu profila
func (h *Handler) AdminProfil(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if k == nil {
http.Redirect(w, r, "/prijava", http.StatusSeeOther)
return
}
// osvežavamo korisnika iz baze da bismo imali aktuelni totp_tajna
svezi, err := h.KorisniciRepo.DohvatiPoID(r.Context(), k.ID)
if err != nil {
http.Error(w, "Greška pri učitavanju profila", http.StatusInternalServerError)
return
}
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "profil"
ps.NaslovStranice = "Moj profil"
var brojRezervnih int
if svezi.TotpTajna != "" {
brojRezervnih, _ = h.RezervniKodoviRepo.BrojPreostalih(r.Context(), svezi.ID)
}
h.renderujTemplate(w, "admin_profil", podaciAdminProfil{
PodaciStranice: ps,
TotpAktivan: svezi.TotpTajna != "",
BrojRezervnih: brojRezervnih,
LokalnaTema: svezi.LokalnaTema,
KoristiLokalnuTemu: svezi.KoristiLokalnuTemu,
JelDemo: h.JelDemo,
})
}
// generisiIPrikaziKodove generiše nove rezervne kodove, čuva njihove bcrypt hešove
// (poništavajući stare) i renderuje profil sa kodovima prikazanim JEDNOM. Čist
// tekst kodova se nigde ne čuva — korisnik mora da ih sačuva sada.
func (h *Handler) generisiIPrikaziKodove(w http.ResponseWriter, r *http.Request, k *model.Korisnik, poruka string) {
kodovi, err := auth.GenerisiRezervneKodove(auth.BrojRezervnihKodova)
if err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri generisanju rezervnih kodova.")
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
return
}
hashevi := make([]string, 0, len(kodovi))
for _, kod := range kodovi {
hash, err := auth.HashujLozinku(kod)
if err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju rezervnih kodova.")
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
return
}
hashevi = append(hashevi, hash)
}
if err := h.RezervniKodoviRepo.Zameni(r.Context(), k.ID, hashevi); err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju rezervnih kodova.")
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
return
}
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "profil"
ps.NaslovStranice = "Moj profil"
ps.Flash = &model.FlashPoruka{Tip: "uspeh", Poruka: poruka}
h.renderujTemplate(w, "admin_profil", podaciAdminProfil{
PodaciStranice: ps,
TotpAktivan: true,
RezervniKodovi: kodovi,
BrojRezervnih: len(kodovi),
JelDemo: h.JelDemo,
})
}
// AdminPromeniLozinku menja lozinku prijavljenog korisnika
func (h *Handler) AdminPromeniLozinku(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if k == nil {
http.Redirect(w, r, "/prijava", http.StatusSeeOther)
return
}
if h.JelDemo {
middleware.SetFlash(w, r, h.DB, "greska", "Promena lozinke nije dozvoljena u demo modu.")
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
return
}
if err := r.ParseForm(); err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
return
}
stara := r.FormValue("stara_lozinka")
nova := r.FormValue("nova_lozinka")
potvrda := r.FormValue("nova_lozinka_potvrda")
if !auth.ProveriLozinku(k.LozinkaHash, stara) {
middleware.SetFlash(w, r, h.DB, "greska", "Stara lozinka nije ispravna.")
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
return
}
if len(nova) < 8 || nova != potvrda {
middleware.SetFlash(w, r, h.DB, "greska", "Nova lozinka mora imati najmanje 8 karaktera i lozinke moraju biti iste.")
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
return
}
hash, err := auth.HashujLozinku(nova)
if err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
return
}
if err := h.KorisniciRepo.PromeniLozinku(r.Context(), k.ID, hash); err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
return
}
http.Redirect(w, r, "/admin/profil?sacuvano=1", http.StatusSeeOther)
}
// AdminTotpPokreni generiše TOTP tajnu i prikazuje QR kod
func (h *Handler) AdminTotpPokreni(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if k == nil {
http.Redirect(w, r, "/prijava", http.StatusSeeOther)
return
}
if h.JelDemo {
middleware.SetFlash(w, r, h.DB, "greska", "Podešavanje 2FA nije dostupno u demo modu.")
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
return
}
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
totp, err := auth.GenerisuTotpTajnu(k.KorisnickoIme, podesavanja["naziv_firme"])
if err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri generisanju 2FA. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
return
}
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "profil"
ps.NaslovStranice = "Podesi 2FA"
h.renderujTemplate(w, "admin_profil", podaciAdminProfil{
PodaciStranice: ps,
TotpURI: totp.URI,
TotpTajna: totp.Tajna,
TotpQR: template.URL("data:image/png;base64," + totp.QRBase64),
JelDemo: h.JelDemo,
})
}
// AdminTotpAktivacija verifikuje TOTP kod i čuva tajnu
func (h *Handler) AdminTotpAktivacija(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if k == nil {
http.Redirect(w, r, "/prijava", http.StatusSeeOther)
return
}
if err := r.ParseForm(); err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
return
}
tajna := r.FormValue("totp_tajna")
kod := r.FormValue("kod")
if !auth.VerifikujTotpKod(kod, tajna) {
// ponovni prikaz sa greškom — regenerišemo isti QR, ne radimo redirect
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "profil"
ps.NaslovStranice = "Podesi 2FA"
nazivFirme := podesavanja["naziv_firme"]
if nazivFirme == "" {
nazivFirme = "NTech"
}
uri, qr := auth.RegenerisiTotpQR(tajna, k.KorisnickoIme, nazivFirme)
h.renderujTemplate(w, "admin_profil", podaciAdminProfil{
PodaciStranice: ps,
TotpURI: uri,
TotpTajna: tajna,
TotpQR: template.URL("data:image/png;base64," + qr),
Greska: "totp",
JelDemo: h.JelDemo,
})
return
}
if err := h.KorisniciRepo.SacuvajTotpTajnu(r.Context(), k.ID, tajna); err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
return
}
// uključenjem 2FA generišemo i rezervne kodove i prikazujemo ih jednom
h.generisiIPrikaziKodove(w, r, k,
"Dvostepena verifikacija je uključena. Sačuvajte rezervne kodove — prikazuju se samo sada.")
}
// AdminTotpRegenerisiKodove generiše nove rezervne kodove i poništava stare
func (h *Handler) AdminTotpRegenerisiKodove(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if k == nil {
http.Redirect(w, r, "/prijava", http.StatusSeeOther)
return
}
svezi, err := h.KorisniciRepo.DohvatiPoID(r.Context(), k.ID)
if err != nil || svezi.TotpTajna == "" {
middleware.SetFlash(w, r, h.DB, "greska", "Dvostepena verifikacija nije uključena.")
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
return
}
h.generisiIPrikaziKodove(w, r, k, "Generisani su novi rezervni kodovi. Stari više ne važe.")
}
// AdminTotpDeaktivacija uklanja TOTP tajnu
func (h *Handler) AdminTotpDeaktivacija(w http.ResponseWriter, r *http.Request) {
k := middleware.KorisnikIzKonteksta(r.Context())
if k == nil {
http.Redirect(w, r, "/prijava", http.StatusSeeOther)
return
}
if err := h.KorisniciRepo.SacuvajTotpTajnu(r.Context(), k.ID, ""); err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
return
}
// isključenjem 2FA brišemo i rezervne kodove
_ = h.RezervniKodoviRepo.Obrisi(r.Context(), k.ID)
http.Redirect(w, r, "/admin/profil?sacuvano=1", 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
}
// 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)
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,
})
}
type podaciAdminDozvole struct {
model.PodaciStranice
Korisnici []model.Korisnik
TrenutniID int64
DozvoleRadnik map[string]bool
DozvoleAdmin map[string]bool
DozvoleSuperadmin map[string]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"
dozvoleAdmin := map[string]bool{}
dozvoleSuperadmin := map[string]bool{}
if k.Uloga == "superadmin" {
dozvoleAdmin = h.DozvoleRepo.SveDozvole(r.Context(), "admin")
dozvoleSuperadmin = h.DozvoleRepo.SveDozvole(r.Context(), "superadmin")
}
h.renderujTemplate(w, "admin_dozvole", podaciAdminDozvole{
PodaciStranice: ps,
Korisnici: lista,
TrenutniID: k.ID,
DozvoleRadnik: h.DozvoleRepo.SveDozvole(r.Context(), "radnik"),
DozvoleAdmin: dozvoleAdmin,
DozvoleSuperadmin: dozvoleSuperadmin,
})
}
// 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 {
middleware.SetFlash(w, r, h.DB, "greska", "Proverite unete podatke.")
http.Redirect(w, r, "/admin/dozvole", http.StatusSeeOther)
return
}
if err := r.ParseForm(); err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Proverite unete podatke.")
http.Redirect(w, r, "/admin/dozvole", http.StatusSeeOther)
return
}
// superadmin ne može menjati svoju vlastitu ulogu
if id == k.ID {
middleware.SetFlash(w, r, h.DB, "greska", "Ova radnja nije dozvoljena.")
http.Redirect(w, r, "/admin/dozvole", 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] {
middleware.SetFlash(w, r, h.DB, "greska", "Proverite unete podatke.")
http.Redirect(w, r, "/admin/dozvole", http.StatusSeeOther)
return
}
ciljni, err := h.KorisniciRepo.DohvatiPoID(r.Context(), id)
if err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/dozvole", http.StatusSeeOther)
return
}
// superadmin uloga se ne može menjati
if ciljni.Uloga == "superadmin" {
middleware.SetFlash(w, r, h.DB, "greska", "Ova radnja nije dozvoljena.")
http.Redirect(w, r, "/admin/dozvole", http.StatusSeeOther)
return
}
if err := h.KorisniciRepo.AzurirajUlogu(r.Context(), id, uloga); err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/dozvole", 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 {
middleware.SetFlash(w, r, h.DB, "greska", "Proverite unete podatke.")
http.Redirect(w, r, "/admin/dozvole", http.StatusSeeOther)
return
}
// čuvamo dozvole: superadmin menja radnik i admin, admin menja samo radnik
uloge := []string{"radnik", "admin"}
if k.Uloga != "superadmin" {
uloge = []string{"radnik"}
}
for _, uloga := range uloge {
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 {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/dozvole", 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 {
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju. Pokušajte ponovo.")
http.Redirect(w, r, "/admin/dozvole", http.StatusSeeOther)
return
}
http.Redirect(w, r, "/admin/dozvole?sacuvano=1", http.StatusSeeOther)
}