Bezbednost: ispravke kontrole pristupa u admin i handler sloju
This commit is contained in:
@@ -118,6 +118,14 @@ type LoginIstorijsaRepository interface {
|
||||
ListaZaKorisnika(ctx context.Context, korisnikID int64, limit int) ([]*model.LoginPokusaj, error)
|
||||
}
|
||||
|
||||
// DozvoleRepository definiše operacije nad dozvolama po ulogama
|
||||
type DozvoleRepository interface {
|
||||
ImaDozvolu(ctx context.Context, uloga, akcija string) bool
|
||||
SveDozvole(ctx context.Context, uloga string) map[string]bool
|
||||
Sacuvaj(ctx context.Context, uloga, akcija string, dozvoljeno bool) error
|
||||
Reset(ctx context.Context) error
|
||||
}
|
||||
|
||||
// PodsetnikRepository definiše operacije nad podsetnicima
|
||||
type PodsetnikRepository interface {
|
||||
Lista(ctx context.Context, filter PodsetnikFilter) ([]model.Podsetnik, error)
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type sqliteDozvoleRepo struct {
|
||||
db *sql.DB
|
||||
defaultFn func(uloga, akcija string) bool // podrazumevane vrednosti iz middleware/dozvole.go
|
||||
sveAkcije []string
|
||||
}
|
||||
|
||||
// NoviDozvoleRepo kreira SQLite implementaciju DozvoleRepository
|
||||
func NoviDozvoleRepo(db *sql.DB, defaultFn func(uloga, akcija string) bool, sveAkcije []string) *sqliteDozvoleRepo {
|
||||
return &sqliteDozvoleRepo{db: db, defaultFn: defaultFn, sveAkcije: sveAkcije}
|
||||
}
|
||||
|
||||
// InicijalizujDozvole popunjava tabelu podrazumevanim vrednostima ako je prazna
|
||||
func InicijalizujDozvole(ctx context.Context, db *sql.DB, defaultFn func(uloga, akcija string) bool, sveAkcije []string) error {
|
||||
var br int
|
||||
_ = db.QueryRowContext(ctx, `SELECT COUNT(*) FROM dozvole`).Scan(&br)
|
||||
if br > 0 {
|
||||
return nil
|
||||
}
|
||||
return popuniPodrazumevano(ctx, db, defaultFn, sveAkcije)
|
||||
}
|
||||
|
||||
// popuniPodrazumevano upisuje sve podrazumevane dozvole u bazu
|
||||
func popuniPodrazumevano(ctx context.Context, db *sql.DB, defaultFn func(uloga, akcija string) bool, sveAkcije []string) error {
|
||||
for _, uloga := range []string{"radnik", "admin", "superadmin"} {
|
||||
for _, akcija := range sveAkcije {
|
||||
dozvoljeno := 0
|
||||
if defaultFn(uloga, akcija) {
|
||||
dozvoljeno = 1
|
||||
}
|
||||
if _, err := db.ExecContext(ctx,
|
||||
`INSERT OR IGNORE INTO dozvole (uloga, akcija, dozvoljeno) VALUES (?, ?, ?)`,
|
||||
uloga, akcija, dozvoljeno); err != nil {
|
||||
return fmt.Errorf("ntech: dozvole.Inicijalizuj: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *sqliteDozvoleRepo) ImaDozvolu(ctx context.Context, uloga, akcija string) bool {
|
||||
var dozvoljeno int
|
||||
err := r.db.QueryRowContext(ctx,
|
||||
`SELECT dozvoljeno FROM dozvole WHERE uloga = ? AND akcija = ?`, uloga, akcija).Scan(&dozvoljeno)
|
||||
if err != nil {
|
||||
// fallback na podrazumevano ako red nije pronađen
|
||||
return r.defaultFn(uloga, akcija)
|
||||
}
|
||||
return dozvoljeno == 1
|
||||
}
|
||||
|
||||
func (r *sqliteDozvoleRepo) SveDozvole(ctx context.Context, uloga string) map[string]bool {
|
||||
rows, err := r.db.QueryContext(ctx,
|
||||
`SELECT akcija, dozvoljeno FROM dozvole WHERE uloga = ?`, uloga)
|
||||
if err != nil {
|
||||
// fallback na podrazumevano
|
||||
m := make(map[string]bool, len(r.sveAkcije))
|
||||
for _, a := range r.sveAkcije {
|
||||
m[a] = r.defaultFn(uloga, a)
|
||||
}
|
||||
return m
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
m := make(map[string]bool, len(r.sveAkcije))
|
||||
for rows.Next() {
|
||||
var akcija string
|
||||
var dozvoljeno int
|
||||
if err := rows.Scan(&akcija, &dozvoljeno); err == nil {
|
||||
m[akcija] = dozvoljeno == 1
|
||||
}
|
||||
}
|
||||
// popuni eventualno nedostajuće akcije podrazumevanim vrednostima
|
||||
for _, a := range r.sveAkcije {
|
||||
if _, ok := m[a]; !ok {
|
||||
m[a] = r.defaultFn(uloga, a)
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (r *sqliteDozvoleRepo) Sacuvaj(ctx context.Context, uloga, akcija string, dozvoljeno bool) error {
|
||||
d := 0
|
||||
if dozvoljeno {
|
||||
d = 1
|
||||
}
|
||||
_, err := r.db.ExecContext(ctx,
|
||||
`INSERT INTO dozvole (uloga, akcija, dozvoljeno) VALUES (?, ?, ?)
|
||||
ON CONFLICT (uloga, akcija) DO UPDATE SET dozvoljeno = excluded.dozvoljeno`,
|
||||
uloga, akcija, d)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ntech: dozvole.Sacuvaj: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *sqliteDozvoleRepo) Reset(ctx context.Context) error {
|
||||
if _, err := r.db.ExecContext(ctx, `DELETE FROM dozvole`); err != nil {
|
||||
return fmt.Errorf("ntech: dozvole.Reset: %w", err)
|
||||
}
|
||||
return popuniPodrazumevano(ctx, r.db, r.defaultFn, r.sveAkcije)
|
||||
}
|
||||
+156
-5
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -88,12 +88,37 @@ func RequireSuperAdmin(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
k := KorisnikIzKonteksta(r.Context())
|
||||
if k == nil || k.Uloga != "superadmin" {
|
||||
http.Error(w, "Pristup odbijen", http.StatusForbidden)
|
||||
postaviFlashGresku(w, "Nemate dozvolu za ovu stranicu.")
|
||||
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// RequireAdmin je middleware koji propušta admin i superadmin korisnike
|
||||
func RequireAdmin(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
k := KorisnikIzKonteksta(r.Context())
|
||||
if k == nil || (k.Uloga != "admin" && k.Uloga != "superadmin") {
|
||||
postaviFlashGresku(w, "Nemate dozvolu za ovu stranicu.")
|
||||
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// postaviFlashGresku upisuje jednokratnu poruku o grešci u kolačić
|
||||
func postaviFlashGresku(w http.ResponseWriter, poruka string) {
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "ntech_flash_greska",
|
||||
Value: poruka,
|
||||
Path: "/",
|
||||
MaxAge: 60,
|
||||
HttpOnly: true,
|
||||
})
|
||||
}
|
||||
|
||||
// ErrNijePrijavljen se vraća kada korisnik nije u contextu
|
||||
var ErrNijePrijavljen = errors.New("korisnik nije prijavljen")
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
package middleware
|
||||
|
||||
// sve poznate akcije u sistemu
|
||||
var sveAkcije = []string{
|
||||
"artikal.pregled",
|
||||
"artikal.dodaj",
|
||||
"artikal.izmeni",
|
||||
"artikal.obrisi",
|
||||
"artikal.premesti",
|
||||
"kategorija.pregled",
|
||||
"kategorija.dodaj",
|
||||
"kategorija.izmeni",
|
||||
"kategorija.obrisi",
|
||||
"nabavka.pregled",
|
||||
"nabavka.dodaj",
|
||||
"nabavka.obrisi",
|
||||
"dobavljac.pregled",
|
||||
"dobavljac.dodaj",
|
||||
"dobavljac.izmeni",
|
||||
"dobavljac.obrisi",
|
||||
"servis.pregled",
|
||||
"servis.dodaj",
|
||||
"servis.izmeni",
|
||||
"servis.obrisi",
|
||||
"prodaja.pregled",
|
||||
"prodaja.dodaj",
|
||||
"prodaja.obrisi",
|
||||
"klijent.pregled",
|
||||
"klijent.dodaj",
|
||||
"klijent.izmeni",
|
||||
"klijent.obrisi",
|
||||
"podsetnik.pregled",
|
||||
"podsetnik.dodaj",
|
||||
"podsetnik.izmeni",
|
||||
"podsetnik.obrisi",
|
||||
"izvestaj.pregled",
|
||||
"podesavanja.pregled",
|
||||
"podesavanja.izmeni",
|
||||
"korisnik.pregled",
|
||||
"korisnik.dodaj",
|
||||
"korisnik.izmeni",
|
||||
"korisnik.obrisi",
|
||||
"korisnik.uloga",
|
||||
"backup.pregled",
|
||||
"backup.pokreni",
|
||||
}
|
||||
|
||||
// SveAkcije vraća listu svih poznatih akcija — koristi se pri inicijalizaciji baze i resetu
|
||||
func SveAkcije() []string {
|
||||
return sveAkcije
|
||||
}
|
||||
|
||||
// ImaDozvolu proverava da li data uloga sme da izvrši traženu akciju
|
||||
func ImaDozvolu(uloga, akcija string) bool {
|
||||
switch uloga {
|
||||
case "superadmin":
|
||||
// superadmin sme sve
|
||||
return true
|
||||
|
||||
case "admin":
|
||||
switch akcija {
|
||||
// artikal
|
||||
case "artikal.pregled", "artikal.dodaj", "artikal.izmeni",
|
||||
"artikal.obrisi", "artikal.premesti":
|
||||
return true
|
||||
// kategorija
|
||||
case "kategorija.pregled", "kategorija.dodaj",
|
||||
"kategorija.izmeni", "kategorija.obrisi":
|
||||
return true
|
||||
// nabavka
|
||||
case "nabavka.pregled", "nabavka.dodaj", "nabavka.obrisi":
|
||||
return true
|
||||
// dobavljač
|
||||
case "dobavljac.pregled", "dobavljac.dodaj",
|
||||
"dobavljac.izmeni", "dobavljac.obrisi":
|
||||
return true
|
||||
// servis
|
||||
case "servis.pregled", "servis.dodaj",
|
||||
"servis.izmeni", "servis.obrisi":
|
||||
return true
|
||||
// prodaja
|
||||
case "prodaja.pregled", "prodaja.dodaj", "prodaja.obrisi":
|
||||
return true
|
||||
// klijent
|
||||
case "klijent.pregled", "klijent.dodaj",
|
||||
"klijent.izmeni", "klijent.obrisi":
|
||||
return true
|
||||
// podsetnik
|
||||
case "podsetnik.pregled", "podsetnik.dodaj",
|
||||
"podsetnik.izmeni", "podsetnik.obrisi":
|
||||
return true
|
||||
// izveštaji i podešavanja
|
||||
case "izvestaj.pregled",
|
||||
"podesavanja.pregled", "podesavanja.izmeni":
|
||||
return true
|
||||
// korisnici (bez promene uloge)
|
||||
case "korisnik.pregled", "korisnik.dodaj",
|
||||
"korisnik.izmeni", "korisnik.obrisi":
|
||||
return true
|
||||
// backup
|
||||
case "backup.pregled", "backup.pokreni":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
case "radnik":
|
||||
switch akcija {
|
||||
// artikal — bez brisanja i premeštanja
|
||||
case "artikal.pregled", "artikal.dodaj", "artikal.izmeni":
|
||||
return true
|
||||
// kategorija — samo pregled
|
||||
case "kategorija.pregled":
|
||||
return true
|
||||
// nabavka — bez brisanja
|
||||
case "nabavka.pregled", "nabavka.dodaj":
|
||||
return true
|
||||
// dobavljač — bez brisanja
|
||||
case "dobavljac.pregled", "dobavljac.dodaj", "dobavljac.izmeni":
|
||||
return true
|
||||
// servis — bez brisanja
|
||||
case "servis.pregled", "servis.dodaj", "servis.izmeni":
|
||||
return true
|
||||
// prodaja — bez brisanja
|
||||
case "prodaja.pregled", "prodaja.dodaj":
|
||||
return true
|
||||
// klijent — bez brisanja
|
||||
case "klijent.pregled", "klijent.dodaj", "klijent.izmeni":
|
||||
return true
|
||||
// podsetnik — sve
|
||||
case "podsetnik.pregled", "podsetnik.dodaj",
|
||||
"podsetnik.izmeni", "podsetnik.obrisi":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SveDozvole vraća mapu svih akcija sa vrednošću true/false za datu ulogu
|
||||
func SveDozvole(uloga string) map[string]bool {
|
||||
m := make(map[string]bool, len(sveAkcije))
|
||||
for _, akcija := range sveAkcije {
|
||||
m[akcija] = ImaDozvolu(uloga, akcija)
|
||||
}
|
||||
return m
|
||||
}
|
||||
@@ -34,8 +34,9 @@ type PodaciStranice struct {
|
||||
LogoPutanja string // putanja do slike, koristi se samo kada je LogoTip "slika"
|
||||
Korisnik string
|
||||
KorisnikIme string // korisničko ime prijavljenog korisnika
|
||||
KorisnikUloga string // uloga: "superadmin", "admin", "radnik"
|
||||
CsrfToken string // CSRF zaštitni token za forme
|
||||
KorisnikUloga string // uloga: "superadmin", "admin", "radnik"
|
||||
CsrfToken string // CSRF zaštitni token za forme
|
||||
Dozvole map[string]bool // mapa akcija → dozvoljeno/nije
|
||||
}
|
||||
|
||||
// PodaciDashboarda su podaci specifični za dashboard stranicu
|
||||
@@ -49,4 +50,5 @@ type PodaciDashboarda struct {
|
||||
PoslednjiServisi []StavkaServisa
|
||||
KriticneZalihe []StavkaZalihe
|
||||
PoslednjeProdaje []StavkaProdajePregled
|
||||
FlashGreska string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user