package routes import ( "bssapp-backend/auth" "bssapp-backend/internal/auditlog" "bssapp-backend/permissions" "database/sql" "encoding/json" "fmt" "net/http" "strconv" "github.com/gorilla/mux" ) func GetRolePermissionMatrix(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { claims, ok := auth.GetClaimsFromContext(r.Context()) if !ok || claims == nil { http.Error(w, "unauthorized", 401) return } roleID, _ := strconv.Atoi(mux.Vars(r)["id"]) rows, err := db.Query(` SELECT module_code, action, allowed FROM mk_sys_role_permissions WHERE role_id=$1 `, roleID) if err != nil { http.Error(w, "db error", 500) return } defer rows.Close() var list []permissions.PermissionMatrixRow for rows.Next() { var row permissions.PermissionMatrixRow if err := rows.Scan( &row.Module, &row.Action, &row.Allowed, ); err != nil { http.Error(w, "scan error", 500) return } row.Source = "role" list = append(list, row) } json.NewEncoder(w).Encode(list) } } func SaveRolePermissionMatrix(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { claims, ok := auth.GetClaimsFromContext(r.Context()) if !ok || claims == nil { http.Error(w, "unauthorized", 401) return } roleID, _ := strconv.Atoi(mux.Vars(r)["id"]) var list []permissions.PermissionMatrixRow if err := json.NewDecoder(r.Body).Decode(&list); err != nil { http.Error(w, "bad payload", 400) return } repo := permissions.NewPermissionRepository(db) // ================= OLD ================= oldRows, _ := repo.GetPermissionMatrixForRoles([]int{roleID}) 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, ok := oldMap[key] if !ok { oldVal = false } if oldVal != p.Allowed { changes = append(changes, map[string]any{ "module": p.Module, "action": p.Action, "before": oldVal, "after": p.Allowed, }) } } // ================= SAVE ================= tx, err := db.Begin() if err != nil { http.Error(w, "tx error", 500) return } defer tx.Rollback() stmt, err := tx.Prepare(` INSERT INTO mk_sys_role_permissions (role_id,module_code,action,allowed) VALUES ($1,$2,$3,$4) ON CONFLICT (role_id,module_code,action) DO UPDATE SET allowed=EXCLUDED.allowed `) if err != nil { http.Error(w, "prepare error", 500) return } defer stmt.Close() for _, p := range list { if _, err := stmt.Exec( roleID, p.Module, p.Action, p.Allowed, ); err != nil { http.Error(w, "exec error", 500) return } } if err := tx.Commit(); err != nil { http.Error(w, "commit error", 500) return } // ================= AUDIT ================= if len(changes) > 0 { var roleCode string _ = db.QueryRow(` SELECT code FROM dfrole WHERE id=$1 `, roleID).Scan(&roleCode) auditlog.Enqueue(r.Context(), auditlog.ActivityLog{ ActionType: "role_permission_change", ActionCategory: "role_permission", ActionTarget: fmt.Sprintf("/api/roles/%d/permissions", roleID), Description: "role permission matrix 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, }) } json.NewEncoder(w).Encode(map[string]bool{ "success": true, }) } }