@@ -113,7 +113,7 @@ import {
113113 resolveAuthzContext ,
114114 isAuthzStoreUnavailableError ,
115115 // ADR-0069 — the authentication-policy gate's ONE decision, asked of the
116- // session `resolveInstallPrincipal` reads (see `admitInstallCaller `).
116+ // session `resolveInstallPrincipal` reads (see `refuseHeldSession `).
117117 evaluateAuthGate ,
118118 type AuthGate ,
119119 // [#16013] The ADMISSION seam's posture derivation, in ONE call — the same
@@ -547,11 +547,28 @@ interface InstallPrincipal {
547547 /**
548548 * ADR-0069: the authentication-policy posture of the session this
549549 * admission resolved, present only for a session the policy holds.
550- * `admitInstallCaller ` refuses on it.
550+ * `refuseHeldSession ` refuses on it.
551551 */
552552 authGate ?: AuthGate ;
553553}
554554
555+ /**
556+ * ADR-0069: the refusal every install-local door gives a session the
557+ * authentication policy HOLDS (an expired password, enforced MFA not yet
558+ * enrolled), right after the 401 and before the caller's capabilities are
559+ * consulted or the ledger is read. Such a session is gated out of every
560+ * protected resource until it remediates, so it gets REST's answer: `403`
561+ * with the gate's own `code` and `message`, in this plugin's envelope. The
562+ * consult itself happens once, in `resolveInstallPrincipal`; this only
563+ * answers on it, in one place, so a door added later cannot copy a weaker
564+ * answer. `null` when the session is not held.
565+ */
566+ function refuseHeldSession ( c : any , principal : InstallPrincipal ) : Response | null {
567+ if ( ! principal . authGate ) return null ;
568+ const { code, message } = principal . authGate ;
569+ return c . json ( { success : false , error : { code, message } } , 403 ) ;
570+ }
571+
555572/**
556573 * [#15204 W2] What an admitted mutating request carries past
557574 * `requireInstallCapability`: the acting user, plus the two inputs ADR-0131
@@ -1742,9 +1759,10 @@ export class MarketplaceInstallLocalPlugin implements Plugin {
17421759 // Before the ledger is touched, exactly as the mutating doors refuse
17431760 // before any work: a refused caller must not be able to learn what is
17441761 // installed from timing or from a downstream storage error.
1745- const admitted = await this . admitInstallCaller ( c , ctx ) ;
1746- if ( ! admitted . ok ) return admitted . response ;
1747- const { principal } = admitted ;
1762+ const principal = await this . resolveInstallPrincipal ( c , ctx ) ;
1763+ if ( ! principal ) return this . refuseUnauthenticated ( c ) ;
1764+ const held = refuseHeldSession ( c , principal ) ;
1765+ if ( held ) return held ;
17481766 const operator = principal . systemPermissions . includes ( INSTALL_LOCAL_CAPABILITY ) ;
17491767
17501768 const { entries, skipped } = this . readAll ( ) ;
@@ -3389,29 +3407,6 @@ export class MarketplaceInstallLocalPlugin implements Plugin {
33893407 error : { code : 'UNAUTHENTICATED' , message : 'Authentication required.' } ,
33903408 } , 401 ) ;
33913409
3392- /**
3393- * ADR-0069: the floor all five doors open with, the listing included.
3394- * Nobody authenticated is `401` ({@link refuseUnauthenticated}). A session
3395- * the authentication policy HOLDS (an expired password, enforced MFA not
3396- * yet enrolled) is gated out of every protected resource until it
3397- * remediates, so it gets REST's answer: `403` with the gate's own `code`
3398- * and `message`, in this plugin's envelope, before its capabilities are
3399- * consulted and before the ledger is read. One helper for the same reason
3400- * the 401 is one literal: a door added later cannot copy a weaker answer.
3401- */
3402- private admitInstallCaller = async (
3403- c : any ,
3404- ctx : PluginContext ,
3405- ) : Promise < { ok : true ; principal : InstallPrincipal } | { ok : false ; response : Response } > => {
3406- const principal = await this . resolveInstallPrincipal ( c , ctx ) ;
3407- if ( ! principal ) return { ok : false , response : this . refuseUnauthenticated ( c ) } ;
3408- if ( principal . authGate ) {
3409- const { code, message } = principal . authGate ;
3410- return { ok : false , response : c . json ( { success : false , error : { code, message } } , 403 ) } ;
3411- }
3412- return { ok : true , principal } ;
3413- } ;
3414-
34153410 /**
34163411 * [#8976] The shared refusal for the four mutating routes: 401 when nobody
34173412 * is authenticated, 403 when somebody is but holds no authoring capability,
@@ -3436,9 +3431,10 @@ export class MarketplaceInstallLocalPlugin implements Plugin {
34363431 ctx : PluginContext ,
34373432 action : string ,
34383433 ) : Promise < InstallAdmission | { ok : false ; response : Response } > => {
3439- const admitted = await this . admitInstallCaller ( c , ctx ) ;
3440- if ( ! admitted . ok ) return admitted ;
3441- const { principal } = admitted ;
3434+ const principal = await this . resolveInstallPrincipal ( c , ctx ) ;
3435+ if ( ! principal ) return { ok : false , response : this . refuseUnauthenticated ( c ) } ;
3436+ const held = refuseHeldSession ( c , principal ) ;
3437+ if ( held ) return { ok : false , response : held } ;
34423438 if ( ! principal . systemPermissions . includes ( INSTALL_LOCAL_CAPABILITY ) ) {
34433439 ctx . logger ?. warn ?.(
34443440 `[MarketplaceInstallLocal] refused ${ action } for ${ principal . userId } — `
0 commit comments