fix(ui): учитывать права на действия мониторинга

This commit is contained in:
Denozordec committed 2026-09-21 16:16:16 +07:00
1 parent 61950638bc
commit bb9278fe58
4 files changed
+125 -27

No files matched your search

+63 -3
View File
@@ -1,9 +1,13 @@
import { test, expect, type Page } from "@playwright/test"
async function setup(page: Page, url: string, failed = 0, longNames = false) {
async function setup(page: Page, url: string, failed = 0, longNames = false, permission?: string, isAdmin = false) {
const control = { writes: [] as { method: string; path: string; body: unknown }[], failed, posts: [] as { path: string; body: unknown }[] }
const servers = [{ id: 1, name: "Источник", host: "192.0.2.1", type: "exit-node", enabled: true, country: "RU", site: "TEST", asn: "64512", status: "online", latency: 1, os: "7", model: "CHR", wanUplinks: [] }]
const probes = ["p1", "p/2"].map(id => ({ id, srcServerId: "1", name: "Проверка сети", target: "192.0.2.10", filter: "main", rtt: null, loss: 100, status: "down", series: [0], enabled: true, showOnDashboard: false }))
if (permission) {
await page.addInitScript(token => localStorage.setItem("mmapp_token", token), uptimeToken(permission, isAdmin))
servers.push({ ...servers[0], id: 2, name: "Назначение", host: "192.0.2.2" })
}
if (longNames) {
servers[0].name = "Источник-с-длинным-названием-площадки-для-проверки-переноса"
probes.forEach(p => { p.name = "Проверка-доступности-длинного-имени-сетевого-узла"; p.target = "router-with-a-long-hostname.example.test"; p.filter = "Фильтр-с-длинным-названием" })
@@ -15,11 +19,11 @@ async function setup(page: Page, url: string, failed = 0, longNames = false) {
if (!["GET", "HEAD"].includes(request.method())) control.writes.push({ method: request.method(), path, body: request.postDataJSON() })
if (request.method() === "POST") control.posts.push({ path, body: request.postDataJSON() })
if (path === "/api/uptime/overview" && control.failed) return route.fulfill({ status: control.failed, json: { error: "Мониторинг недоступен" } })
const json = path === "/api/auth/config" ? { required: false }
const json = path === "/api/auth/config" ? { required: Boolean(permission) }
: path === "/api/evobgp/settings" ? { enabled: false, baseUrl: "", secretConfigured: false }
: path === "/api/servers" ? servers
: path === "/api/uptime/overview" ? { probes, resources: [] }
: path === "/api/uptime/speed-probes" ? { probes: [] }
: path === "/api/uptime/speed-probes" ? { probes: permission ? [{ id: "s1", srcServerId: "1", dstServerId: "2", srcInterface: "", dstInterface: "", protocol: "tcp", direction: "both", durationSec: "10", enabled: true }] : [] }
: path === "/api/uptime/speed-test/runs" ? { runs: [] }
: path === "/api/bgp/sessions" ? []
: path === "/api/filters/gre-tunnels" || path === "/api/vxlan" ? { tunnels: [] }
@@ -126,3 +130,59 @@ for (const width of [320, 390, 1440]) {
await expect.poll(() => control.writes.some(w => w.method === "PUT" && w.path === "/api/uptime/probes")).toBe(true)
})
}
// UI claim fixtures; JWT verification remains a separate integration check.
function uptimeToken(permission: string, isAdmin = false) {
return "fixture." + Buffer.from(JSON.stringify({ apps: ["mm"], permissions: ["mm:servers:read", permission], is_admin: isAdmin, exp: Math.floor(Date.now() / 1000) + 3600 })).toString("base64url") + ".fixture"
}
for (const [permission, isAdmin] of [["mm:uptime:read", false], ["mm:uptime:read", true], ["mm:servers:write", false]] as const) {
test(`мониторинг только чтение: ${permission}, is_admin=${isAdmin}`, async ({ page }) => {
const control = await setup(page, "/uptime", 0, false, permission, isAdmin)
await expect(page.getByText("Только просмотр", { exact: true })).toBeVisible()
await expect(page.getByRole("button", { name: "Новая проба", exact: true })).toBeDisabled()
const row = page.locator('[data-probe-id="p1"]')
for (const name of ["Показать на дашборде", "Редактировать пробу", "Удалить пробу"]) {
await expect(row.getByRole("button", { name, exact: true })).toBeDisabled()
}
await expect(row.getByRole("switch")).toBeDisabled()
await expect(page.getByRole("button", { name: /Добавить интерфейс для/ })).toBeDisabled()
await expect(page.getByRole("button", { name: "Обновить", exact: true })).toBeDisabled()
await page.getByRole("button", { name: "Обновить данные", exact: true }).click()
await page.getByRole("button", { name: /Ресурсы/ }).click()
await page.getByRole("button", { name: "Обновить данные", exact: true }).click()
await page.getByRole("button", { name: /Скорость/ }).click()
await expect(page.getByRole("button", { name: "Новая speed-проба", exact: true })).toBeDisabled()
for (const name of ["Запустить", "Редактировать speed-пробу", "Удалить speed-пробу"]) {
await expect(page.getByRole("button", { name, exact: true })).toBeDisabled()
}
await expect(page.getByRole("switch")).toBeDisabled()
await page.getByRole("button", { name: "Обновить данные", exact: true }).click()
expect(control.writes).toEqual([])
})
}
for (const permission of ["mm:uptime:write", "mm:uptime:admin"]) {
test(`мониторинг: ${permission} разрешает ручной ping`, async ({ page }) => {
const control = await setup(page, "/uptime?probe=p1", 0, false, permission)
await expect(page.getByRole("button", { name: "Новая проба", exact: true })).toBeEnabled()
await page.getByRole("button", { name: "Обновить", exact: true }).click()
await expect.poll(() => control.posts.map(p => p.path)).toEqual(["/api/uptime/probes/collect-group"])
await page.getByRole("button", { name: /Скорость/ }).click()
await expect(page.getByRole("button", { name: "Запустить", exact: true })).toBeEnabled()
})
}
for (const speed of [false, true]) {
test(`мониторинг: отзыв права перед сохранением ${speed ? "Speed" : "Ping"}`, async ({ page }) => {
const control = await setup(page, "/uptime?probe=p1", 0, false, "mm:uptime:write")
if (speed) await page.getByRole("button", { name: /Скорость/ }).click()
await page.getByRole("button", { name: speed ? "Редактировать speed-пробу" : "Редактировать пробу", exact: true }).click()
await expect(page.getByRole("dialog")).toBeVisible()
await page.evaluate(token => localStorage.setItem("mmapp_token", token), uptimeToken("mm:uptime:read"))
await page.getByRole("dialog").getByRole("button", { name: "Сохранить", exact: true }).click()
await expect(page.getByRole("dialog")).toBeVisible()
await expect(page.getByRole("dialog").getByRole("alert")).toContainText("Недостаточно прав")
expect(control.writes).toEqual([])
})
}