@rpcbase/db 0.26.0 → 0.28.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/dist/acl/accessible.d.ts +3 -0
- package/dist/acl/accessible.d.ts.map +1 -0
- package/dist/acl/buildAbility.d.ts +3 -0
- package/dist/acl/buildAbility.d.ts.map +1 -0
- package/dist/acl/can.d.ts +3 -0
- package/dist/acl/can.d.ts.map +1 -0
- package/dist/acl/index.d.ts +7 -0
- package/dist/acl/index.d.ts.map +1 -0
- package/dist/acl/registry.d.ts +10 -0
- package/dist/acl/registry.d.ts.map +1 -0
- package/dist/acl/session.d.ts +17 -0
- package/dist/acl/session.d.ts.map +1 -0
- package/dist/acl/types.d.ts +22 -0
- package/dist/acl/types.d.ts.map +1 -0
- package/dist/acl.d.ts +2 -0
- package/dist/acl.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +185 -2
- package/dist/models/RBNotification.d.ts +2 -0
- package/dist/models/RBNotification.d.ts.map +1 -1
- package/dist/models/RBNotificationSettings.d.ts +2 -0
- package/dist/models/RBNotificationSettings.d.ts.map +1 -1
- package/dist/models/RBUploadSession.d.ts +2 -0
- package/dist/models/RBUploadSession.d.ts.map +1 -1
- package/dist/models/User.d.ts +1 -0
- package/dist/models/User.d.ts.map +1 -1
- package/dist/registerModels.d.ts.map +1 -1
- package/package.json +20 -4
- package/dist/schema/index.test.d.ts +0 -2
- package/dist/schema/index.test.d.ts.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessible.d.ts","sourceRoot":"","sources":["../../src/acl/accessible.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAGpE,eAAO,MAAM,oBAAoB,GAC/B,SAAS,UAAU,EACnB,QAAQ,SAAS,EACjB,SAAS,OAAO,CAAC,cAAc,EAAE,KAAK,CAAC,KACtC,MAAM,CAAC,MAAM,EAAE,OAAO,CAExB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildAbility.d.ts","sourceRoot":"","sources":["../../src/acl/buildAbility.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAGrD,eAAO,MAAM,YAAY,GAAI,KAAK,UAAU,KAAG,UAc9C,CAAA"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { AclAction, AclSubjectMap, AclSubjectType, AppAbility } from './types';
|
|
2
|
+
export declare const can: <TSubject extends Exclude<AclSubjectType, "all">>(ability: AppAbility, action: AclAction, subjectType: TSubject, object?: Partial<AclSubjectMap[TSubject]> | null) => boolean;
|
|
3
|
+
//# sourceMappingURL=can.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"can.d.ts","sourceRoot":"","sources":["../../src/acl/can.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAc,aAAa,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAG/F,eAAO,MAAM,GAAG,GAAI,QAAQ,SAAS,OAAO,CAAC,cAAc,EAAE,KAAK,CAAC,EACjE,SAAS,UAAU,EACnB,QAAQ,SAAS,EACjB,aAAa,QAAQ,EACrB,SAAS,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,KAC/C,OAKF,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/acl/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,OAAO,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AbilityBuilder } from '@casl/ability';
|
|
2
|
+
import { AclContext, AclSubjectType, AppAbility } from './types';
|
|
3
|
+
export type AclPolicy = {
|
|
4
|
+
subject: Exclude<AclSubjectType, "all">;
|
|
5
|
+
define: (builder: AbilityBuilder<AppAbility>, ctx: AclContext) => void;
|
|
6
|
+
};
|
|
7
|
+
export declare const registerPolicy: (policy: AclPolicy) => void;
|
|
8
|
+
export declare const registerPoliciesFromModules: (modules: Record<string, unknown>) => void;
|
|
9
|
+
export declare const getRegisteredPolicies: () => Array<AclPolicy["define"]>;
|
|
10
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/acl/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEnD,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAGrE,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,OAAO,CAAC,cAAc,EAAE,KAAK,CAAC,CAAA;IACvC,MAAM,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,UAAU,KAAK,IAAI,CAAA;CACvE,CAAA;AAuBD,eAAO,MAAM,cAAc,GAAI,QAAQ,SAAS,KAAG,IAIlD,CAAA;AAED,eAAO,MAAM,2BAA2B,GAAI,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAmB9E,CAAA;AAED,eAAO,MAAM,qBAAqB,QAAO,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,CAMjE,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { AppAbility } from './types';
|
|
2
|
+
type SessionUserLike = {
|
|
3
|
+
id?: unknown;
|
|
4
|
+
signedInTenants?: unknown;
|
|
5
|
+
tenantRoles?: unknown;
|
|
6
|
+
};
|
|
7
|
+
type SessionLike = {
|
|
8
|
+
user?: SessionUserLike;
|
|
9
|
+
};
|
|
10
|
+
export declare const getTenantRolesFromSessionUser: (user: unknown, tenantId: string) => string[];
|
|
11
|
+
export declare const buildAbilityFromSession: ({ tenantId, session, claims, }: {
|
|
12
|
+
tenantId: string;
|
|
13
|
+
session: SessionLike | null | undefined;
|
|
14
|
+
claims?: Record<string, unknown>;
|
|
15
|
+
}) => AppAbility;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/acl/session.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAyB,MAAM,SAAS,CAAA;AAGhE,KAAK,eAAe,GAAG;IACrB,EAAE,CAAC,EAAE,OAAO,CAAA;IACZ,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAED,KAAK,WAAW,GAAG;IACjB,IAAI,CAAC,EAAE,eAAe,CAAA;CACvB,CAAA;AA+BD,eAAO,MAAM,6BAA6B,GAAI,MAAM,OAAO,EAAE,UAAU,MAAM,KAAG,MAAM,EAKrF,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAI,gCAIrC;IACD,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,CAAA;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC,KAAG,UAuBH,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ForcedSubject, MongoAbility } from '@casl/ability';
|
|
2
|
+
export type AclAction = "manage" | "create" | "read" | "update" | "delete";
|
|
3
|
+
export interface AclSubjectMap {
|
|
4
|
+
RBUploadSession: Record<string, unknown>;
|
|
5
|
+
RBNotification: Record<string, unknown>;
|
|
6
|
+
RBNotificationSettings: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
export type AclSubjectType = keyof AclSubjectMap | "all";
|
|
9
|
+
type AclSubjectObject = {
|
|
10
|
+
[K in keyof AclSubjectMap]: ForcedSubject<K> & Partial<AclSubjectMap[K]>;
|
|
11
|
+
}[keyof AclSubjectMap];
|
|
12
|
+
export type AclSubject = AclSubjectType | AclSubjectObject;
|
|
13
|
+
export type AppAbility = MongoAbility<[AclAction, AclSubject]>;
|
|
14
|
+
export type TenantRolesByTenantId = Record<string, string[]>;
|
|
15
|
+
export type AclContext = {
|
|
16
|
+
tenantId: string;
|
|
17
|
+
userId: string | null;
|
|
18
|
+
roles: string[];
|
|
19
|
+
claims?: Record<string, unknown>;
|
|
20
|
+
};
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/acl/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAGhE,MAAM,MAAM,SAAS,GACjB,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,QAAQ,CAAA;AAEZ,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACxC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACvC,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChD;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,aAAa,GAAG,KAAK,CAAA;AAExD,KAAK,gBAAgB,GAAG;KACrB,CAAC,IAAI,MAAM,aAAa,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;CACzE,CAAC,MAAM,aAAa,CAAC,CAAA;AAEtB,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,gBAAgB,CAAA;AAE1D,MAAM,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAA;AAE9D,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;AAE5D,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC,CAAA"}
|
package/dist/acl.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acl.d.ts","sourceRoot":"","sources":["../src/acl.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,sBAAsB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,sBAAsB,CAAA;AACpC,cAAc,OAAO,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -3,12 +3,15 @@ import { z, ZodMap, ZodRecord, ZodAny, ZodUnion, ZodNullable, ZodOptional, ZodDe
|
|
|
3
3
|
export * from "zod";
|
|
4
4
|
import { z as z2 } from "zod";
|
|
5
5
|
import assert from "assert";
|
|
6
|
+
import { accessibleBy, accessibleRecordsPlugin } from "@casl/mongoose";
|
|
7
|
+
import { AbilityBuilder, createMongoAbility, subject } from "@casl/ability";
|
|
6
8
|
const ZRBUser = z.object({
|
|
7
9
|
email: z.string().email().optional(),
|
|
8
10
|
password: z.string(),
|
|
9
11
|
name: z.string().optional(),
|
|
10
12
|
phone: z.string().optional(),
|
|
11
13
|
tenants: z.array(z.string()),
|
|
14
|
+
tenantRoles: z.record(z.string(), z.array(z.string())).optional(),
|
|
12
15
|
emailVerificationCode: z.string().length(6).optional(),
|
|
13
16
|
emailVerificationExpiresAt: z.date().optional()
|
|
14
17
|
});
|
|
@@ -18,6 +21,7 @@ const RBUserSchema = new Schema({
|
|
|
18
21
|
password: { type: String, required: true },
|
|
19
22
|
name: String,
|
|
20
23
|
tenants: { type: [String], index: true, required: true },
|
|
24
|
+
tenantRoles: { type: Map, of: [String], required: false, default: {} },
|
|
21
25
|
emailVerificationCode: { type: String, required: false },
|
|
22
26
|
emailVerificationExpiresAt: { type: Date, required: false }
|
|
23
27
|
}, { collection: "users" });
|
|
@@ -244,6 +248,23 @@ const RBUploadSessionSchema = new Schema(
|
|
|
244
248
|
}
|
|
245
249
|
);
|
|
246
250
|
RBUploadSessionSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
|
|
251
|
+
const RBUploadSessionPolicy = {
|
|
252
|
+
subject: "RBUploadSession",
|
|
253
|
+
define: (builder, ctx) => {
|
|
254
|
+
builder.can("create", "RBUploadSession");
|
|
255
|
+
if (ctx.userId) {
|
|
256
|
+
builder.can("read", "RBUploadSession", { userId: ctx.userId });
|
|
257
|
+
builder.can("update", "RBUploadSession", { userId: ctx.userId });
|
|
258
|
+
builder.can("delete", "RBUploadSession", { userId: ctx.userId });
|
|
259
|
+
}
|
|
260
|
+
const uploadKeyHash = typeof ctx.claims?.uploadKeyHash === "string" ? ctx.claims.uploadKeyHash.trim() : "";
|
|
261
|
+
if (uploadKeyHash) {
|
|
262
|
+
builder.can("read", "RBUploadSession", { ownerKeyHash: uploadKeyHash });
|
|
263
|
+
builder.can("update", "RBUploadSession", { ownerKeyHash: uploadKeyHash });
|
|
264
|
+
builder.can("delete", "RBUploadSession", { ownerKeyHash: uploadKeyHash });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
};
|
|
247
268
|
const ZRBUploadChunk = z.object({
|
|
248
269
|
uploadId: z.string(),
|
|
249
270
|
index: z.number().int().min(0),
|
|
@@ -305,6 +326,16 @@ RBNotificationSchema.index({ userId: 1, archivedAt: 1, createdAt: -1 });
|
|
|
305
326
|
RBNotificationSchema.index({ userId: 1, seenAt: 1, archivedAt: 1, createdAt: -1 });
|
|
306
327
|
RBNotificationSchema.index({ userId: 1, readAt: 1, archivedAt: 1, createdAt: -1 });
|
|
307
328
|
RBNotificationSchema.index({ archivedAt: 1 }, { expireAfterSeconds: TTL_90_DAYS_S });
|
|
329
|
+
const RBNotificationPolicy = {
|
|
330
|
+
subject: "RBNotification",
|
|
331
|
+
define: (builder, ctx) => {
|
|
332
|
+
if (!ctx.userId) return;
|
|
333
|
+
builder.can("create", "RBNotification");
|
|
334
|
+
builder.can("read", "RBNotification", { userId: ctx.userId });
|
|
335
|
+
builder.can("update", "RBNotification", { userId: ctx.userId });
|
|
336
|
+
builder.can("delete", "RBNotification", { userId: ctx.userId });
|
|
337
|
+
}
|
|
338
|
+
};
|
|
308
339
|
const ZRBNotificationDigestFrequency = z.enum(["off", "daily", "weekly"]);
|
|
309
340
|
const ZRBNotificationTopicPreference = z.object({
|
|
310
341
|
topic: z.string(),
|
|
@@ -350,9 +381,20 @@ const RBNotificationSettingsSchema = new Schema(
|
|
|
350
381
|
);
|
|
351
382
|
RBNotificationSettingsSchema.index({ userId: 1 }, { unique: true });
|
|
352
383
|
RBNotificationSettingsSchema.index({ userId: 1, "topicPreferences.topic": 1 });
|
|
384
|
+
const RBNotificationSettingsPolicy = {
|
|
385
|
+
subject: "RBNotificationSettings",
|
|
386
|
+
define: (builder, ctx) => {
|
|
387
|
+
if (!ctx.userId) return;
|
|
388
|
+
builder.can("create", "RBNotificationSettings");
|
|
389
|
+
builder.can("read", "RBNotificationSettings", { userId: ctx.userId });
|
|
390
|
+
builder.can("update", "RBNotificationSettings", { userId: ctx.userId });
|
|
391
|
+
}
|
|
392
|
+
};
|
|
353
393
|
const frameworkSchemas = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
354
394
|
__proto__: null,
|
|
395
|
+
RBNotificationPolicy,
|
|
355
396
|
RBNotificationSchema,
|
|
397
|
+
RBNotificationSettingsPolicy,
|
|
356
398
|
RBNotificationSettingsSchema,
|
|
357
399
|
RBRtsChangeSchema,
|
|
358
400
|
RBRtsCounterSchema,
|
|
@@ -360,6 +402,7 @@ const frameworkSchemas = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de
|
|
|
360
402
|
RBTenantSubscriptionEventSchema,
|
|
361
403
|
RBTenantSubscriptionSchema,
|
|
362
404
|
RBUploadChunkSchema,
|
|
405
|
+
RBUploadSessionPolicy,
|
|
363
406
|
RBUploadSessionSchema,
|
|
364
407
|
RBUserSchema,
|
|
365
408
|
ZRBNotification,
|
|
@@ -1262,10 +1305,129 @@ const rtsChangeLogPlugin = (schema) => {
|
|
|
1262
1305
|
}
|
|
1263
1306
|
});
|
|
1264
1307
|
};
|
|
1265
|
-
const
|
|
1308
|
+
const POLICIES_GLOBAL_KEY = /* @__PURE__ */ Symbol.for("@rpcbase/db/acl/policiesBySubject");
|
|
1309
|
+
const getGlobalPoliciesMap = () => {
|
|
1310
|
+
const store = globalThis;
|
|
1311
|
+
const existing = store[POLICIES_GLOBAL_KEY];
|
|
1312
|
+
if (existing instanceof Map) {
|
|
1313
|
+
return existing;
|
|
1314
|
+
}
|
|
1315
|
+
const created = /* @__PURE__ */ new Map();
|
|
1316
|
+
store[POLICIES_GLOBAL_KEY] = created;
|
|
1317
|
+
return created;
|
|
1318
|
+
};
|
|
1319
|
+
const policiesBySubject = getGlobalPoliciesMap();
|
|
1320
|
+
const isPolicy = (value) => {
|
|
1321
|
+
if (!value || typeof value !== "object") return false;
|
|
1322
|
+
const maybe = value;
|
|
1323
|
+
return typeof maybe.subject === "string" && typeof maybe.define === "function";
|
|
1324
|
+
};
|
|
1325
|
+
const registerPolicy = (policy) => {
|
|
1326
|
+
const set = policiesBySubject.get(policy.subject) ?? /* @__PURE__ */ new Set();
|
|
1327
|
+
set.add(policy.define);
|
|
1328
|
+
policiesBySubject.set(policy.subject, set);
|
|
1329
|
+
};
|
|
1330
|
+
const registerPoliciesFromModules = (modules) => {
|
|
1331
|
+
for (const [exportName, value] of Object.entries(modules)) {
|
|
1332
|
+
if (!isPolicy(value)) continue;
|
|
1333
|
+
if (!exportName.endsWith("Policy")) {
|
|
1334
|
+
throw new Error(
|
|
1335
|
+
`Invalid policy export name "${exportName}". Policies must be exported as "<ModelName>Policy".`
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
const expectedSubject = exportName.slice(0, -"Policy".length);
|
|
1339
|
+
if (value.subject !== expectedSubject) {
|
|
1340
|
+
throw new Error(
|
|
1341
|
+
`Invalid policy "${exportName}": expected subject "${expectedSubject}", got "${value.subject}".`
|
|
1342
|
+
);
|
|
1343
|
+
}
|
|
1344
|
+
registerPolicy(value);
|
|
1345
|
+
}
|
|
1346
|
+
};
|
|
1347
|
+
const getRegisteredPolicies = () => {
|
|
1348
|
+
const out = [];
|
|
1349
|
+
for (const set of policiesBySubject.values()) {
|
|
1350
|
+
for (const fn of set) out.push(fn);
|
|
1351
|
+
}
|
|
1352
|
+
return out;
|
|
1353
|
+
};
|
|
1354
|
+
const buildAbility = (ctx) => {
|
|
1355
|
+
const builder = new AbilityBuilder(createMongoAbility);
|
|
1356
|
+
const { can: can2 } = builder;
|
|
1357
|
+
if (ctx.roles.includes("owner") || ctx.roles.includes("admin")) {
|
|
1358
|
+
can2("manage", "all");
|
|
1359
|
+
}
|
|
1360
|
+
for (const define of getRegisteredPolicies()) {
|
|
1361
|
+
define(builder, ctx);
|
|
1362
|
+
}
|
|
1363
|
+
return builder.build();
|
|
1364
|
+
};
|
|
1365
|
+
const normalizeRole = (raw) => {
|
|
1366
|
+
if (typeof raw !== "string") return null;
|
|
1367
|
+
const normalized = raw.trim();
|
|
1368
|
+
return normalized ? normalized : null;
|
|
1369
|
+
};
|
|
1370
|
+
const normalizeRoles = (raw) => {
|
|
1371
|
+
if (Array.isArray(raw)) {
|
|
1372
|
+
return raw.map(normalizeRole).filter((r) => Boolean(r));
|
|
1373
|
+
}
|
|
1374
|
+
const role = normalizeRole(raw);
|
|
1375
|
+
return role ? [role] : [];
|
|
1376
|
+
};
|
|
1377
|
+
const normalizeRolesByTenantId = (raw) => {
|
|
1378
|
+
if (!raw || typeof raw !== "object") return null;
|
|
1379
|
+
if (raw instanceof Map) {
|
|
1380
|
+
return Object.fromEntries(Array.from(raw.entries()).map(([key, value]) => [String(key), normalizeRoles(value)]));
|
|
1381
|
+
}
|
|
1382
|
+
const obj = raw;
|
|
1383
|
+
const out = {};
|
|
1384
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1385
|
+
const tenantId = String(key).trim();
|
|
1386
|
+
if (!tenantId) continue;
|
|
1387
|
+
out[tenantId] = normalizeRoles(value);
|
|
1388
|
+
}
|
|
1389
|
+
return out;
|
|
1390
|
+
};
|
|
1391
|
+
const getTenantRolesFromSessionUser = (user, tenantId) => {
|
|
1392
|
+
if (!user || typeof user !== "object") return [];
|
|
1393
|
+
const rolesByTenantId = normalizeRolesByTenantId(user.tenantRoles);
|
|
1394
|
+
if (!rolesByTenantId) return [];
|
|
1395
|
+
return rolesByTenantId[tenantId]?.filter(Boolean) ?? [];
|
|
1396
|
+
};
|
|
1397
|
+
const buildAbilityFromSession = ({
|
|
1398
|
+
tenantId,
|
|
1399
|
+
session,
|
|
1400
|
+
claims
|
|
1401
|
+
}) => {
|
|
1402
|
+
const user = session?.user;
|
|
1403
|
+
const userId = typeof user?.id === "string" ? user.id.trim() : "";
|
|
1404
|
+
const rolesByTenantId = normalizeRolesByTenantId(user?.tenantRoles);
|
|
1405
|
+
const hasExplicitTenantEntry = Boolean(
|
|
1406
|
+
rolesByTenantId && Object.prototype.hasOwnProperty.call(rolesByTenantId, tenantId)
|
|
1407
|
+
);
|
|
1408
|
+
const signedInTenantsRaw = user?.signedInTenants;
|
|
1409
|
+
const signedInTenants = Array.isArray(signedInTenantsRaw) ? signedInTenantsRaw.map(String) : [];
|
|
1410
|
+
const roles = hasExplicitTenantEntry ? rolesByTenantId[tenantId] ?? [] : userId && signedInTenants.includes(tenantId) ? ["owner"] : getTenantRolesFromSessionUser(user, tenantId);
|
|
1411
|
+
return buildAbility({
|
|
1412
|
+
tenantId,
|
|
1413
|
+
userId: userId || null,
|
|
1414
|
+
roles,
|
|
1415
|
+
claims
|
|
1416
|
+
});
|
|
1417
|
+
};
|
|
1418
|
+
const getAccessibleByQuery = (ability, action, subject2) => {
|
|
1419
|
+
return accessibleBy(ability, action).ofType(subject2);
|
|
1420
|
+
};
|
|
1421
|
+
const can = (ability, action, subjectType, object) => {
|
|
1422
|
+
if (object && typeof object === "object") {
|
|
1423
|
+
return ability.can(action, subject(subjectType, object));
|
|
1424
|
+
}
|
|
1425
|
+
return ability.can(action, subjectType);
|
|
1426
|
+
};
|
|
1266
1427
|
const rtsChangeLogApplied = /* @__PURE__ */ new WeakSet();
|
|
1428
|
+
const accessibleRecordsApplied = /* @__PURE__ */ new WeakSet();
|
|
1267
1429
|
let cachedModels = null;
|
|
1268
|
-
const isRtsChangelogExcludedModelName = (name) => name.startsWith("RB")
|
|
1430
|
+
const isRtsChangelogExcludedModelName = (name) => name.startsWith("RB");
|
|
1269
1431
|
const assertSchema = (exportName, value) => {
|
|
1270
1432
|
if (value instanceof mongoose.Schema) return value;
|
|
1271
1433
|
throw new Error(
|
|
@@ -1279,6 +1441,10 @@ const assertSchema = (exportName, value) => {
|
|
|
1279
1441
|
const buildAppModels = (modules) => Object.entries(modules).filter(([key]) => key.endsWith("Schema")).map(([key, schemaValue]) => {
|
|
1280
1442
|
const schema = assertSchema(key, schemaValue);
|
|
1281
1443
|
const name = key.replace(/Schema$/, "");
|
|
1444
|
+
if (!accessibleRecordsApplied.has(schema)) {
|
|
1445
|
+
schema.plugin(accessibleRecordsPlugin);
|
|
1446
|
+
accessibleRecordsApplied.add(schema);
|
|
1447
|
+
}
|
|
1282
1448
|
if (!isRtsChangelogExcludedModelName(name)) {
|
|
1283
1449
|
if (!rtsChangeLogApplied.has(schema)) {
|
|
1284
1450
|
schema.plugin(rtsChangeLogPlugin);
|
|
@@ -1293,6 +1459,10 @@ const buildAppModels = (modules) => Object.entries(modules).filter(([key]) => ke
|
|
|
1293
1459
|
const buildFrameworkModels = () => Object.entries(frameworkSchemas).filter(([key]) => key.endsWith("Schema")).map(([key, schemaValue]) => {
|
|
1294
1460
|
const schema = assertSchema(key, schemaValue);
|
|
1295
1461
|
const name = key.replace(/Schema$/, "");
|
|
1462
|
+
if (!accessibleRecordsApplied.has(schema)) {
|
|
1463
|
+
schema.plugin(accessibleRecordsPlugin);
|
|
1464
|
+
accessibleRecordsApplied.add(schema);
|
|
1465
|
+
}
|
|
1296
1466
|
if (!isRtsChangelogExcludedModelName(name)) {
|
|
1297
1467
|
if (!rtsChangeLogApplied.has(schema)) {
|
|
1298
1468
|
schema.plugin(rtsChangeLogPlugin);
|
|
@@ -1313,6 +1483,8 @@ const buildModels = (modules) => {
|
|
|
1313
1483
|
}, {});
|
|
1314
1484
|
};
|
|
1315
1485
|
const registerModels = (modules) => {
|
|
1486
|
+
registerPoliciesFromModules(frameworkSchemas);
|
|
1487
|
+
registerPoliciesFromModules(modules);
|
|
1316
1488
|
cachedModels = buildModels(modules);
|
|
1317
1489
|
};
|
|
1318
1490
|
const getRegisteredModels = () => {
|
|
@@ -1390,7 +1562,9 @@ const getTenantFilesystemDbFromCtx = async (ctx) => {
|
|
|
1390
1562
|
};
|
|
1391
1563
|
export {
|
|
1392
1564
|
LANGUAGE_CODE_REGEX,
|
|
1565
|
+
RBNotificationPolicy,
|
|
1393
1566
|
RBNotificationSchema,
|
|
1567
|
+
RBNotificationSettingsPolicy,
|
|
1394
1568
|
RBNotificationSettingsSchema,
|
|
1395
1569
|
RBRtsChangeSchema,
|
|
1396
1570
|
RBRtsCounterSchema,
|
|
@@ -1398,6 +1572,7 @@ export {
|
|
|
1398
1572
|
RBTenantSubscriptionEventSchema,
|
|
1399
1573
|
RBTenantSubscriptionSchema,
|
|
1400
1574
|
RBUploadChunkSchema,
|
|
1575
|
+
RBUploadSessionPolicy,
|
|
1401
1576
|
RBUploadSessionSchema,
|
|
1402
1577
|
RBUserSchema,
|
|
1403
1578
|
ZRBNotification,
|
|
@@ -1420,14 +1595,22 @@ export {
|
|
|
1420
1595
|
ZRBUploadSession,
|
|
1421
1596
|
ZRBUploadSessionStatus,
|
|
1422
1597
|
ZRBUser,
|
|
1598
|
+
buildAbility,
|
|
1599
|
+
buildAbilityFromSession,
|
|
1423
1600
|
buildLocaleFallbackChain,
|
|
1601
|
+
can,
|
|
1424
1602
|
createModels,
|
|
1425
1603
|
extendZod,
|
|
1604
|
+
getAccessibleByQuery,
|
|
1605
|
+
getRegisteredPolicies,
|
|
1426
1606
|
getTenantFilesystemDb,
|
|
1427
1607
|
getTenantFilesystemDbFromCtx,
|
|
1428
1608
|
getTenantFilesystemDbName,
|
|
1609
|
+
getTenantRolesFromSessionUser,
|
|
1429
1610
|
loadModel,
|
|
1430
1611
|
loadRbModel,
|
|
1612
|
+
registerPoliciesFromModules,
|
|
1613
|
+
registerPolicy,
|
|
1431
1614
|
resolveLocalizedString,
|
|
1432
1615
|
withLocalizedStringFallback,
|
|
1433
1616
|
z2 as z,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Schema } from '../../../vite/node_modules/mongoose';
|
|
2
2
|
import { z } from '../../../vite/node_modules/zod';
|
|
3
|
+
import { AclPolicy } from '../acl';
|
|
3
4
|
export declare const ZRBNotification: z.ZodObject<{
|
|
4
5
|
userId: z.ZodString;
|
|
5
6
|
topic: z.ZodOptional<z.ZodString>;
|
|
@@ -14,4 +15,5 @@ export declare const ZRBNotification: z.ZodObject<{
|
|
|
14
15
|
}, z.core.$strip>;
|
|
15
16
|
export type IRBNotification = z.infer<typeof ZRBNotification>;
|
|
16
17
|
export declare const RBNotificationSchema: Schema;
|
|
18
|
+
export declare const RBNotificationPolicy: AclPolicy;
|
|
17
19
|
//# sourceMappingURL=RBNotification.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RBNotification.d.ts","sourceRoot":"","sources":["../../src/models/RBNotification.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"RBNotification.d.ts","sourceRoot":"","sources":["../../src/models/RBNotification.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAGvC,eAAO,MAAM,eAAe;;;;;;;;;;;iBAW1B,CAAA;AAEF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAI7D,eAAO,MAAM,oBAAoB,EAAE,MAiBlC,CAAA;AAOD,eAAO,MAAM,oBAAoB,EAAE,SASlC,CAAA"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Schema } from '../../../vite/node_modules/mongoose';
|
|
2
2
|
import { z } from '../../../vite/node_modules/zod';
|
|
3
|
+
import { AclPolicy } from '../acl';
|
|
3
4
|
export declare const ZRBNotificationDigestFrequency: z.ZodEnum<{
|
|
4
5
|
off: "off";
|
|
5
6
|
daily: "daily";
|
|
@@ -28,4 +29,5 @@ export declare const ZRBNotificationSettings: z.ZodObject<{
|
|
|
28
29
|
}, z.core.$strip>;
|
|
29
30
|
export type IRBNotificationSettings = z.infer<typeof ZRBNotificationSettings>;
|
|
30
31
|
export declare const RBNotificationSettingsSchema: Schema;
|
|
32
|
+
export declare const RBNotificationSettingsPolicy: AclPolicy;
|
|
31
33
|
//# sourceMappingURL=RBNotificationSettings.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RBNotificationSettings.d.ts","sourceRoot":"","sources":["../../src/models/RBNotificationSettings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"RBNotificationSettings.d.ts","sourceRoot":"","sources":["../../src/models/RBNotificationSettings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAGvC,eAAO,MAAM,8BAA8B;;;;EAAqC,CAAA;AAEhF,eAAO,MAAM,8BAA8B;;;;;iBAKzC,CAAA;AAEF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;iBAKlC,CAAA;AAEF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAA;AAY7E,eAAO,MAAM,4BAA4B,EAAE,MAoB1C,CAAA;AAKD,eAAO,MAAM,4BAA4B,EAAE,SAQ1C,CAAA"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Schema } from '../../../vite/node_modules/mongoose';
|
|
2
2
|
import { z } from '../../../vite/node_modules/zod';
|
|
3
|
+
import { AclPolicy } from '../acl';
|
|
3
4
|
export declare const ZRBUploadSessionStatus: z.ZodEnum<{
|
|
4
5
|
error: "error";
|
|
5
6
|
done: "done";
|
|
@@ -28,4 +29,5 @@ export declare const ZRBUploadSession: z.ZodObject<{
|
|
|
28
29
|
}, z.core.$strip>;
|
|
29
30
|
export type IRBUploadSession = z.infer<typeof ZRBUploadSession>;
|
|
30
31
|
export declare const RBUploadSessionSchema: Schema;
|
|
32
|
+
export declare const RBUploadSessionPolicy: AclPolicy;
|
|
31
33
|
//# sourceMappingURL=RBUploadSession.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RBUploadSession.d.ts","sourceRoot":"","sources":["../../src/models/RBUploadSession.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"RBUploadSession.d.ts","sourceRoot":"","sources":["../../src/models/RBUploadSession.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAGvC,eAAO,MAAM,sBAAsB;;;;;EAAuD,CAAA;AAE1F,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;iBAc3B,CAAA;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAE/D,eAAO,MAAM,qBAAqB,EAAE,MAoBnC,CAAA;AAID,eAAO,MAAM,qBAAqB,EAAE,SAkBnC,CAAA"}
|
package/dist/models/User.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export declare const ZRBUser: z.ZodObject<{
|
|
|
6
6
|
name: z.ZodOptional<z.ZodString>;
|
|
7
7
|
phone: z.ZodOptional<z.ZodString>;
|
|
8
8
|
tenants: z.ZodArray<z.ZodString>;
|
|
9
|
+
tenantRoles: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
|
|
9
10
|
emailVerificationCode: z.ZodOptional<z.ZodString>;
|
|
10
11
|
emailVerificationExpiresAt: z.ZodOptional<z.ZodDate>;
|
|
11
12
|
}, z.core.$strip>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"User.d.ts","sourceRoot":"","sources":["../../src/models/User.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,OAAO
|
|
1
|
+
{"version":3,"file":"User.d.ts","sourceRoot":"","sources":["../../src/models/User.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,OAAO;;;;;;;;;iBASlB,CAAA;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,OAAO,CAAC,CAAC;AAE9C,eAAO,MAAM,YAAY,EAAE,MASA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registerModels.d.ts","sourceRoot":"","sources":["../src/registerModels.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"registerModels.d.ts","sourceRoot":"","sources":["../src/registerModels.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAA;AAa/B,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAwE3C,eAAO,MAAM,cAAc,GAAI,SAAS,YAAY,SAInD,CAAA;AAED,eAAO,MAAM,mBAAmB,sEAK/B,CAAA;AAED,YAAY,EAAE,YAAY,EAAE,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rpcbase/db",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
7
7
|
],
|
|
8
8
|
"main": "./dist/index.js",
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./acl": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
10
22
|
"scripts": {
|
|
11
23
|
"build": "wireit",
|
|
12
24
|
"test": "vitest run --coverage",
|
|
@@ -55,9 +67,13 @@
|
|
|
55
67
|
"mongoose": "^9",
|
|
56
68
|
"zod": "^4"
|
|
57
69
|
},
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"@casl/ability": "6.7.5",
|
|
72
|
+
"@casl/mongoose": "8.0.3"
|
|
73
|
+
},
|
|
58
74
|
"devDependencies": {
|
|
59
|
-
"@types/node": "
|
|
60
|
-
"@vitest/coverage-v8": "4.0.
|
|
61
|
-
"vitest": "4.0.
|
|
75
|
+
"@types/node": "25.0.3",
|
|
76
|
+
"@vitest/coverage-v8": "4.0.16",
|
|
77
|
+
"vitest": "4.0.16"
|
|
62
78
|
}
|
|
63
79
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/schema/index.test.ts"],"names":[],"mappings":""}
|