@c9up/warden 0.1.3 → 0.1.5
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/README.md +57 -0
- package/dist/AuthManager.d.ts +98 -0
- package/dist/AuthManager.d.ts.map +1 -0
- package/dist/AuthManager.js +147 -0
- package/dist/AuthManager.js.map +1 -0
- package/dist/AuthRateLimiter.d.ts +29 -0
- package/dist/AuthRateLimiter.d.ts.map +1 -0
- package/dist/AuthRateLimiter.js +88 -0
- package/dist/AuthRateLimiter.js.map +1 -0
- package/dist/Guard.d.ts +35 -0
- package/dist/Guard.d.ts.map +1 -0
- package/dist/Guard.js +68 -0
- package/dist/Guard.js.map +1 -0
- package/dist/RedisBlacklistDriver.d.ts +32 -0
- package/dist/RedisBlacklistDriver.d.ts.map +1 -0
- package/dist/RedisBlacklistDriver.js +37 -0
- package/dist/RedisBlacklistDriver.js.map +1 -0
- package/dist/RefreshTokenStore.d.ts +34 -0
- package/dist/RefreshTokenStore.d.ts.map +1 -0
- package/dist/RefreshTokenStore.js +48 -0
- package/dist/RefreshTokenStore.js.map +1 -0
- package/dist/ResilientBlacklistDriver.d.ts +35 -0
- package/dist/ResilientBlacklistDriver.d.ts.map +1 -0
- package/dist/ResilientBlacklistDriver.js +61 -0
- package/dist/ResilientBlacklistDriver.js.map +1 -0
- package/dist/TokenBlacklist.d.ts +30 -0
- package/dist/TokenBlacklist.d.ts.map +1 -0
- package/dist/TokenBlacklist.js +50 -0
- package/dist/TokenBlacklist.js.map +1 -0
- package/dist/WardenProvider.d.ts +19 -0
- package/dist/WardenProvider.d.ts.map +1 -0
- package/dist/WardenProvider.js +71 -0
- package/dist/WardenProvider.js.map +1 -0
- package/dist/bouncer/AuthorizationResponse.d.ts +25 -0
- package/dist/bouncer/AuthorizationResponse.d.ts.map +1 -0
- package/dist/bouncer/AuthorizationResponse.js +31 -0
- package/dist/bouncer/AuthorizationResponse.js.map +1 -0
- package/dist/bouncer/BasePolicy.d.ts +47 -0
- package/dist/bouncer/BasePolicy.d.ts.map +1 -0
- package/dist/bouncer/BasePolicy.js +36 -0
- package/dist/bouncer/BasePolicy.js.map +1 -0
- package/dist/bouncer/Bouncer.d.ts +47 -0
- package/dist/bouncer/Bouncer.d.ts.map +1 -0
- package/dist/bouncer/Bouncer.js +134 -0
- package/dist/bouncer/Bouncer.js.map +1 -0
- package/dist/bouncer/PolicyAuthorizer.d.ts +24 -0
- package/dist/bouncer/PolicyAuthorizer.d.ts.map +1 -0
- package/dist/bouncer/PolicyAuthorizer.js +92 -0
- package/dist/bouncer/PolicyAuthorizer.js.map +1 -0
- package/dist/bouncer/decorators.d.ts +25 -0
- package/dist/bouncer/decorators.d.ts.map +1 -0
- package/dist/bouncer/decorators.js +36 -0
- package/dist/bouncer/decorators.js.map +1 -0
- package/dist/bouncer/evaluate.d.ts +36 -0
- package/dist/bouncer/evaluate.d.ts.map +1 -0
- package/dist/bouncer/evaluate.js +64 -0
- package/dist/bouncer/evaluate.js.map +1 -0
- package/dist/bouncer/policyContext.d.ts +29 -0
- package/dist/bouncer/policyContext.d.ts.map +1 -0
- package/dist/bouncer/policyContext.js +36 -0
- package/dist/bouncer/policyContext.js.map +1 -0
- package/dist/bouncer/types.d.ts +52 -0
- package/dist/bouncer/types.d.ts.map +1 -0
- package/dist/bouncer/types.js +9 -0
- package/dist/bouncer/types.js.map +1 -0
- package/dist/config.d.ts +51 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +21 -0
- package/dist/config.js.map +1 -0
- package/dist/configure.d.ts +10 -0
- package/dist/configure.d.ts.map +1 -0
- package/dist/configure.js +44 -0
- package/dist/configure.js.map +1 -0
- package/dist/errors.d.ts +18 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +21 -0
- package/dist/errors.js.map +1 -0
- package/dist/firstcontact/FirstContactManager.d.ts +25 -0
- package/dist/firstcontact/FirstContactManager.d.ts.map +1 -0
- package/dist/firstcontact/FirstContactManager.js +38 -0
- package/dist/firstcontact/FirstContactManager.js.map +1 -0
- package/dist/firstcontact/drivers/GitHubDriver.d.ts +14 -0
- package/dist/firstcontact/drivers/GitHubDriver.d.ts.map +1 -0
- package/dist/firstcontact/drivers/GitHubDriver.js +61 -0
- package/dist/firstcontact/drivers/GitHubDriver.js.map +1 -0
- package/dist/firstcontact/drivers/GoogleDriver.d.ts +14 -0
- package/dist/firstcontact/drivers/GoogleDriver.d.ts.map +1 -0
- package/dist/firstcontact/drivers/GoogleDriver.js +64 -0
- package/dist/firstcontact/drivers/GoogleDriver.js.map +1 -0
- package/dist/firstcontact/types.d.ts +34 -0
- package/dist/firstcontact/types.d.ts.map +1 -0
- package/dist/firstcontact/types.js +5 -0
- package/dist/firstcontact/types.js.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/mfa/BackupCodesProvider.d.ts +39 -0
- package/dist/mfa/BackupCodesProvider.d.ts.map +1 -0
- package/dist/mfa/BackupCodesProvider.js +87 -0
- package/dist/mfa/BackupCodesProvider.js.map +1 -0
- package/dist/mfa/MfaManager.d.ts +107 -0
- package/dist/mfa/MfaManager.d.ts.map +1 -0
- package/dist/mfa/MfaManager.js +219 -0
- package/dist/mfa/MfaManager.js.map +1 -0
- package/dist/mfa/OtpProvider.d.ts +67 -0
- package/dist/mfa/OtpProvider.d.ts.map +1 -0
- package/dist/mfa/OtpProvider.js +104 -0
- package/dist/mfa/OtpProvider.js.map +1 -0
- package/dist/mfa/TotpProvider.d.ts +52 -0
- package/dist/mfa/TotpProvider.d.ts.map +1 -0
- package/dist/mfa/TotpProvider.js +103 -0
- package/dist/mfa/TotpProvider.js.map +1 -0
- package/dist/mfa/WebauthnProvider.d.ts +175 -0
- package/dist/mfa/WebauthnProvider.d.ts.map +1 -0
- package/dist/mfa/WebauthnProvider.js +239 -0
- package/dist/mfa/WebauthnProvider.js.map +1 -0
- package/dist/mfa/base32.d.ts +13 -0
- package/dist/mfa/base32.d.ts.map +1 -0
- package/dist/mfa/base32.js +52 -0
- package/dist/mfa/base32.js.map +1 -0
- package/dist/mfa/webauthn-codec.d.ts +58 -0
- package/dist/mfa/webauthn-codec.d.ts.map +1 -0
- package/dist/mfa/webauthn-codec.js +221 -0
- package/dist/mfa/webauthn-codec.js.map +1 -0
- package/dist/middleware.d.ts +51 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +213 -0
- package/dist/middleware.js.map +1 -0
- package/dist/native.d.ts +14 -0
- package/dist/native.d.ts.map +1 -0
- package/dist/native.js +39 -0
- package/dist/native.js.map +1 -0
- package/dist/rights/MemoryRightsStore.d.ts +29 -0
- package/dist/rights/MemoryRightsStore.d.ts.map +1 -0
- package/dist/rights/MemoryRightsStore.js +70 -0
- package/dist/rights/MemoryRightsStore.js.map +1 -0
- package/dist/rights/RightsResolver.d.ts +29 -0
- package/dist/rights/RightsResolver.d.ts.map +1 -0
- package/dist/rights/RightsResolver.js +73 -0
- package/dist/rights/RightsResolver.js.map +1 -0
- package/dist/rights/types.d.ts +55 -0
- package/dist/rights/types.d.ts.map +1 -0
- package/dist/rights/types.js +18 -0
- package/dist/rights/types.js.map +1 -0
- package/dist/services/main.d.ts +19 -0
- package/dist/services/main.d.ts.map +1 -0
- package/dist/services/main.js +32 -0
- package/dist/services/main.js.map +1 -0
- package/dist/standalone.d.ts +37 -0
- package/dist/standalone.d.ts.map +1 -0
- package/dist/standalone.js +92 -0
- package/dist/standalone.js.map +1 -0
- package/dist/strategies/ApiKeyStrategy.d.ts +28 -0
- package/dist/strategies/ApiKeyStrategy.d.ts.map +1 -0
- package/dist/strategies/ApiKeyStrategy.js +36 -0
- package/dist/strategies/ApiKeyStrategy.js.map +1 -0
- package/dist/strategies/JwtStrategy.d.ts +51 -0
- package/dist/strategies/JwtStrategy.d.ts.map +1 -0
- package/dist/strategies/JwtStrategy.js +150 -0
- package/dist/strategies/JwtStrategy.js.map +1 -0
- package/dist/strategies/SessionStrategy.d.ts +58 -0
- package/dist/strategies/SessionStrategy.d.ts.map +1 -0
- package/dist/strategies/SessionStrategy.js +70 -0
- package/dist/strategies/SessionStrategy.js.map +1 -0
- 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 +1 -1
- package/src/Guard.ts +21 -0
- package/src/RedisBlacklistDriver.ts +60 -0
- package/src/ResilientBlacklistDriver.ts +84 -0
- package/src/WardenProvider.ts +11 -0
- package/src/config.ts +7 -0
- package/src/index.ts +58 -0
- package/src/mfa/BackupCodesProvider.ts +125 -0
- package/src/mfa/MfaManager.ts +307 -0
- package/src/mfa/OtpProvider.ts +177 -0
- package/src/mfa/TotpProvider.ts +140 -0
- package/src/mfa/WebauthnProvider.ts +416 -0
- package/src/mfa/base32.ts +54 -0
- package/src/mfa/webauthn-codec.ts +264 -0
- package/src/middleware.ts +14 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bouncer — Layer 2 (EVALUATION) of Warden's unified authorization (Epic 56).
|
|
3
|
+
*
|
|
4
|
+
* A Bouncer-faithful evaluation core (verified context7 `/adonisjs/bouncer`,
|
|
5
|
+
* 2026-06-01): standalone abilities via `Bouncer.ability`, class-based policies
|
|
6
|
+
* via `with(Policy)`, the four verbs `allows`/`denies`/`authorize`/`execute`,
|
|
7
|
+
* and guest-denied-by-default. The user is `UserPayload | null` (guest = null,
|
|
8
|
+
* D3); `authorize` throws `WARDEN_AUTHORIZATION_FAILURE` carrying `status: 403`
|
|
9
|
+
* (D2). 56.2 ships PURE mechanics — it does NOT consult the Layer-1
|
|
10
|
+
* `RightsResolver` (D4); abilities/policy methods receive `(user, ...args)` and
|
|
11
|
+
* the developer writes the predicate, exactly as Adonis does.
|
|
12
|
+
*/
|
|
13
|
+
import { WardenError } from "../errors.js";
|
|
14
|
+
import { AuthorizationResponse } from "./AuthorizationResponse.js";
|
|
15
|
+
import { evaluate, isAction, throwAuthorizationFailure } from "./evaluate.js";
|
|
16
|
+
import { PolicyAuthorizer } from "./PolicyAuthorizer.js";
|
|
17
|
+
import { emptyPermissions } from "./policyContext.js";
|
|
18
|
+
export class Bouncer {
|
|
19
|
+
#user;
|
|
20
|
+
#abilities;
|
|
21
|
+
#policies;
|
|
22
|
+
#scope;
|
|
23
|
+
#resolver;
|
|
24
|
+
/** Memoized resolution — a Bouncer is fixed per `(user, scope)`, so resolve once (D3). */
|
|
25
|
+
#resolved;
|
|
26
|
+
constructor(user, abilities, policies, context) {
|
|
27
|
+
this.#user = user;
|
|
28
|
+
this.#abilities = abilities ?? {};
|
|
29
|
+
this.#policies = policies ?? {};
|
|
30
|
+
this.#scope = context?.scope ?? "global";
|
|
31
|
+
this.#resolver = context?.resolver;
|
|
32
|
+
}
|
|
33
|
+
/** The active resolution scope (default `"global"`). */
|
|
34
|
+
get scope() {
|
|
35
|
+
return this.#scope;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Resolve this Bouncer's `(user, scope)` to `EffectivePermissions`, memoized
|
|
39
|
+
* once per instance (D3). Guest (`user === null`) or no resolver ⇒ empty
|
|
40
|
+
* permissions — `resolve()` is never called for a guest (D9). 56.6 relocates
|
|
41
|
+
* this cache to the request context without changing the policy-facing API.
|
|
42
|
+
*/
|
|
43
|
+
#resolvePermissions() {
|
|
44
|
+
if (this.#resolved === undefined) {
|
|
45
|
+
this.#resolved =
|
|
46
|
+
this.#resolver !== undefined && this.#user !== null
|
|
47
|
+
? this.#resolver.resolve(this.#user, this.#scope)
|
|
48
|
+
: Promise.resolve(emptyPermissions(this.#scope));
|
|
49
|
+
}
|
|
50
|
+
return this.#resolved;
|
|
51
|
+
}
|
|
52
|
+
static ability(optionsOrCallback, maybeCallback) {
|
|
53
|
+
let allowGuest = false;
|
|
54
|
+
let raw;
|
|
55
|
+
if (typeof optionsOrCallback === "function") {
|
|
56
|
+
raw = optionsOrCallback;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
allowGuest = optionsOrCallback.allowGuest ?? false;
|
|
60
|
+
raw = maybeCallback;
|
|
61
|
+
}
|
|
62
|
+
// The typed overloads above carry the real callback signature for the
|
|
63
|
+
// caller; the impl param is the universal-function supertype so both
|
|
64
|
+
// overloads are accepted. `isAction` reinterprets it as the invokable
|
|
65
|
+
// `Action` shape (a type guard, not a cast — a function's parameter types
|
|
66
|
+
// are not observable at runtime).
|
|
67
|
+
if (!isAction(raw)) {
|
|
68
|
+
throw new WardenError("INVALID_ABILITY", "Bouncer.ability requires a callback alongside the options object.", { hint: "Bouncer.ability({ allowGuest: true }, (user) => ...)" });
|
|
69
|
+
}
|
|
70
|
+
const callback = raw;
|
|
71
|
+
const execute = (user, ...args) => callback(user, ...args);
|
|
72
|
+
return { allowGuest, execute };
|
|
73
|
+
}
|
|
74
|
+
/** Convenience — equivalent to `AuthorizationResponse.deny` (Adonis `bouncer.deny`). */
|
|
75
|
+
deny(message, status) {
|
|
76
|
+
return AuthorizationResponse.deny(message, status);
|
|
77
|
+
}
|
|
78
|
+
/** Open a policy for checks (D8 — fresh `new PolicyClass()` per check). */
|
|
79
|
+
with(policy) {
|
|
80
|
+
const factory = typeof policy === "string"
|
|
81
|
+
? this.#resolvePolicyClass(policy)
|
|
82
|
+
: () => new policy();
|
|
83
|
+
// The PolicyAuthorizer inherits the Bouncer's scope + the shared memoized
|
|
84
|
+
// resolve so every policy check sees the active `(user, scope)` (AC1/AC3).
|
|
85
|
+
return new PolicyAuthorizer(this.#user, factory, this.#scope, () => this.#resolvePermissions());
|
|
86
|
+
}
|
|
87
|
+
execute(ability, ...args) {
|
|
88
|
+
return this.#evaluateAbility(ability, args);
|
|
89
|
+
}
|
|
90
|
+
async allows(ability, ...args) {
|
|
91
|
+
return (await this.#evaluateAbility(ability, args)).authorized;
|
|
92
|
+
}
|
|
93
|
+
async denies(ability, ...args) {
|
|
94
|
+
return !(await this.#evaluateAbility(ability, args)).authorized;
|
|
95
|
+
}
|
|
96
|
+
async authorize(ability, ...args) {
|
|
97
|
+
const response = await this.#evaluateAbility(ability, args);
|
|
98
|
+
if (!response.authorized) {
|
|
99
|
+
throwAuthorizationFailure(response);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async #evaluateAbility(ability, args) {
|
|
103
|
+
const resolved = typeof ability === "string" ? this.#resolveAbility(ability) : ability;
|
|
104
|
+
return evaluate({
|
|
105
|
+
user: this.#user,
|
|
106
|
+
action: typeof ability === "string" ? ability : "(ability)",
|
|
107
|
+
allowGuest: resolved.allowGuest,
|
|
108
|
+
run: (user) => resolved.execute(user, ...args),
|
|
109
|
+
args,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
#resolveAbility(name) {
|
|
113
|
+
const found = this.#abilities[name];
|
|
114
|
+
if (found === undefined) {
|
|
115
|
+
throw new WardenError("UNKNOWN_ABILITY", `No ability "${name}" is registered on this Bouncer.`, { hint: "Register it via new Bouncer(user, { [name]: ability })." });
|
|
116
|
+
}
|
|
117
|
+
return found;
|
|
118
|
+
}
|
|
119
|
+
#resolvePolicyClass(name) {
|
|
120
|
+
// Lazy: the lookup runs when a verb constructs the policy, so an unknown
|
|
121
|
+
// name surfaces as a promise rejection (like every other verb error),
|
|
122
|
+
// not a synchronous throw at `with()` time.
|
|
123
|
+
return () => {
|
|
124
|
+
const ctor = this.#policies[name];
|
|
125
|
+
if (ctor === undefined) {
|
|
126
|
+
throw new WardenError("UNKNOWN_POLICY", `No policy "${name}" is registered on this Bouncer.`, {
|
|
127
|
+
hint: "Register it via new Bouncer(user, abilities, { [name]: Policy }).",
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return new ctor();
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=Bouncer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Bouncer.js","sourceRoot":"","sources":["../../src/bouncer/Bouncer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PolicyAuthorizer — the object returned by `bouncer.with(Policy)`. Exposes the
|
|
3
|
+
* same four verbs as the Bouncer, dispatched to a named action method on a
|
|
4
|
+
* freshly-constructed policy instance (D8 — `new PolicyClass()` per check). The
|
|
5
|
+
* `before`/`after` hooks and the per-method `@allowGuest` rule run through the
|
|
6
|
+
* shared D5 pipeline.
|
|
7
|
+
*/
|
|
8
|
+
import type { UserPayload } from "../AuthManager.js";
|
|
9
|
+
import type { EffectivePermissions, Scope } from "../rights/types.js";
|
|
10
|
+
import type { AuthorizationResponse } from "./AuthorizationResponse.js";
|
|
11
|
+
import { BasePolicy } from "./BasePolicy.js";
|
|
12
|
+
export declare class PolicyAuthorizer {
|
|
13
|
+
#private;
|
|
14
|
+
constructor(user: UserPayload | null, factory: () => BasePolicy, scope?: Scope, resolvePermissions?: () => Promise<EffectivePermissions>);
|
|
15
|
+
/** Run a check and resolve to the full response (D8 — fresh policy per check). */
|
|
16
|
+
execute(action: string, ...args: unknown[]): Promise<AuthorizationResponse>;
|
|
17
|
+
/** True iff the action is authorized. Never throws on denial. */
|
|
18
|
+
allows(action: string, ...args: unknown[]): Promise<boolean>;
|
|
19
|
+
/** Boolean negation of {@link allows}. Never throws on denial. */
|
|
20
|
+
denies(action: string, ...args: unknown[]): Promise<boolean>;
|
|
21
|
+
/** Resolves on allow; throws `WARDEN_AUTHORIZATION_FAILURE` on deny (D2). */
|
|
22
|
+
authorize(action: string, ...args: unknown[]): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=PolicyAuthorizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PolicyAuthorizer.d.ts","sourceRoot":"","sources":["../../src/bouncer/PolicyAuthorizer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,KAAK,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAqD7C,qBAAa,gBAAgB;;gBAO3B,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,OAAO,EAAE,MAAM,UAAU,EACzB,KAAK,GAAE,KAAgB,EACvB,kBAAkB,GAAE,MAAM,OAAO,CAAC,oBAAoB,CACb;IAQ1C,kFAAkF;IAC5E,OAAO,CACZ,MAAM,EAAE,MAAM,EACd,GAAG,IAAI,EAAE,OAAO,EAAE,GAChB,OAAO,CAAC,qBAAqB,CAAC;IAmBjC,iEAAiE;IAC3D,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAIlE,kEAAkE;IAC5D,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAIlE,6EAA6E;IACvE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAMlE"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PolicyAuthorizer — the object returned by `bouncer.with(Policy)`. Exposes the
|
|
3
|
+
* same four verbs as the Bouncer, dispatched to a named action method on a
|
|
4
|
+
* freshly-constructed policy instance (D8 — `new PolicyClass()` per check). The
|
|
5
|
+
* `before`/`after` hooks and the per-method `@allowGuest` rule run through the
|
|
6
|
+
* shared D5 pipeline.
|
|
7
|
+
*/
|
|
8
|
+
import { WardenError } from "../errors.js";
|
|
9
|
+
import { BasePolicy } from "./BasePolicy.js";
|
|
10
|
+
import { getActionMetadata } from "./decorators.js";
|
|
11
|
+
import { evaluate, isAction, throwAuthorizationFailure, } from "./evaluate.js";
|
|
12
|
+
import { emptyPermissions, setPolicyContext } from "./policyContext.js";
|
|
13
|
+
/**
|
|
14
|
+
* Resolve a named action method declared anywhere on the policy's own subclass
|
|
15
|
+
* chain (so an action inherited from an intermediate policy base class still
|
|
16
|
+
* dispatches). Rejects `constructor`, the `before`/`after` hooks, and any
|
|
17
|
+
* `BasePolicy`/`Object.prototype` member so a verb call can never dispatch to a
|
|
18
|
+
* non-action method — a guard the adversarial review specifically probes.
|
|
19
|
+
*/
|
|
20
|
+
function resolveActionMethod(policy, action) {
|
|
21
|
+
// Reject the hooks + constructor by name, then accept an action declared
|
|
22
|
+
// anywhere on the policy's own subclass chain — the immediate prototype up to
|
|
23
|
+
// (but not including) BasePolicy.prototype / Object.prototype — so a method
|
|
24
|
+
// inherited from an intermediate policy base class (`class A extends B`) still
|
|
25
|
+
// dispatches, while BasePolicy's own members (scope/permissions getters) and
|
|
26
|
+
// Object.prototype members stay unreachable.
|
|
27
|
+
const isHookOrCtor = action === "constructor" || action === "before" || action === "after";
|
|
28
|
+
let declaredHere = false;
|
|
29
|
+
for (let proto = Object.getPrototypeOf(policy); proto !== null &&
|
|
30
|
+
proto !== BasePolicy.prototype &&
|
|
31
|
+
proto !== Object.prototype; proto = Object.getPrototypeOf(proto)) {
|
|
32
|
+
if (Object.hasOwn(proto, action)) {
|
|
33
|
+
declaredHere = true;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const candidate = Reflect.get(policy, action);
|
|
38
|
+
if (isHookOrCtor) {
|
|
39
|
+
declaredHere = false;
|
|
40
|
+
}
|
|
41
|
+
if (!declaredHere || !isAction(candidate)) {
|
|
42
|
+
throw new WardenError("UNKNOWN_POLICY_ACTION", `Policy "${policy.constructor.name}" has no action "${action}"`, { hint: "Declare the action as a method on the policy class." });
|
|
43
|
+
}
|
|
44
|
+
return candidate.bind(policy);
|
|
45
|
+
}
|
|
46
|
+
export class PolicyAuthorizer {
|
|
47
|
+
#user;
|
|
48
|
+
#factory;
|
|
49
|
+
#scope;
|
|
50
|
+
#resolvePermissions;
|
|
51
|
+
constructor(user, factory, scope = "global", resolvePermissions = () => Promise.resolve(emptyPermissions(scope))) {
|
|
52
|
+
this.#user = user;
|
|
53
|
+
this.#factory = factory;
|
|
54
|
+
this.#scope = scope;
|
|
55
|
+
this.#resolvePermissions = resolvePermissions;
|
|
56
|
+
}
|
|
57
|
+
/** Run a check and resolve to the full response (D8 — fresh policy per check). */
|
|
58
|
+
async execute(action, ...args) {
|
|
59
|
+
const policy = this.#factory();
|
|
60
|
+
// Attach the active scope + resolved permissions BEFORE dispatch so the
|
|
61
|
+
// before/method/after pipeline all read `this.scope` / `this.permissions`.
|
|
62
|
+
const permissions = await this.#resolvePermissions();
|
|
63
|
+
setPolicyContext(policy, { scope: this.#scope, permissions });
|
|
64
|
+
const method = resolveActionMethod(policy, action);
|
|
65
|
+
const options = getActionMetadata(policy, action);
|
|
66
|
+
return evaluate({
|
|
67
|
+
user: this.#user,
|
|
68
|
+
action,
|
|
69
|
+
allowGuest: options.allowGuest ?? false,
|
|
70
|
+
run: (user) => method(user, ...args),
|
|
71
|
+
args,
|
|
72
|
+
before: policy.before?.bind(policy),
|
|
73
|
+
after: policy.after?.bind(policy),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/** True iff the action is authorized. Never throws on denial. */
|
|
77
|
+
async allows(action, ...args) {
|
|
78
|
+
return (await this.execute(action, ...args)).authorized;
|
|
79
|
+
}
|
|
80
|
+
/** Boolean negation of {@link allows}. Never throws on denial. */
|
|
81
|
+
async denies(action, ...args) {
|
|
82
|
+
return !(await this.execute(action, ...args)).authorized;
|
|
83
|
+
}
|
|
84
|
+
/** Resolves on allow; throws `WARDEN_AUTHORIZATION_FAILURE` on deny (D2). */
|
|
85
|
+
async authorize(action, ...args) {
|
|
86
|
+
const response = await this.execute(action, ...args);
|
|
87
|
+
if (!response.authorized) {
|
|
88
|
+
throwAuthorizationFailure(response);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=PolicyAuthorizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PolicyAuthorizer.js","sourceRoot":"","sources":["../../src/bouncer/PolicyAuthorizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bouncer policy decorators — `@allowGuest()` / `@action()` mark policy methods
|
|
3
|
+
* that a guest (null user) may reach. Mirrors the `Guard.ts` idiom (D9):
|
|
4
|
+
* `reflect-metadata` side-effect import, a `Symbol.for` key, the legacy
|
|
5
|
+
* `MethodDecorator` signature, and a `getActionMetadata` reader.
|
|
6
|
+
*/
|
|
7
|
+
import "reflect-metadata";
|
|
8
|
+
import type { AbilityOptions } from "./types.js";
|
|
9
|
+
/**
|
|
10
|
+
* `@action({ allowGuest })` — configure a policy method's evaluation options.
|
|
11
|
+
*/
|
|
12
|
+
export declare function action(options: AbilityOptions): MethodDecorator;
|
|
13
|
+
/**
|
|
14
|
+
* `@allowGuest()` — let a guest (null user) reach this policy method.
|
|
15
|
+
* Equivalent to `@action({ allowGuest: true })`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function allowGuest(): MethodDecorator;
|
|
18
|
+
/**
|
|
19
|
+
* Read a policy method's action metadata. Walks the prototype chain
|
|
20
|
+
* (`Reflect.getMetadata`) so it resolves from a policy INSTANCE — legacy
|
|
21
|
+
* decorators write metadata onto the class prototype, where the instance
|
|
22
|
+
* inherits it. Absent ⇒ `{}` (⇒ `allowGuest` falsy ⇒ guest denied).
|
|
23
|
+
*/
|
|
24
|
+
export declare function getActionMetadata(target: object, propertyKey: string | symbol): AbilityOptions;
|
|
25
|
+
//# sourceMappingURL=decorators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../../src/bouncer/decorators.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAKjD;;GAEG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,cAAc,GAAG,eAAe,CAI/D;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,eAAe,CAE5C;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAChC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAAG,MAAM,GAC1B,cAAc,CAEhB"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bouncer policy decorators — `@allowGuest()` / `@action()` mark policy methods
|
|
3
|
+
* that a guest (null user) may reach. Mirrors the `Guard.ts` idiom (D9):
|
|
4
|
+
* `reflect-metadata` side-effect import, a `Symbol.for` key, the legacy
|
|
5
|
+
* `MethodDecorator` signature, and a `getActionMetadata` reader.
|
|
6
|
+
*/
|
|
7
|
+
// Side-effect import: registers `Reflect.defineMetadata` / `getMetadata`. Pulled
|
|
8
|
+
// in here (not transitively) so warden stays self-sufficient when published.
|
|
9
|
+
import "reflect-metadata";
|
|
10
|
+
/** Action metadata key — Symbol.for ensures cross-module accessibility. */
|
|
11
|
+
const ACTION_KEY = Symbol.for("warden:bouncer:action");
|
|
12
|
+
/**
|
|
13
|
+
* `@action({ allowGuest })` — configure a policy method's evaluation options.
|
|
14
|
+
*/
|
|
15
|
+
export function action(options) {
|
|
16
|
+
return (target, propertyKey) => {
|
|
17
|
+
Reflect.defineMetadata(ACTION_KEY, options, target, propertyKey);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* `@allowGuest()` — let a guest (null user) reach this policy method.
|
|
22
|
+
* Equivalent to `@action({ allowGuest: true })`.
|
|
23
|
+
*/
|
|
24
|
+
export function allowGuest() {
|
|
25
|
+
return action({ allowGuest: true });
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Read a policy method's action metadata. Walks the prototype chain
|
|
29
|
+
* (`Reflect.getMetadata`) so it resolves from a policy INSTANCE — legacy
|
|
30
|
+
* decorators write metadata onto the class prototype, where the instance
|
|
31
|
+
* inherits it. Absent ⇒ `{}` (⇒ `allowGuest` falsy ⇒ guest denied).
|
|
32
|
+
*/
|
|
33
|
+
export function getActionMetadata(target, propertyKey) {
|
|
34
|
+
return Reflect.getMetadata(ACTION_KEY, target, propertyKey) ?? {};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=decorators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorators.js","sourceRoot":"","sources":["../../src/bouncer/decorators.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,iFAAiF;AACjF,6EAA6E;AAC7E,OAAO,kBAAkB,CAAC;AAG1B,2EAA2E;AAC3E,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;AAEvD;;GAEG;AACH,MAAM,UAAU,MAAM,CAAC,OAAuB;IAC7C,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE;QAC9B,OAAO,CAAC,cAAc,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAClE,CAAC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU;IACzB,OAAO,MAAM,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;AACrC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAChC,MAAc,EACd,WAA4B;IAE5B,OAAO,OAAO,CAAC,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared evaluation pipeline — the single place that encodes the Adonis Bouncer
|
|
3
|
+
* evaluation order (D5), used by BOTH standalone abilities (no hooks) and policy
|
|
4
|
+
* methods (with `before`/`after`). Pinned by `bouncer-parity.test.ts` (AC-E2).
|
|
5
|
+
*
|
|
6
|
+
* Order: (1) `before` — a non-`undefined` return short-circuits the action;
|
|
7
|
+
* (2) guest-deny — `user === null` && !allowGuest ⇒ auto-deny WITHOUT running the
|
|
8
|
+
* action; (3) run the action; (4) `after` — a non-`undefined` return overrides.
|
|
9
|
+
*/
|
|
10
|
+
import type { UserPayload } from "../AuthManager.js";
|
|
11
|
+
import { AuthorizationResponse } from "./AuthorizationResponse.js";
|
|
12
|
+
import type { AuthorizerResponse, HookResponse } from "./types.js";
|
|
13
|
+
/** A policy action method / ability callback, structurally. */
|
|
14
|
+
export type Action = (user: UserPayload | null, ...args: unknown[]) => AuthorizerResponse;
|
|
15
|
+
/** Boolean sugar → response; an explicit response passes through (D7). */
|
|
16
|
+
export declare function normalizeResponse(value: boolean | AuthorizationResponse): AuthorizationResponse;
|
|
17
|
+
/**
|
|
18
|
+
* Type guard for a callable action. A function's parameter/return types are not
|
|
19
|
+
* observable at runtime, so this asserts the structural `Action` shape from a
|
|
20
|
+
* `typeof === "function"` check — a type guard (not an `as` cast), the standard
|
|
21
|
+
* escape for dynamic dispatch.
|
|
22
|
+
*/
|
|
23
|
+
export declare function isAction(value: unknown): value is Action;
|
|
24
|
+
/** Throw the canonical authorization failure for a denied response (D2). */
|
|
25
|
+
export declare function throwAuthorizationFailure(response: AuthorizationResponse): never;
|
|
26
|
+
/** Run the D5 evaluation pipeline and resolve to the final response. */
|
|
27
|
+
export declare function evaluate(params: {
|
|
28
|
+
user: UserPayload | null;
|
|
29
|
+
action: string;
|
|
30
|
+
allowGuest: boolean;
|
|
31
|
+
run: (user: UserPayload | null) => AuthorizerResponse;
|
|
32
|
+
args: unknown[];
|
|
33
|
+
before?: (user: UserPayload | null, action: string, ...args: unknown[]) => HookResponse;
|
|
34
|
+
after?: (user: UserPayload | null, action: string, result: AuthorizationResponse) => HookResponse;
|
|
35
|
+
}): Promise<AuthorizationResponse>;
|
|
36
|
+
//# sourceMappingURL=evaluate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../src/bouncer/evaluate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEnE,+DAA+D;AAC/D,MAAM,MAAM,MAAM,GAAG,CACpB,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,GAAG,IAAI,EAAE,OAAO,EAAE,KACd,kBAAkB,CAAC;AAExB,0EAA0E;AAC1E,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,OAAO,GAAG,qBAAqB,GACpC,qBAAqB,CAKvB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAExD;AAED,4EAA4E;AAC5E,wBAAgB,yBAAyB,CACxC,QAAQ,EAAE,qBAAqB,GAC7B,KAAK,CASP;AAED,wEAAwE;AACxE,wBAAsB,QAAQ,CAAC,MAAM,EAAE;IACtC,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,GAAG,EAAE,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,KAAK,kBAAkB,CAAC;IACtD,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,CACR,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,MAAM,EAAE,MAAM,EACd,GAAG,IAAI,EAAE,OAAO,EAAE,KACd,YAAY,CAAC;IAClB,KAAK,CAAC,EAAE,CACP,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,qBAAqB,KACzB,YAAY,CAAC;CAClB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA+BjC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared evaluation pipeline — the single place that encodes the Adonis Bouncer
|
|
3
|
+
* evaluation order (D5), used by BOTH standalone abilities (no hooks) and policy
|
|
4
|
+
* methods (with `before`/`after`). Pinned by `bouncer-parity.test.ts` (AC-E2).
|
|
5
|
+
*
|
|
6
|
+
* Order: (1) `before` — a non-`undefined` return short-circuits the action;
|
|
7
|
+
* (2) guest-deny — `user === null` && !allowGuest ⇒ auto-deny WITHOUT running the
|
|
8
|
+
* action; (3) run the action; (4) `after` — a non-`undefined` return overrides.
|
|
9
|
+
*/
|
|
10
|
+
import { WardenError } from "../errors.js";
|
|
11
|
+
import { AuthorizationResponse } from "./AuthorizationResponse.js";
|
|
12
|
+
/** Boolean sugar → response; an explicit response passes through (D7). */
|
|
13
|
+
export function normalizeResponse(value) {
|
|
14
|
+
if (value instanceof AuthorizationResponse) {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
return value ? AuthorizationResponse.allow() : AuthorizationResponse.deny();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Type guard for a callable action. A function's parameter/return types are not
|
|
21
|
+
* observable at runtime, so this asserts the structural `Action` shape from a
|
|
22
|
+
* `typeof === "function"` check — a type guard (not an `as` cast), the standard
|
|
23
|
+
* escape for dynamic dispatch.
|
|
24
|
+
*/
|
|
25
|
+
export function isAction(value) {
|
|
26
|
+
return typeof value === "function";
|
|
27
|
+
}
|
|
28
|
+
/** Throw the canonical authorization failure for a denied response (D2). */
|
|
29
|
+
export function throwAuthorizationFailure(response) {
|
|
30
|
+
throw new WardenError("AUTHORIZATION_FAILURE", response.message ?? "Authorization failed", {
|
|
31
|
+
hint: "The current user is not authorized for this action.",
|
|
32
|
+
status: response.status ?? 403,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/** Run the D5 evaluation pipeline and resolve to the final response. */
|
|
36
|
+
export async function evaluate(params) {
|
|
37
|
+
const { user, action, allowGuest, run, args, before, after } = params;
|
|
38
|
+
let response;
|
|
39
|
+
// (1) before — non-undefined short-circuits the action.
|
|
40
|
+
if (before) {
|
|
41
|
+
const early = await before(user, action, ...args);
|
|
42
|
+
if (early !== undefined) {
|
|
43
|
+
response = normalizeResponse(early);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// (2) guest-deny + (3) action — only if before did not short-circuit.
|
|
47
|
+
if (response === undefined) {
|
|
48
|
+
if (user === null && !allowGuest) {
|
|
49
|
+
response = AuthorizationResponse.deny();
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
response = normalizeResponse(await run(user));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// (4) after — non-undefined overrides.
|
|
56
|
+
if (after) {
|
|
57
|
+
const override = await after(user, action, response);
|
|
58
|
+
if (override !== undefined) {
|
|
59
|
+
response = normalizeResponse(override);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return response;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=evaluate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluate.js","sourceRoot":"","sources":["../../src/bouncer/evaluate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AASnE,0EAA0E;AAC1E,MAAM,UAAU,iBAAiB,CAChC,KAAsC;IAEtC,IAAI,KAAK,YAAY,qBAAqB,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,CAAC,qBAAqB,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,qBAAqB,CAAC,IAAI,EAAE,CAAC;AAC7E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAc;IACtC,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC;AACpC,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,yBAAyB,CACxC,QAA+B;IAE/B,MAAM,IAAI,WAAW,CACpB,uBAAuB,EACvB,QAAQ,CAAC,OAAO,IAAI,sBAAsB,EAC1C;QACC,IAAI,EAAE,qDAAqD;QAC3D,MAAM,EAAE,QAAQ,CAAC,MAAM,IAAI,GAAG;KAC9B,CACD,CAAC;AACH,CAAC;AAED,wEAAwE;AACxE,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,MAgB9B;IACA,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IAEtE,IAAI,QAA2C,CAAC;IAEhD,wDAAwD;IACxD,IAAI,MAAM,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;QAClD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACzB,QAAQ,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;IACF,CAAC;IAED,sEAAsE;IACtE,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAClC,QAAQ,GAAG,qBAAqB,CAAC,IAAI,EAAE,CAAC;QACzC,CAAC;aAAM,CAAC;YACP,QAAQ,GAAG,iBAAiB,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/C,CAAC;IACF,CAAC;IAED,uCAAuC;IACvC,IAAI,KAAK,EAAE,CAAC;QACX,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QACrD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC5B,QAAQ,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QACxC,CAAC;IACF,CAAC;IAED,OAAO,QAAQ,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal policy context attach — Layer-2 ⨯ Layer-1 wiring (Epic 56, story 56.3).
|
|
3
|
+
*
|
|
4
|
+
* The active `scope` and the resolved `EffectivePermissions` for the in-flight
|
|
5
|
+
* `(user, scope)` are attached to a freshly-constructed policy via a package-
|
|
6
|
+
* internal `WeakMap` (D2) — NOT a public setter on `BasePolicy` and NOT barrelled.
|
|
7
|
+
* `BasePolicy`'s `this.scope` / `this.permissions` getters read it back; the
|
|
8
|
+
* `PolicyAuthorizer` writes it before dispatch. A policy used without a Bouncer
|
|
9
|
+
* has no entry, so the getters fall back to `global` + {@link emptyPermissions}.
|
|
10
|
+
*/
|
|
11
|
+
import type { EffectivePermissions, Scope } from "../rights/types.js";
|
|
12
|
+
import type { BasePolicy } from "./BasePolicy.js";
|
|
13
|
+
/** What the authorizer attaches to a policy instance for one check. */
|
|
14
|
+
export interface PolicyContext {
|
|
15
|
+
readonly scope: Scope;
|
|
16
|
+
readonly permissions: EffectivePermissions;
|
|
17
|
+
}
|
|
18
|
+
/** Attach the active scope + resolved permissions to a policy instance (internal). */
|
|
19
|
+
export declare function setPolicyContext(policy: BasePolicy, context: PolicyContext): void;
|
|
20
|
+
/** Read back a policy's attached context, or `undefined` when used standalone. */
|
|
21
|
+
export declare function getPolicyContext(policy: BasePolicy): PolicyContext | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* The empty `EffectivePermissions` for a guest / no-resolver Bouncer (D9). Built
|
|
24
|
+
* locally — never a `resolve()` call (the resolver requires a non-null user) and
|
|
25
|
+
* never importing 56.1's non-exported `ResolvedPermissions`. `has` → false;
|
|
26
|
+
* `hasAll([])` → true (vacuous); `hasAny` → false; empty sets; `scope` = active.
|
|
27
|
+
*/
|
|
28
|
+
export declare function emptyPermissions(scope: Scope): EffectivePermissions;
|
|
29
|
+
//# sourceMappingURL=policyContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policyContext.d.ts","sourceRoot":"","sources":["../../src/bouncer/policyContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,oBAAoB,CAAC;CAC3C;AAID,sFAAsF;AACtF,wBAAgB,gBAAgB,CAC/B,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,aAAa,GACpB,IAAI,CAEN;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAC/B,MAAM,EAAE,UAAU,GAChB,aAAa,GAAG,SAAS,CAE3B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,oBAAoB,CASnE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal policy context attach — Layer-2 ⨯ Layer-1 wiring (Epic 56, story 56.3).
|
|
3
|
+
*
|
|
4
|
+
* The active `scope` and the resolved `EffectivePermissions` for the in-flight
|
|
5
|
+
* `(user, scope)` are attached to a freshly-constructed policy via a package-
|
|
6
|
+
* internal `WeakMap` (D2) — NOT a public setter on `BasePolicy` and NOT barrelled.
|
|
7
|
+
* `BasePolicy`'s `this.scope` / `this.permissions` getters read it back; the
|
|
8
|
+
* `PolicyAuthorizer` writes it before dispatch. A policy used without a Bouncer
|
|
9
|
+
* has no entry, so the getters fall back to `global` + {@link emptyPermissions}.
|
|
10
|
+
*/
|
|
11
|
+
const contexts = new WeakMap();
|
|
12
|
+
/** Attach the active scope + resolved permissions to a policy instance (internal). */
|
|
13
|
+
export function setPolicyContext(policy, context) {
|
|
14
|
+
contexts.set(policy, context);
|
|
15
|
+
}
|
|
16
|
+
/** Read back a policy's attached context, or `undefined` when used standalone. */
|
|
17
|
+
export function getPolicyContext(policy) {
|
|
18
|
+
return contexts.get(policy);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The empty `EffectivePermissions` for a guest / no-resolver Bouncer (D9). Built
|
|
22
|
+
* locally — never a `resolve()` call (the resolver requires a non-null user) and
|
|
23
|
+
* never importing 56.1's non-exported `ResolvedPermissions`. `has` → false;
|
|
24
|
+
* `hasAll([])` → true (vacuous); `hasAny` → false; empty sets; `scope` = active.
|
|
25
|
+
*/
|
|
26
|
+
export function emptyPermissions(scope) {
|
|
27
|
+
return {
|
|
28
|
+
has: () => false,
|
|
29
|
+
hasAll: (permissions) => permissions.length === 0,
|
|
30
|
+
hasAny: () => false,
|
|
31
|
+
permissions: new Set(),
|
|
32
|
+
roles: new Set(),
|
|
33
|
+
scope,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=policyContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policyContext.js","sourceRoot":"","sources":["../../src/bouncer/policyContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAWH,MAAM,QAAQ,GAAG,IAAI,OAAO,EAA6B,CAAC;AAE1D,sFAAsF;AACtF,MAAM,UAAU,gBAAgB,CAC/B,MAAkB,EAClB,OAAsB;IAEtB,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,gBAAgB,CAC/B,MAAkB;IAElB,OAAO,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAY;IAC5C,OAAO;QACN,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK;QAChB,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;QACjD,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK;QACnB,WAAW,EAAE,IAAI,GAAG,EAAU;QAC9B,KAAK,EAAE,IAAI,GAAG,EAAU;QACxB,KAAK;KACL,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bouncer evaluation contract — Layer 2 of Warden's unified authorization.
|
|
3
|
+
*
|
|
4
|
+
* The shapes here are faithful to AdonisJS Bouncer (verified context7
|
|
5
|
+
* `/adonisjs/bouncer`, 2026-06-01): a predicate returns a boolean (sugar for
|
|
6
|
+
* allow / deny) or an explicit `AuthorizationResponse`, sync or async.
|
|
7
|
+
*/
|
|
8
|
+
import type { UserPayload } from "../AuthManager.js";
|
|
9
|
+
import type { RightsResolver } from "../rights/RightsResolver.js";
|
|
10
|
+
import type { Scope } from "../rights/types.js";
|
|
11
|
+
import type { AuthorizationResponse } from "./AuthorizationResponse.js";
|
|
12
|
+
/**
|
|
13
|
+
* Optional 4th `Bouncer` ctor argument (56.3, D1) — the scope dimension + the
|
|
14
|
+
* Layer-1 resolver. Both optional; omitting it ⇒ the implicit `global` scope
|
|
15
|
+
* with no resolver (single-tenant zero-config, D7). Additive: every 56.2 call
|
|
16
|
+
* site keeps compiling.
|
|
17
|
+
*/
|
|
18
|
+
export interface BouncerContext {
|
|
19
|
+
/** Active resolution scope. Defaults to `"global"`. */
|
|
20
|
+
readonly scope?: Scope;
|
|
21
|
+
/** Layer-1 resolver consulted for this Bouncer's `(user, scope)`. */
|
|
22
|
+
readonly resolver?: RightsResolver;
|
|
23
|
+
}
|
|
24
|
+
/** A predicate's return — bool sugar or an explicit response, sync or async (D7). */
|
|
25
|
+
export type AuthorizerResponse = boolean | AuthorizationResponse | Promise<boolean | AuthorizationResponse>;
|
|
26
|
+
/** A `before`/`after` hook return — like {@link AuthorizerResponse} plus `undefined` (fall-through). */
|
|
27
|
+
export type HookResponse = boolean | AuthorizationResponse | undefined | Promise<boolean | AuthorizationResponse | undefined>;
|
|
28
|
+
/** Options accepted by `Bouncer.ability` and the `@action` decorator. */
|
|
29
|
+
export interface AbilityOptions {
|
|
30
|
+
allowGuest?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* An opaque ability reference produced by `Bouncer.ability`. Usable by-reference
|
|
34
|
+
* (typed call args via the `Args` phantom) AND by-name (registered in the
|
|
35
|
+
* Bouncer ctor's abilities record).
|
|
36
|
+
*
|
|
37
|
+
* `Args` is carried by the contravariant `__args` phantom — never read at
|
|
38
|
+
* runtime — so a concrete `Ability<[Post]>` is assignable to the universal
|
|
39
|
+
* `Ability<never[]>` storage type while `execute` stays invokable with the real
|
|
40
|
+
* args. This is a branding pattern (carry the type parameter in the signature),
|
|
41
|
+
* not a cast: `execute` itself takes `unknown[]` and the call sites validate
|
|
42
|
+
* args via the phantom at the verb boundary.
|
|
43
|
+
*/
|
|
44
|
+
export interface Ability<Args extends unknown[] = unknown[]> {
|
|
45
|
+
/** Whether a guest (null user) may invoke the callback (D5 step 2). */
|
|
46
|
+
readonly allowGuest: boolean;
|
|
47
|
+
/** Runs the ability callback. Guest-deny is applied by the evaluator, not here. */
|
|
48
|
+
readonly execute: (user: UserPayload | null, ...args: unknown[]) => AuthorizerResponse;
|
|
49
|
+
/** Phantom — ties the by-reference call args to `Args`; never read at runtime. */
|
|
50
|
+
readonly __args?: (...args: Args) => void;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/bouncer/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAExE;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC9B,uDAAuD;IACvD,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IACvB,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;CACnC;AAED,qFAAqF;AACrF,MAAM,MAAM,kBAAkB,GAC3B,OAAO,GACP,qBAAqB,GACrB,OAAO,CAAC,OAAO,GAAG,qBAAqB,CAAC,CAAC;AAE5C,wGAAwG;AACxG,MAAM,MAAM,YAAY,GACrB,OAAO,GACP,qBAAqB,GACrB,SAAS,GACT,OAAO,CAAC,OAAO,GAAG,qBAAqB,GAAG,SAAS,CAAC,CAAC;AAExD,yEAAyE;AACzE,MAAM,WAAW,cAAc;IAC9B,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,OAAO,CAAC,IAAI,SAAS,OAAO,EAAE,GAAG,OAAO,EAAE;IAC1D,uEAAuE;IACvE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,mFAAmF;IACnF,QAAQ,CAAC,OAAO,EAAE,CACjB,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,GAAG,IAAI,EAAE,OAAO,EAAE,KACd,kBAAkB,CAAC;IACxB,kFAAkF;IAClF,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;CAC1C"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bouncer evaluation contract — Layer 2 of Warden's unified authorization.
|
|
3
|
+
*
|
|
4
|
+
* The shapes here are faithful to AdonisJS Bouncer (verified context7
|
|
5
|
+
* `/adonisjs/bouncer`, 2026-06-01): a predicate returns a boolean (sugar for
|
|
6
|
+
* allow / deny) or an explicit `AuthorizationResponse`, sync or async.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/bouncer/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|