|
3 | 3 | */ |
4 | 4 | import { afterEach, describe, expect, it } from "vitest"; |
5 | 5 | import { createEmptyPluginRegistry } from "../plugins/registry-empty.js"; |
6 | | -import { |
7 | | - pinActivePluginChannelRegistry, |
8 | | - pinActivePluginHttpRouteRegistry, |
9 | | - setActivePluginRegistry, |
10 | | -} from "../plugins/runtime.js"; |
| 6 | +import { setActivePluginRegistry } from "../plugins/runtime.js"; |
11 | 7 | import { |
12 | 8 | authorizeOperatorScopesForMethod, |
13 | 9 | isGatewayMethodClassified, |
14 | 10 | resolveLeastPrivilegeOperatorScopesForMethod, |
15 | | - resolveRequiredOperatorScopeForMethod, |
16 | 11 | } from "./method-scopes.js"; |
17 | 12 | import { createPluginGatewayMethodDescriptor } from "./methods/registry.js"; |
18 | 13 | import { listGatewayMethods } from "./server-methods-list.js"; |
@@ -230,144 +225,6 @@ describe("method scope resolution", () => { |
230 | 225 | "operator.admin", |
231 | 226 | ]); |
232 | 227 | }); |
233 | | - |
234 | | - // Regression coverage for #92044: plugin-registered gateway methods were |
235 | | - // silently requiring operator.admin because resolveScopedMethod only looked at |
236 | | - // activeRegistry.gatewayMethodDescriptors. The plugin descriptors can live in |
237 | | - // the http-route or channel surface instead, and a request entering through |
238 | | - // those surfaces must still see the plugin-declared scope. |
239 | | - it("resolves a plugin method scope when its descriptor lives on the http route surface", () => { |
240 | | - const registry = createEmptyPluginRegistry(); |
241 | | - registry.gatewayHandlers["workboard.cards.dispatch"] = pluginHandler; |
242 | | - registry.gatewayMethodDescriptors.push( |
243 | | - createPluginGatewayMethodDescriptor({ |
244 | | - pluginId: "workboard", |
245 | | - name: "workboard.cards.dispatch", |
246 | | - handler: pluginHandler, |
247 | | - scope: "operator.write", |
248 | | - }), |
249 | | - ); |
250 | | - setActivePluginRegistry(createEmptyPluginRegistry()); |
251 | | - pinActivePluginHttpRouteRegistry(registry); |
252 | | - |
253 | | - expect(resolveRequiredOperatorScopeForMethod("workboard.cards.dispatch")).toBe( |
254 | | - "operator.write", |
255 | | - ); |
256 | | - expect( |
257 | | - authorizeOperatorScopesForMethod("workboard.cards.dispatch", ["operator.write"]), |
258 | | - ).toEqual({ allowed: true }); |
259 | | - expect(authorizeOperatorScopesForMethod("workboard.cards.dispatch", ["operator.read"])).toEqual( |
260 | | - { allowed: false, missingScope: "operator.write" }, |
261 | | - ); |
262 | | - }); |
263 | | - |
264 | | - it("resolves a plugin method scope when its descriptor lives on the channel surface", () => { |
265 | | - const registry = createEmptyPluginRegistry(); |
266 | | - registry.gatewayHandlers["workboard.cards.dispatch"] = pluginHandler; |
267 | | - registry.gatewayMethodDescriptors.push( |
268 | | - createPluginGatewayMethodDescriptor({ |
269 | | - pluginId: "workboard", |
270 | | - name: "workboard.cards.dispatch", |
271 | | - handler: pluginHandler, |
272 | | - scope: "operator.write", |
273 | | - }), |
274 | | - ); |
275 | | - setActivePluginRegistry(createEmptyPluginRegistry()); |
276 | | - pinActivePluginChannelRegistry(registry); |
277 | | - |
278 | | - expect(resolveRequiredOperatorScopeForMethod("workboard.cards.dispatch")).toBe( |
279 | | - "operator.write", |
280 | | - ); |
281 | | - expect( |
282 | | - authorizeOperatorScopesForMethod("workboard.cards.dispatch", ["operator.write"]), |
283 | | - ).toEqual({ allowed: true }); |
284 | | - }); |
285 | | - |
286 | | - it("prefers the active surface over http-route or channel surfaces for plugin scopes", () => { |
287 | | - const httpRouteRegistry = createEmptyPluginRegistry(); |
288 | | - httpRouteRegistry.gatewayHandlers["workboard.cards.dispatch"] = pluginHandler; |
289 | | - httpRouteRegistry.gatewayMethodDescriptors.push( |
290 | | - createPluginGatewayMethodDescriptor({ |
291 | | - pluginId: "workboard", |
292 | | - name: "workboard.cards.dispatch", |
293 | | - handler: pluginHandler, |
294 | | - scope: "operator.read", |
295 | | - }), |
296 | | - ); |
297 | | - const activeRegistry = createEmptyPluginRegistry(); |
298 | | - activeRegistry.gatewayHandlers["workboard.cards.dispatch"] = pluginHandler; |
299 | | - activeRegistry.gatewayMethodDescriptors.push( |
300 | | - createPluginGatewayMethodDescriptor({ |
301 | | - pluginId: "workboard", |
302 | | - name: "workboard.cards.dispatch", |
303 | | - handler: pluginHandler, |
304 | | - scope: "operator.write", |
305 | | - }), |
306 | | - ); |
307 | | - setActivePluginRegistry(activeRegistry); |
308 | | - pinActivePluginHttpRouteRegistry(httpRouteRegistry); |
309 | | - |
310 | | - expect(resolveRequiredOperatorScopeForMethod("workboard.cards.dispatch")).toBe( |
311 | | - "operator.write", |
312 | | - ); |
313 | | - }); |
314 | | - |
315 | | - it("still returns the admin-scope default for an unknown method when surfaces are empty", () => { |
316 | | - setActivePluginRegistry(createEmptyPluginRegistry()); |
317 | | - pinActivePluginHttpRouteRegistry(createEmptyPluginRegistry()); |
318 | | - pinActivePluginChannelRegistry(createEmptyPluginRegistry()); |
319 | | - |
320 | | - // Unknown method should fall through to admin via authorizeOperatorScopesForMethod |
321 | | - // (resolveScopedMethod returns undefined, then the auth check defaults to admin). |
322 | | - expect(authorizeOperatorScopesForMethod("totally.unknown.method", ["operator.write"])).toEqual({ |
323 | | - allowed: false, |
324 | | - missingScope: "operator.admin", |
325 | | - }); |
326 | | - }); |
327 | | - |
328 | | - // Sibling coverage for #78894 (memory-core dream-promotion cron, same root-cause |
329 | | - // shape): a memory-core style plugin that registers its method via |
330 | | - // api.registerGatewayMethod should resolve to its declared scope through the same |
331 | | - // cross-surface path. |
332 | | - it("resolves a memory-core style plugin descriptor from the active registry", () => { |
333 | | - const registry = createEmptyPluginRegistry(); |
334 | | - registry.gatewayHandlers["memory.dream.promote"] = pluginHandler; |
335 | | - registry.gatewayMethodDescriptors.push( |
336 | | - createPluginGatewayMethodDescriptor({ |
337 | | - pluginId: "memory-core", |
338 | | - name: "memory.dream.promote", |
339 | | - handler: pluginHandler, |
340 | | - scope: "operator.write", |
341 | | - }), |
342 | | - ); |
343 | | - setActivePluginRegistry(registry); |
344 | | - |
345 | | - expect(resolveRequiredOperatorScopeForMethod("memory.dream.promote")).toBe("operator.write"); |
346 | | - expect(authorizeOperatorScopesForMethod("memory.dream.promote", ["operator.write"])).toEqual({ |
347 | | - allowed: true, |
348 | | - }); |
349 | | - expect(authorizeOperatorScopesForMethod("memory.dream.promote", ["operator.read"])).toEqual({ |
350 | | - allowed: false, |
351 | | - missingScope: "operator.write", |
352 | | - }); |
353 | | - }); |
354 | | - |
355 | | - it("resolves a memory-core style plugin descriptor from the channel surface", () => { |
356 | | - const registry = createEmptyPluginRegistry(); |
357 | | - registry.gatewayHandlers["memory.dream.promote"] = pluginHandler; |
358 | | - registry.gatewayMethodDescriptors.push( |
359 | | - createPluginGatewayMethodDescriptor({ |
360 | | - pluginId: "memory-core", |
361 | | - name: "memory.dream.promote", |
362 | | - handler: pluginHandler, |
363 | | - scope: "operator.write", |
364 | | - }), |
365 | | - ); |
366 | | - setActivePluginRegistry(createEmptyPluginRegistry()); |
367 | | - pinActivePluginChannelRegistry(registry); |
368 | | - |
369 | | - expect(resolveRequiredOperatorScopeForMethod("memory.dream.promote")).toBe("operator.write"); |
370 | | - }); |
371 | 228 | }); |
372 | 229 |
|
373 | 230 | describe("operator scope authorization", () => { |
|
0 commit comments