@objectstack/runtime 14.3.0 → 14.6.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
@@ -2161,7 +2161,7 @@ function safeGet(ctx, name) {
2161
2161
  var import_core5 = require("@objectstack/core");
2162
2162
  var import_types3 = require("@objectstack/types");
2163
2163
  var import_system = require("@objectstack/spec/system");
2164
- var import_ai = require("@objectstack/spec/ai");
2164
+ var import_ai2 = require("@objectstack/spec/ai");
2165
2165
  var import_shared2 = require("@objectstack/spec/shared");
2166
2166
  init_package_state_store();
2167
2167
 
@@ -2195,6 +2195,7 @@ function checkApiExposure(def, action) {
2195
2195
  }
2196
2196
 
2197
2197
  // src/security/resolve-execution-context.ts
2198
+ var import_ai = require("@objectstack/spec/ai");
2198
2199
  var import_core3 = require("@objectstack/core");
2199
2200
  function extractJwtBearer(headers) {
2200
2201
  const auth = headers.get("authorization");
@@ -2255,7 +2256,15 @@ async function resolveExecutionContext(opts) {
2255
2256
  isSystem: false
2256
2257
  };
2257
2258
  if (authz.userId) {
2258
- ctx.principalKind = "human";
2259
+ if (oauthPrincipal?.clientId) {
2260
+ ctx.principalKind = "agent";
2261
+ ctx.onBehalfOf = { userId: authz.userId, principalKind: "human" };
2262
+ ctx.permissions = (0, import_ai.scopesToAgentPermissionSets)(oauthPrincipal.scopes);
2263
+ ctx.positions = [];
2264
+ ctx.systemPermissions = oauthPrincipal.scopes?.includes(import_ai.MCP_OAUTH_SCOPE_ACTIONS) ? authz.systemPermissions ?? [] : [];
2265
+ } else {
2266
+ ctx.principalKind = "human";
2267
+ }
2259
2268
  } else {
2260
2269
  ctx.principalKind = "guest";
2261
2270
  ctx.positions = ["guest"];
@@ -2331,6 +2340,7 @@ var _HttpDispatcher = class _HttpDispatcher {
2331
2340
  }
2332
2341
  };
2333
2342
  this.enforceMembership = options?.enforceProjectMembership ?? true;
2343
+ this.requireAuth = options?.requireAuth ?? false;
2334
2344
  this.kernelResolver = options?.kernelResolver ?? resolveService("kernel-resolver");
2335
2345
  this.scopeManager = options?.scopeManager ?? resolveService("scope-manager");
2336
2346
  }
@@ -2548,14 +2558,14 @@ var _HttpDispatcher = class _HttpDispatcher {
2548
2558
  return { handled: true, response };
2549
2559
  }
2550
2560
  const grantedScopes = Array.isArray(ec.oauthScopes) ? ec.oauthScopes : void 0;
2551
- if (grantedScopes && !grantedScopes.some((s) => import_ai.MCP_OAUTH_SCOPES.includes(s))) {
2561
+ if (grantedScopes && !grantedScopes.some((s) => import_ai2.MCP_OAUTH_SCOPES.includes(s))) {
2552
2562
  const resourceMetadataUrl = await this.getMcpResourceMetadataUrl(context);
2553
2563
  const response = this.error(
2554
- `Forbidden: the access token grants none of the MCP scopes (${import_ai.MCP_OAUTH_SCOPES.join(", ")})`,
2564
+ `Forbidden: the access token grants none of the MCP scopes (${import_ai2.MCP_OAUTH_SCOPES.join(", ")})`,
2555
2565
  403
2556
2566
  );
2557
2567
  response.headers = {
2558
- "WWW-Authenticate": `Bearer error="insufficient_scope", scope="${import_ai.MCP_OAUTH_SCOPES.join(" ")}"` + (resourceMetadataUrl ? `, resource_metadata="${resourceMetadataUrl}"` : "")
2568
+ "WWW-Authenticate": `Bearer error="insufficient_scope", scope="${import_ai2.MCP_OAUTH_SCOPES.join(" ")}"` + (resourceMetadataUrl ? `, resource_metadata="${resourceMetadataUrl}"` : "")
2559
2569
  };
2560
2570
  return { handled: true, response };
2561
2571
  }
@@ -3496,6 +3506,15 @@ var _HttpDispatcher = class _HttpDispatcher {
3496
3506
  * Fallback for backward compat: /metadata (all objects), /metadata/:objectName (get object)
3497
3507
  */
3498
3508
  async handleMetadata(path, _context, method, body, query) {
3509
+ if (this.requireAuth) {
3510
+ const ec = _context.executionContext;
3511
+ if (!ec?.userId && !ec?.isSystem) {
3512
+ return {
3513
+ handled: true,
3514
+ response: this.error("Authentication is required to access this endpoint.", 401, { code: "unauthenticated" })
3515
+ };
3516
+ }
3517
+ }
3499
3518
  const parts = path.replace(/^\/+/, "").split("/").filter(Boolean);
3500
3519
  if (parts[0] === "types") {
3501
3520
  const protocol = await this.resolveService("protocol");
@@ -3795,13 +3814,13 @@ var _HttpDispatcher = class _HttpDispatcher {
3795
3814
  * Handles Analytics requests
3796
3815
  * path: sub-path after /analytics/
3797
3816
  */
3798
- async handleAnalytics(path, method, body, _context) {
3817
+ async handleAnalytics(path, method, body, context) {
3799
3818
  const analyticsService = await this.getService(import_system.CoreServiceName.enum.analytics);
3800
3819
  if (!analyticsService) return { handled: false };
3801
3820
  const m = method.toUpperCase();
3802
3821
  const subPath = path.replace(/^\/+/, "");
3803
3822
  if (subPath === "query" && m === "POST") {
3804
- const result = await analyticsService.query(body);
3823
+ const result = await analyticsService.query(body, context?.executionContext);
3805
3824
  return { handled: true, response: this.success(result) };
3806
3825
  }
3807
3826
  if (subPath === "meta" && m === "GET") {
@@ -3809,7 +3828,7 @@ var _HttpDispatcher = class _HttpDispatcher {
3809
3828
  return { handled: true, response: this.success(result) };
3810
3829
  }
3811
3830
  if (subPath === "sql" && m === "POST") {
3812
- const result = await analyticsService.generateSql(body);
3831
+ const result = await analyticsService.generateSql(body, context?.executionContext);
3813
3832
  return { handled: true, response: this.success(result) };
3814
3833
  }
3815
3834
  return { handled: false };
@@ -4997,6 +5016,15 @@ var _HttpDispatcher = class _HttpDispatcher {
4997
5016
  if (route.method !== method) continue;
4998
5017
  const params = matchRoute(route.path, fullPath);
4999
5018
  if (params === null) continue;
5019
+ if (this.requireAuth && route.auth !== false) {
5020
+ const gec = context.executionContext;
5021
+ if (!gec?.userId && !gec?.isSystem) {
5022
+ return {
5023
+ handled: true,
5024
+ response: this.error("Authentication is required to access this endpoint.", 401, { code: "unauthenticated" })
5025
+ };
5026
+ }
5027
+ }
5000
5028
  const ec = context.executionContext;
5001
5029
  const user = ec?.userId ? {
5002
5030
  userId: ec.userId,
@@ -5719,7 +5747,7 @@ function resolveErrorReporter(ctx, override) {
5719
5747
  }
5720
5748
 
5721
5749
  // src/dispatcher-plugin.ts
5722
- function mountRouteOnServer(route, server, routePath, securityHeaders, resolveUser) {
5750
+ function mountRouteOnServer(route, server, routePath, securityHeaders, resolveUser, requireAuth = false) {
5723
5751
  const handler = async (req, res) => {
5724
5752
  try {
5725
5753
  let user;
@@ -5729,6 +5757,17 @@ function mountRouteOnServer(route, server, routePath, securityHeaders, resolveUs
5729
5757
  } catch {
5730
5758
  }
5731
5759
  }
5760
+ if (requireAuth && route.auth !== false && !user) {
5761
+ res.status(401);
5762
+ if (securityHeaders) {
5763
+ for (const [k, v] of Object.entries(securityHeaders)) res.header(k, v);
5764
+ }
5765
+ res.json({
5766
+ error: "unauthenticated",
5767
+ message: "Authentication is required to access this endpoint."
5768
+ });
5769
+ return;
5770
+ }
5732
5771
  const result = await route.handler({
5733
5772
  body: req.body,
5734
5773
  params: req.params,
@@ -5912,8 +5951,10 @@ function createDispatcherPlugin(config = {}) {
5912
5951
  if (!server) return;
5913
5952
  const kernel = ctx.getKernel();
5914
5953
  const enforceMembership = config.enforceProjectMembership ?? (config.scoping?.enableProjectScoping ?? false);
5954
+ const requireAuth = config.requireAuth ?? config.scoping?.requireAuth ?? false;
5915
5955
  const dispatcher = new HttpDispatcher(kernel, void 0, {
5916
- enforceProjectMembership: enforceMembership
5956
+ enforceProjectMembership: enforceMembership,
5957
+ requireAuth
5917
5958
  });
5918
5959
  const prefix = config.prefix || "/api/v1";
5919
5960
  const securityHeaders = config.securityHeaders === false ? void 0 : buildSecurityHeaders(
@@ -6423,11 +6464,11 @@ function createDispatcherPlugin(config = {}) {
6423
6464
  const routePath = route.path.startsWith("/api/v1") ? route.path : `${prefix}${route.path}`;
6424
6465
  let count = 0;
6425
6466
  if (enableProjectScoping && projectResolution === "required") {
6426
- if (mountRouteOnServer(route, server, toScopedPath(routePath), securityHeaders, resolveRequestUser)) count++;
6467
+ if (mountRouteOnServer(route, server, toScopedPath(routePath), securityHeaders, resolveRequestUser, requireAuth)) count++;
6427
6468
  } else {
6428
- if (mountRouteOnServer(route, server, routePath, securityHeaders, resolveRequestUser)) count++;
6469
+ if (mountRouteOnServer(route, server, routePath, securityHeaders, resolveRequestUser, requireAuth)) count++;
6429
6470
  if (enableProjectScoping) {
6430
- if (mountRouteOnServer(route, server, toScopedPath(routePath), securityHeaders, resolveRequestUser)) count++;
6471
+ if (mountRouteOnServer(route, server, toScopedPath(routePath), securityHeaders, resolveRequestUser, requireAuth)) count++;
6431
6472
  }
6432
6473
  }
6433
6474
  return count;