@cullet/erp-core 1.3.0 → 1.5.0
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/KIT_CONTEXT.md +11 -3
- package/README.md +160 -0
- package/dist/abac/index.cjs +7 -0
- package/dist/abac/index.d.cts +2 -0
- package/dist/abac/index.d.ts +2 -0
- package/dist/abac/index.js +2 -0
- package/dist/app-error.cjs +201 -0
- package/dist/app-error.cjs.map +1 -0
- package/dist/app-error.js +178 -0
- package/dist/app-error.js.map +1 -0
- package/dist/application/index.cjs +2 -1
- package/dist/application/index.d.cts +2 -1
- package/dist/application/index.d.ts +2 -1
- package/dist/application/index.js +2 -1
- package/dist/authorization-error.cjs +157 -0
- package/dist/authorization-error.cjs.map +1 -0
- package/dist/authorization-error.d.cts +113 -0
- package/dist/authorization-error.d.ts +113 -0
- package/dist/authorization-error.js +152 -0
- package/dist/authorization-error.js.map +1 -0
- package/dist/authorizer.port.d.cts +130 -0
- package/dist/authorizer.port.d.ts +130 -0
- package/dist/composite-authorizer.d.cts +216 -0
- package/dist/composite-authorizer.d.ts +216 -0
- package/dist/condition-evaluator.cjs +579 -0
- package/dist/condition-evaluator.cjs.map +1 -0
- package/dist/condition-evaluator.js +550 -0
- package/dist/condition-evaluator.js.map +1 -0
- package/dist/{gate-types.d.ts → core-config.d.cts} +78 -77
- package/dist/{gate-types.d.cts → core-config.d.ts} +78 -77
- package/dist/domain/index.cjs +2 -1
- package/dist/domain/index.d.cts +2 -1
- package/dist/domain/index.d.ts +2 -1
- package/dist/domain/index.js +2 -1
- package/dist/domain-event-contracts.cjs +3 -2
- package/dist/domain-event-contracts.cjs.map +1 -1
- package/dist/domain-event-contracts.js +2 -1
- package/dist/domain-event-contracts.js.map +1 -1
- package/dist/entity.cjs +122 -0
- package/dist/entity.cjs.map +1 -0
- package/dist/entity.js +111 -0
- package/dist/entity.js.map +1 -0
- package/dist/errors/index.cjs +10 -7
- package/dist/errors/index.d.cts +2 -1
- package/dist/errors/index.d.ts +2 -1
- package/dist/errors/index.js +6 -3
- package/dist/gate-engine-registry.cjs +5 -4
- package/dist/gate-engine-registry.cjs.map +1 -1
- package/dist/gate-engine-registry.d.cts +3 -2
- package/dist/gate-engine-registry.d.ts +3 -2
- package/dist/gate-engine-registry.js +2 -1
- package/dist/gate-engine-registry.js.map +1 -1
- package/dist/gate-v1-payload.schema.cjs +0 -562
- package/dist/gate-v1-payload.schema.cjs.map +1 -1
- package/dist/gate-v1-payload.schema.js +1 -533
- package/dist/gate-v1-payload.schema.js.map +1 -1
- package/dist/hashing.cjs +2 -44
- package/dist/hashing.cjs.map +1 -1
- package/dist/hashing.js +2 -38
- package/dist/hashing.js.map +1 -1
- package/dist/index.cjs +36 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -12
- package/dist/index.d.ts +19 -12
- package/dist/index.js +17 -10
- package/dist/index.js.map +1 -1
- package/dist/not-found-error.cjs +5 -5
- package/dist/not-found-error.cjs.map +1 -1
- package/dist/not-found-error.js +1 -1
- package/dist/outcome.d.cts +1 -83
- package/dist/outcome.d.ts +1 -83
- package/dist/parse-gate-payload.d.cts +2 -2
- package/dist/parse-gate-payload.d.ts +2 -2
- package/dist/path.d.cts +2 -2
- package/dist/path.d.ts +2 -2
- package/dist/policies/engines/index.d.cts +1 -1
- package/dist/policies/engines/index.d.ts +1 -1
- package/dist/policies/engines/v1/gate/index.cjs +5 -4
- package/dist/policies/engines/v1/gate/index.cjs.map +1 -1
- package/dist/policies/engines/v1/gate/index.d.cts +4 -2
- package/dist/policies/engines/v1/gate/index.d.ts +4 -2
- package/dist/policies/engines/v1/gate/index.js +2 -1
- package/dist/policies/engines/v1/gate/index.js.map +1 -1
- package/dist/policies/index.cjs +5 -5
- package/dist/policies/index.d.cts +3 -2
- package/dist/policies/index.d.ts +3 -2
- package/dist/policies/index.js +2 -2
- package/dist/policy-bridge.cjs +437 -0
- package/dist/policy-bridge.cjs.map +1 -0
- package/dist/policy-bridge.d.cts +185 -0
- package/dist/policy-bridge.d.ts +185 -0
- package/dist/policy-bridge.js +396 -0
- package/dist/policy-bridge.js.map +1 -0
- package/dist/policy-service.cjs +241 -240
- package/dist/policy-service.cjs.map +1 -1
- package/dist/policy-service.d.cts +2 -2
- package/dist/policy-service.d.ts +2 -2
- package/dist/policy-service.js +240 -239
- package/dist/policy-service.js.map +1 -1
- package/dist/rbac/index.cjs +9 -0
- package/dist/rbac/index.d.cts +3 -0
- package/dist/rbac/index.d.ts +3 -0
- package/dist/rbac/index.js +2 -0
- package/dist/requested-by.cjs +54 -0
- package/dist/requested-by.cjs.map +1 -0
- package/dist/requested-by.d.cts +25 -0
- package/dist/requested-by.d.ts +25 -0
- package/dist/requested-by.js +49 -0
- package/dist/requested-by.js.map +1 -0
- package/dist/result/index.d.cts +2 -1
- package/dist/result/index.d.ts +2 -1
- package/dist/result.d.cts +84 -0
- package/dist/result.d.ts +84 -0
- package/dist/rule.cjs +327 -0
- package/dist/rule.cjs.map +1 -0
- package/dist/rule.js +298 -0
- package/dist/rule.js.map +1 -0
- package/dist/stable-stringify.cjs +47 -0
- package/dist/stable-stringify.cjs.map +1 -0
- package/dist/stable-stringify.js +42 -0
- package/dist/stable-stringify.js.map +1 -0
- package/dist/temporal-use-case.cjs +6 -53
- package/dist/temporal-use-case.cjs.map +1 -1
- package/dist/temporal-use-case.d.cts +11 -28
- package/dist/temporal-use-case.d.ts +11 -28
- package/dist/temporal-use-case.js +7 -48
- package/dist/temporal-use-case.js.map +1 -1
- package/dist/unexpected-error.cjs +3 -192
- package/dist/unexpected-error.cjs.map +1 -1
- package/dist/unexpected-error.js +2 -167
- package/dist/unexpected-error.js.map +1 -1
- package/dist/use-case.cjs +2 -6
- package/dist/use-case.cjs.map +1 -1
- package/dist/use-case.js +2 -6
- package/dist/use-case.js.map +1 -1
- package/dist/uuid-identifier.cjs +2 -7
- package/dist/uuid-identifier.cjs.map +1 -1
- package/dist/uuid-identifier.d.cts +2 -85
- package/dist/uuid-identifier.d.ts +2 -85
- package/dist/uuid-identifier.js +1 -6
- package/dist/uuid-identifier.js.map +1 -1
- package/dist/uuid.cjs +18 -0
- package/dist/uuid.cjs.map +1 -0
- package/dist/uuid.js +13 -0
- package/dist/uuid.js.map +1 -0
- package/dist/validation-error.cjs +33 -166
- package/dist/validation-error.cjs.map +1 -1
- package/dist/validation-error.d.cts +12 -103
- package/dist/validation-error.d.ts +12 -103
- package/dist/validation-error.js +7 -134
- package/dist/validation-error.js.map +1 -1
- package/dist/value-object.cjs +12 -126
- package/dist/value-object.cjs.map +1 -1
- package/dist/value-object.d.cts +97 -0
- package/dist/value-object.d.ts +97 -0
- package/dist/value-object.js +13 -115
- package/dist/value-object.js.map +1 -1
- package/meta.json +20 -4
- package/package.json +27 -1
- package/src/abac/index.ts +1 -0
- package/src/core/abac/abac-request.ts +29 -0
- package/src/core/abac/attributes.ts +39 -0
- package/src/core/abac/authorizer.ts +108 -0
- package/src/core/abac/combining.ts +63 -0
- package/src/core/abac/composite-authorizer.ts +45 -0
- package/src/core/abac/domain/policy-set.ts +52 -0
- package/src/core/abac/domain/rule.ts +197 -0
- package/src/core/abac/index.ts +21 -0
- package/src/core/application/commands/requested-by.ts +3 -4
- package/src/core/application/ports/abac-authorizer.port.ts +20 -0
- package/src/core/application/ports/authorizer.port.ts +22 -0
- package/src/core/application/queries/query.ts +6 -1
- package/src/core/application/use-case.ts +1 -1
- package/src/core/domain/entity.ts +1 -1
- package/src/core/domain/uuid-identifier.ts +1 -8
- package/src/core/domain/value-object.ts +12 -3
- package/src/core/errors/authorization-error.ts +26 -0
- package/src/core/errors/error-codes.ts +1 -0
- package/src/core/errors/utils/json-safe.ts +20 -11
- package/src/core/index.ts +7 -0
- package/src/core/policies/engines/v1/condition-evaluator.ts +56 -1
- package/src/core/rbac/access-request.ts +32 -0
- package/src/core/rbac/authorizer.ts +91 -0
- package/src/core/rbac/domain/grant.ts +96 -0
- package/src/core/rbac/domain/permission-set.ts +67 -0
- package/src/core/rbac/domain/permission.ts +119 -0
- package/src/core/rbac/domain/role.ts +101 -0
- package/src/core/rbac/domain/scope.ts +84 -0
- package/src/core/rbac/index.ts +15 -0
- package/src/core/rbac/policy-bridge.ts +44 -0
- package/src/core/shared/hashing.ts +1 -55
- package/src/core/shared/stable-stringify.ts +57 -0
- package/src/core/shared/uuid.ts +11 -0
- package/src/examples/application/authorize-cancel-order-abac.example.ts +107 -0
- package/src/examples/application/authorize-cancel-order.example.ts +101 -0
- package/src/rbac/index.ts +1 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { t as RequestedBy } from "./requested-by.cjs";
|
|
2
|
+
import { t as AuthorizationError } from "./authorization-error.cjs";
|
|
3
|
+
import { r as Result } from "./result.cjs";
|
|
4
|
+
import { t as ValueObject } from "./value-object.cjs";
|
|
5
|
+
import { a as Permission, n as AccessRequest, r as Scope } from "./authorizer.port.cjs";
|
|
6
|
+
|
|
7
|
+
//#region src/core/rbac/domain/role.d.ts
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The serializable shape of a role: a name plus its permissions kept as their
|
|
11
|
+
* primitive `"resource:action"` strings (so the role round-trips through JSON
|
|
12
|
+
* and stays deep-frozen). The {@link Role.permissions} getter rehydrates them
|
|
13
|
+
* back into {@link Permission} objects.
|
|
14
|
+
*/
|
|
15
|
+
type RoleProps = {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly permissions: readonly string[];
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* A named bundle of {@link Permission}s — `"cashier"`, `"manager"`, `"admin"`.
|
|
21
|
+
* There is **no role hierarchy** in v1: a role grants exactly the permissions
|
|
22
|
+
* it lists (wildcards included). Duplicate permissions are collapsed on
|
|
23
|
+
* construction, preserving first-seen order.
|
|
24
|
+
*
|
|
25
|
+
* A zod-free value object. Build through {@link of}; reconstruct a serialized
|
|
26
|
+
* one through {@link fromProps}.
|
|
27
|
+
*/
|
|
28
|
+
declare class Role extends ValueObject<RoleProps, RoleProps> {
|
|
29
|
+
private readonly _permissions;
|
|
30
|
+
private constructor();
|
|
31
|
+
/**
|
|
32
|
+
* Builds a role from a name and its permissions. Throws
|
|
33
|
+
* {@link InvalidValueException} when the name is blank. Duplicate
|
|
34
|
+
* permissions are deduplicated.
|
|
35
|
+
*/
|
|
36
|
+
static of(name: string, permissions: readonly Permission[]): Role;
|
|
37
|
+
/**
|
|
38
|
+
* Rebuilds a role from its serialized {@link RoleProps} — the inverse of
|
|
39
|
+
* {@link toPrimitive}. Each stored string is re-parsed through
|
|
40
|
+
* {@link Permission.of}, so a malformed payload still fails loudly.
|
|
41
|
+
*/
|
|
42
|
+
static fromProps(props: RoleProps): Role;
|
|
43
|
+
get name(): string;
|
|
44
|
+
/** The role's permissions, rehydrated from their stored primitive form. */
|
|
45
|
+
get permissions(): readonly Permission[];
|
|
46
|
+
/** Whether any permission in this role {@link Permission.implies | implies} `required`. */
|
|
47
|
+
grants(required: Permission): boolean;
|
|
48
|
+
toPrimitive(): RoleProps;
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/core/rbac/domain/grant.d.ts
|
|
52
|
+
/**
|
|
53
|
+
* The serializable shape a consumer persists and loads: the actor's identity
|
|
54
|
+
* (`RequestedBy.raw`), the role they hold (as {@link RoleProps}), and the scope
|
|
55
|
+
* the role applies in (the {@link Scope} string).
|
|
56
|
+
*/
|
|
57
|
+
type GrantProps = {
|
|
58
|
+
readonly subject: string;
|
|
59
|
+
readonly role: RoleProps;
|
|
60
|
+
readonly scope: string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* A role binding — it ties one actor to one {@link Role} within one
|
|
64
|
+
* {@link Scope}. This is the unit the consumer stores (a `(subject, role,
|
|
65
|
+
* scope)` row) and rehydrates into the pure decisor; the kit itself persists
|
|
66
|
+
* nothing.
|
|
67
|
+
*
|
|
68
|
+
* A zod-free value object built through {@link of} from live
|
|
69
|
+
* `RequestedBy`/`Role`/`Scope` objects; the getters reconstruct those objects
|
|
70
|
+
* back from the frozen primitive form.
|
|
71
|
+
*/
|
|
72
|
+
declare class Grant extends ValueObject<GrantProps, GrantProps> {
|
|
73
|
+
private readonly _subject;
|
|
74
|
+
private readonly _role;
|
|
75
|
+
private readonly _scope;
|
|
76
|
+
private constructor();
|
|
77
|
+
static of(params: {
|
|
78
|
+
subject: RequestedBy;
|
|
79
|
+
role: Role;
|
|
80
|
+
scope: Scope;
|
|
81
|
+
}): Grant;
|
|
82
|
+
/**
|
|
83
|
+
* Rebuilds a grant from its serialized {@link GrantProps} — the inverse of
|
|
84
|
+
* {@link toPrimitive}, symmetric to {@link Role.fromProps}. Each part is
|
|
85
|
+
* re-validated (subject through {@link RequestedBy.parse}, role through
|
|
86
|
+
* {@link Role.fromProps}, scope through {@link Scope.of}), so a malformed
|
|
87
|
+
* payload still fails loudly.
|
|
88
|
+
*/
|
|
89
|
+
static fromProps(props: GrantProps): Grant;
|
|
90
|
+
get subject(): RequestedBy;
|
|
91
|
+
get role(): Role;
|
|
92
|
+
get scope(): Scope;
|
|
93
|
+
/** Whether this grant belongs to `actor`. */
|
|
94
|
+
appliesTo(actor: RequestedBy): boolean;
|
|
95
|
+
toPrimitive(): GrantProps;
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/core/rbac/authorizer.d.ts
|
|
99
|
+
/**
|
|
100
|
+
* The pure RBAC decisor. Given an {@link AccessRequest} and the {@link Grant}s
|
|
101
|
+
* already loaded for the actor, it answers "may this actor do this?" with no
|
|
102
|
+
* I/O — no storage, no network, no clock — returning a {@link Result}. An
|
|
103
|
+
* authorization *decision* is never thrown, always a value, mirroring the
|
|
104
|
+
* "errors as values" contract of `UseCase`/`PolicyService`. Loading the grants
|
|
105
|
+
* is the consumer's job (behind an `AuthorizerPort` adapter); this class only
|
|
106
|
+
* decides.
|
|
107
|
+
*
|
|
108
|
+
* The one thing it *does* throw is {@link InvalidValueException} when the caller
|
|
109
|
+
* misuses the API by passing a wildcard `required` permission — that is a
|
|
110
|
+
* programming error surfaced loudly, not an authorization outcome.
|
|
111
|
+
*/
|
|
112
|
+
declare class RbacAuthorizer {
|
|
113
|
+
/**
|
|
114
|
+
* Decides the request against the supplied grants. The checks run
|
|
115
|
+
* role → capability → scope so the denial reason is the most informative
|
|
116
|
+
* one available:
|
|
117
|
+
*
|
|
118
|
+
* 1. no grant for the actor → `missing_role`;
|
|
119
|
+
* 2. has grants but none grants the permission → `missing_capability`;
|
|
120
|
+
* 3. grants the permission but not in scope → `out_of_scope`;
|
|
121
|
+
* 4. otherwise → ALLOW (`Result.ok`).
|
|
122
|
+
*
|
|
123
|
+
* @throws {@link InvalidValueException} when `request.required` is a wildcard
|
|
124
|
+
* permission (only *granted* permissions may wildcard, never the *required*
|
|
125
|
+
* one).
|
|
126
|
+
*/
|
|
127
|
+
authorize(request: AccessRequest, grants: readonly Grant[]): Result<void, AuthorizationError>;
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region src/core/rbac/domain/permission-set.d.ts
|
|
131
|
+
/**
|
|
132
|
+
* The flattened, deduplicated permissions an actor effectively holds across a
|
|
133
|
+
* set of {@link Grant}s, together with the role names that contributed them.
|
|
134
|
+
*
|
|
135
|
+
* Pure resolution, isolated from the decisor so the "roles → permissions"
|
|
136
|
+
* collapse can be tested on its own. Build through {@link fromGrants}; pre-filter
|
|
137
|
+
* the grants to a single actor first when that matters (the set does not know
|
|
138
|
+
* which actor it describes).
|
|
139
|
+
*/
|
|
140
|
+
declare class PermissionSet {
|
|
141
|
+
private readonly permissions;
|
|
142
|
+
private readonly _roleNames;
|
|
143
|
+
private constructor();
|
|
144
|
+
/**
|
|
145
|
+
* Collapses the roles across `grants` into one permission set. Permissions
|
|
146
|
+
* are deduplicated by their primitive form and role names by value, both
|
|
147
|
+
* preserving first-seen order.
|
|
148
|
+
*/
|
|
149
|
+
static fromGrants(grants: readonly Grant[]): PermissionSet;
|
|
150
|
+
/** Whether any held permission {@link Permission.implies | implies} `required`. */
|
|
151
|
+
has(required: Permission): boolean;
|
|
152
|
+
/** The effective permissions, deduplicated. */
|
|
153
|
+
toArray(): readonly Permission[];
|
|
154
|
+
/** The names of the roles that contributed to this set, deduplicated. */
|
|
155
|
+
get roleNames(): readonly string[];
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/core/rbac/policy-bridge.d.ts
|
|
159
|
+
/**
|
|
160
|
+
* Projects an actor's RBAC facts into flat {@link PolicyContext}-style fields so
|
|
161
|
+
* a declarative gate policy can reason over them (ABAC) — without importing the
|
|
162
|
+
* policy engine or `zod`. It only builds a plain object; the consumer merges it
|
|
163
|
+
* into `seed.fields` of `PolicyService.evaluate(...)`.
|
|
164
|
+
*
|
|
165
|
+
* The grants are filtered to `actor` first, so passing an actor's full grant
|
|
166
|
+
* list (or a mixed one) is safe.
|
|
167
|
+
*
|
|
168
|
+
* **Wildcards are projected literally — they are NOT expanded.** `actor.roles`
|
|
169
|
+
* and `actor.permissions` are the exact stored strings, so a permission such as
|
|
170
|
+
* `"orders:*"` or `"*:*"` appears verbatim. A gate condition like
|
|
171
|
+
* `{ field: "actor.permissions", op: "in", value: "orders:cancel" }` does a
|
|
172
|
+
* *literal* membership test and will therefore NOT match a holder of
|
|
173
|
+
* `"orders:*"`. Open-ended wildcards cannot be enumerated into a finite list, so
|
|
174
|
+
* for wildcard-aware checks either match on the finite `actor.roles` instead, or
|
|
175
|
+
* make the wildcard-aware decision with `RbacAuthorizer` (which knows
|
|
176
|
+
* {@link Permission.implies}) and use the gate only for the extra ABAC
|
|
177
|
+
* conditions.
|
|
178
|
+
*
|
|
179
|
+
* @returns `{ "actor.id", "actor.roles", "actor.permissions" }`, where roles and
|
|
180
|
+
* permissions are plain string arrays (wildcards left literal).
|
|
181
|
+
*/
|
|
182
|
+
declare function rbacContextFields(actor: RequestedBy, grants: readonly Grant[]): Record<string, unknown>;
|
|
183
|
+
//#endregion
|
|
184
|
+
export { GrantProps as a, Grant as i, PermissionSet as n, Role as o, RbacAuthorizer as r, RoleProps as s, rbacContextFields as t };
|
|
185
|
+
//# sourceMappingURL=policy-bridge.d.cts.map
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { t as RequestedBy } from "./requested-by.js";
|
|
2
|
+
import { t as AuthorizationError } from "./authorization-error.js";
|
|
3
|
+
import { r as Result } from "./result.js";
|
|
4
|
+
import { t as ValueObject } from "./value-object.js";
|
|
5
|
+
import { a as Permission, n as AccessRequest, r as Scope } from "./authorizer.port.js";
|
|
6
|
+
|
|
7
|
+
//#region src/core/rbac/domain/role.d.ts
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The serializable shape of a role: a name plus its permissions kept as their
|
|
11
|
+
* primitive `"resource:action"` strings (so the role round-trips through JSON
|
|
12
|
+
* and stays deep-frozen). The {@link Role.permissions} getter rehydrates them
|
|
13
|
+
* back into {@link Permission} objects.
|
|
14
|
+
*/
|
|
15
|
+
type RoleProps = {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly permissions: readonly string[];
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* A named bundle of {@link Permission}s — `"cashier"`, `"manager"`, `"admin"`.
|
|
21
|
+
* There is **no role hierarchy** in v1: a role grants exactly the permissions
|
|
22
|
+
* it lists (wildcards included). Duplicate permissions are collapsed on
|
|
23
|
+
* construction, preserving first-seen order.
|
|
24
|
+
*
|
|
25
|
+
* A zod-free value object. Build through {@link of}; reconstruct a serialized
|
|
26
|
+
* one through {@link fromProps}.
|
|
27
|
+
*/
|
|
28
|
+
declare class Role extends ValueObject<RoleProps, RoleProps> {
|
|
29
|
+
private readonly _permissions;
|
|
30
|
+
private constructor();
|
|
31
|
+
/**
|
|
32
|
+
* Builds a role from a name and its permissions. Throws
|
|
33
|
+
* {@link InvalidValueException} when the name is blank. Duplicate
|
|
34
|
+
* permissions are deduplicated.
|
|
35
|
+
*/
|
|
36
|
+
static of(name: string, permissions: readonly Permission[]): Role;
|
|
37
|
+
/**
|
|
38
|
+
* Rebuilds a role from its serialized {@link RoleProps} — the inverse of
|
|
39
|
+
* {@link toPrimitive}. Each stored string is re-parsed through
|
|
40
|
+
* {@link Permission.of}, so a malformed payload still fails loudly.
|
|
41
|
+
*/
|
|
42
|
+
static fromProps(props: RoleProps): Role;
|
|
43
|
+
get name(): string;
|
|
44
|
+
/** The role's permissions, rehydrated from their stored primitive form. */
|
|
45
|
+
get permissions(): readonly Permission[];
|
|
46
|
+
/** Whether any permission in this role {@link Permission.implies | implies} `required`. */
|
|
47
|
+
grants(required: Permission): boolean;
|
|
48
|
+
toPrimitive(): RoleProps;
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/core/rbac/domain/grant.d.ts
|
|
52
|
+
/**
|
|
53
|
+
* The serializable shape a consumer persists and loads: the actor's identity
|
|
54
|
+
* (`RequestedBy.raw`), the role they hold (as {@link RoleProps}), and the scope
|
|
55
|
+
* the role applies in (the {@link Scope} string).
|
|
56
|
+
*/
|
|
57
|
+
type GrantProps = {
|
|
58
|
+
readonly subject: string;
|
|
59
|
+
readonly role: RoleProps;
|
|
60
|
+
readonly scope: string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* A role binding — it ties one actor to one {@link Role} within one
|
|
64
|
+
* {@link Scope}. This is the unit the consumer stores (a `(subject, role,
|
|
65
|
+
* scope)` row) and rehydrates into the pure decisor; the kit itself persists
|
|
66
|
+
* nothing.
|
|
67
|
+
*
|
|
68
|
+
* A zod-free value object built through {@link of} from live
|
|
69
|
+
* `RequestedBy`/`Role`/`Scope` objects; the getters reconstruct those objects
|
|
70
|
+
* back from the frozen primitive form.
|
|
71
|
+
*/
|
|
72
|
+
declare class Grant extends ValueObject<GrantProps, GrantProps> {
|
|
73
|
+
private readonly _subject;
|
|
74
|
+
private readonly _role;
|
|
75
|
+
private readonly _scope;
|
|
76
|
+
private constructor();
|
|
77
|
+
static of(params: {
|
|
78
|
+
subject: RequestedBy;
|
|
79
|
+
role: Role;
|
|
80
|
+
scope: Scope;
|
|
81
|
+
}): Grant;
|
|
82
|
+
/**
|
|
83
|
+
* Rebuilds a grant from its serialized {@link GrantProps} — the inverse of
|
|
84
|
+
* {@link toPrimitive}, symmetric to {@link Role.fromProps}. Each part is
|
|
85
|
+
* re-validated (subject through {@link RequestedBy.parse}, role through
|
|
86
|
+
* {@link Role.fromProps}, scope through {@link Scope.of}), so a malformed
|
|
87
|
+
* payload still fails loudly.
|
|
88
|
+
*/
|
|
89
|
+
static fromProps(props: GrantProps): Grant;
|
|
90
|
+
get subject(): RequestedBy;
|
|
91
|
+
get role(): Role;
|
|
92
|
+
get scope(): Scope;
|
|
93
|
+
/** Whether this grant belongs to `actor`. */
|
|
94
|
+
appliesTo(actor: RequestedBy): boolean;
|
|
95
|
+
toPrimitive(): GrantProps;
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/core/rbac/authorizer.d.ts
|
|
99
|
+
/**
|
|
100
|
+
* The pure RBAC decisor. Given an {@link AccessRequest} and the {@link Grant}s
|
|
101
|
+
* already loaded for the actor, it answers "may this actor do this?" with no
|
|
102
|
+
* I/O — no storage, no network, no clock — returning a {@link Result}. An
|
|
103
|
+
* authorization *decision* is never thrown, always a value, mirroring the
|
|
104
|
+
* "errors as values" contract of `UseCase`/`PolicyService`. Loading the grants
|
|
105
|
+
* is the consumer's job (behind an `AuthorizerPort` adapter); this class only
|
|
106
|
+
* decides.
|
|
107
|
+
*
|
|
108
|
+
* The one thing it *does* throw is {@link InvalidValueException} when the caller
|
|
109
|
+
* misuses the API by passing a wildcard `required` permission — that is a
|
|
110
|
+
* programming error surfaced loudly, not an authorization outcome.
|
|
111
|
+
*/
|
|
112
|
+
declare class RbacAuthorizer {
|
|
113
|
+
/**
|
|
114
|
+
* Decides the request against the supplied grants. The checks run
|
|
115
|
+
* role → capability → scope so the denial reason is the most informative
|
|
116
|
+
* one available:
|
|
117
|
+
*
|
|
118
|
+
* 1. no grant for the actor → `missing_role`;
|
|
119
|
+
* 2. has grants but none grants the permission → `missing_capability`;
|
|
120
|
+
* 3. grants the permission but not in scope → `out_of_scope`;
|
|
121
|
+
* 4. otherwise → ALLOW (`Result.ok`).
|
|
122
|
+
*
|
|
123
|
+
* @throws {@link InvalidValueException} when `request.required` is a wildcard
|
|
124
|
+
* permission (only *granted* permissions may wildcard, never the *required*
|
|
125
|
+
* one).
|
|
126
|
+
*/
|
|
127
|
+
authorize(request: AccessRequest, grants: readonly Grant[]): Result<void, AuthorizationError>;
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region src/core/rbac/domain/permission-set.d.ts
|
|
131
|
+
/**
|
|
132
|
+
* The flattened, deduplicated permissions an actor effectively holds across a
|
|
133
|
+
* set of {@link Grant}s, together with the role names that contributed them.
|
|
134
|
+
*
|
|
135
|
+
* Pure resolution, isolated from the decisor so the "roles → permissions"
|
|
136
|
+
* collapse can be tested on its own. Build through {@link fromGrants}; pre-filter
|
|
137
|
+
* the grants to a single actor first when that matters (the set does not know
|
|
138
|
+
* which actor it describes).
|
|
139
|
+
*/
|
|
140
|
+
declare class PermissionSet {
|
|
141
|
+
private readonly permissions;
|
|
142
|
+
private readonly _roleNames;
|
|
143
|
+
private constructor();
|
|
144
|
+
/**
|
|
145
|
+
* Collapses the roles across `grants` into one permission set. Permissions
|
|
146
|
+
* are deduplicated by their primitive form and role names by value, both
|
|
147
|
+
* preserving first-seen order.
|
|
148
|
+
*/
|
|
149
|
+
static fromGrants(grants: readonly Grant[]): PermissionSet;
|
|
150
|
+
/** Whether any held permission {@link Permission.implies | implies} `required`. */
|
|
151
|
+
has(required: Permission): boolean;
|
|
152
|
+
/** The effective permissions, deduplicated. */
|
|
153
|
+
toArray(): readonly Permission[];
|
|
154
|
+
/** The names of the roles that contributed to this set, deduplicated. */
|
|
155
|
+
get roleNames(): readonly string[];
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/core/rbac/policy-bridge.d.ts
|
|
159
|
+
/**
|
|
160
|
+
* Projects an actor's RBAC facts into flat {@link PolicyContext}-style fields so
|
|
161
|
+
* a declarative gate policy can reason over them (ABAC) — without importing the
|
|
162
|
+
* policy engine or `zod`. It only builds a plain object; the consumer merges it
|
|
163
|
+
* into `seed.fields` of `PolicyService.evaluate(...)`.
|
|
164
|
+
*
|
|
165
|
+
* The grants are filtered to `actor` first, so passing an actor's full grant
|
|
166
|
+
* list (or a mixed one) is safe.
|
|
167
|
+
*
|
|
168
|
+
* **Wildcards are projected literally — they are NOT expanded.** `actor.roles`
|
|
169
|
+
* and `actor.permissions` are the exact stored strings, so a permission such as
|
|
170
|
+
* `"orders:*"` or `"*:*"` appears verbatim. A gate condition like
|
|
171
|
+
* `{ field: "actor.permissions", op: "in", value: "orders:cancel" }` does a
|
|
172
|
+
* *literal* membership test and will therefore NOT match a holder of
|
|
173
|
+
* `"orders:*"`. Open-ended wildcards cannot be enumerated into a finite list, so
|
|
174
|
+
* for wildcard-aware checks either match on the finite `actor.roles` instead, or
|
|
175
|
+
* make the wildcard-aware decision with `RbacAuthorizer` (which knows
|
|
176
|
+
* {@link Permission.implies}) and use the gate only for the extra ABAC
|
|
177
|
+
* conditions.
|
|
178
|
+
*
|
|
179
|
+
* @returns `{ "actor.id", "actor.roles", "actor.permissions" }`, where roles and
|
|
180
|
+
* permissions are plain string arrays (wildcards left literal).
|
|
181
|
+
*/
|
|
182
|
+
declare function rbacContextFields(actor: RequestedBy, grants: readonly Grant[]): Record<string, unknown>;
|
|
183
|
+
//#endregion
|
|
184
|
+
export { GrantProps as a, Grant as i, PermissionSet as n, Role as o, RbacAuthorizer as r, RoleProps as s, rbacContextFields as t };
|
|
185
|
+
//# sourceMappingURL=policy-bridge.d.ts.map
|