Files
bssapp/svc/routes/role_department_permissions.go
MEHMETKECECI 03d6c61587 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	ui/src/pages/OrderList.vue
2026-02-13 15:17:23 +03:00

569 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package routes
import (
"bssapp-backend/auth"
"bssapp-backend/internal/auditlog"
"bssapp-backend/permissions"
"bssapp-backend/queries"
"database/sql"
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"strings"
"github.com/gorilla/mux"
)
type IdTitleOption struct {
ID string `json:"id"`
Title string `json:"title"`
}
type Row struct {
Route string `json:"route"`
CanAccess bool `json:"can_access"`
}
type RoleDeptPermissionSummary struct {
RoleID int `json:"role_id"`
RoleTitle string `json:"role_title"`
DepartmentCode string `json:"department_code"`
DepartmentTitle string `json:"department_title"`
ModuleFlags map[string]bool `json:"module_flags"`
}
type ModuleLookupOption struct {
Value string `json:"value"`
Label string `json:"label"`
}
type ModuleActionLookupOption struct {
ModuleCode string `json:"module_code"`
Action string `json:"action"`
}
type RoleDeptPermissionListResponse struct {
Modules []ModuleLookupOption `json:"modules"`
ModuleActions []ModuleActionLookupOption `json:"module_actions"`
Rows []RoleDeptPermissionSummary `json:"rows"`
}
type RoleDepartmentPermissionHandler struct {
DB *sql.DB
Repo *permissions.RoleDepartmentPermissionRepo
}
func NewRoleDepartmentPermissionHandler(db *sql.DB) *RoleDepartmentPermissionHandler {
return &RoleDepartmentPermissionHandler{
DB: db, // ✅ EKLENDİ
Repo: permissions.NewRoleDepartmentPermissionRepo(db),
}
}
/* ======================================================
LIST
====================================================== */
func (h *RoleDepartmentPermissionHandler) List(w http.ResponseWriter, r *http.Request) {
claims, ok := auth.GetClaimsFromContext(r.Context())
if !ok || claims == nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
search := strings.TrimSpace(r.URL.Query().Get("search"))
modRows, err := h.DB.Query(queries.GetModuleLookup)
if err != nil {
http.Error(w, "module lookup error", http.StatusInternalServerError)
return
}
defer modRows.Close()
modules := make([]ModuleLookupOption, 0, 32)
for modRows.Next() {
var m ModuleLookupOption
if err := modRows.Scan(&m.Value, &m.Label); err != nil {
http.Error(w, "module lookup scan error", http.StatusInternalServerError)
return
}
modules = append(modules, m)
}
if err := modRows.Err(); err != nil {
http.Error(w, "module lookup rows error", http.StatusInternalServerError)
return
}
actionRows, err := h.DB.Query(queries.GetModuleActionLookup)
if err != nil {
http.Error(w, "module action lookup error", http.StatusInternalServerError)
return
}
defer actionRows.Close()
moduleActions := make([]ModuleActionLookupOption, 0, 128)
for actionRows.Next() {
var a ModuleActionLookupOption
if err := actionRows.Scan(&a.ModuleCode, &a.Action); err != nil {
http.Error(w, "module action scan error", http.StatusInternalServerError)
return
}
moduleActions = append(moduleActions, a)
}
if err := actionRows.Err(); err != nil {
http.Error(w, "module action rows error", http.StatusInternalServerError)
return
}
rows, err := h.DB.Query(queries.ListRoleDepartmentPermissionSets, search)
if err != nil {
http.Error(w, "db error", http.StatusInternalServerError)
return
}
defer rows.Close()
list := make([]RoleDeptPermissionSummary, 0, 128)
for rows.Next() {
var item RoleDeptPermissionSummary
var rawFlags []byte
if err := rows.Scan(
&item.RoleID,
&item.RoleTitle,
&item.DepartmentCode,
&item.DepartmentTitle,
&rawFlags,
); err != nil {
http.Error(w, "scan error", http.StatusInternalServerError)
return
}
item.ModuleFlags = map[string]bool{}
if len(rawFlags) > 0 {
if err := json.Unmarshal(rawFlags, &item.ModuleFlags); err != nil {
http.Error(w, "module flags parse error", http.StatusInternalServerError)
return
}
}
list = append(list, item)
}
if err := rows.Err(); err != nil {
http.Error(w, "rows error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
_ = json.NewEncoder(w).Encode(RoleDeptPermissionListResponse{
Modules: modules,
ModuleActions: moduleActions,
Rows: list,
})
}
/* ======================================================
GET
====================================================== */
func (h *RoleDepartmentPermissionHandler) Get(w http.ResponseWriter, r *http.Request) {
claims, ok := auth.GetClaimsFromContext(r.Context())
if !ok || claims == nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
vars := mux.Vars(r)
roleID, err := strconv.Atoi(vars["roleId"])
if err != nil || roleID <= 0 {
http.Error(w, "invalid roleId", http.StatusBadRequest)
return
}
deptCode := vars["deptCode"]
if deptCode == "" {
http.Error(w, "invalid deptCode", http.StatusBadRequest)
return
}
list, err := h.Repo.Get(roleID, deptCode)
if err != nil {
http.Error(w, "db error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
_ = json.NewEncoder(w).Encode(list)
}
/* ======================================================
POST
====================================================== */
func (h *RoleDepartmentPermissionHandler) Save(w http.ResponseWriter, r *http.Request) {
claims, ok := auth.GetClaimsFromContext(r.Context())
if !ok || claims == nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
vars := mux.Vars(r)
roleID, err := strconv.Atoi(vars["roleId"])
if err != nil || roleID <= 0 {
http.Error(w, "invalid roleId", http.StatusBadRequest)
return
}
deptCode := vars["deptCode"]
if deptCode == "" {
http.Error(w, "invalid deptCode", http.StatusBadRequest)
return
}
var list []permissions.RoleDepartmentPermission
if err := json.NewDecoder(r.Body).Decode(&list); err != nil {
http.Error(w, "bad payload", http.StatusBadRequest)
return
}
// ================= OLD =================
oldRows, err := h.Repo.Get(roleID, deptCode)
if err != nil {
log.Println("OLD PERM LOAD ERROR:", err)
}
oldMap := map[string]bool{}
for _, p := range oldRows {
key := p.Module + ":" + p.Action
oldMap[key] = p.Allowed
}
// ================= DIFF =================
var changes []map[string]any
for _, p := range list {
key := p.Module + ":" + p.Action
oldVal := oldMap[key]
if oldVal != p.Allowed {
changes = append(changes, map[string]any{
"module": p.Module,
"action": p.Action,
"before": oldVal,
"after": p.Allowed,
})
}
}
// ================= SAVE =================
if err := h.Repo.Save(roleID, deptCode, list); err != nil {
http.Error(w, "save error", http.StatusInternalServerError)
return
}
// ================= AUDIT =================
if len(changes) > 0 {
auditlog.Enqueue(r.Context(), auditlog.ActivityLog{
ActionType: "role_department_permission_change",
ActionCategory: "role_permission",
ActionTarget: fmt.Sprintf(
"/api/roles/%d/departments/%s/permissions",
roleID,
deptCode,
),
Description: "role+department permissions updated",
Username: claims.Username,
RoleCode: claims.RoleCode,
DfUsrID: int64(claims.ID),
ChangeBefore: map[string]any{
"permissions": oldRows,
},
ChangeAfter: map[string]any{
"changes": changes,
},
IsSuccess: true,
})
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
_ = json.NewEncoder(w).Encode(map[string]bool{"success": true})
}
func GetModuleLookupRoute(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
rows, err := db.Query(queries.GetModuleLookup)
if err != nil {
http.Error(w, "db error", 500)
return
}
defer rows.Close()
type Row struct {
Value string `json:"value"`
Label string `json:"label"`
}
var list []Row
for rows.Next() {
var r Row
if err := rows.Scan(
&r.Value,
&r.Label,
); err != nil {
http.Error(w, "scan error", 500)
return
}
list = append(list, r)
}
json.NewEncoder(w).Encode(list)
}
}
func GetRolesForPermissionSelectRoute(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
rows, err := db.Query(queries.GetRolesForPermissionSelect)
if err != nil {
http.Error(w, "roles select error", 500)
return
}
defer rows.Close()
list := make([]IdTitleOption, 0)
for rows.Next() {
var o IdTitleOption
if err := rows.Scan(&o.ID, &o.Title); err == nil {
list = append(list, o)
}
}
_ = json.NewEncoder(w).Encode(list)
}
}
func GetDepartmentsForPermissionSelectRoute(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
rows, err := db.Query(queries.GetDepartmentsForPermissionSelect)
if err != nil {
http.Error(w, "departments select error", 500)
return
}
defer rows.Close()
list := make([]IdTitleOption, 0)
for rows.Next() {
var o IdTitleOption
if err := rows.Scan(&o.ID, &o.Title); err == nil {
list = append(list, o)
}
}
_ = json.NewEncoder(w).Encode(list)
}
}
func GetUsersForPermissionSelectRoute(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
rows, err := db.Query(queries.GetUserLookupForPermission)
if err != nil {
http.Error(w, "users lookup error", http.StatusInternalServerError)
return
}
defer rows.Close()
list := make([]IdTitleOption, 0, 128)
for rows.Next() {
var o IdTitleOption
if err := rows.Scan(&o.ID, &o.Title); err == nil {
list = append(list, o)
}
}
_ = json.NewEncoder(w).Encode(list)
}
}
func (h *PermissionHandler) GetUserOverrides(w http.ResponseWriter, r *http.Request) {
claims, ok := auth.GetClaimsFromContext(r.Context())
if !ok || claims == nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
userID, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
if err != nil || userID <= 0 {
http.Error(w, "invalid id", http.StatusBadRequest)
return
}
list, err := h.Repo.GetUserOverridesByUserID(userID)
if err != nil {
log.Println("❌ USER OVERRIDE LOAD ERROR:", err)
http.Error(w, "db error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
_ = json.NewEncoder(w).Encode(list)
}
func GetUserRoutePermissionsHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
claims, ok := auth.GetClaimsFromContext(r.Context())
if !ok || claims == nil {
http.Error(w, "unauthorized", 401)
return
}
repo := permissions.NewPermissionRepository(db)
// JWTden departmanlar
depts := claims.DepartmentCodes
rows, err := db.Query(`
SELECT DISTINCT
module_code,
action,
path
FROM mk_sys_routes
`)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
defer rows.Close()
type Row struct {
Route string `json:"route"`
CanAccess bool `json:"can_access"`
}
list := make([]Row, 0, 64)
for rows.Next() {
var module, action, path string
if err := rows.Scan(
&module,
&action,
&path,
); err != nil {
continue
}
allowed, err := repo.ResolvePermissionChain(
int64(claims.ID),
int64(claims.RoleID),
depts,
module,
action,
)
if err != nil {
log.Println("PERM RESOLVE ERROR:", err)
continue
}
list = append(list, Row{
Route: path,
CanAccess: allowed,
})
}
_ = json.NewEncoder(w).Encode(list)
}
}
func GetMyEffectivePermissions(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
claims, ok := auth.GetClaimsFromContext(r.Context())
if !ok || claims == nil {
http.Error(w, "unauthorized", 401)
return
}
repo := permissions.NewPermissionRepository(db)
// ✅ JWT'DEN DEPARTMENTS
depts := claims.DepartmentCodes
log.Printf("🧪 EFFECTIVE PERM | user=%d role=%d depts=%v",
claims.ID,
claims.RoleID,
depts,
)
// all system perms
all, err := db.Query(`
SELECT DISTINCT module_code, action
FROM mk_sys_routes
`)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
defer all.Close()
type Row struct {
Module string `json:"module"`
Action string `json:"action"`
Allowed bool `json:"allowed"`
}
list := make([]Row, 0, 128)
for all.Next() {
var m, a string
if err := all.Scan(&m, &a); err != nil {
continue
}
allowed, err := repo.ResolvePermissionChain(
int64(claims.ID),
int64(claims.RoleID),
depts,
m,
a,
)
if err != nil {
continue
}
list = append(list, Row{
Module: m,
Action: a,
Allowed: allowed,
})
}
_ = json.NewEncoder(w).Encode(list)
}
}