@objectstack/core 11.0.0 → 11.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -66,12 +66,14 @@ __export(index_exports, {
66
66
  createMemoryQueue: () => createMemoryQueue,
67
67
  createPluginConfigValidator: () => createPluginConfigValidator,
68
68
  createPluginPermissionEnforcer: () => createPluginPermissionEnforcer,
69
+ evaluateAuthGate: () => evaluateAuthGate,
69
70
  extractApiKey: () => extractApiKey,
70
71
  generateApiKey: () => generateApiKey,
71
72
  generateEd25519KeyPair: () => generateEd25519KeyPair,
72
73
  getEnv: () => getEnv,
73
74
  getMemoryUsage: () => getMemoryUsage,
74
75
  hashApiKey: () => hashApiKey,
76
+ isAuthGateAllowlisted: () => isAuthGateAllowlisted,
75
77
  isExpired: () => isExpired,
76
78
  isNode: () => isNode,
77
79
  parseScopes: () => parseScopes,
@@ -4126,9 +4128,19 @@ async function resolveAuthzContext(input) {
4126
4128
  ctx.userId = userId;
4127
4129
  if (tenantId) ctx.tenantId = tenantId;
4128
4130
  if (!ql || typeof ql.find !== "function") return ctx;
4131
+ let userRowLoaded = false;
4132
+ let userRow;
4133
+ const getUserRow = async () => {
4134
+ if (!userRowLoaded) {
4135
+ userRowLoaded = true;
4136
+ const rows = await tryFind(ql, "sys_user", { id: userId }, 1);
4137
+ userRow = rows[0];
4138
+ }
4139
+ return userRow;
4140
+ };
4129
4141
  if (!ctx.email) {
4130
- const userRows = await tryFind(ql, "sys_user", { id: userId }, 1);
4131
- if (userRows[0]?.email) ctx.email = String(userRows[0].email);
4142
+ const u = await getUserRow();
4143
+ if (u?.email) ctx.email = String(u.email);
4132
4144
  }
4133
4145
  const memberWhere = tenantId ? { user_id: userId, organization_id: tenantId } : { user_id: userId };
4134
4146
  const members = await tryFind(ql, "sys_member", memberWhere, 50);
@@ -4209,8 +4221,7 @@ async function resolveAuthzContext(input) {
4209
4221
  ctx.roles.unshift(import_spec.BUILTIN_ROLE_PLATFORM_ADMIN);
4210
4222
  }
4211
4223
  if (!ctx.permissions.includes("ai_seat")) {
4212
- const seatRows = await tryFind(ql, "sys_user", { id: userId }, 1);
4213
- const aiAccess = seatRows?.[0]?.ai_access;
4224
+ const aiAccess = (await getUserRow())?.ai_access;
4214
4225
  if (aiAccess === true || aiAccess === 1 || aiAccess === "1") ctx.permissions.push("ai_seat");
4215
4226
  }
4216
4227
  return ctx;
@@ -4252,13 +4263,45 @@ async function resolveLocalizationContext(input) {
4252
4263
  }
4253
4264
  } catch {
4254
4265
  }
4255
- const tzRows = await tryFind(ql, "sys_setting", { namespace: "localization", key: "timezone", scope: "tenant" }, 1);
4256
- const localeRows = await tryFind(ql, "sys_setting", { namespace: "localization", key: "locale", scope: "tenant" }, 1);
4257
- const currencyRows = await tryFind(ql, "sys_setting", { namespace: "localization", key: "currency", scope: "tenant" }, 1);
4266
+ const rows = await tryFind(
4267
+ ql,
4268
+ "sys_setting",
4269
+ { namespace: "localization", key: { $in: ["timezone", "locale", "currency"] }, scope: "tenant" },
4270
+ 10
4271
+ );
4272
+ const valueOf = (k) => rows.find((r) => r.key === k)?.value;
4273
+ return {
4274
+ timezone: coerceTimeZone(valueOf("timezone")) ?? "UTC",
4275
+ locale: coerceLocale(valueOf("locale")) ?? "en-US",
4276
+ currency: coerceCurrency(valueOf("currency"))
4277
+ };
4278
+ }
4279
+
4280
+ // src/security/auth-gate.ts
4281
+ var ALLOW_PREFIXES = ["/api/v1/auth/", "/api/auth/", "/auth/"];
4282
+ var ALLOW_SUFFIXES = ["/health", "/ready", "/discovery", "/me/apps", "/me/localization"];
4283
+ function isAuthGateAllowlisted(rawPath) {
4284
+ if (!rawPath) return true;
4285
+ let path = rawPath.split("?")[0] || "/";
4286
+ let end = path.length;
4287
+ while (end > 1 && path.charCodeAt(end - 1) === 47) end--;
4288
+ path = path.slice(0, end) || "/";
4289
+ if (path.includes("/auth/")) return true;
4290
+ for (const p of ALLOW_PREFIXES) {
4291
+ if (path.startsWith(p) || path === p.replace(/\/$/, "")) return true;
4292
+ }
4293
+ for (const s of ALLOW_SUFFIXES) {
4294
+ if (path.endsWith(s)) return true;
4295
+ }
4296
+ return false;
4297
+ }
4298
+ function evaluateAuthGate(sessionUser, path) {
4299
+ const gate = sessionUser?.authGate;
4300
+ if (!gate || typeof gate.code !== "string") return null;
4301
+ if (isAuthGateAllowlisted(path)) return null;
4258
4302
  return {
4259
- timezone: coerceTimeZone(tzRows[0]?.value) ?? "UTC",
4260
- locale: coerceLocale(localeRows[0]?.value) ?? "en-US",
4261
- currency: coerceCurrency(currencyRows[0]?.value)
4303
+ code: gate.code,
4304
+ message: typeof gate.message === "string" && gate.message ? gate.message : "Access is blocked by an authentication policy."
4262
4305
  };
4263
4306
  }
4264
4307
 
@@ -5238,12 +5281,14 @@ var NamespaceResolver = class {
5238
5281
  createMemoryQueue,
5239
5282
  createPluginConfigValidator,
5240
5283
  createPluginPermissionEnforcer,
5284
+ evaluateAuthGate,
5241
5285
  extractApiKey,
5242
5286
  generateApiKey,
5243
5287
  generateEd25519KeyPair,
5244
5288
  getEnv,
5245
5289
  getMemoryUsage,
5246
5290
  hashApiKey,
5291
+ isAuthGateAllowlisted,
5247
5292
  isExpired,
5248
5293
  isNode,
5249
5294
  parseScopes,