@rpcbase/db 0.26.0 → 0.27.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 +184 -0
- 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
|
@@ -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,8 +1305,128 @@ const rtsChangeLogPlugin = (schema) => {
|
|
|
1262
1305
|
}
|
|
1263
1306
|
});
|
|
1264
1307
|
};
|
|
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
|
+
};
|
|
1265
1427
|
const RTS_CHANGELOG_EXCLUDED_MODELS = /* @__PURE__ */ new Set(["RBRtsChange", "RBRtsCounter"]);
|
|
1266
1428
|
const rtsChangeLogApplied = /* @__PURE__ */ new WeakSet();
|
|
1429
|
+
const accessibleRecordsApplied = /* @__PURE__ */ new WeakSet();
|
|
1267
1430
|
let cachedModels = null;
|
|
1268
1431
|
const isRtsChangelogExcludedModelName = (name) => name.startsWith("RB") || RTS_CHANGELOG_EXCLUDED_MODELS.has(name);
|
|
1269
1432
|
const assertSchema = (exportName, value) => {
|
|
@@ -1279,6 +1442,10 @@ const assertSchema = (exportName, value) => {
|
|
|
1279
1442
|
const buildAppModels = (modules) => Object.entries(modules).filter(([key]) => key.endsWith("Schema")).map(([key, schemaValue]) => {
|
|
1280
1443
|
const schema = assertSchema(key, schemaValue);
|
|
1281
1444
|
const name = key.replace(/Schema$/, "");
|
|
1445
|
+
if (!accessibleRecordsApplied.has(schema)) {
|
|
1446
|
+
schema.plugin(accessibleRecordsPlugin);
|
|
1447
|
+
accessibleRecordsApplied.add(schema);
|
|
1448
|
+
}
|
|
1282
1449
|
if (!isRtsChangelogExcludedModelName(name)) {
|
|
1283
1450
|
if (!rtsChangeLogApplied.has(schema)) {
|
|
1284
1451
|
schema.plugin(rtsChangeLogPlugin);
|
|
@@ -1293,6 +1460,10 @@ const buildAppModels = (modules) => Object.entries(modules).filter(([key]) => ke
|
|
|
1293
1460
|
const buildFrameworkModels = () => Object.entries(frameworkSchemas).filter(([key]) => key.endsWith("Schema")).map(([key, schemaValue]) => {
|
|
1294
1461
|
const schema = assertSchema(key, schemaValue);
|
|
1295
1462
|
const name = key.replace(/Schema$/, "");
|
|
1463
|
+
if (!accessibleRecordsApplied.has(schema)) {
|
|
1464
|
+
schema.plugin(accessibleRecordsPlugin);
|
|
1465
|
+
accessibleRecordsApplied.add(schema);
|
|
1466
|
+
}
|
|
1296
1467
|
if (!isRtsChangelogExcludedModelName(name)) {
|
|
1297
1468
|
if (!rtsChangeLogApplied.has(schema)) {
|
|
1298
1469
|
schema.plugin(rtsChangeLogPlugin);
|
|
@@ -1313,6 +1484,8 @@ const buildModels = (modules) => {
|
|
|
1313
1484
|
}, {});
|
|
1314
1485
|
};
|
|
1315
1486
|
const registerModels = (modules) => {
|
|
1487
|
+
registerPoliciesFromModules(frameworkSchemas);
|
|
1488
|
+
registerPoliciesFromModules(modules);
|
|
1316
1489
|
cachedModels = buildModels(modules);
|
|
1317
1490
|
};
|
|
1318
1491
|
const getRegisteredModels = () => {
|
|
@@ -1390,7 +1563,9 @@ const getTenantFilesystemDbFromCtx = async (ctx) => {
|
|
|
1390
1563
|
};
|
|
1391
1564
|
export {
|
|
1392
1565
|
LANGUAGE_CODE_REGEX,
|
|
1566
|
+
RBNotificationPolicy,
|
|
1393
1567
|
RBNotificationSchema,
|
|
1568
|
+
RBNotificationSettingsPolicy,
|
|
1394
1569
|
RBNotificationSettingsSchema,
|
|
1395
1570
|
RBRtsChangeSchema,
|
|
1396
1571
|
RBRtsCounterSchema,
|
|
@@ -1398,6 +1573,7 @@ export {
|
|
|
1398
1573
|
RBTenantSubscriptionEventSchema,
|
|
1399
1574
|
RBTenantSubscriptionSchema,
|
|
1400
1575
|
RBUploadChunkSchema,
|
|
1576
|
+
RBUploadSessionPolicy,
|
|
1401
1577
|
RBUploadSessionSchema,
|
|
1402
1578
|
RBUserSchema,
|
|
1403
1579
|
ZRBNotification,
|
|
@@ -1420,14 +1596,22 @@ export {
|
|
|
1420
1596
|
ZRBUploadSession,
|
|
1421
1597
|
ZRBUploadSessionStatus,
|
|
1422
1598
|
ZRBUser,
|
|
1599
|
+
buildAbility,
|
|
1600
|
+
buildAbilityFromSession,
|
|
1423
1601
|
buildLocaleFallbackChain,
|
|
1602
|
+
can,
|
|
1424
1603
|
createModels,
|
|
1425
1604
|
extendZod,
|
|
1605
|
+
getAccessibleByQuery,
|
|
1606
|
+
getRegisteredPolicies,
|
|
1426
1607
|
getTenantFilesystemDb,
|
|
1427
1608
|
getTenantFilesystemDbFromCtx,
|
|
1428
1609
|
getTenantFilesystemDbName,
|
|
1610
|
+
getTenantRolesFromSessionUser,
|
|
1429
1611
|
loadModel,
|
|
1430
1612
|
loadRbModel,
|
|
1613
|
+
registerPoliciesFromModules,
|
|
1614
|
+
registerPolicy,
|
|
1431
1615
|
resolveLocalizedString,
|
|
1432
1616
|
withLocalizedStringFallback,
|
|
1433
1617
|
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;AAyE3C,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.27.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
|
}
|