@c9up/warden 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/middleware.d.ts +43 -13
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +56 -14
- package/dist/middleware.js.map +1 -1
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +2 -2
- package/src/middleware.ts +100 -32
package/dist/middleware.d.ts
CHANGED
|
@@ -22,30 +22,50 @@ import type { Ability } from "./bouncer/types.js";
|
|
|
22
22
|
import type { ScopeRequestContext } from "./config.js";
|
|
23
23
|
import type { Scope } from "./rights/types.js";
|
|
24
24
|
import type { SessionStore } from "./strategies/SessionStrategy.js";
|
|
25
|
+
/**
|
|
26
|
+
* Tokens warden resolves from the container: a service class, or a string/symbol
|
|
27
|
+
* alias (`"bouncer:registry"`). Mirrors Ream's `ServiceToken` without importing
|
|
28
|
+
* it — warden declares the shape so it stays framework-agnostic.
|
|
29
|
+
*/
|
|
30
|
+
type ResolvableToken = string | symbol | (abstract new (...args: never[]) => unknown);
|
|
31
|
+
/**
|
|
32
|
+
* Per-request IoC resolver Ream exposes as `ctx.containerResolver` (Adonis
|
|
33
|
+
* idiom). Warden resolves AuthManager / RightsResolver / the bouncer registry
|
|
34
|
+
* through this — reading from the context it is HANDED — so the package never
|
|
35
|
+
* imports `@c9up/ream` at runtime. A host that provides none (non-Ream, or a
|
|
36
|
+
* misconfigured kernel) yields no resolution: guarded routes fail CLOSED.
|
|
37
|
+
*/
|
|
38
|
+
interface ContainerResolver {
|
|
39
|
+
make(token: ResolvableToken): unknown;
|
|
40
|
+
}
|
|
25
41
|
export interface WardenContext {
|
|
26
42
|
request: {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
headers: Record<string, string>;
|
|
43
|
+
/** Ream's `HttpContext` exposes headers as a METHOD, not a property. */
|
|
44
|
+
headers(): Record<string, string>;
|
|
30
45
|
};
|
|
46
|
+
/**
|
|
47
|
+
* Per-request IoC resolver (Ream's `ctx.containerResolver`). Warden resolves
|
|
48
|
+
* AuthManager + the bouncer registry through it — agnostic, no `@c9up/ream`
|
|
49
|
+
* import. Absent on a non-Ream host → guarded routes fail closed.
|
|
50
|
+
*/
|
|
51
|
+
containerResolver?: ContainerResolver;
|
|
31
52
|
response: {
|
|
32
|
-
status: (code: number) =>
|
|
53
|
+
status: (code: number) => unknown;
|
|
33
54
|
json: (data: unknown) => void;
|
|
34
55
|
};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Controller prototype + method that own the route — Ream populates these
|
|
58
|
+
* top-level on the `HttpContext` (from `match.route.controller`). Absent for
|
|
59
|
+
* inline-function routes. Read by the guard-metadata lookup.
|
|
60
|
+
*/
|
|
61
|
+
controller?: object;
|
|
62
|
+
action?: string | symbol;
|
|
38
63
|
/** Session store — set by a session middleware upstream. */
|
|
39
64
|
session?: SessionStore;
|
|
40
|
-
/** Set by the middleware after successful auth. */
|
|
65
|
+
/** Set by the middleware after successful auth (Ream's `auth` slot). */
|
|
41
66
|
auth?: AuthResult;
|
|
42
67
|
/** Set by `initializeBouncer` — the per-request authorization entry point. */
|
|
43
68
|
bouncer?: Bouncer;
|
|
44
|
-
/** The route handler metadata (decorators). */
|
|
45
|
-
route?: {
|
|
46
|
-
controller?: object;
|
|
47
|
-
action?: string | symbol;
|
|
48
|
-
};
|
|
49
69
|
}
|
|
50
70
|
type WardenNext = () => Promise<void> | void;
|
|
51
71
|
/**
|
|
@@ -78,5 +98,15 @@ export interface BouncerRegistry {
|
|
|
78
98
|
* including for guests — matching AdonisJS's `initialize_bouncer_middleware`.
|
|
79
99
|
*/
|
|
80
100
|
export declare function initializeBouncer(ctx: WardenContext, next: WardenNext): Promise<void>;
|
|
101
|
+
/**
|
|
102
|
+
* Default export — the Adonis-style class form Ream's lazy middleware resolver
|
|
103
|
+
* expects (`new mod.default().handle(ctx, next)`). Without it the documented
|
|
104
|
+
* `router.use([() => import('@c9up/warden/middleware')])` crashes with
|
|
105
|
+
* `new undefined()`. The named `wardenMiddleware` / `initializeBouncer` stay for
|
|
106
|
+
* direct registration `router.use([wardenMiddleware])`.
|
|
107
|
+
*/
|
|
108
|
+
export default class WardenMiddleware {
|
|
109
|
+
handle(ctx: WardenContext, next: WardenNext): Promise<void>;
|
|
110
|
+
}
|
|
81
111
|
export {};
|
|
82
112
|
//# sourceMappingURL=middleware.d.ts.map
|
package/dist/middleware.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAEN,KAAK,UAAU,EAGf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAEN,KAAK,UAAU,EAGf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AASvD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAcpE;;;;GAIG;AACH,KAAK,eAAe,GACjB,MAAM,GACN,MAAM,GACN,CAAC,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC,CAAC;AAEhD;;;;;;GAMG;AACH,UAAU,iBAAiB;IAC1B,IAAI,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE;QACR,wEAAwE;QACxE,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAClC,CAAC;IACF;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,QAAQ,EAAE;QACT,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAClC,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;KAC9B,CAAC;IACF;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,wEAAwE;IACxE,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,8EAA8E;IAC9E,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AA0BD,KAAK,UAAU,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAE7C;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,iBAoI1E;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC5C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,UAAU,CAAC,CAAC;IAC/C,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;CACpE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,iBAmB3E;AA0ID;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACpC,MAAM,CAAC,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAG3D"}
|
package/dist/middleware.js
CHANGED
|
@@ -17,21 +17,53 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { AuthManager, sanitizePayload, } from "./AuthManager.js";
|
|
19
19
|
import { Bouncer } from "./bouncer/Bouncer.js";
|
|
20
|
+
import { WardenError } from "./errors.js";
|
|
20
21
|
import { getGuardMetadata, getPermissionMetadata, getRequireMfaMetadata, getRoleMetadata, } from "./Guard.js";
|
|
21
22
|
import { RightsResolver } from "./rights/RightsResolver.js";
|
|
22
23
|
function hasVerifyWithContext(strategy) {
|
|
23
24
|
return (typeof strategy.verifyWithContext === "function");
|
|
24
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve a token from the request's IoC resolver (`ctx.containerResolver`,
|
|
28
|
+
* Adonis idiom). Returns undefined when no resolver is present or the token is
|
|
29
|
+
* unbound — callers decide fail-open (the bouncer registry) vs fail-closed (the
|
|
30
|
+
* AuthManager gate in `wardenMiddleware`). No `@c9up/ream` import: warden reads
|
|
31
|
+
* only from the context Ream hands it.
|
|
32
|
+
*/
|
|
33
|
+
function resolveFromCtx(ctx, token) {
|
|
34
|
+
try {
|
|
35
|
+
return ctx.containerResolver?.make(token);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Bridge Ream's ctx to the `resolveScope` hook's `ScopeRequestContext`, which
|
|
43
|
+
* takes headers as a plain object (a snapshot) — keeps the app-facing hook
|
|
44
|
+
* contract stable while warden reads Ream's `headers()` method internally.
|
|
45
|
+
*/
|
|
46
|
+
function toScopeContext(ctx) {
|
|
47
|
+
return { request: { headers: ctx.request.headers() }, auth: ctx.auth };
|
|
48
|
+
}
|
|
25
49
|
/**
|
|
26
50
|
* Ream auth middleware — resolves AuthManager from the container,
|
|
27
51
|
* reads `@Guard`/`@Permission`/`@Role` metadata from the route handler,
|
|
28
52
|
* and authenticates + authorizes the request.
|
|
29
53
|
*/
|
|
30
54
|
export async function wardenMiddleware(ctx, next) {
|
|
31
|
-
const
|
|
55
|
+
const resolvedAuth = resolveFromCtx(ctx, AuthManager);
|
|
56
|
+
if (!(resolvedAuth instanceof AuthManager)) {
|
|
57
|
+
// Fail CLOSED — a missing AuthManager (WardenProvider not registered, or
|
|
58
|
+
// no `ctx.containerResolver` on a non-Ream host) must never silently let
|
|
59
|
+
// a guarded request through.
|
|
60
|
+
throw new WardenError("AUTH_NOT_REGISTERED", "AuthManager is not registered in the container — add WardenProvider to your providers list.");
|
|
61
|
+
}
|
|
62
|
+
const auth = resolvedAuth;
|
|
32
63
|
// Read guard metadata from the route handler (if declared via decorators).
|
|
33
|
-
|
|
34
|
-
const
|
|
64
|
+
// Ream populates `ctx.controller` / `ctx.action` top-level.
|
|
65
|
+
const controller = ctx.controller;
|
|
66
|
+
const action = ctx.action;
|
|
35
67
|
const strategies = controller && action ? getGuardMetadata(controller, action) : [];
|
|
36
68
|
// No guard on this route — pass through (public endpoint).
|
|
37
69
|
if (strategies.length === 0) {
|
|
@@ -43,7 +75,8 @@ export async function wardenMiddleware(ctx, next) {
|
|
|
43
75
|
// 2. API key header — reads the header name from the ApiKeyStrategy config
|
|
44
76
|
// (defaults to 'x-api-key' if no ApiKeyStrategy is registered)
|
|
45
77
|
// 3. Cookie/session (Session strategy — handled by the strategy itself via context)
|
|
46
|
-
const
|
|
78
|
+
const headers = ctx.request.headers();
|
|
79
|
+
const authHeader = headers.authorization ?? "";
|
|
47
80
|
const bearerToken = authHeader.startsWith("Bearer ")
|
|
48
81
|
? authHeader.slice(7)
|
|
49
82
|
: "";
|
|
@@ -61,7 +94,7 @@ export async function wardenMiddleware(ctx, next) {
|
|
|
61
94
|
// declared with `headerName: "X-Custom-Key"` would never match the
|
|
62
95
|
// incoming `x-custom-key` key without this normalisation — auth
|
|
63
96
|
// would silently fail even with the right header on the wire.
|
|
64
|
-
const apiKey =
|
|
97
|
+
const apiKey = headers[apiKeyHeader.toLowerCase()] ?? "";
|
|
65
98
|
const hasSessionStrategy = strategies.some((s) => s === "session");
|
|
66
99
|
if (!bearerToken && !apiKey && !hasSessionStrategy) {
|
|
67
100
|
ctx.response.status(401);
|
|
@@ -150,7 +183,7 @@ export async function initializeBouncer(ctx, next) {
|
|
|
150
183
|
const registry = isBouncerRegistry(candidate) ? candidate : undefined;
|
|
151
184
|
const user = ctx.auth?.user ?? null;
|
|
152
185
|
const scope = registry?.resolveScope
|
|
153
|
-
? await registry.resolveScope(ctx)
|
|
186
|
+
? await registry.resolveScope(toScopeContext(ctx))
|
|
154
187
|
: "global";
|
|
155
188
|
ctx.bouncer = new Bouncer(user, registry?.abilities, registry?.policies, {
|
|
156
189
|
scope,
|
|
@@ -158,14 +191,9 @@ export async function initializeBouncer(ctx, next) {
|
|
|
158
191
|
});
|
|
159
192
|
await next();
|
|
160
193
|
}
|
|
161
|
-
/** Resolve a container token, returning undefined instead of throwing when absent. */
|
|
194
|
+
/** Resolve a container token from the request resolver, returning undefined instead of throwing when absent. */
|
|
162
195
|
function tryResolve(ctx, token) {
|
|
163
|
-
|
|
164
|
-
return ctx.container.resolve(token);
|
|
165
|
-
}
|
|
166
|
-
catch {
|
|
167
|
-
return undefined;
|
|
168
|
-
}
|
|
196
|
+
return resolveFromCtx(ctx, token);
|
|
169
197
|
}
|
|
170
198
|
/** Structural guard — the registry is a plain object carrying ability/policy tables. */
|
|
171
199
|
function isBouncerRegistry(value) {
|
|
@@ -183,7 +211,9 @@ function isBouncerRegistry(value) {
|
|
|
183
211
|
async function resolveRequestScope(ctx) {
|
|
184
212
|
const candidate = tryResolve(ctx, "bouncer:registry");
|
|
185
213
|
const registry = isBouncerRegistry(candidate) ? candidate : undefined;
|
|
186
|
-
return registry?.resolveScope
|
|
214
|
+
return registry?.resolveScope
|
|
215
|
+
? await registry.resolveScope(toScopeContext(ctx))
|
|
216
|
+
: "global";
|
|
187
217
|
}
|
|
188
218
|
/**
|
|
189
219
|
* Try each declared strategy in order — session strategies via
|
|
@@ -271,4 +301,16 @@ async function checkAuthorization(auth, user, controller, action, scope) {
|
|
|
271
301
|
}
|
|
272
302
|
return null;
|
|
273
303
|
}
|
|
304
|
+
/**
|
|
305
|
+
* Default export — the Adonis-style class form Ream's lazy middleware resolver
|
|
306
|
+
* expects (`new mod.default().handle(ctx, next)`). Without it the documented
|
|
307
|
+
* `router.use([() => import('@c9up/warden/middleware')])` crashes with
|
|
308
|
+
* `new undefined()`. The named `wardenMiddleware` / `initializeBouncer` stay for
|
|
309
|
+
* direct registration `router.use([wardenMiddleware])`.
|
|
310
|
+
*/
|
|
311
|
+
export default class WardenMiddleware {
|
|
312
|
+
handle(ctx, next) {
|
|
313
|
+
return wardenMiddleware(ctx, next);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
274
316
|
//# sourceMappingURL=middleware.js.map
|
package/dist/middleware.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACN,WAAW,EAGX,eAAe,GACf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAG/C,OAAO,EACN,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAQ5D,SAAS,oBAAoB,CAC5B,QAAsB;IAEtB,OAAO,CACN,OAAQ,QAAgC,CAAC,iBAAiB,KAAK,UAAU,CACzE,CAAC;AACH,CAAC;
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
|
package/index.darwin-arm64.node
CHANGED
|
Binary file
|
package/index.darwin-x64.node
CHANGED
|
Binary file
|
|
Binary file
|
package/index.linux-x64-gnu.node
CHANGED
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c9up/warden",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Warden — Authentication & authorization for the Ream framework",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@c9up/ream": "^0.1.
|
|
48
|
+
"@c9up/ream": "^0.1.10"
|
|
49
49
|
},
|
|
50
50
|
"peerDependenciesMeta": {
|
|
51
51
|
"@c9up/ream": {
|
package/src/middleware.ts
CHANGED
|
@@ -26,6 +26,7 @@ import type { BasePolicy } from "./bouncer/BasePolicy.js";
|
|
|
26
26
|
import { Bouncer } from "./bouncer/Bouncer.js";
|
|
27
27
|
import type { Ability } from "./bouncer/types.js";
|
|
28
28
|
import type { ScopeRequestContext } from "./config.js";
|
|
29
|
+
import { WardenError } from "./errors.js";
|
|
29
30
|
import {
|
|
30
31
|
getGuardMetadata,
|
|
31
32
|
getPermissionMetadata,
|
|
@@ -48,32 +49,79 @@ function hasVerifyWithContext(
|
|
|
48
49
|
);
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Tokens warden resolves from the container: a service class, or a string/symbol
|
|
54
|
+
* alias (`"bouncer:registry"`). Mirrors Ream's `ServiceToken` without importing
|
|
55
|
+
* it — warden declares the shape so it stays framework-agnostic.
|
|
56
|
+
*/
|
|
57
|
+
type ResolvableToken =
|
|
58
|
+
| string
|
|
59
|
+
| symbol
|
|
60
|
+
| (abstract new (...args: never[]) => unknown);
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Per-request IoC resolver Ream exposes as `ctx.containerResolver` (Adonis
|
|
64
|
+
* idiom). Warden resolves AuthManager / RightsResolver / the bouncer registry
|
|
65
|
+
* through this — reading from the context it is HANDED — so the package never
|
|
66
|
+
* imports `@c9up/ream` at runtime. A host that provides none (non-Ream, or a
|
|
67
|
+
* misconfigured kernel) yields no resolution: guarded routes fail CLOSED.
|
|
68
|
+
*/
|
|
69
|
+
interface ContainerResolver {
|
|
70
|
+
make(token: ResolvableToken): unknown;
|
|
71
|
+
}
|
|
72
|
+
|
|
51
73
|
export interface WardenContext {
|
|
52
74
|
request: {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
headers: Record<string, string>;
|
|
75
|
+
/** Ream's `HttpContext` exposes headers as a METHOD, not a property. */
|
|
76
|
+
headers(): Record<string, string>;
|
|
56
77
|
};
|
|
78
|
+
/**
|
|
79
|
+
* Per-request IoC resolver (Ream's `ctx.containerResolver`). Warden resolves
|
|
80
|
+
* AuthManager + the bouncer registry through it — agnostic, no `@c9up/ream`
|
|
81
|
+
* import. Absent on a non-Ream host → guarded routes fail closed.
|
|
82
|
+
*/
|
|
83
|
+
containerResolver?: ContainerResolver;
|
|
57
84
|
response: {
|
|
58
|
-
status: (code: number) =>
|
|
85
|
+
status: (code: number) => unknown;
|
|
59
86
|
json: (data: unknown) => void;
|
|
60
87
|
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Controller prototype + method that own the route — Ream populates these
|
|
90
|
+
* top-level on the `HttpContext` (from `match.route.controller`). Absent for
|
|
91
|
+
* inline-function routes. Read by the guard-metadata lookup.
|
|
92
|
+
*/
|
|
93
|
+
controller?: object;
|
|
94
|
+
action?: string | symbol;
|
|
66
95
|
/** Session store — set by a session middleware upstream. */
|
|
67
96
|
session?: SessionStore;
|
|
68
|
-
/** Set by the middleware after successful auth. */
|
|
97
|
+
/** Set by the middleware after successful auth (Ream's `auth` slot). */
|
|
69
98
|
auth?: AuthResult;
|
|
70
99
|
/** Set by `initializeBouncer` — the per-request authorization entry point. */
|
|
71
100
|
bouncer?: Bouncer;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Resolve a token from the request's IoC resolver (`ctx.containerResolver`,
|
|
105
|
+
* Adonis idiom). Returns undefined when no resolver is present or the token is
|
|
106
|
+
* unbound — callers decide fail-open (the bouncer registry) vs fail-closed (the
|
|
107
|
+
* AuthManager gate in `wardenMiddleware`). No `@c9up/ream` import: warden reads
|
|
108
|
+
* only from the context Ream hands it.
|
|
109
|
+
*/
|
|
110
|
+
function resolveFromCtx(ctx: WardenContext, token: ResolvableToken): unknown {
|
|
111
|
+
try {
|
|
112
|
+
return ctx.containerResolver?.make(token);
|
|
113
|
+
} catch {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Bridge Ream's ctx to the `resolveScope` hook's `ScopeRequestContext`, which
|
|
120
|
+
* takes headers as a plain object (a snapshot) — keeps the app-facing hook
|
|
121
|
+
* contract stable while warden reads Ream's `headers()` method internally.
|
|
122
|
+
*/
|
|
123
|
+
function toScopeContext(ctx: WardenContext): ScopeRequestContext {
|
|
124
|
+
return { request: { headers: ctx.request.headers() }, auth: ctx.auth };
|
|
77
125
|
}
|
|
78
126
|
|
|
79
127
|
type WardenNext = () => Promise<void> | void;
|
|
@@ -84,11 +132,22 @@ type WardenNext = () => Promise<void> | void;
|
|
|
84
132
|
* and authenticates + authorizes the request.
|
|
85
133
|
*/
|
|
86
134
|
export async function wardenMiddleware(ctx: WardenContext, next: WardenNext) {
|
|
87
|
-
const
|
|
135
|
+
const resolvedAuth = resolveFromCtx(ctx, AuthManager);
|
|
136
|
+
if (!(resolvedAuth instanceof AuthManager)) {
|
|
137
|
+
// Fail CLOSED — a missing AuthManager (WardenProvider not registered, or
|
|
138
|
+
// no `ctx.containerResolver` on a non-Ream host) must never silently let
|
|
139
|
+
// a guarded request through.
|
|
140
|
+
throw new WardenError(
|
|
141
|
+
"AUTH_NOT_REGISTERED",
|
|
142
|
+
"AuthManager is not registered in the container — add WardenProvider to your providers list.",
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
const auth = resolvedAuth;
|
|
88
146
|
|
|
89
147
|
// Read guard metadata from the route handler (if declared via decorators).
|
|
90
|
-
|
|
91
|
-
const
|
|
148
|
+
// Ream populates `ctx.controller` / `ctx.action` top-level.
|
|
149
|
+
const controller = ctx.controller;
|
|
150
|
+
const action = ctx.action;
|
|
92
151
|
const strategies =
|
|
93
152
|
controller && action ? getGuardMetadata(controller, action) : [];
|
|
94
153
|
|
|
@@ -103,7 +162,8 @@ export async function wardenMiddleware(ctx: WardenContext, next: WardenNext) {
|
|
|
103
162
|
// 2. API key header — reads the header name from the ApiKeyStrategy config
|
|
104
163
|
// (defaults to 'x-api-key' if no ApiKeyStrategy is registered)
|
|
105
164
|
// 3. Cookie/session (Session strategy — handled by the strategy itself via context)
|
|
106
|
-
const
|
|
165
|
+
const headers = ctx.request.headers();
|
|
166
|
+
const authHeader = headers.authorization ?? "";
|
|
107
167
|
const bearerToken = authHeader.startsWith("Bearer ")
|
|
108
168
|
? authHeader.slice(7)
|
|
109
169
|
: "";
|
|
@@ -120,7 +180,7 @@ export async function wardenMiddleware(ctx: WardenContext, next: WardenNext) {
|
|
|
120
180
|
// declared with `headerName: "X-Custom-Key"` would never match the
|
|
121
181
|
// incoming `x-custom-key` key without this normalisation — auth
|
|
122
182
|
// would silently fail even with the right header on the wire.
|
|
123
|
-
const apiKey =
|
|
183
|
+
const apiKey = headers[apiKeyHeader.toLowerCase()] ?? "";
|
|
124
184
|
|
|
125
185
|
const hasSessionStrategy = strategies.some((s) => s === "session");
|
|
126
186
|
if (!bearerToken && !apiKey && !hasSessionStrategy) {
|
|
@@ -240,7 +300,7 @@ export async function initializeBouncer(ctx: WardenContext, next: WardenNext) {
|
|
|
240
300
|
|
|
241
301
|
const user = ctx.auth?.user ?? null;
|
|
242
302
|
const scope: Scope = registry?.resolveScope
|
|
243
|
-
? await registry.resolveScope(ctx)
|
|
303
|
+
? await registry.resolveScope(toScopeContext(ctx))
|
|
244
304
|
: "global";
|
|
245
305
|
|
|
246
306
|
ctx.bouncer = new Bouncer(user, registry?.abilities, registry?.policies, {
|
|
@@ -250,16 +310,9 @@ export async function initializeBouncer(ctx: WardenContext, next: WardenNext) {
|
|
|
250
310
|
await next();
|
|
251
311
|
}
|
|
252
312
|
|
|
253
|
-
/** Resolve a container token, returning undefined instead of throwing when absent. */
|
|
254
|
-
function tryResolve(
|
|
255
|
-
ctx
|
|
256
|
-
token: string | (abstract new (...args: never[]) => unknown),
|
|
257
|
-
): unknown {
|
|
258
|
-
try {
|
|
259
|
-
return ctx.container.resolve(token);
|
|
260
|
-
} catch {
|
|
261
|
-
return undefined;
|
|
262
|
-
}
|
|
313
|
+
/** Resolve a container token from the request resolver, returning undefined instead of throwing when absent. */
|
|
314
|
+
function tryResolve(ctx: WardenContext, token: ResolvableToken): unknown {
|
|
315
|
+
return resolveFromCtx(ctx, token);
|
|
263
316
|
}
|
|
264
317
|
|
|
265
318
|
/** Structural guard — the registry is a plain object carrying ability/policy tables. */
|
|
@@ -281,7 +334,9 @@ function isBouncerRegistry(value: unknown): value is BouncerRegistry {
|
|
|
281
334
|
async function resolveRequestScope(ctx: WardenContext): Promise<Scope> {
|
|
282
335
|
const candidate = tryResolve(ctx, "bouncer:registry");
|
|
283
336
|
const registry = isBouncerRegistry(candidate) ? candidate : undefined;
|
|
284
|
-
return registry?.resolveScope
|
|
337
|
+
return registry?.resolveScope
|
|
338
|
+
? await registry.resolveScope(toScopeContext(ctx))
|
|
339
|
+
: "global";
|
|
285
340
|
}
|
|
286
341
|
|
|
287
342
|
/**
|
|
@@ -390,3 +445,16 @@ async function checkAuthorization(
|
|
|
390
445
|
}
|
|
391
446
|
return null;
|
|
392
447
|
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Default export — the Adonis-style class form Ream's lazy middleware resolver
|
|
451
|
+
* expects (`new mod.default().handle(ctx, next)`). Without it the documented
|
|
452
|
+
* `router.use([() => import('@c9up/warden/middleware')])` crashes with
|
|
453
|
+
* `new undefined()`. The named `wardenMiddleware` / `initializeBouncer` stay for
|
|
454
|
+
* direct registration `router.use([wardenMiddleware])`.
|
|
455
|
+
*/
|
|
456
|
+
export default class WardenMiddleware {
|
|
457
|
+
handle(ctx: WardenContext, next: WardenNext): Promise<void> {
|
|
458
|
+
return wardenMiddleware(ctx, next);
|
|
459
|
+
}
|
|
460
|
+
}
|