@@ -254,10 +254,20 @@ function makeDispatcher(
254254 * file order- and shard-independent, which is how the 403 reached CI at all:
255255 * the branch was cut before #7027 merged, so the gate did not exist locally.
256256 */
257- const ctx = ( ) : any => ( {
257+ const ctx = ( activeOrganizationId ?: string ) : any => ( {
258258 request : { headers : { } } ,
259259 environmentId : 'env_1' ,
260- executionContext : { userId : 'usr_1' , systemPermissions : [ 'manage_metadata' ] } ,
260+ // [#20408] The execution context the identity resolver builds from the
261+ // session `makeDispatcher` answers: that session's active organization rides
262+ // it as `tenantId` (there is no tenancy wall here, so the claim is kept, not
263+ // dropped). The dispatcher's `/meta` doors scope by THIS value — the vetted
264+ // one `RestServer`'s doors read — never the claim as stored, so a case whose
265+ // session has an active organization hands the same one in here.
266+ executionContext : {
267+ userId : 'usr_1' ,
268+ systemPermissions : [ 'manage_metadata' ] ,
269+ ...( activeOrganizationId ? { tenantId : activeOrganizationId } : { } ) ,
270+ } ,
261271} ) ;
262272
263273function makeStack ( activeOrganizationId : string | undefined , metadataService ?: unknown ) {
@@ -361,7 +371,7 @@ describe('#7018 — the registry decides whether a metadata write carries the se
361371 it ( 'a NON-overridable type lands env-wide even though the session has an active org' , async ( ) => {
362372 const { engine, dispatcher } = makeStack ( ACTIVE_ORG ) ;
363373
364- const res = responseOf ( await dispatcher . handleMetadata ( `/flow/${ FLOW . name } ` , ctx ( ) , 'PUT' , FLOW ) ) ;
374+ const res = responseOf ( await dispatcher . handleMetadata ( `/flow/${ FLOW . name } ` , ctx ( ACTIVE_ORG ) , 'PUT' , FLOW ) ) ;
365375
366376 expect ( res . status ) . toBe ( 200 ) ;
367377 const row = metaRow ( engine , 'flow' , FLOW . name ) ;
@@ -383,7 +393,7 @@ describe('#7018 — the registry decides whether a metadata write carries the se
383393 fields : { subject : { type : 'text' , label : 'Subject' } } ,
384394 } ;
385395
386- const res = responseOf ( await dispatcher . handleMetadata ( `/object/${ OBJECT . name } ` , ctx ( ) , 'PUT' , OBJECT ) ) ;
396+ const res = responseOf ( await dispatcher . handleMetadata ( `/object/${ OBJECT . name } ` , ctx ( ACTIVE_ORG ) , 'PUT' , OBJECT ) ) ;
387397
388398 expect ( res . status ) . toBe ( 200 ) ;
389399 expect ( metaRow ( engine , 'object' , OBJECT . name ) ! . organization_id ) . toBeNull ( ) ;
@@ -398,7 +408,7 @@ describe('#7018 — the registry decides whether a metadata write carries the se
398408 const withOrg = makeStack ( ACTIVE_ORG ) ;
399409 const withoutOrg = makeStack ( undefined ) ;
400410
401- const a = responseOf ( await withOrg . dispatcher . handleMetadata ( `/flow/${ FLOW . name } ` , ctx ( ) , 'PUT' , FLOW ) ) ;
411+ const a = responseOf ( await withOrg . dispatcher . handleMetadata ( `/flow/${ FLOW . name } ` , ctx ( ACTIVE_ORG ) , 'PUT' , FLOW ) ) ;
402412 const b = responseOf ( await withoutOrg . dispatcher . handleMetadata ( `/flow/${ FLOW . name } ` , ctx ( ) , 'PUT' , FLOW ) ) ;
403413
404414 expect ( a . status ) . toBe ( 200 ) ;
@@ -409,7 +419,7 @@ describe('#7018 — the registry decides whether a metadata write carries the se
409419 it ( 'CONTROL — an `allowOrgOverride: true` type keeps its org scoping exactly as before' , async ( ) => {
410420 const { engine, dispatcher } = makeStack ( ACTIVE_ORG ) ;
411421
412- const res = responseOf ( await dispatcher . handleMetadata ( `/view/${ VIEW . name } ` , ctx ( ) , 'PUT' , VIEW ) ) ;
422+ const res = responseOf ( await dispatcher . handleMetadata ( `/view/${ VIEW . name } ` , ctx ( ACTIVE_ORG ) , 'PUT' , VIEW ) ) ;
413423
414424 expect ( res . status ) . toBe ( 200 ) ;
415425 // ADR-0005's per-org overlay is the point of the flag and must survive
@@ -420,7 +430,7 @@ describe('#7018 — the registry decides whether a metadata write carries the se
420430 it ( 'CONTROL — the plural URL spelling of an overridable type is scoped the same way' , async ( ) => {
421431 const { engine, dispatcher } = makeStack ( ACTIVE_ORG ) ;
422432
423- const res = responseOf ( await dispatcher . handleMetadata ( `/views/${ VIEW . name } ` , ctx ( ) , 'PUT' , VIEW ) ) ;
433+ const res = responseOf ( await dispatcher . handleMetadata ( `/views/${ VIEW . name } ` , ctx ( ACTIVE_ORG ) , 'PUT' , VIEW ) ) ;
424434
425435 expect ( res . status ) . toBe ( 200 ) ;
426436 expect ( metaRow ( engine , 'view' , VIEW . name ) ! . organization_id ) . toBe ( ACTIVE_ORG ) ;
@@ -449,7 +459,7 @@ describe('#7018 — the registry decides whether a metadata write carries the se
449459 success : true , publishedCount : 0 , failedCount : 0 , published : [ ] , failed : [ ] ,
450460 } ) ;
451461
452- const res = responseOf ( await dispatcher . handlePackages ( '/crm_pkg/publish-drafts' , 'POST' , { } , { } , ctx ( ) ) ) ;
462+ const res = responseOf ( await dispatcher . handlePackages ( '/crm_pkg/publish-drafts' , 'POST' , { } , { } , ctx ( ACTIVE_ORG ) ) ) ;
453463
454464 expect ( res . status ) . toBe ( 200 ) ;
455465 expect ( res . body . data . unhiddenApps ) . toEqual ( [ APP . name ] ) ;
@@ -484,7 +494,7 @@ describe('#7018 — the registry decides whether a metadata write carries the se
484494 } ) ;
485495 error . mockClear ( ) ;
486496
487- const res = responseOf ( await dispatcher . handlePackages ( '/crm_pkg/publish-drafts' , 'POST' , { } , { } , ctx ( ) ) ) ;
497+ const res = responseOf ( await dispatcher . handlePackages ( '/crm_pkg/publish-drafts' , 'POST' , { } , { } , ctx ( ACTIVE_ORG ) ) ) ;
488498
489499 expect ( res . body . data . unhiddenApps ) . toEqual ( [ APP . name ] ) ;
490500 const flipComplaints = ( error . mock . calls as unknown [ ] [ ] )
@@ -600,7 +610,7 @@ describe('#10503 the dispatcher /metadata transport decides org scope on the FOL
600610 // receipted 200, served by nothing.
601611 const viaPlural = makeStack ( ACTIVE_ORG ) ;
602612 const resPlural = responseOf (
603- await viaPlural . dispatcher . handleMetadata ( `/${ plural } /${ item . name } ` , ctx ( ) , 'PUT' , item ) ,
613+ await viaPlural . dispatcher . handleMetadata ( `/${ plural } /${ item . name } ` , ctx ( ACTIVE_ORG ) , 'PUT' , item ) ,
604614 ) ;
605615 expect ( resPlural . status ) . toBe ( 200 ) ;
606616 const pluralRow = metaRow ( viaPlural . engine , singular , item . name ) ;
@@ -612,7 +622,7 @@ describe('#10503 the dispatcher /metadata transport decides org scope on the FOL
612622 // must not have moved.
613623 const viaSingular = makeStack ( ACTIVE_ORG ) ;
614624 const resSingular = responseOf (
615- await viaSingular . dispatcher . handleMetadata ( `/${ singular } /${ item . name } ` , ctx ( ) , 'PUT' , item ) ,
625+ await viaSingular . dispatcher . handleMetadata ( `/${ singular } /${ item . name } ` , ctx ( ACTIVE_ORG ) , 'PUT' , item ) ,
616626 ) ;
617627 expect ( resSingular . status ) . toBe ( 200 ) ;
618628 const singularRow = metaRow ( viaSingular . engine , singular , item . name ) ;
@@ -628,10 +638,10 @@ describe('#10503 the dispatcher /metadata transport decides org scope on the FOL
628638 const { engine, dispatcher } = makeStack ( ACTIVE_ORG ) ;
629639
630640 expect ( responseOf (
631- await dispatcher . handleMetadata ( `/${ plural } /${ item . name } ` , ctx ( ) , 'PUT' , item ) ,
641+ await dispatcher . handleMetadata ( `/${ plural } /${ item . name } ` , ctx ( ACTIVE_ORG ) , 'PUT' , item ) ,
632642 ) . status ) . toBe ( 200 ) ;
633643 expect ( responseOf (
634- await dispatcher . handleMetadata ( `/${ singular } /${ item . name } ` , ctx ( ) , 'PUT' , item ) ,
644+ await dispatcher . handleMetadata ( `/${ singular } /${ item . name } ` , ctx ( ACTIVE_ORG ) , 'PUT' , item ) ,
635645 ) . status ) . toBe ( 200 ) ;
636646
637647 const rows = engine . metaRows ( ) . filter (
@@ -669,7 +679,7 @@ describe('#10503 the dispatcher /metadata transport decides org scope on the FOL
669679 fields : { subject : { type : 'text' , label : 'Subject' } } ,
670680 } ;
671681
672- const res = responseOf ( await dispatcher . handleMetadata ( `/objects/${ OBJECT . name } ` , ctx ( ) , 'PUT' , OBJECT ) ) ;
682+ const res = responseOf ( await dispatcher . handleMetadata ( `/objects/${ OBJECT . name } ` , ctx ( ACTIVE_ORG ) , 'PUT' , OBJECT ) ) ;
673683
674684 expect ( res . status ) . toBe ( 200 ) ;
675685 expect ( metaRow ( engine , 'object' , OBJECT . name ) ! . organization_id ) . toBeNull ( ) ;
@@ -695,7 +705,7 @@ describe('#10503 the dispatcher /metadata transport decides org scope on the FOL
695705 const saveMetaItem = vi . fn ( ) . mockResolvedValue ( { success : true , version : 'v1' , seq : 1 } ) ;
696706 protocol . saveMetaItem = saveMetaItem ;
697707
698- await dispatcher . handleMetadata ( `/${ spelling } /specimen` , ctx ( ) , 'PUT' , { name : 'specimen' } ) ;
708+ await dispatcher . handleMetadata ( `/${ spelling } /specimen` , ctx ( ACTIVE_ORG ) , 'PUT' , { name : 'specimen' } ) ;
699709
700710 expect (
701711 saveMetaItem . mock . calls [ 0 ] [ 0 ] . organizationId ,
@@ -714,7 +724,7 @@ describe('#10503 the dispatcher /metadata transport decides org scope on the FOL
714724 const saveMetaItem = vi . fn ( ) . mockResolvedValue ( { success : true , version : 'v1' , seq : 1 } ) ;
715725 protocol . saveMetaItem = saveMetaItem ;
716726
717- await dispatcher . handleMetadata ( '/translations/zh-CN' , ctx ( ) , 'PUT' , { name : 'zh-CN' } ) ;
727+ await dispatcher . handleMetadata ( '/translations/zh-CN' , ctx ( ACTIVE_ORG ) , 'PUT' , { name : 'zh-CN' } ) ;
718728
719729 const request = saveMetaItem . mock . calls [ 0 ] [ 0 ] ;
720730 expect ( request . type ) . toBe ( 'translations' ) ;
@@ -749,11 +759,11 @@ describe('#10503 the dispatcher /metadata transport decides org scope on the FOL
749759 it ( `/${ plural } /:name/published answers what /${ singular } /:name/published answers` , async ( ) => {
750760 const viaPlural = publishedStore ( singular , item . name ) ;
751761 const pluralRes = responseOf ( await makeStack ( ACTIVE_ORG , viaPlural . service )
752- . dispatcher . handleMetadata ( `/${ plural } /${ item . name } /published` , ctx ( ) , 'GET' ) ) ;
762+ . dispatcher . handleMetadata ( `/${ plural } /${ item . name } /published` , ctx ( ACTIVE_ORG ) , 'GET' ) ) ;
753763
754764 const viaSingular = publishedStore ( singular , item . name ) ;
755765 const singularRes = responseOf ( await makeStack ( ACTIVE_ORG , viaSingular . service )
756- . dispatcher . handleMetadata ( `/${ singular } /${ item . name } /published` , ctx ( ) , 'GET' ) ) ;
766+ . dispatcher . handleMetadata ( `/${ singular } /${ item . name } /published` , ctx ( ACTIVE_ORG ) , 'GET' ) ) ;
757767
758768 // Same status and same body — before the fold the plural was a
759769 // 404 for an item that IS published, the singular a 200.
@@ -775,7 +785,7 @@ describe('#10503 the dispatcher /metadata transport decides org scope on the FOL
775785 expect ( canonicalMetaUrlType ( 'webhook' ) ) . toBe ( 'webhook' ) ;
776786 const store = publishedStore ( 'webhook' , 'stripe' ) ;
777787 const res = responseOf ( await makeStack ( ACTIVE_ORG , store . service )
778- . dispatcher . handleMetadata ( '/webhook/stripe/published' , ctx ( ) , 'GET' ) ) ;
788+ . dispatcher . handleMetadata ( '/webhook/stripe/published' , ctx ( ACTIVE_ORG ) , 'GET' ) ) ;
779789
780790 expect ( res . status ) . toBe ( 200 ) ;
781791 expect ( store . asked ) . toEqual ( [ [ 'webhook' , 'stripe' ] ] ) ;
0 commit comments