@happyvertical/smrt-users 0.38.2 → 0.38.4
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/AGENTS.md +22 -0
- package/dist/chunks/{TerminalAuthService-BLxjzcQj.js → TerminalAuthService-DXaxydM6.js} +732 -87
- package/dist/chunks/TerminalAuthService-DXaxydM6.js.map +1 -0
- package/dist/collections/RoleCollection.d.ts +6 -1
- package/dist/collections/RoleCollection.d.ts.map +1 -1
- package/dist/collections/RolePermissionCollection.d.ts +35 -0
- package/dist/collections/RolePermissionCollection.d.ts.map +1 -1
- package/dist/collections/index.d.ts +2 -2
- package/dist/collections/index.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +101 -436
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +35 -4
- package/dist/models/Permission.d.ts +7 -4
- package/dist/models/Permission.d.ts.map +1 -1
- package/dist/services/MagicLinkService.d.ts.map +1 -1
- package/dist/services/OperationPermissionService.d.ts +45 -0
- package/dist/services/OperationPermissionService.d.ts.map +1 -0
- package/dist/services/PermissionCatalogService.d.ts +12 -0
- package/dist/services/PermissionCatalogService.d.ts.map +1 -1
- package/dist/services/PermissionResolver.d.ts.map +1 -1
- package/dist/services/SessionService.d.ts.map +1 -1
- package/dist/services/TenantService.d.ts.map +1 -1
- package/dist/services/TerminalAuthService.d.ts.map +1 -1
- package/dist/services/index.d.ts +2 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +9 -8
- package/dist/sveltekit.js +1 -1
- package/package.json +11 -11
- package/dist/chunks/TerminalAuthService-BLxjzcQj.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
import { ObjectRegistry, SmrtCollection, SmrtObject, field, findManifestEntryByQualifiedName,
|
|
1
|
+
import { $ as UsersCliAuthRequestCollection, A as DEFAULT_ROLE_PERMISSION_PATTERNS, B as Role, C as TenantHierarchyError, D as DEFAULT_SESSION_TTL, E as SessionCollection, F as normalizeOperationPermissionAction, G as MembershipCollection, H as Permission, I as registerPermissionDefinitions, J as GroupRole, K as Membership, L as syncPermissionCatalog, M as PermissionCatalogService, N as deriveOperationPermissionCollectionName, O as Session, P as deriveOperationPermissionSlug, Q as Group, R as RolePermission, S as TenantCollection, T as Tenant, U as MembershipOverrideCollection, V as PermissionCollection, W as MembershipOverride, X as GroupMember, Y as GroupMemberCollection, Z as GroupCollection, _ as resolveOidcProviderConfig, a as TerminalAuthRateLimitError, at as DEFAULT_ROLES, b as TenantPermissionOverrideCollection, c as getRequestScopedDatabase, ct as MembershipStatus, d as PermissionResolver, dt as TenantPermissionEffect, et as UsersCliAuthRequest, f as OidcLoginError, ft as TenantStatus, g as getUsersOidcConfig, h as encodeOidcTransaction, i as TerminalAuthError, it as AccessRequestStatus, j as RolePermissionCollection, k as generateSessionId, l as withSessionPermissionContext, lt as OverrideEffect, m as decodeOidcTransaction, n as DEFAULT_CLI_AUTH_REQUEST_TTL_SECONDS, nt as isValidEmail, o as TerminalAuthService, ot as DEFAULT_ROLE_SLUGS, p as OidcLoginService, pt as UserStatus, q as GroupRoleCollection, r as DEFAULT_CLI_SESSION_TTL_SECONDS, rt as normalizeEmail, s as getCurrentSessionPermissionContext, st as DEFAULT_TENANT_POLICY, t as DEFAULT_CLI_AUTH_POLL_INTERVAL_SECONDS, tt as User, u as SessionService, ut as SessionStatus, w as MAX_TENANT_HIERARCHY_DEPTH, x as TenantPermissionOverride, y as UserCollection, z as RoleCollection } from "./chunks/TerminalAuthService-DXaxydM6.js";
|
|
2
|
+
import { ObjectRegistry, SmrtCollection, SmrtObject, field, findManifestEntryByQualifiedName, smrt } from "@happyvertical/smrt-core";
|
|
3
3
|
import { createLogger } from "@happyvertical/logger";
|
|
4
|
-
import { getPackageConfig } from "@happyvertical/smrt-config";
|
|
5
4
|
import { createHash } from "node:crypto";
|
|
6
5
|
//#region src/models/AccessRequest.ts
|
|
7
|
-
var __defProp$
|
|
8
|
-
var __getOwnPropDesc$
|
|
9
|
-
var __decorateClass$
|
|
10
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
6
|
+
var __defProp$1 = Object.defineProperty;
|
|
7
|
+
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
|
|
8
|
+
var __decorateClass$1 = (decorators, target, key, kind) => {
|
|
9
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
|
|
11
10
|
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
12
|
-
if (kind && result) __defProp$
|
|
11
|
+
if (kind && result) __defProp$1(target, key, result);
|
|
13
12
|
return result;
|
|
14
13
|
};
|
|
15
14
|
var AccessRequest = class extends SmrtObject {
|
|
@@ -94,24 +93,24 @@ var AccessRequest = class extends SmrtObject {
|
|
|
94
93
|
return this.status === AccessRequestStatus.DECLINED || this.status === AccessRequestStatus.GRADUATED || this.status === AccessRequestStatus.CANCELED;
|
|
95
94
|
}
|
|
96
95
|
};
|
|
97
|
-
__decorateClass$
|
|
96
|
+
__decorateClass$1([field({
|
|
98
97
|
required: true,
|
|
99
98
|
indexed: true
|
|
100
99
|
})], AccessRequest.prototype, "email", 2);
|
|
101
|
-
__decorateClass$
|
|
102
|
-
__decorateClass$
|
|
100
|
+
__decorateClass$1([field({ nullable: true })], AccessRequest.prototype, "name", 2);
|
|
101
|
+
__decorateClass$1([field({
|
|
103
102
|
type: "text",
|
|
104
103
|
indexed: true
|
|
105
104
|
})], AccessRequest.prototype, "status", 2);
|
|
106
|
-
__decorateClass$
|
|
107
|
-
__decorateClass$
|
|
108
|
-
__decorateClass$
|
|
109
|
-
__decorateClass$
|
|
110
|
-
__decorateClass$
|
|
111
|
-
__decorateClass$
|
|
112
|
-
__decorateClass$
|
|
113
|
-
__decorateClass$
|
|
114
|
-
AccessRequest = __decorateClass$
|
|
105
|
+
__decorateClass$1([field({ indexed: true })], AccessRequest.prototype, "source", 2);
|
|
106
|
+
__decorateClass$1([field()], AccessRequest.prototype, "requestContext", 2);
|
|
107
|
+
__decorateClass$1([field({ nullable: true })], AccessRequest.prototype, "note", 2);
|
|
108
|
+
__decorateClass$1([field()], AccessRequest.prototype, "requestedAt", 2);
|
|
109
|
+
__decorateClass$1([field({ nullable: true })], AccessRequest.prototype, "decidedAt", 2);
|
|
110
|
+
__decorateClass$1([field({ nullable: true })], AccessRequest.prototype, "decidedBy", 2);
|
|
111
|
+
__decorateClass$1([field({ nullable: true })], AccessRequest.prototype, "resultingUserId", 2);
|
|
112
|
+
__decorateClass$1([field({ nullable: true })], AccessRequest.prototype, "tenantHint", 2);
|
|
113
|
+
AccessRequest = __decorateClass$1([smrt({
|
|
115
114
|
tableName: "access_requests",
|
|
116
115
|
conflictColumns: ["id"],
|
|
117
116
|
api: { include: [] },
|
|
@@ -165,12 +164,12 @@ var AccessRequestCollection = class extends SmrtCollection {
|
|
|
165
164
|
};
|
|
166
165
|
//#endregion
|
|
167
166
|
//#region src/models/MagicLinkToken.ts
|
|
168
|
-
var __defProp
|
|
169
|
-
var __getOwnPropDesc
|
|
170
|
-
var __decorateClass
|
|
171
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc
|
|
167
|
+
var __defProp = Object.defineProperty;
|
|
168
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
169
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
170
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
172
171
|
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
173
|
-
if (kind && result) __defProp
|
|
172
|
+
if (kind && result) __defProp(target, key, result);
|
|
174
173
|
return result;
|
|
175
174
|
};
|
|
176
175
|
var DEFAULT_TOKEN_EXPIRY_SECONDS = 600;
|
|
@@ -198,11 +197,11 @@ var UsersMagicLinkToken = class extends SmrtObject {
|
|
|
198
197
|
return !this.used && !this.isExpired();
|
|
199
198
|
}
|
|
200
199
|
};
|
|
201
|
-
__decorateClass
|
|
200
|
+
__decorateClass([field({
|
|
202
201
|
required: true,
|
|
203
202
|
unique: true
|
|
204
203
|
})], UsersMagicLinkToken.prototype, "nonce", 2);
|
|
205
|
-
UsersMagicLinkToken = __decorateClass
|
|
204
|
+
UsersMagicLinkToken = __decorateClass([smrt({
|
|
206
205
|
tableName: "users_magic_link_tokens",
|
|
207
206
|
api: { include: [] },
|
|
208
207
|
mcp: { include: [] },
|
|
@@ -247,140 +246,6 @@ var UsersMagicLinkTokenCollection = class extends SmrtCollection {
|
|
|
247
246
|
}
|
|
248
247
|
};
|
|
249
248
|
//#endregion
|
|
250
|
-
//#region src/models/Role.ts
|
|
251
|
-
var __defProp = Object.defineProperty;
|
|
252
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
253
|
-
var __decorateClass = (decorators, target, key, kind) => {
|
|
254
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
255
|
-
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
256
|
-
if (kind && result) __defProp(target, key, result);
|
|
257
|
-
return result;
|
|
258
|
-
};
|
|
259
|
-
var Role = class extends SmrtObject {
|
|
260
|
-
tenantId;
|
|
261
|
-
/**
|
|
262
|
-
* Display name for the role
|
|
263
|
-
*/
|
|
264
|
-
name = "";
|
|
265
|
-
/**
|
|
266
|
-
* Description of the role
|
|
267
|
-
*/
|
|
268
|
-
description = "";
|
|
269
|
-
/**
|
|
270
|
-
* Whether this is a system role (cannot be deleted)
|
|
271
|
-
*/
|
|
272
|
-
isSystem = false;
|
|
273
|
-
constructor(options = {}) {
|
|
274
|
-
super(options);
|
|
275
|
-
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
276
|
-
if (options.name !== void 0) this.name = options.name;
|
|
277
|
-
if (options.description !== void 0) this.description = options.description;
|
|
278
|
-
if (options.isSystem !== void 0) this.isSystem = options.isSystem;
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Check if this is a system-wide role
|
|
282
|
-
*/
|
|
283
|
-
isSystemRole() {
|
|
284
|
-
return this.tenantId === null || this.tenantId === void 0;
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Check if this is a tenant-specific role
|
|
288
|
-
*/
|
|
289
|
-
isTenantRole() {
|
|
290
|
-
return this.tenantId !== null && this.tenantId !== void 0;
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* Check if this role can be deleted.
|
|
294
|
-
* System roles (isSystem = true) cannot be deleted.
|
|
295
|
-
* @returns true if the role can be deleted
|
|
296
|
-
*/
|
|
297
|
-
canDelete() {
|
|
298
|
-
return !this.isSystem;
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Delete guard - prevents deletion of system roles.
|
|
302
|
-
* Override the delete method to check isSystem flag first.
|
|
303
|
-
*/
|
|
304
|
-
async delete() {
|
|
305
|
-
if (this.isSystem) throw new Error(`Cannot delete system role '${this.slug}'. System roles are protected.`);
|
|
306
|
-
return super.delete();
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
__decorateClass([foreignKey("Tenant", { nullable: true })], Role.prototype, "tenantId", 2);
|
|
310
|
-
Role = __decorateClass([smrt({
|
|
311
|
-
api: { include: ["list", "get"] },
|
|
312
|
-
mcp: { include: ["list", "get"] },
|
|
313
|
-
cli: true
|
|
314
|
-
})], Role);
|
|
315
|
-
//#endregion
|
|
316
|
-
//#region src/collections/RoleCollection.ts
|
|
317
|
-
var RoleCollection = class extends SmrtCollection {
|
|
318
|
-
static _itemClass = Role;
|
|
319
|
-
/**
|
|
320
|
-
* Find all system roles (tenantId is null)
|
|
321
|
-
*/
|
|
322
|
-
async findSystemRoles() {
|
|
323
|
-
return await this.query(`SELECT * FROM ${this.tableName} WHERE tenant_id IS NULL ORDER BY name ASC`);
|
|
324
|
-
}
|
|
325
|
-
/**
|
|
326
|
-
* Find roles available for a tenant (system + tenant-specific)
|
|
327
|
-
*/
|
|
328
|
-
async findByTenant(tenantId) {
|
|
329
|
-
return await this.query(`SELECT * FROM ${this.tableName}
|
|
330
|
-
WHERE tenant_id IS NULL OR tenant_id = ?
|
|
331
|
-
ORDER BY is_system DESC, name ASC`, [tenantId]);
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Find tenant-specific roles only
|
|
335
|
-
*/
|
|
336
|
-
async findTenantRoles(tenantId) {
|
|
337
|
-
return await this.list({
|
|
338
|
-
where: { tenantId },
|
|
339
|
-
orderBy: "name ASC"
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* Find role by slug within a tenant context
|
|
344
|
-
*/
|
|
345
|
-
async findBySlug(slug, tenantId) {
|
|
346
|
-
if (tenantId) {
|
|
347
|
-
const tenantRoles = await this.list({
|
|
348
|
-
where: {
|
|
349
|
-
slug,
|
|
350
|
-
tenantId
|
|
351
|
-
},
|
|
352
|
-
limit: 1
|
|
353
|
-
});
|
|
354
|
-
if (tenantRoles.length > 0) return tenantRoles[0];
|
|
355
|
-
}
|
|
356
|
-
const systemRoles = await this.query(`SELECT * FROM ${this.tableName} WHERE slug = ? AND tenant_id IS NULL LIMIT 1`, [slug]);
|
|
357
|
-
return systemRoles.length > 0 ? systemRoles[0] : null;
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* Seed default system roles
|
|
361
|
-
*/
|
|
362
|
-
async seedSystemRoles() {
|
|
363
|
-
const roles = [];
|
|
364
|
-
for (const roleDef of DEFAULT_ROLES) {
|
|
365
|
-
const existing = await this.findBySlug(roleDef.slug);
|
|
366
|
-
if (existing) {
|
|
367
|
-
roles.push(existing);
|
|
368
|
-
continue;
|
|
369
|
-
}
|
|
370
|
-
const role = await this.create({
|
|
371
|
-
slug: roleDef.slug,
|
|
372
|
-
name: roleDef.name,
|
|
373
|
-
description: roleDef.description,
|
|
374
|
-
tenantId: null,
|
|
375
|
-
isSystem: true
|
|
376
|
-
});
|
|
377
|
-
await role.save();
|
|
378
|
-
roles.push(role);
|
|
379
|
-
}
|
|
380
|
-
return roles;
|
|
381
|
-
}
|
|
382
|
-
};
|
|
383
|
-
//#endregion
|
|
384
249
|
//#region src/services/AccessRequestService.ts
|
|
385
250
|
var logger = createLogger({ level: "info" });
|
|
386
251
|
var ACCESS_REQUEST_CAPABILITIES = {
|
|
@@ -871,7 +736,7 @@ var MagicLinkService = class MagicLinkService {
|
|
|
871
736
|
* The caller is responsible for emailing the token to the user.
|
|
872
737
|
*/
|
|
873
738
|
async generate(email) {
|
|
874
|
-
const { SignJWT } = await import("./chunks/TerminalAuthService-
|
|
739
|
+
const { SignJWT } = await import("./chunks/TerminalAuthService-DXaxydM6.js").then((n) => n.v);
|
|
875
740
|
const key = await this.getSigningKey();
|
|
876
741
|
const nonce = crypto.randomUUID();
|
|
877
742
|
const normalizedEmail = normalizeEmail(email);
|
|
@@ -900,7 +765,7 @@ var MagicLinkService = class MagicLinkService {
|
|
|
900
765
|
* @throws {MagicLinkError} If the token is invalid, expired, or already used
|
|
901
766
|
*/
|
|
902
767
|
async verify(token) {
|
|
903
|
-
const { jwtVerify, errors } = await import("./chunks/TerminalAuthService-
|
|
768
|
+
const { jwtVerify, errors } = await import("./chunks/TerminalAuthService-DXaxydM6.js").then((n) => n.v);
|
|
904
769
|
const key = await this.getSigningKey();
|
|
905
770
|
let payload;
|
|
906
771
|
try {
|
|
@@ -934,289 +799,89 @@ var MagicLinkService = class MagicLinkService {
|
|
|
934
799
|
}
|
|
935
800
|
};
|
|
936
801
|
//#endregion
|
|
937
|
-
//#region src/services/
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
return pluralize(toSnakeCase$1(className));
|
|
952
|
-
}
|
|
953
|
-
function humanizeResource(resource) {
|
|
954
|
-
return resource.replace(/[_-]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
955
|
-
}
|
|
956
|
-
function capitalize(value) {
|
|
957
|
-
return `${value[0]?.toUpperCase() ?? ""}${value.slice(1)}`;
|
|
958
|
-
}
|
|
959
|
-
function defaultPermissionName(slug) {
|
|
960
|
-
const parsed = parsePermissionSlug(slug);
|
|
961
|
-
if (!parsed.isValid) return humanizeResource(slug);
|
|
962
|
-
return `${capitalize(parsed.action)} ${humanizeResource(parsed.resource)}`;
|
|
963
|
-
}
|
|
964
|
-
function defaultPermissionDescription(slug) {
|
|
965
|
-
const parsed = parsePermissionSlug(slug);
|
|
966
|
-
if (!parsed.isValid) return `Allows ${slug}`;
|
|
967
|
-
return `Allows ${parsed.action} access for ${humanizeResource(parsed.resource).toLowerCase()}`;
|
|
968
|
-
}
|
|
969
|
-
function isCollectionManifestEntry(objectDef) {
|
|
970
|
-
return objectDef?.extends === "SmrtCollection" || objectDef?.extendsTypeArg !== void 0;
|
|
971
|
-
}
|
|
972
|
-
function getPublicCustomMethodNames(methodEntries, standardActions) {
|
|
973
|
-
return Array.from(new Set(methodEntries.filter((method) => Boolean(method?.name) && method?.isPublic === true && !standardActions.includes(method.name)).map((method) => method.name)));
|
|
974
|
-
}
|
|
975
|
-
function getCustomMethodExposureNames(config, availableCustomMethods) {
|
|
976
|
-
if (!config || config === false) return /* @__PURE__ */ new Set();
|
|
977
|
-
if (config === true || typeof config !== "object") return new Set(availableCustomMethods);
|
|
978
|
-
const rawInclude = config.include;
|
|
979
|
-
const include = Array.isArray(rawInclude) ? [...rawInclude] : void 0;
|
|
980
|
-
const rawExclude = config.exclude;
|
|
981
|
-
const exclude = Array.isArray(rawExclude) ? [...rawExclude] : [];
|
|
982
|
-
if (!include) return new Set(availableCustomMethods.filter((methodName) => !exclude.includes(methodName)));
|
|
983
|
-
const baseMethods = include.filter((methodName) => availableCustomMethods.includes(methodName));
|
|
984
|
-
return new Set(baseMethods.filter((methodName) => !exclude.includes(methodName)));
|
|
985
|
-
}
|
|
986
|
-
function isOperationEnabled(config, action) {
|
|
987
|
-
if (config === false) return false;
|
|
988
|
-
if (config && typeof config === "object") {
|
|
989
|
-
const include = Array.isArray(config.include) ? config.include : void 0;
|
|
990
|
-
const rawExclude = config.exclude;
|
|
991
|
-
const exclude = Array.isArray(rawExclude) ? [...rawExclude] : [];
|
|
992
|
-
if (include && !include.includes(action)) return false;
|
|
993
|
-
if (exclude.includes(action)) return false;
|
|
994
|
-
}
|
|
995
|
-
return true;
|
|
802
|
+
//#region src/services/OperationPermissionService.ts
|
|
803
|
+
var OperationPermissionError = class extends Error {
|
|
804
|
+
decision;
|
|
805
|
+
permission;
|
|
806
|
+
status = 403;
|
|
807
|
+
constructor(decision) {
|
|
808
|
+
super(decision.permission ? `Permission denied for '${decision.permission}'.` : "Permission denied.");
|
|
809
|
+
this.name = "OperationPermissionError";
|
|
810
|
+
this.decision = decision;
|
|
811
|
+
this.permission = decision.permission;
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
function isModuleNotFoundError(error) {
|
|
815
|
+
return error instanceof Error && (error.code === "ERR_MODULE_NOT_FOUND" || /Cannot find package '@happyvertical\/smrt-tenancy'/.test(error.message));
|
|
996
816
|
}
|
|
997
|
-
function
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
817
|
+
async function readTenancyBypassContext() {
|
|
818
|
+
try {
|
|
819
|
+
const tenancy = await import("@happyvertical/smrt-tenancy");
|
|
820
|
+
return {
|
|
821
|
+
superAdminBypass: tenancy.isSuperAdminBypass(),
|
|
822
|
+
systemContext: tenancy.isSystemContext()
|
|
823
|
+
};
|
|
824
|
+
} catch (error) {
|
|
825
|
+
if (!isModuleNotFoundError(error)) throw error;
|
|
826
|
+
return {
|
|
827
|
+
superAdminBypass: false,
|
|
828
|
+
systemContext: false
|
|
829
|
+
};
|
|
830
|
+
}
|
|
1001
831
|
}
|
|
1002
|
-
function
|
|
832
|
+
function deny(permission, reason, error) {
|
|
1003
833
|
return {
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
tenantField: binding.tenantField
|
|
834
|
+
allowed: false,
|
|
835
|
+
error,
|
|
836
|
+
permission,
|
|
837
|
+
reason
|
|
1009
838
|
};
|
|
1010
839
|
}
|
|
1011
|
-
function
|
|
1012
|
-
const existingValue = existing[fieldName];
|
|
1013
|
-
const incomingValue = incoming[fieldName];
|
|
1014
|
-
if (!incomingValue) return;
|
|
1015
|
-
if (!existingValue) {
|
|
1016
|
-
existing[fieldName] = incomingValue;
|
|
1017
|
-
return;
|
|
1018
|
-
}
|
|
1019
|
-
if (existingValue !== incomingValue) throw new Error(`Conflicting permission metadata for '${slug}' field '${fieldName}': '${existingValue}' !== '${incomingValue}'`);
|
|
1020
|
-
}
|
|
1021
|
-
function mergeBindings(existing, incoming) {
|
|
1022
|
-
const existingBindings = existing.postgres?.bindings ?? [];
|
|
1023
|
-
const incomingBindings = incoming.postgres?.bindings ?? [];
|
|
1024
|
-
if (incomingBindings.length === 0) return;
|
|
1025
|
-
const seen = new Set(existingBindings.map((binding) => [
|
|
1026
|
-
binding.permission,
|
|
1027
|
-
binding.action,
|
|
1028
|
-
binding.schemaName ?? "",
|
|
1029
|
-
binding.tableName,
|
|
1030
|
-
binding.tenantField ?? ""
|
|
1031
|
-
].join("|")));
|
|
1032
|
-
const mergedBindings = [...existingBindings];
|
|
1033
|
-
for (const binding of incomingBindings) {
|
|
1034
|
-
const normalized = normalizeBinding(binding, incoming.slug);
|
|
1035
|
-
const key = [
|
|
1036
|
-
normalized.permission,
|
|
1037
|
-
normalized.action,
|
|
1038
|
-
normalized.schemaName ?? "",
|
|
1039
|
-
normalized.tableName,
|
|
1040
|
-
normalized.tenantField ?? ""
|
|
1041
|
-
].join("|");
|
|
1042
|
-
if (!seen.has(key)) {
|
|
1043
|
-
seen.add(key);
|
|
1044
|
-
mergedBindings.push(normalized);
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
existing.postgres = { bindings: mergedBindings };
|
|
1048
|
-
}
|
|
1049
|
-
function normalizeDefinition(definition, source) {
|
|
1050
|
-
if (!definition.slug || !isValidPermissionSlug(definition.slug.trim())) throw new Error(`Invalid permission slug '${definition.slug}'. Expected 'resource.action'.`);
|
|
1051
|
-
const slug = definition.slug.trim();
|
|
840
|
+
function allow(permission, reason) {
|
|
1052
841
|
return {
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
description: definition.description ?? defaultPermissionDescription(slug),
|
|
1057
|
-
name: definition.name ?? defaultPermissionName(slug),
|
|
1058
|
-
postgres: definition.postgres?.bindings ? { bindings: definition.postgres.bindings.map((binding) => normalizeBinding(binding, slug)) } : void 0,
|
|
1059
|
-
qualifiedName: definition.qualifiedName,
|
|
1060
|
-
slug,
|
|
1061
|
-
source
|
|
842
|
+
allowed: true,
|
|
843
|
+
permission,
|
|
844
|
+
reason
|
|
1062
845
|
};
|
|
1063
846
|
}
|
|
1064
|
-
function
|
|
1065
|
-
|
|
1066
|
-
const definition = normalizeDefinition(rawDefinition, source);
|
|
1067
|
-
const existing = current.get(definition.slug);
|
|
1068
|
-
if (!existing) {
|
|
1069
|
-
current.set(definition.slug, definition);
|
|
1070
|
-
continue;
|
|
1071
|
-
}
|
|
1072
|
-
mergeStringField("category", existing, definition, definition.slug);
|
|
1073
|
-
mergeStringField("className", existing, definition, definition.slug);
|
|
1074
|
-
mergeStringField("collection", existing, definition, definition.slug);
|
|
1075
|
-
mergeStringField("description", existing, definition, definition.slug);
|
|
1076
|
-
mergeStringField("name", existing, definition, definition.slug);
|
|
1077
|
-
mergeStringField("qualifiedName", existing, definition, definition.slug);
|
|
1078
|
-
mergeBindings(existing, definition);
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
function registerPermissionDefinitions(definitions) {
|
|
1082
|
-
globalThis.__smrtUsersPermissionRegistrationCounter = (globalThis.__smrtUsersPermissionRegistrationCounter ?? 0) + 1;
|
|
1083
|
-
const registrationId = globalThis.__smrtUsersPermissionRegistrationCounter;
|
|
1084
|
-
getRuntimePermissionRegistrations().set(registrationId, definitions);
|
|
1085
|
-
return () => {
|
|
1086
|
-
getRuntimePermissionRegistrations().delete(registrationId);
|
|
1087
|
-
};
|
|
847
|
+
function hasCatalogSlug(catalog, permission) {
|
|
848
|
+
return catalog.permissions.some((definition) => definition.slug === permission);
|
|
1088
849
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
return getPackageConfig("users", {});
|
|
1096
|
-
}
|
|
1097
|
-
getRuntimePermissionDefinitions() {
|
|
1098
|
-
return Array.from(getRuntimePermissionRegistrations().values()).flat();
|
|
1099
|
-
}
|
|
1100
|
-
getCustomPermissionDefinitions() {
|
|
1101
|
-
return this.getUsersConfig().permissions?.custom ?? [];
|
|
1102
|
-
}
|
|
1103
|
-
getCatalog() {
|
|
1104
|
-
const manifestPermissions = this.getManifestPermissionDefinitions();
|
|
1105
|
-
const customPermissions = this.getCustomPermissionDefinitions();
|
|
1106
|
-
const runtimePermissions = this.getRuntimePermissionDefinitions();
|
|
1107
|
-
const merged = /* @__PURE__ */ new Map();
|
|
1108
|
-
mergeDefinitionSet(merged, manifestPermissions, "manifest");
|
|
1109
|
-
mergeDefinitionSet(merged, customPermissions, "config");
|
|
1110
|
-
mergeDefinitionSet(merged, runtimePermissions, "runtime");
|
|
1111
|
-
return {
|
|
1112
|
-
customPermissions: customPermissions.map((definition) => normalizeDefinition(definition, "config")),
|
|
1113
|
-
manifestPermissions: manifestPermissions.map((definition) => normalizeDefinition(definition, "manifest")),
|
|
1114
|
-
permissions: Array.from(merged.values()).sort((left, right) => left.slug.localeCompare(right.slug)),
|
|
1115
|
-
runtimePermissions: runtimePermissions.map((definition) => normalizeDefinition(definition, "runtime"))
|
|
1116
|
-
};
|
|
1117
|
-
}
|
|
1118
|
-
async syncPermissionCatalog() {
|
|
1119
|
-
const catalog = this.getCatalog();
|
|
1120
|
-
const permissions = await PermissionCollection.create(this.options);
|
|
1121
|
-
const created = [];
|
|
1122
|
-
const unchanged = [];
|
|
1123
|
-
const updated = [];
|
|
1124
|
-
for (const definition of catalog.permissions) {
|
|
1125
|
-
const existing = await permissions.findBySlug(definition.slug);
|
|
1126
|
-
if (!existing) {
|
|
1127
|
-
await (await permissions.create({
|
|
1128
|
-
category: definition.category ?? parsePermissionSlug(definition.slug).resource,
|
|
1129
|
-
description: definition.description ?? "",
|
|
1130
|
-
name: definition.name ?? definition.slug,
|
|
1131
|
-
slug: definition.slug
|
|
1132
|
-
})).save();
|
|
1133
|
-
created.push(definition.slug);
|
|
1134
|
-
continue;
|
|
1135
|
-
}
|
|
1136
|
-
const nextName = definition.name ?? existing.name;
|
|
1137
|
-
const nextDescription = definition.description ?? existing.description;
|
|
1138
|
-
const nextCategory = definition.category ?? existing.category;
|
|
1139
|
-
if (existing.name === nextName && existing.description === nextDescription && existing.category === nextCategory) {
|
|
1140
|
-
unchanged.push(definition.slug);
|
|
1141
|
-
continue;
|
|
1142
|
-
}
|
|
1143
|
-
existing.name = nextName;
|
|
1144
|
-
existing.description = nextDescription;
|
|
1145
|
-
existing.category = nextCategory;
|
|
1146
|
-
await existing.save();
|
|
1147
|
-
updated.push(definition.slug);
|
|
1148
|
-
}
|
|
1149
|
-
return {
|
|
1150
|
-
catalog,
|
|
1151
|
-
created,
|
|
1152
|
-
unchanged,
|
|
1153
|
-
updated
|
|
1154
|
-
};
|
|
1155
|
-
}
|
|
1156
|
-
getManifestPermissionDefinitions() {
|
|
1157
|
-
const standardActions = [
|
|
1158
|
-
"list",
|
|
1159
|
-
"get",
|
|
1160
|
-
"create",
|
|
1161
|
-
"update",
|
|
1162
|
-
"delete"
|
|
1163
|
-
];
|
|
1164
|
-
const definitions = /* @__PURE__ */ new Map();
|
|
1165
|
-
for (const metadata of ObjectRegistry.getAllObjectMetadata()) {
|
|
1166
|
-
const registered = ObjectRegistry.getClassByConstructor(metadata.constructor) ?? ObjectRegistry.getClass(metadata.name);
|
|
1167
|
-
const manifestEntry = registered?.qualifiedName ? findManifestEntryByQualifiedName(registered.qualifiedName) : void 0;
|
|
1168
|
-
if (isCollectionManifestEntry(manifestEntry)) continue;
|
|
1169
|
-
const className = metadata.name;
|
|
1170
|
-
const qualifiedName = registered?.qualifiedName;
|
|
1171
|
-
const objectConfig = manifestEntry?.decoratorConfig ?? metadata.config;
|
|
1172
|
-
const rawCollection = objectConfig?.collection;
|
|
1173
|
-
const collection = (typeof rawCollection === "string" && rawCollection.length > 0 ? rawCollection : void 0) ?? manifestEntry?.collection ?? deriveCollectionName(metadata.name);
|
|
1174
|
-
if (isOperationEnabled(objectConfig.api, "list") || isOperationEnabled(objectConfig.api, "get") || isOperationEnabled(objectConfig.cli, "list") || isOperationEnabled(objectConfig.cli, "get") || isOperationEnabled(objectConfig.mcp, "list") || isOperationEnabled(objectConfig.mcp, "get")) definitions.set(`${collection}.read`, {
|
|
1175
|
-
className,
|
|
1176
|
-
collection,
|
|
1177
|
-
qualifiedName,
|
|
1178
|
-
slug: `${collection}.read`
|
|
1179
|
-
});
|
|
1180
|
-
for (const action of [
|
|
1181
|
-
"create",
|
|
1182
|
-
"update",
|
|
1183
|
-
"delete"
|
|
1184
|
-
]) {
|
|
1185
|
-
if (!(isOperationEnabled(objectConfig.api, action) || isOperationEnabled(objectConfig.cli, action) || isOperationEnabled(objectConfig.mcp, action))) continue;
|
|
1186
|
-
definitions.set(`${collection}.${action}`, {
|
|
1187
|
-
className,
|
|
1188
|
-
collection,
|
|
1189
|
-
qualifiedName,
|
|
1190
|
-
slug: `${collection}.${action}`
|
|
1191
|
-
});
|
|
1192
|
-
}
|
|
1193
|
-
const publicCustomMethodNames = getPublicCustomMethodNames(manifestEntry?.methods ? Object.values(manifestEntry.methods) : Array.from(metadata.methods.values()), standardActions);
|
|
1194
|
-
const customApiMethods = /* @__PURE__ */ new Set();
|
|
1195
|
-
const customCliMethods = getCustomMethodExposureNames(objectConfig.cli, publicCustomMethodNames);
|
|
1196
|
-
const customMcpMethods = getCustomMethodExposureNames(objectConfig.mcp, publicCustomMethodNames);
|
|
1197
|
-
for (const methodName of publicCustomMethodNames) if (isOperationEnabled(objectConfig.api, methodName)) customApiMethods.add(methodName);
|
|
1198
|
-
const customMethods = /* @__PURE__ */ new Set([
|
|
1199
|
-
...customApiMethods,
|
|
1200
|
-
...customCliMethods,
|
|
1201
|
-
...customMcpMethods
|
|
1202
|
-
]);
|
|
1203
|
-
for (const methodName of customMethods) definitions.set(`${collection}.${methodName}`, {
|
|
1204
|
-
className,
|
|
1205
|
-
collection,
|
|
1206
|
-
description: `Allows ${methodName} on ${humanizeResource(collection).toLowerCase()}`,
|
|
1207
|
-
name: `${capitalize(methodName)} ${humanizeResource(collection)}`,
|
|
1208
|
-
qualifiedName,
|
|
1209
|
-
slug: `${collection}.${methodName}`
|
|
1210
|
-
});
|
|
1211
|
-
}
|
|
1212
|
-
return Array.from(definitions.values()).sort((left, right) => left.slug.localeCompare(right.slug));
|
|
850
|
+
async function checkOperationPermission(options) {
|
|
851
|
+
let permission;
|
|
852
|
+
try {
|
|
853
|
+
permission = deriveOperationPermissionSlug(options.collection, options.action);
|
|
854
|
+
} catch (error) {
|
|
855
|
+
return deny(null, "invalid_operation", error);
|
|
1213
856
|
}
|
|
1214
|
-
|
|
1215
|
-
|
|
857
|
+
if (!hasCatalogSlug(options.catalog ?? PermissionCatalogService.create(options).getCatalog(), permission)) return deny(permission, "unknown_permission");
|
|
858
|
+
const sessionContext = getCurrentSessionPermissionContext();
|
|
859
|
+
let tenancyContext;
|
|
860
|
+
try {
|
|
861
|
+
tenancyContext = await readTenancyBypassContext();
|
|
862
|
+
} catch (error) {
|
|
863
|
+
return deny(permission, "resolution_error", error);
|
|
864
|
+
}
|
|
865
|
+
const systemContext = sessionContext?.systemContext === true || tenancyContext.systemContext;
|
|
866
|
+
const superAdminBypass = sessionContext?.superAdminBypass === true || tenancyContext.superAdminBypass;
|
|
867
|
+
if (systemContext && options.allowSystemContextBypass !== false) return allow(permission, "system_context_bypass");
|
|
868
|
+
if (superAdminBypass && options.allowSuperAdminBypass !== false) return allow(permission, "super_admin_bypass");
|
|
869
|
+
const userId = options.userId ?? sessionContext?.userId ?? null;
|
|
870
|
+
const tenantId = options.tenantId ?? sessionContext?.tenantId ?? null;
|
|
871
|
+
if (!userId || !tenantId) return deny(permission, "missing_principal");
|
|
872
|
+
try {
|
|
873
|
+
return await (options.resolver ?? await PermissionResolver.create(options)).hasPermission(userId, tenantId, permission, { membership: options.membership }) ? allow(permission, "permission_granted") : deny(permission, "permission_denied");
|
|
874
|
+
} catch (error) {
|
|
875
|
+
return deny(permission, "resolution_error", error);
|
|
1216
876
|
}
|
|
1217
|
-
}
|
|
1218
|
-
async function
|
|
1219
|
-
return
|
|
877
|
+
}
|
|
878
|
+
async function hasOperationPermission(options) {
|
|
879
|
+
return (await checkOperationPermission(options)).allowed;
|
|
880
|
+
}
|
|
881
|
+
async function assertOperationPermission(options) {
|
|
882
|
+
const decision = await checkOperationPermission(options);
|
|
883
|
+
if (decision.allowed || options.onDeny === "return") return decision;
|
|
884
|
+
throw new OperationPermissionError(decision);
|
|
1220
885
|
}
|
|
1221
886
|
//#endregion
|
|
1222
887
|
//#region src/services/PostgresPermissionPolicies.ts
|
|
@@ -1658,6 +1323,6 @@ var TenantService = class TenantService {
|
|
|
1658
1323
|
}
|
|
1659
1324
|
};
|
|
1660
1325
|
//#endregion
|
|
1661
|
-
export { ACCESS_REQUEST_CAPABILITIES, AccessRequest, AccessRequestCollection, AccessRequestError, AccessRequestService, AccessRequestStatus, UsersCliAuthRequest as CliAuthRequest, UsersCliAuthRequest, UsersCliAuthRequestCollection as CliAuthRequestCollection, UsersCliAuthRequestCollection, DEFAULT_CLI_AUTH_POLL_INTERVAL_SECONDS, DEFAULT_CLI_AUTH_REQUEST_TTL_SECONDS, DEFAULT_CLI_SESSION_TTL_SECONDS, DEFAULT_ROLES, DEFAULT_ROLE_SLUGS, DEFAULT_SESSION_TTL, DEFAULT_TENANT_POLICY, DEFAULT_TOKEN_EXPIRY_SECONDS, Group, GroupCollection, GroupMember, GroupMemberCollection, GroupRole, GroupRoleCollection, MAX_TENANT_HIERARCHY_DEPTH, MagicLinkError, MagicLinkService, UsersMagicLinkToken as MagicLinkToken, UsersMagicLinkToken, UsersMagicLinkTokenCollection as MagicLinkTokenCollection, UsersMagicLinkTokenCollection, Membership, MembershipCollection, MembershipOverride, MembershipOverrideCollection, MembershipStatus, OidcLoginError, OidcLoginService, OverrideEffect, Permission, PermissionCatalogService, PermissionCollection, PermissionResolver, Role, RoleCollection, RolePermission, RolePermissionCollection, Session, SessionCollection, SessionService, SessionStatus, Tenant, TenantCollection, TenantHierarchyError, TenantPermissionEffect, TenantPermissionOverride, TenantPermissionOverrideCollection, TenantService, TenantStatus, TerminalAuthError, TerminalAuthRateLimitError, TerminalAuthService, User, UserCollection, UserStatus, applyPostgresPermissionPolicies, decodeOidcTransaction, encodeOidcTransaction, generatePostgresPermissionSql, generateSessionId, getCurrentSessionPermissionContext, getRequestScopedDatabase, getUsersOidcConfig, registerPermissionDefinitions, resolveOidcProviderConfig, syncPermissionCatalog, withSessionPermissionContext };
|
|
1326
|
+
export { ACCESS_REQUEST_CAPABILITIES, AccessRequest, AccessRequestCollection, AccessRequestError, AccessRequestService, AccessRequestStatus, UsersCliAuthRequest as CliAuthRequest, UsersCliAuthRequest, UsersCliAuthRequestCollection as CliAuthRequestCollection, UsersCliAuthRequestCollection, DEFAULT_CLI_AUTH_POLL_INTERVAL_SECONDS, DEFAULT_CLI_AUTH_REQUEST_TTL_SECONDS, DEFAULT_CLI_SESSION_TTL_SECONDS, DEFAULT_ROLES, DEFAULT_ROLE_PERMISSION_PATTERNS, DEFAULT_ROLE_SLUGS, DEFAULT_SESSION_TTL, DEFAULT_TENANT_POLICY, DEFAULT_TOKEN_EXPIRY_SECONDS, Group, GroupCollection, GroupMember, GroupMemberCollection, GroupRole, GroupRoleCollection, MAX_TENANT_HIERARCHY_DEPTH, MagicLinkError, MagicLinkService, UsersMagicLinkToken as MagicLinkToken, UsersMagicLinkToken, UsersMagicLinkTokenCollection as MagicLinkTokenCollection, UsersMagicLinkTokenCollection, Membership, MembershipCollection, MembershipOverride, MembershipOverrideCollection, MembershipStatus, OidcLoginError, OidcLoginService, OperationPermissionError, OverrideEffect, Permission, PermissionCatalogService, PermissionCollection, PermissionResolver, Role, RoleCollection, RolePermission, RolePermissionCollection, Session, SessionCollection, SessionService, SessionStatus, Tenant, TenantCollection, TenantHierarchyError, TenantPermissionEffect, TenantPermissionOverride, TenantPermissionOverrideCollection, TenantService, TenantStatus, TerminalAuthError, TerminalAuthRateLimitError, TerminalAuthService, User, UserCollection, UserStatus, applyPostgresPermissionPolicies, assertOperationPermission, checkOperationPermission, decodeOidcTransaction, deriveOperationPermissionCollectionName, deriveOperationPermissionSlug, encodeOidcTransaction, generatePostgresPermissionSql, generateSessionId, getCurrentSessionPermissionContext, getRequestScopedDatabase, getUsersOidcConfig, hasOperationPermission, normalizeOperationPermissionAction, registerPermissionDefinitions, resolveOidcProviderConfig, syncPermissionCatalog, withSessionPermissionContext };
|
|
1662
1327
|
|
|
1663
1328
|
//# sourceMappingURL=index.js.map
|