fix(ui): подтверждать применение OSPF и согласовать права

This commit is contained in:
Denozordec committed 2026-09-30 22:56:31 +07:00
1 parent bf2815b5a4
commit c88e1400bf
8 files changed
+147 -35

No files matched your search

+1 -1
View File
@@ -13,7 +13,7 @@
"db:studio": "drizzle-kit studio",
"db:migrate-from-sqlite": "tsx src/scripts/migrate-sqlite-to-pg.ts",
"facts:rebuild": "tsx src/scripts/rebuild-flow-facts.ts",
"test:auth": "tsx src/lib/permissions.test.ts && tsx src/plugins/auth.smoke.test.ts",
"test:auth": "tsx src/lib/permissions.test.ts && tsx src/plugins/auth.smoke.test.ts && tsx src/plugins/auth.ospf-optimizer.test.ts",
"test:wireguard": "npx tsx src/services/wireguard-config.test.ts",
"test:traffic-rate": "tsx src/services/traffic-rate.test.ts",
"test:traffic-flow": "tsx src/services/traffic-flow-parse.test.ts && tsx src/services/traffic-flow-map-exporter.test.ts && tsx src/services/traffic-flow-ifaces.test.ts && tsx src/services/traffic-flow-ifindex.test.ts && tsx src/services/traffic-flow-dedup.test.ts && tsx src/services/traffic-flow-planes.test.ts && tsx src/services/traffic-flow-ip.test.ts && tsx src/services/traffic-flow-dest.test.ts && tsx src/services/traffic-flow-classify.test.ts && tsx src/services/traffic-flow-ripe.test.ts && tsx src/services/traffic-flow-brands.test.ts && tsx src/services/traffic-flow-ingest.test.ts && tsx src/services/traffic-flow-analytics.test.ts && tsx src/services/traffic-flow-map-hops.test.ts && tsx src/services/traffic-flow-purge.test.ts && tsx src/services/traffic-flow-geoip.test.ts && tsx src/services/traffic-flow-facts.test.ts && tsx src/services/traffic-flow-facts-filter.test.ts && tsx src/services/traffic-flow-facts-rebuild.test.ts && tsx src/services/statistics-aggregate.test.ts",
+5
View File
@@ -61,4 +61,9 @@ assert.equal(hasPermission(["mm:settings:admin"], "mm:users:read"), true)
assert.equal(hasPermission(["mm:settings:admin"], "mm:users:write"), true)
assert.equal(hasPermission(["mm:dashboard:read"], "mm:users:write"), false)
assert.equal(permissionForRequest("GET","/api/servers/1/ospf"),"mm:network:read")
assert.equal(permissionForRequest("POST","/api/servers/1/ospf/optimize/preview?x=1"),"mm:network:read")
assert.equal(permissionForRequest("POST","/api/servers/1/ospf/optimize"),"mm:network:write")
assert.equal(permissionForRequest("POST","/api/servers/1/ospf/optimize/preview/extra"),"mm:network:write")
assert.equal(permissionForRequest("PUT","/api/servers/1"),"mm:servers:write")
console.log("permissions.test.ts: ok")
+16
View File
@@ -42,6 +42,22 @@ type Rule = {
}
const RULES: Rule[] = [
{
methods: ["GET"],
match: p => /^\/api\/servers\/\d+\/ospf(?:\/|$)/.test(p),
permission: "mm:network:read",
},
{
methods: ["POST"],
match: p => /^\/api\/servers\/\d+\/ospf\/optimize\/preview$/.test(p),
permission: "mm:network:read",
},
{
methods: ["POST", "PUT", "PATCH", "DELETE"],
match: p => /^\/api\/servers\/\d+\/ospf(?:\/|$)/.test(p),
permission: "mm:network:write",
},
{
methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
match: (p) =>
@@ -0,0 +1,27 @@
import assert from "node:assert/strict"
import Fastify from "fastify"
import {SignJWT} from "jose"
process.env.AUTH_REQUIRED="true"
process.env.AUTH_JWT_SECRET="optimizer-test-secret"
process.env.AUTH_ISSUER="https://auth.test.local"
process.env.NODE_ENV="test"
const {default:authPlugin,requireAuth}=await import("./auth.js")
const app=Fastify()
await app.register(authPlugin)
app.addHook("preHandler",requireAuth)
let reads=0,writes=0
app.post("/api/servers/:id/ospf/optimize/preview",async()=>{reads++;return {ok:true}})
app.post("/api/servers/:id/ospf/optimize",async()=>{writes++;return {ok:true}})
const secret=new TextEncoder().encode("optimizer-test-secret")
for(const permission of ["mm:network:read","mm:network:write","mm:servers:write"]){
const token=await new SignJWT({sub:"reader",apps:["mm"],permissions:[permission]}).setProtectedHeader({alg:"HS256"}).setIssuer("https://auth.test.local").setExpirationTime("1h").sign(secret)
const headers={authorization:"Bearer "+token}
const preview=await app.inject({method:"POST",url:"/api/servers/1/ospf/optimize/preview",headers})
const apply=await app.inject({method:"POST",url:"/api/servers/1/ospf/optimize",headers})
assert.equal(preview.statusCode,permission==="mm:servers:write"?403:200)
assert.equal(apply.statusCode,permission==="mm:network:write"?200:403)
}
assert.equal(reads,2);assert.equal(writes,1)
assert.equal((await app.inject({method:"POST",url:"/api/servers/1/ospf/optimize"})).statusCode,401)
await app.close()
console.log("OSPF optimizer RBAC: signed JWT read/write/servers-only, no DB/RouterOS")