@rbacbee-lib/core 0.1.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/README.md +20 -0
- package/dist/index.cjs +629 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +300 -0
- package/dist/index.d.ts +300 -0
- package/dist/index.js +579 -0
- package/dist/index.js.map +1 -0
- package/package.json +37 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
// src/domain/errors/rbac-error.ts
|
|
2
|
+
var RbacError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
constructor(code, message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = new.target.name;
|
|
7
|
+
this.code = code;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var RbacValidationError = class extends RbacError {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super("RBAC_VALIDATION_ERROR", message);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var RbacAuthorizationError = class extends RbacError {
|
|
16
|
+
constructor(message = "Authorization denied") {
|
|
17
|
+
super("RBAC_AUTHORIZATION_DENIED", message);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var RbacConfigurationError = class extends RbacError {
|
|
21
|
+
constructor(message) {
|
|
22
|
+
super("RBAC_CONFIGURATION_ERROR", message);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// src/domain/value-objects/identifiers.ts
|
|
27
|
+
var ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_:@./-]{0,255}$/;
|
|
28
|
+
var PERMISSION_PATTERN = /^[A-Za-z0-9*][A-Za-z0-9:_.*-]{0,127}$/;
|
|
29
|
+
var StringValueObject = class {
|
|
30
|
+
value;
|
|
31
|
+
constructor(value, name, pattern, maxLength) {
|
|
32
|
+
const normalized = value.trim();
|
|
33
|
+
if (normalized.length === 0) {
|
|
34
|
+
throw new RbacValidationError(`${name} cannot be empty`);
|
|
35
|
+
}
|
|
36
|
+
if (normalized.length > maxLength) {
|
|
37
|
+
throw new RbacValidationError(`${name} cannot exceed ${maxLength} characters`);
|
|
38
|
+
}
|
|
39
|
+
if (!pattern.test(normalized)) {
|
|
40
|
+
throw new RbacValidationError(`${name} contains invalid characters`);
|
|
41
|
+
}
|
|
42
|
+
this.value = normalized;
|
|
43
|
+
Object.freeze(this);
|
|
44
|
+
}
|
|
45
|
+
equals(other) {
|
|
46
|
+
return this.value === other.value;
|
|
47
|
+
}
|
|
48
|
+
toString() {
|
|
49
|
+
return this.value;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var UserId = class _UserId extends StringValueObject {
|
|
53
|
+
constructor(value) {
|
|
54
|
+
super(value, "UserId", ID_PATTERN, 256);
|
|
55
|
+
}
|
|
56
|
+
static create(value) {
|
|
57
|
+
return new _UserId(value);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
var RoleId = class _RoleId extends StringValueObject {
|
|
61
|
+
constructor(value) {
|
|
62
|
+
super(value, "RoleId", ID_PATTERN, 256);
|
|
63
|
+
}
|
|
64
|
+
static create(value) {
|
|
65
|
+
return new _RoleId(value);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var TenantId = class _TenantId extends StringValueObject {
|
|
69
|
+
constructor(value) {
|
|
70
|
+
super(value, "TenantId", ID_PATTERN, 256);
|
|
71
|
+
}
|
|
72
|
+
static create(value) {
|
|
73
|
+
return new _TenantId(value);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
var ResourceId = class _ResourceId extends StringValueObject {
|
|
77
|
+
constructor(value) {
|
|
78
|
+
super(value, "ResourceId", ID_PATTERN, 256);
|
|
79
|
+
}
|
|
80
|
+
static create(value) {
|
|
81
|
+
return new _ResourceId(value);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var PermissionKey = class _PermissionKey extends StringValueObject {
|
|
85
|
+
constructor(value) {
|
|
86
|
+
super(value, "PermissionKey", PERMISSION_PATTERN, 128);
|
|
87
|
+
}
|
|
88
|
+
static create(value) {
|
|
89
|
+
return new _PermissionKey(value);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
var PolicyKey = class _PolicyKey extends StringValueObject {
|
|
93
|
+
constructor(value) {
|
|
94
|
+
super(value, "PolicyKey", PERMISSION_PATTERN, 128);
|
|
95
|
+
}
|
|
96
|
+
static create(value) {
|
|
97
|
+
return new _PolicyKey(value);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// src/domain/policies/policy-registry.ts
|
|
102
|
+
var PolicyRegistry = class {
|
|
103
|
+
handlers = /* @__PURE__ */ new Map();
|
|
104
|
+
register(policy, handler) {
|
|
105
|
+
const key = PolicyKey.create(policy).value;
|
|
106
|
+
this.handlers.set(key, handler);
|
|
107
|
+
}
|
|
108
|
+
has(policy) {
|
|
109
|
+
return this.handlers.has(PolicyKey.create(policy).value);
|
|
110
|
+
}
|
|
111
|
+
async evaluate(policy, principal, context) {
|
|
112
|
+
const key = PolicyKey.create(policy).value;
|
|
113
|
+
const handler = this.handlers.get(key);
|
|
114
|
+
if (handler === void 0) {
|
|
115
|
+
return {
|
|
116
|
+
decision: "deny",
|
|
117
|
+
reason: "policy not registered",
|
|
118
|
+
principal,
|
|
119
|
+
context,
|
|
120
|
+
policy: key
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const result = await handler({ principal, context });
|
|
124
|
+
if (typeof result === "boolean") {
|
|
125
|
+
return {
|
|
126
|
+
decision: result ? "allow" : "deny",
|
|
127
|
+
reason: result ? "policy allowed" : "policy denied",
|
|
128
|
+
principal,
|
|
129
|
+
context,
|
|
130
|
+
policy: key
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// src/infrastructure/clock/system-clock.ts
|
|
138
|
+
var SystemClock = class {
|
|
139
|
+
now() {
|
|
140
|
+
return /* @__PURE__ */ new Date();
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// src/domain/services/permission-evaluator.ts
|
|
145
|
+
var PermissionEvaluator = class {
|
|
146
|
+
evaluatePermission(profile, principal, requiredPermission, context) {
|
|
147
|
+
const now = context.now ?? /* @__PURE__ */ new Date();
|
|
148
|
+
const matched = profile.permissions.filter((permission) => permissionApplies(permission, requiredPermission, context, now)).map((permission) => permission.key);
|
|
149
|
+
if (matched.length > 0) {
|
|
150
|
+
return {
|
|
151
|
+
decision: "allow",
|
|
152
|
+
reason: "permission matched",
|
|
153
|
+
principal,
|
|
154
|
+
context,
|
|
155
|
+
permission: requiredPermission,
|
|
156
|
+
matched
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
decision: "deny",
|
|
161
|
+
reason: "permission not found",
|
|
162
|
+
principal,
|
|
163
|
+
context,
|
|
164
|
+
permission: requiredPermission
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
evaluateRole(profile, principal, requiredRole, context) {
|
|
168
|
+
const now = context.now ?? /* @__PURE__ */ new Date();
|
|
169
|
+
const matched = profile.roles.filter((role) => roleApplies(role, requiredRole, context, now)).map((role) => role.id);
|
|
170
|
+
if (matched.length > 0) {
|
|
171
|
+
return {
|
|
172
|
+
decision: "allow",
|
|
173
|
+
reason: "role matched",
|
|
174
|
+
principal,
|
|
175
|
+
context,
|
|
176
|
+
role: requiredRole,
|
|
177
|
+
matched
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
decision: "deny",
|
|
182
|
+
reason: "role not found",
|
|
183
|
+
principal,
|
|
184
|
+
context,
|
|
185
|
+
role: requiredRole
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
function matchesPermission(granted, required) {
|
|
190
|
+
if (granted === "*" || granted === required) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
const grantedParts = granted.split(":");
|
|
194
|
+
const requiredParts = required.split(":");
|
|
195
|
+
for (let index = 0; index < grantedParts.length; index += 1) {
|
|
196
|
+
const grantedPart = grantedParts[index];
|
|
197
|
+
const requiredPart = requiredParts[index];
|
|
198
|
+
if (grantedPart === void 0) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
if (grantedPart === "*") {
|
|
202
|
+
return index === grantedParts.length - 1 || requiredPart !== void 0;
|
|
203
|
+
}
|
|
204
|
+
if (requiredPart === void 0 || grantedPart !== requiredPart) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return grantedParts.length === requiredParts.length;
|
|
209
|
+
}
|
|
210
|
+
function permissionApplies(granted, requiredPermission, context, now) {
|
|
211
|
+
if (granted.expiresAt !== void 0 && granted.expiresAt <= now) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
if (!scopeApplies(granted, context)) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
return matchesPermission(granted.key, requiredPermission);
|
|
218
|
+
}
|
|
219
|
+
function roleApplies(granted, requiredRole, context, now) {
|
|
220
|
+
if (granted.expiresAt !== void 0 && granted.expiresAt <= now) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
if (!scopeApplies(granted, context)) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
return granted.id === requiredRole || granted.name === requiredRole;
|
|
227
|
+
}
|
|
228
|
+
function scopeApplies(granted, context) {
|
|
229
|
+
if (granted.tenantId !== void 0 && granted.tenantId !== context.tenantId) {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
if (granted.resourceType !== void 0 && granted.resourceType !== context.resourceType) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
if (granted.resourceId !== void 0 && granted.resourceId !== context.resourceId) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/application/use-cases/check-permission.ts
|
|
242
|
+
var CheckPermissionUseCase = class {
|
|
243
|
+
accessRepository;
|
|
244
|
+
evaluator;
|
|
245
|
+
cache;
|
|
246
|
+
audit;
|
|
247
|
+
clock;
|
|
248
|
+
cacheTtlMs;
|
|
249
|
+
constructor(dependencies) {
|
|
250
|
+
this.accessRepository = dependencies.accessRepository;
|
|
251
|
+
this.evaluator = dependencies.evaluator ?? new PermissionEvaluator();
|
|
252
|
+
this.cache = dependencies.cache;
|
|
253
|
+
this.audit = dependencies.audit;
|
|
254
|
+
this.clock = dependencies.clock ?? new SystemClock();
|
|
255
|
+
this.cacheTtlMs = dependencies.cacheTtlMs;
|
|
256
|
+
}
|
|
257
|
+
async execute(principal, requiredPermission, context = {}) {
|
|
258
|
+
UserId.create(principal.userId);
|
|
259
|
+
PermissionKey.create(requiredPermission);
|
|
260
|
+
const normalizedContext = normalizeContext(principal, context, this.clock.now());
|
|
261
|
+
const profile = await this.loadProfile(principal, normalizedContext);
|
|
262
|
+
const result = profile === null ? deny(principal, normalizedContext, requiredPermission, "access profile not found") : this.evaluator.evaluatePermission(
|
|
263
|
+
profile,
|
|
264
|
+
principal,
|
|
265
|
+
requiredPermission,
|
|
266
|
+
normalizedContext
|
|
267
|
+
);
|
|
268
|
+
await this.audit?.record({
|
|
269
|
+
type: "authorization.checked",
|
|
270
|
+
occurredAt: this.clock.now(),
|
|
271
|
+
decision: result.decision,
|
|
272
|
+
principal,
|
|
273
|
+
permission: requiredPermission,
|
|
274
|
+
context: normalizedContext,
|
|
275
|
+
reason: result.reason
|
|
276
|
+
});
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
279
|
+
async loadProfile(principal, context) {
|
|
280
|
+
const cacheKey = accessProfileCacheKey(principal.userId, context.tenantId);
|
|
281
|
+
const cachedProfile = await this.cache?.get(cacheKey);
|
|
282
|
+
if (cachedProfile !== void 0) {
|
|
283
|
+
return cachedProfile;
|
|
284
|
+
}
|
|
285
|
+
const query = { userId: principal.userId };
|
|
286
|
+
if (context.tenantId !== void 0) {
|
|
287
|
+
TenantId.create(context.tenantId);
|
|
288
|
+
Object.assign(query, { tenantId: context.tenantId });
|
|
289
|
+
}
|
|
290
|
+
const profile = await this.accessRepository.getAccessProfile(query);
|
|
291
|
+
if (profile !== null) {
|
|
292
|
+
await this.cache?.set(cacheKey, profile, this.cacheTtlMs);
|
|
293
|
+
}
|
|
294
|
+
return profile;
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
function normalizeContext(principal, context, now) {
|
|
298
|
+
return {
|
|
299
|
+
...context,
|
|
300
|
+
tenantId: context.tenantId ?? principal.tenantId,
|
|
301
|
+
now: context.now ?? now
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
function accessProfileCacheKey(userId, tenantId) {
|
|
305
|
+
return `rbac:access-profile:${userId}:${tenantId ?? "global"}`;
|
|
306
|
+
}
|
|
307
|
+
function deny(principal, context, permission, reason) {
|
|
308
|
+
return {
|
|
309
|
+
decision: "deny",
|
|
310
|
+
reason,
|
|
311
|
+
principal,
|
|
312
|
+
context,
|
|
313
|
+
permission
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// src/application/use-cases/check-role.ts
|
|
318
|
+
var CheckRoleUseCase = class {
|
|
319
|
+
accessRepository;
|
|
320
|
+
evaluator;
|
|
321
|
+
cache;
|
|
322
|
+
audit;
|
|
323
|
+
clock;
|
|
324
|
+
cacheTtlMs;
|
|
325
|
+
constructor(dependencies) {
|
|
326
|
+
this.accessRepository = dependencies.accessRepository;
|
|
327
|
+
this.evaluator = dependencies.evaluator ?? new PermissionEvaluator();
|
|
328
|
+
this.cache = dependencies.cache;
|
|
329
|
+
this.audit = dependencies.audit;
|
|
330
|
+
this.clock = dependencies.clock ?? new SystemClock();
|
|
331
|
+
this.cacheTtlMs = dependencies.cacheTtlMs;
|
|
332
|
+
}
|
|
333
|
+
async execute(principal, requiredRole, context = {}) {
|
|
334
|
+
UserId.create(principal.userId);
|
|
335
|
+
RoleId.create(requiredRole);
|
|
336
|
+
const normalizedContext = normalizeContext(principal, context, this.clock.now());
|
|
337
|
+
const profile = await this.loadProfile(principal, normalizedContext);
|
|
338
|
+
const result = profile === null ? deny2(principal, normalizedContext, requiredRole, "access profile not found") : this.evaluator.evaluateRole(profile, principal, requiredRole, normalizedContext);
|
|
339
|
+
await this.audit?.record({
|
|
340
|
+
type: "role.checked",
|
|
341
|
+
occurredAt: this.clock.now(),
|
|
342
|
+
decision: result.decision,
|
|
343
|
+
principal,
|
|
344
|
+
role: requiredRole,
|
|
345
|
+
context: normalizedContext,
|
|
346
|
+
reason: result.reason
|
|
347
|
+
});
|
|
348
|
+
return result;
|
|
349
|
+
}
|
|
350
|
+
async loadProfile(principal, context) {
|
|
351
|
+
const cacheKey = accessProfileCacheKey(principal.userId, context.tenantId);
|
|
352
|
+
const cachedProfile = await this.cache?.get(cacheKey);
|
|
353
|
+
if (cachedProfile !== void 0) {
|
|
354
|
+
return cachedProfile;
|
|
355
|
+
}
|
|
356
|
+
const query = { userId: principal.userId };
|
|
357
|
+
if (context.tenantId !== void 0) {
|
|
358
|
+
TenantId.create(context.tenantId);
|
|
359
|
+
Object.assign(query, { tenantId: context.tenantId });
|
|
360
|
+
}
|
|
361
|
+
const profile = await this.accessRepository.getAccessProfile(query);
|
|
362
|
+
if (profile !== null) {
|
|
363
|
+
await this.cache?.set(cacheKey, profile, this.cacheTtlMs);
|
|
364
|
+
}
|
|
365
|
+
return profile;
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
function deny2(principal, context, role, reason) {
|
|
369
|
+
return {
|
|
370
|
+
decision: "deny",
|
|
371
|
+
reason,
|
|
372
|
+
principal,
|
|
373
|
+
context,
|
|
374
|
+
role
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// src/application/rbac-engine.ts
|
|
379
|
+
var DefaultRbacEngine = class {
|
|
380
|
+
checkPermission;
|
|
381
|
+
checkRole;
|
|
382
|
+
policies;
|
|
383
|
+
audit;
|
|
384
|
+
clock;
|
|
385
|
+
constructor(dependencies) {
|
|
386
|
+
this.clock = dependencies.clock ?? new SystemClock();
|
|
387
|
+
this.audit = dependencies.audit;
|
|
388
|
+
this.policies = dependencies.policies ?? new PolicyRegistry();
|
|
389
|
+
this.checkPermission = new CheckPermissionUseCase(dependencies);
|
|
390
|
+
this.checkRole = new CheckRoleUseCase(dependencies);
|
|
391
|
+
}
|
|
392
|
+
can(principal, permission, context = {}) {
|
|
393
|
+
return this.checkPermission.execute(principal, permission, context);
|
|
394
|
+
}
|
|
395
|
+
async assertCan(principal, permission, context = {}) {
|
|
396
|
+
const result = await this.can(principal, permission, context);
|
|
397
|
+
if (result.decision === "deny") {
|
|
398
|
+
throw new RbacAuthorizationError(result.reason);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
hasRole(principal, role, context = {}) {
|
|
402
|
+
return this.checkRole.execute(principal, role, context);
|
|
403
|
+
}
|
|
404
|
+
async evaluatePolicy(policy, principal, context = {}) {
|
|
405
|
+
const key = PolicyKey.create(policy).value;
|
|
406
|
+
const result = await this.policies.evaluate(key, principal, {
|
|
407
|
+
...context,
|
|
408
|
+
tenantId: context.tenantId ?? principal.tenantId,
|
|
409
|
+
now: context.now ?? this.clock.now()
|
|
410
|
+
});
|
|
411
|
+
await this.audit?.record({
|
|
412
|
+
type: "policy.checked",
|
|
413
|
+
occurredAt: this.clock.now(),
|
|
414
|
+
decision: result.decision,
|
|
415
|
+
principal,
|
|
416
|
+
policy: key,
|
|
417
|
+
context: result.context,
|
|
418
|
+
reason: result.reason
|
|
419
|
+
});
|
|
420
|
+
return result;
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
function createRbacEngine(dependencies) {
|
|
424
|
+
return new DefaultRbacEngine(dependencies);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// src/domain/entities/assignment.ts
|
|
428
|
+
var Assignment = class _Assignment {
|
|
429
|
+
userId;
|
|
430
|
+
roleId;
|
|
431
|
+
tenantId;
|
|
432
|
+
resourceType;
|
|
433
|
+
resourceId;
|
|
434
|
+
expiresAt;
|
|
435
|
+
constructor(input) {
|
|
436
|
+
this.userId = UserId.create(input.userId);
|
|
437
|
+
this.roleId = RoleId.create(input.roleId);
|
|
438
|
+
this.tenantId = input.tenantId === void 0 ? void 0 : TenantId.create(input.tenantId);
|
|
439
|
+
this.resourceType = input.resourceType;
|
|
440
|
+
this.resourceId = input.resourceId === void 0 ? void 0 : ResourceId.create(input.resourceId);
|
|
441
|
+
this.expiresAt = input.expiresAt;
|
|
442
|
+
Object.freeze(this);
|
|
443
|
+
}
|
|
444
|
+
static create(input) {
|
|
445
|
+
return new _Assignment(input);
|
|
446
|
+
}
|
|
447
|
+
toPrimitives() {
|
|
448
|
+
const output = {
|
|
449
|
+
userId: this.userId.value,
|
|
450
|
+
roleId: this.roleId.value
|
|
451
|
+
};
|
|
452
|
+
return {
|
|
453
|
+
...output,
|
|
454
|
+
...this.tenantId === void 0 ? {} : { tenantId: this.tenantId.value },
|
|
455
|
+
...this.resourceType === void 0 ? {} : { resourceType: this.resourceType },
|
|
456
|
+
...this.resourceId === void 0 ? {} : { resourceId: this.resourceId.value },
|
|
457
|
+
...this.expiresAt === void 0 ? {} : { expiresAt: this.expiresAt }
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
// src/domain/entities/permission.ts
|
|
463
|
+
var Permission = class _Permission {
|
|
464
|
+
key;
|
|
465
|
+
description;
|
|
466
|
+
constructor(key, description) {
|
|
467
|
+
this.key = key;
|
|
468
|
+
this.description = description;
|
|
469
|
+
Object.freeze(this);
|
|
470
|
+
}
|
|
471
|
+
static create(input) {
|
|
472
|
+
return new _Permission(PermissionKey.create(input.key), input.description);
|
|
473
|
+
}
|
|
474
|
+
toPrimitives() {
|
|
475
|
+
const output = { key: this.key.value };
|
|
476
|
+
if (this.description !== void 0) {
|
|
477
|
+
return { ...output, description: this.description };
|
|
478
|
+
}
|
|
479
|
+
return output;
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
// src/domain/entities/role.ts
|
|
484
|
+
var Role = class _Role {
|
|
485
|
+
id;
|
|
486
|
+
name;
|
|
487
|
+
tenantId;
|
|
488
|
+
permissions;
|
|
489
|
+
constructor(id, name, permissions, tenantId) {
|
|
490
|
+
const normalizedName = name.trim();
|
|
491
|
+
if (normalizedName.length === 0) {
|
|
492
|
+
throw new Error("Role name cannot be empty");
|
|
493
|
+
}
|
|
494
|
+
this.id = id;
|
|
495
|
+
this.name = normalizedName;
|
|
496
|
+
this.permissions = Object.freeze([...permissions]);
|
|
497
|
+
this.tenantId = tenantId;
|
|
498
|
+
Object.freeze(this);
|
|
499
|
+
}
|
|
500
|
+
static create(input) {
|
|
501
|
+
return new _Role(
|
|
502
|
+
RoleId.create(input.id),
|
|
503
|
+
input.name,
|
|
504
|
+
(input.permissions ?? []).map((permission) => PermissionKey.create(permission)),
|
|
505
|
+
input.tenantId === void 0 ? void 0 : TenantId.create(input.tenantId)
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
toPrimitives() {
|
|
509
|
+
const output = {
|
|
510
|
+
id: this.id.value,
|
|
511
|
+
name: this.name,
|
|
512
|
+
permissions: this.permissions.map((permission) => permission.value)
|
|
513
|
+
};
|
|
514
|
+
if (this.tenantId !== void 0) {
|
|
515
|
+
return { ...output, tenantId: this.tenantId.value };
|
|
516
|
+
}
|
|
517
|
+
return output;
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
// src/infrastructure/cache/memory-cache.ts
|
|
522
|
+
var MemoryCache = class {
|
|
523
|
+
entries = /* @__PURE__ */ new Map();
|
|
524
|
+
async get(key) {
|
|
525
|
+
const entry = this.entries.get(key);
|
|
526
|
+
if (entry === void 0) {
|
|
527
|
+
return void 0;
|
|
528
|
+
}
|
|
529
|
+
if (entry.expiresAt !== void 0 && entry.expiresAt <= Date.now()) {
|
|
530
|
+
this.entries.delete(key);
|
|
531
|
+
return void 0;
|
|
532
|
+
}
|
|
533
|
+
return entry.value;
|
|
534
|
+
}
|
|
535
|
+
async set(key, value, ttlMs) {
|
|
536
|
+
const entry = ttlMs === void 0 ? { value } : { value, expiresAt: Date.now() + ttlMs };
|
|
537
|
+
this.entries.set(key, entry);
|
|
538
|
+
}
|
|
539
|
+
async delete(key) {
|
|
540
|
+
this.entries.delete(key);
|
|
541
|
+
}
|
|
542
|
+
async deleteByPrefix(prefix) {
|
|
543
|
+
for (const key of this.entries.keys()) {
|
|
544
|
+
if (key.startsWith(prefix)) {
|
|
545
|
+
this.entries.delete(key);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
clear() {
|
|
550
|
+
this.entries.clear();
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
export {
|
|
554
|
+
Assignment,
|
|
555
|
+
CheckPermissionUseCase,
|
|
556
|
+
CheckRoleUseCase,
|
|
557
|
+
DefaultRbacEngine,
|
|
558
|
+
MemoryCache,
|
|
559
|
+
Permission,
|
|
560
|
+
PermissionEvaluator,
|
|
561
|
+
PermissionKey,
|
|
562
|
+
PolicyKey,
|
|
563
|
+
PolicyRegistry,
|
|
564
|
+
RbacAuthorizationError,
|
|
565
|
+
RbacConfigurationError,
|
|
566
|
+
RbacError,
|
|
567
|
+
RbacValidationError,
|
|
568
|
+
ResourceId,
|
|
569
|
+
Role,
|
|
570
|
+
RoleId,
|
|
571
|
+
SystemClock,
|
|
572
|
+
TenantId,
|
|
573
|
+
UserId,
|
|
574
|
+
accessProfileCacheKey,
|
|
575
|
+
createRbacEngine,
|
|
576
|
+
matchesPermission,
|
|
577
|
+
normalizeContext
|
|
578
|
+
};
|
|
579
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/domain/errors/rbac-error.ts","../src/domain/value-objects/identifiers.ts","../src/domain/policies/policy-registry.ts","../src/infrastructure/clock/system-clock.ts","../src/domain/services/permission-evaluator.ts","../src/application/use-cases/check-permission.ts","../src/application/use-cases/check-role.ts","../src/application/rbac-engine.ts","../src/domain/entities/assignment.ts","../src/domain/entities/permission.ts","../src/domain/entities/role.ts","../src/infrastructure/cache/memory-cache.ts"],"sourcesContent":["export class RbacError extends Error {\n public readonly code: string;\n\n public constructor(code: string, message: string) {\n super(message);\n this.name = new.target.name;\n this.code = code;\n }\n}\n\nexport class RbacValidationError extends RbacError {\n public constructor(message: string) {\n super('RBAC_VALIDATION_ERROR', message);\n }\n}\n\nexport class RbacAuthorizationError extends RbacError {\n public constructor(message = 'Authorization denied') {\n super('RBAC_AUTHORIZATION_DENIED', message);\n }\n}\n\nexport class RbacConfigurationError extends RbacError {\n public constructor(message: string) {\n super('RBAC_CONFIGURATION_ERROR', message);\n }\n}\n","import { RbacValidationError } from '../errors/rbac-error';\n\nconst ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_:@./-]{0,255}$/;\nconst PERMISSION_PATTERN = /^[A-Za-z0-9*][A-Za-z0-9:_.*-]{0,127}$/;\n\nabstract class StringValueObject {\n public readonly value: string;\n\n protected constructor(value: string, name: string, pattern: RegExp, maxLength: number) {\n const normalized = value.trim();\n\n if (normalized.length === 0) {\n throw new RbacValidationError(`${name} cannot be empty`);\n }\n\n if (normalized.length > maxLength) {\n throw new RbacValidationError(`${name} cannot exceed ${maxLength} characters`);\n }\n\n if (!pattern.test(normalized)) {\n throw new RbacValidationError(`${name} contains invalid characters`);\n }\n\n this.value = normalized;\n Object.freeze(this);\n }\n\n public equals(other: StringValueObject): boolean {\n return this.value === other.value;\n }\n\n public toString(): string {\n return this.value;\n }\n}\n\nexport class UserId extends StringValueObject {\n private constructor(value: string) {\n super(value, 'UserId', ID_PATTERN, 256);\n }\n\n public static create(value: string): UserId {\n return new UserId(value);\n }\n}\n\nexport class RoleId extends StringValueObject {\n private constructor(value: string) {\n super(value, 'RoleId', ID_PATTERN, 256);\n }\n\n public static create(value: string): RoleId {\n return new RoleId(value);\n }\n}\n\nexport class TenantId extends StringValueObject {\n private constructor(value: string) {\n super(value, 'TenantId', ID_PATTERN, 256);\n }\n\n public static create(value: string): TenantId {\n return new TenantId(value);\n }\n}\n\nexport class ResourceId extends StringValueObject {\n private constructor(value: string) {\n super(value, 'ResourceId', ID_PATTERN, 256);\n }\n\n public static create(value: string): ResourceId {\n return new ResourceId(value);\n }\n}\n\nexport class PermissionKey extends StringValueObject {\n private constructor(value: string) {\n super(value, 'PermissionKey', PERMISSION_PATTERN, 128);\n }\n\n public static create(value: string): PermissionKey {\n return new PermissionKey(value);\n }\n}\n\nexport class PolicyKey extends StringValueObject {\n private constructor(value: string) {\n super(value, 'PolicyKey', PERMISSION_PATTERN, 128);\n }\n\n public static create(value: string): PolicyKey {\n return new PolicyKey(value);\n }\n}\n","import { PolicyKey } from '../value-objects/identifiers';\nimport type { AuthorizationResult, RbacAuthorizationContext, RbacPrincipal } from '../rbac-types';\n\nexport interface RbacPolicyInput {\n readonly principal: RbacPrincipal;\n readonly context: RbacAuthorizationContext;\n}\n\nexport type RbacPolicyHandler = (\n input: RbacPolicyInput\n) => boolean | AuthorizationResult | Promise<boolean | AuthorizationResult>;\n\nexport class PolicyRegistry {\n private readonly handlers = new Map<string, RbacPolicyHandler>();\n\n public register(policy: string, handler: RbacPolicyHandler): void {\n const key = PolicyKey.create(policy).value;\n this.handlers.set(key, handler);\n }\n\n public has(policy: string): boolean {\n return this.handlers.has(PolicyKey.create(policy).value);\n }\n\n public async evaluate(\n policy: string,\n principal: RbacPrincipal,\n context: RbacAuthorizationContext\n ): Promise<AuthorizationResult> {\n const key = PolicyKey.create(policy).value;\n const handler = this.handlers.get(key);\n\n if (handler === undefined) {\n return {\n decision: 'deny',\n reason: 'policy not registered',\n principal,\n context,\n policy: key\n };\n }\n\n const result = await handler({ principal, context });\n\n if (typeof result === 'boolean') {\n return {\n decision: result ? 'allow' : 'deny',\n reason: result ? 'policy allowed' : 'policy denied',\n principal,\n context,\n policy: key\n };\n }\n\n return result;\n }\n}\n","import type { ClockPort } from '../../ports/clock-port';\n\nexport class SystemClock implements ClockPort {\n public now(): Date {\n return new Date();\n }\n}\n","import type {\n AccessPermission,\n AccessProfile,\n AccessRole,\n AuthorizationResult,\n RbacAuthorizationContext,\n RbacPrincipal\n} from '../rbac-types';\n\nexport class PermissionEvaluator {\n public evaluatePermission(\n profile: AccessProfile,\n principal: RbacPrincipal,\n requiredPermission: string,\n context: RbacAuthorizationContext\n ): AuthorizationResult {\n const now = context.now ?? new Date();\n const matched = profile.permissions\n .filter((permission) => permissionApplies(permission, requiredPermission, context, now))\n .map((permission) => permission.key);\n\n if (matched.length > 0) {\n return {\n decision: 'allow',\n reason: 'permission matched',\n principal,\n context,\n permission: requiredPermission,\n matched\n };\n }\n\n return {\n decision: 'deny',\n reason: 'permission not found',\n principal,\n context,\n permission: requiredPermission\n };\n }\n\n public evaluateRole(\n profile: AccessProfile,\n principal: RbacPrincipal,\n requiredRole: string,\n context: RbacAuthorizationContext\n ): AuthorizationResult {\n const now = context.now ?? new Date();\n const matched = profile.roles\n .filter((role) => roleApplies(role, requiredRole, context, now))\n .map((role) => role.id);\n\n if (matched.length > 0) {\n return {\n decision: 'allow',\n reason: 'role matched',\n principal,\n context,\n role: requiredRole,\n matched\n };\n }\n\n return {\n decision: 'deny',\n reason: 'role not found',\n principal,\n context,\n role: requiredRole\n };\n }\n}\n\nexport function matchesPermission(granted: string, required: string): boolean {\n if (granted === '*' || granted === required) {\n return true;\n }\n\n const grantedParts = granted.split(':');\n const requiredParts = required.split(':');\n\n for (let index = 0; index < grantedParts.length; index += 1) {\n const grantedPart = grantedParts[index];\n const requiredPart = requiredParts[index];\n\n if (grantedPart === undefined) {\n return false;\n }\n\n if (grantedPart === '*') {\n return index === grantedParts.length - 1 || requiredPart !== undefined;\n }\n\n if (requiredPart === undefined || grantedPart !== requiredPart) {\n return false;\n }\n }\n\n return grantedParts.length === requiredParts.length;\n}\n\nfunction permissionApplies(\n granted: AccessPermission,\n requiredPermission: string,\n context: RbacAuthorizationContext,\n now: Date\n): boolean {\n if (granted.expiresAt !== undefined && granted.expiresAt <= now) {\n return false;\n }\n\n if (!scopeApplies(granted, context)) {\n return false;\n }\n\n return matchesPermission(granted.key, requiredPermission);\n}\n\nfunction roleApplies(\n granted: AccessRole,\n requiredRole: string,\n context: RbacAuthorizationContext,\n now: Date\n): boolean {\n if (granted.expiresAt !== undefined && granted.expiresAt <= now) {\n return false;\n }\n\n if (!scopeApplies(granted, context)) {\n return false;\n }\n\n return granted.id === requiredRole || granted.name === requiredRole;\n}\n\nfunction scopeApplies(\n granted: Pick<AccessPermission | AccessRole, 'tenantId' | 'resourceType' | 'resourceId'>,\n context: RbacAuthorizationContext\n): boolean {\n if (granted.tenantId !== undefined && granted.tenantId !== context.tenantId) {\n return false;\n }\n\n if (granted.resourceType !== undefined && granted.resourceType !== context.resourceType) {\n return false;\n }\n\n if (granted.resourceId !== undefined && granted.resourceId !== context.resourceId) {\n return false;\n }\n\n return true;\n}\n","import { PermissionKey, TenantId, UserId } from '../../domain/value-objects/identifiers';\nimport { PermissionEvaluator } from '../../domain/services/permission-evaluator';\nimport type {\n AccessProfile,\n AuthorizationResult,\n RbacAuthorizationContext,\n RbacPrincipal\n} from '../../domain/rbac-types';\nimport type { AccessProfileQuery, AccessRepository } from '../../ports/access-repository';\nimport type { AuditPort } from '../../ports/audit-port';\nimport type { CachePort } from '../../ports/cache-port';\nimport type { ClockPort } from '../../ports/clock-port';\nimport { SystemClock } from '../../infrastructure/clock/system-clock';\n\nexport interface CheckPermissionDependencies {\n readonly accessRepository: AccessRepository;\n readonly evaluator?: PermissionEvaluator;\n readonly cache?: CachePort;\n readonly audit?: AuditPort;\n readonly clock?: ClockPort;\n readonly cacheTtlMs?: number;\n}\n\nexport class CheckPermissionUseCase {\n private readonly accessRepository: AccessRepository;\n private readonly evaluator: PermissionEvaluator;\n private readonly cache?: CachePort;\n private readonly audit?: AuditPort;\n private readonly clock: ClockPort;\n private readonly cacheTtlMs?: number;\n\n public constructor(dependencies: CheckPermissionDependencies) {\n this.accessRepository = dependencies.accessRepository;\n this.evaluator = dependencies.evaluator ?? new PermissionEvaluator();\n this.cache = dependencies.cache;\n this.audit = dependencies.audit;\n this.clock = dependencies.clock ?? new SystemClock();\n this.cacheTtlMs = dependencies.cacheTtlMs;\n }\n\n public async execute(\n principal: RbacPrincipal,\n requiredPermission: string,\n context: RbacAuthorizationContext = {}\n ): Promise<AuthorizationResult> {\n UserId.create(principal.userId);\n PermissionKey.create(requiredPermission);\n\n const normalizedContext = normalizeContext(principal, context, this.clock.now());\n const profile = await this.loadProfile(principal, normalizedContext);\n\n const result =\n profile === null\n ? deny(principal, normalizedContext, requiredPermission, 'access profile not found')\n : this.evaluator.evaluatePermission(\n profile,\n principal,\n requiredPermission,\n normalizedContext\n );\n\n await this.audit?.record({\n type: 'authorization.checked',\n occurredAt: this.clock.now(),\n decision: result.decision,\n principal,\n permission: requiredPermission,\n context: normalizedContext,\n reason: result.reason\n });\n\n return result;\n }\n\n private async loadProfile(\n principal: RbacPrincipal,\n context: RbacAuthorizationContext\n ): Promise<AccessProfile | null> {\n const cacheKey = accessProfileCacheKey(principal.userId, context.tenantId);\n const cachedProfile = await this.cache?.get<AccessProfile>(cacheKey);\n\n if (cachedProfile !== undefined) {\n return cachedProfile;\n }\n\n const query: AccessProfileQuery = { userId: principal.userId };\n\n if (context.tenantId !== undefined) {\n TenantId.create(context.tenantId);\n Object.assign(query, { tenantId: context.tenantId });\n }\n\n const profile = await this.accessRepository.getAccessProfile(query);\n\n if (profile !== null) {\n await this.cache?.set(cacheKey, profile, this.cacheTtlMs);\n }\n\n return profile;\n }\n}\n\nexport function normalizeContext(\n principal: RbacPrincipal,\n context: RbacAuthorizationContext,\n now: Date\n): RbacAuthorizationContext {\n return {\n ...context,\n tenantId: context.tenantId ?? principal.tenantId,\n now: context.now ?? now\n };\n}\n\nexport function accessProfileCacheKey(userId: string, tenantId?: string): string {\n return `rbac:access-profile:${userId}:${tenantId ?? 'global'}`;\n}\n\nfunction deny(\n principal: RbacPrincipal,\n context: RbacAuthorizationContext,\n permission: string,\n reason: string\n): AuthorizationResult {\n return {\n decision: 'deny',\n reason,\n principal,\n context,\n permission\n };\n}\n","import { RoleId, TenantId, UserId } from '../../domain/value-objects/identifiers';\nimport { PermissionEvaluator } from '../../domain/services/permission-evaluator';\nimport type {\n AccessProfile,\n AuthorizationResult,\n RbacAuthorizationContext,\n RbacPrincipal\n} from '../../domain/rbac-types';\nimport type { AccessProfileQuery, AccessRepository } from '../../ports/access-repository';\nimport type { AuditPort } from '../../ports/audit-port';\nimport type { CachePort } from '../../ports/cache-port';\nimport type { ClockPort } from '../../ports/clock-port';\nimport { SystemClock } from '../../infrastructure/clock/system-clock';\nimport { accessProfileCacheKey, normalizeContext } from './check-permission';\n\nexport interface CheckRoleDependencies {\n readonly accessRepository: AccessRepository;\n readonly evaluator?: PermissionEvaluator;\n readonly cache?: CachePort;\n readonly audit?: AuditPort;\n readonly clock?: ClockPort;\n readonly cacheTtlMs?: number;\n}\n\nexport class CheckRoleUseCase {\n private readonly accessRepository: AccessRepository;\n private readonly evaluator: PermissionEvaluator;\n private readonly cache?: CachePort;\n private readonly audit?: AuditPort;\n private readonly clock: ClockPort;\n private readonly cacheTtlMs?: number;\n\n public constructor(dependencies: CheckRoleDependencies) {\n this.accessRepository = dependencies.accessRepository;\n this.evaluator = dependencies.evaluator ?? new PermissionEvaluator();\n this.cache = dependencies.cache;\n this.audit = dependencies.audit;\n this.clock = dependencies.clock ?? new SystemClock();\n this.cacheTtlMs = dependencies.cacheTtlMs;\n }\n\n public async execute(\n principal: RbacPrincipal,\n requiredRole: string,\n context: RbacAuthorizationContext = {}\n ): Promise<AuthorizationResult> {\n UserId.create(principal.userId);\n RoleId.create(requiredRole);\n\n const normalizedContext = normalizeContext(principal, context, this.clock.now());\n const profile = await this.loadProfile(principal, normalizedContext);\n\n const result =\n profile === null\n ? deny(principal, normalizedContext, requiredRole, 'access profile not found')\n : this.evaluator.evaluateRole(profile, principal, requiredRole, normalizedContext);\n\n await this.audit?.record({\n type: 'role.checked',\n occurredAt: this.clock.now(),\n decision: result.decision,\n principal,\n role: requiredRole,\n context: normalizedContext,\n reason: result.reason\n });\n\n return result;\n }\n\n private async loadProfile(\n principal: RbacPrincipal,\n context: RbacAuthorizationContext\n ): Promise<AccessProfile | null> {\n const cacheKey = accessProfileCacheKey(principal.userId, context.tenantId);\n const cachedProfile = await this.cache?.get<AccessProfile>(cacheKey);\n\n if (cachedProfile !== undefined) {\n return cachedProfile;\n }\n\n const query: AccessProfileQuery = { userId: principal.userId };\n\n if (context.tenantId !== undefined) {\n TenantId.create(context.tenantId);\n Object.assign(query, { tenantId: context.tenantId });\n }\n\n const profile = await this.accessRepository.getAccessProfile(query);\n\n if (profile !== null) {\n await this.cache?.set(cacheKey, profile, this.cacheTtlMs);\n }\n\n return profile;\n }\n}\n\nfunction deny(\n principal: RbacPrincipal,\n context: RbacAuthorizationContext,\n role: string,\n reason: string\n): AuthorizationResult {\n return {\n decision: 'deny',\n reason,\n principal,\n context,\n role\n };\n}\n","import { RbacAuthorizationError } from '../domain/errors/rbac-error';\nimport { PolicyRegistry } from '../domain/policies/policy-registry';\nimport type {\n AuthorizationResult,\n RbacAuthorizationContext,\n RbacPrincipal\n} from '../domain/rbac-types';\nimport { PolicyKey } from '../domain/value-objects/identifiers';\nimport type { AccessRepository } from '../ports/access-repository';\nimport type { AuditPort } from '../ports/audit-port';\nimport type { CachePort } from '../ports/cache-port';\nimport type { ClockPort } from '../ports/clock-port';\nimport { SystemClock } from '../infrastructure/clock/system-clock';\nimport { CheckPermissionUseCase } from './use-cases/check-permission';\nimport { CheckRoleUseCase } from './use-cases/check-role';\n\nexport interface RbacEngine {\n can(\n principal: RbacPrincipal,\n permission: string,\n context?: RbacAuthorizationContext\n ): Promise<AuthorizationResult>;\n assertCan(\n principal: RbacPrincipal,\n permission: string,\n context?: RbacAuthorizationContext\n ): Promise<void>;\n hasRole(\n principal: RbacPrincipal,\n role: string,\n context?: RbacAuthorizationContext\n ): Promise<AuthorizationResult>;\n evaluatePolicy(\n policy: string,\n principal: RbacPrincipal,\n context?: RbacAuthorizationContext\n ): Promise<AuthorizationResult>;\n}\n\nexport interface RbacEngineDependencies {\n readonly accessRepository: AccessRepository;\n readonly cache?: CachePort;\n readonly audit?: AuditPort;\n readonly clock?: ClockPort;\n readonly policies?: PolicyRegistry;\n readonly cacheTtlMs?: number;\n}\n\nexport class DefaultRbacEngine implements RbacEngine {\n private readonly checkPermission: CheckPermissionUseCase;\n private readonly checkRole: CheckRoleUseCase;\n private readonly policies: PolicyRegistry;\n private readonly audit?: AuditPort;\n private readonly clock: ClockPort;\n\n public constructor(dependencies: RbacEngineDependencies) {\n this.clock = dependencies.clock ?? new SystemClock();\n this.audit = dependencies.audit;\n this.policies = dependencies.policies ?? new PolicyRegistry();\n this.checkPermission = new CheckPermissionUseCase(dependencies);\n this.checkRole = new CheckRoleUseCase(dependencies);\n }\n\n public can(\n principal: RbacPrincipal,\n permission: string,\n context: RbacAuthorizationContext = {}\n ): Promise<AuthorizationResult> {\n return this.checkPermission.execute(principal, permission, context);\n }\n\n public async assertCan(\n principal: RbacPrincipal,\n permission: string,\n context: RbacAuthorizationContext = {}\n ): Promise<void> {\n const result = await this.can(principal, permission, context);\n\n if (result.decision === 'deny') {\n throw new RbacAuthorizationError(result.reason);\n }\n }\n\n public hasRole(\n principal: RbacPrincipal,\n role: string,\n context: RbacAuthorizationContext = {}\n ): Promise<AuthorizationResult> {\n return this.checkRole.execute(principal, role, context);\n }\n\n public async evaluatePolicy(\n policy: string,\n principal: RbacPrincipal,\n context: RbacAuthorizationContext = {}\n ): Promise<AuthorizationResult> {\n const key = PolicyKey.create(policy).value;\n const result = await this.policies.evaluate(key, principal, {\n ...context,\n tenantId: context.tenantId ?? principal.tenantId,\n now: context.now ?? this.clock.now()\n });\n\n await this.audit?.record({\n type: 'policy.checked',\n occurredAt: this.clock.now(),\n decision: result.decision,\n principal,\n policy: key,\n context: result.context,\n reason: result.reason\n });\n\n return result;\n }\n}\n\nexport function createRbacEngine(dependencies: RbacEngineDependencies): RbacEngine {\n return new DefaultRbacEngine(dependencies);\n}\n","import type { RoleAssignment } from '../rbac-types';\nimport { ResourceId, RoleId, TenantId, UserId } from '../value-objects/identifiers';\n\nexport class Assignment {\n public readonly userId: UserId;\n public readonly roleId: RoleId;\n public readonly tenantId?: TenantId;\n public readonly resourceType?: string;\n public readonly resourceId?: ResourceId;\n public readonly expiresAt?: Date;\n\n private constructor(input: RoleAssignment) {\n this.userId = UserId.create(input.userId);\n this.roleId = RoleId.create(input.roleId);\n this.tenantId = input.tenantId === undefined ? undefined : TenantId.create(input.tenantId);\n this.resourceType = input.resourceType;\n this.resourceId =\n input.resourceId === undefined ? undefined : ResourceId.create(input.resourceId);\n this.expiresAt = input.expiresAt;\n Object.freeze(this);\n }\n\n public static create(input: RoleAssignment): Assignment {\n return new Assignment(input);\n }\n\n public toPrimitives(): RoleAssignment {\n const output: RoleAssignment = {\n userId: this.userId.value,\n roleId: this.roleId.value\n };\n\n return {\n ...output,\n ...(this.tenantId === undefined ? {} : { tenantId: this.tenantId.value }),\n ...(this.resourceType === undefined ? {} : { resourceType: this.resourceType }),\n ...(this.resourceId === undefined ? {} : { resourceId: this.resourceId.value }),\n ...(this.expiresAt === undefined ? {} : { expiresAt: this.expiresAt })\n };\n }\n}\n","import type { PermissionDefinition } from '../rbac-types';\nimport { PermissionKey } from '../value-objects/identifiers';\n\nexport class Permission {\n public readonly key: PermissionKey;\n public readonly description?: string;\n\n private constructor(key: PermissionKey, description?: string) {\n this.key = key;\n this.description = description;\n Object.freeze(this);\n }\n\n public static create(input: PermissionDefinition): Permission {\n return new Permission(PermissionKey.create(input.key), input.description);\n }\n\n public toPrimitives(): PermissionDefinition {\n const output: PermissionDefinition = { key: this.key.value };\n\n if (this.description !== undefined) {\n return { ...output, description: this.description };\n }\n\n return output;\n }\n}\n","import type { RoleDefinition } from '../rbac-types';\nimport { PermissionKey, RoleId, TenantId } from '../value-objects/identifiers';\n\nexport class Role {\n public readonly id: RoleId;\n public readonly name: string;\n public readonly tenantId?: TenantId;\n public readonly permissions: readonly PermissionKey[];\n\n private constructor(\n id: RoleId,\n name: string,\n permissions: readonly PermissionKey[],\n tenantId?: TenantId\n ) {\n const normalizedName = name.trim();\n\n if (normalizedName.length === 0) {\n throw new Error('Role name cannot be empty');\n }\n\n this.id = id;\n this.name = normalizedName;\n this.permissions = Object.freeze([...permissions]);\n this.tenantId = tenantId;\n Object.freeze(this);\n }\n\n public static create(input: RoleDefinition): Role {\n return new Role(\n RoleId.create(input.id),\n input.name,\n (input.permissions ?? []).map((permission) => PermissionKey.create(permission)),\n input.tenantId === undefined ? undefined : TenantId.create(input.tenantId)\n );\n }\n\n public toPrimitives(): RoleDefinition {\n const output: RoleDefinition = {\n id: this.id.value,\n name: this.name,\n permissions: this.permissions.map((permission) => permission.value)\n };\n\n if (this.tenantId !== undefined) {\n return { ...output, tenantId: this.tenantId.value };\n }\n\n return output;\n }\n}\n","import type { CachePort } from '../../ports/cache-port';\n\ninterface MemoryCacheEntry {\n readonly value: unknown;\n readonly expiresAt?: number;\n}\n\nexport class MemoryCache implements CachePort {\n private readonly entries = new Map<string, MemoryCacheEntry>();\n\n public async get<TValue>(key: string): Promise<TValue | undefined> {\n const entry = this.entries.get(key);\n\n if (entry === undefined) {\n return undefined;\n }\n\n if (entry.expiresAt !== undefined && entry.expiresAt <= Date.now()) {\n this.entries.delete(key);\n return undefined;\n }\n\n return entry.value as TValue;\n }\n\n public async set<TValue>(key: string, value: TValue, ttlMs?: number): Promise<void> {\n const entry: MemoryCacheEntry =\n ttlMs === undefined ? { value } : { value, expiresAt: Date.now() + ttlMs };\n\n this.entries.set(key, entry);\n }\n\n public async delete(key: string): Promise<void> {\n this.entries.delete(key);\n }\n\n public async deleteByPrefix(prefix: string): Promise<void> {\n for (const key of this.entries.keys()) {\n if (key.startsWith(prefix)) {\n this.entries.delete(key);\n }\n }\n }\n\n public clear(): void {\n this.entries.clear();\n }\n}\n"],"mappings":"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","names":["deny"]}
|