@constructive-io/graphql-server 5.12.2 → 5.13.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.
@@ -41,14 +41,19 @@ const scopedApiNameLookupSql = (routingSchema) => `
41
41
  * Build a LoaderContext from the API row and options.
42
42
  * This is used to resolve per-database module settings via the loader registry.
43
43
  */
44
- const buildLoaderContext = (routingPool, opts, row) => ({
45
- routingPool,
46
- routingSchema: getRoutingSchema(opts),
47
- tenantPool: getPgPool({ ...opts.pg, database: row.dbname }),
48
- databaseId: row.database_id,
49
- apiId: row.api_id,
50
- dbname: row.dbname
51
- });
44
+ const buildLoaderContext = (routingPool, opts, row) => {
45
+ // Scoped APIs leave dbname NULL when their schemas live in the serving
46
+ // database (pooled tenants); fall back to the server's own database.
47
+ const dbname = row.dbname || opts.pg?.database || '';
48
+ return {
49
+ routingPool,
50
+ routingSchema: getRoutingSchema(opts),
51
+ tenantPool: getPgPool({ ...opts.pg, database: dbname }),
52
+ databaseId: row.database_id,
53
+ apiId: row.api_id,
54
+ dbname
55
+ };
56
+ };
52
57
  /**
53
58
  * Resolve all per-database module settings in parallel via the loader registry.
54
59
  * Each loader independently caches by databaseId — repeated calls are cheap.
@@ -225,6 +225,34 @@ const buildPreset = (pool, schemas, anonRole, roleName, databaseSettings, apiId,
225
225
  }
226
226
  return { pgSettings };
227
227
  }
228
+ // Private (in-cluster) surface: there is no token — identity
229
+ // arrives on the trusted internal X-* headers stamped by the
230
+ // dispatching worker/sync gateway (the same vocabulary as
231
+ // X-Database-Id above). Map it into per-request claims so writes
232
+ // made through this surface carry actor attribution. Never applied
233
+ // on the public surface, where client-supplied identity headers
234
+ // must not assert identity.
235
+ const headerActorId = req.get('X-Actor-Id');
236
+ if (req.api?.isPublic === false && headerActorId) {
237
+ const pgSettings = {
238
+ role: roleName,
239
+ 'jwt.claims.user_id': headerActorId,
240
+ 'jwt.claims.principal_id': headerActorId,
241
+ ...context
242
+ };
243
+ const headerEntityId = req.get('X-Entity-Id');
244
+ if (headerEntityId) {
245
+ pgSettings['jwt.claims.entity_id'] = headerEntityId;
246
+ }
247
+ const headerOrganizationId = req.get('X-Organization-Id');
248
+ if (headerOrganizationId) {
249
+ pgSettings['jwt.claims.organization_id'] = headerOrganizationId;
250
+ }
251
+ if (req.requestId) {
252
+ pgSettings['request.id'] = req.requestId;
253
+ }
254
+ return { pgSettings };
255
+ }
228
256
  }
229
257
  const anonSettings = {
230
258
  role: anonRole,
package/middleware/api.js CHANGED
@@ -47,14 +47,19 @@ const scopedApiNameLookupSql = (routingSchema) => `
47
47
  * Build a LoaderContext from the API row and options.
48
48
  * This is used to resolve per-database module settings via the loader registry.
49
49
  */
50
- const buildLoaderContext = (routingPool, opts, row) => ({
51
- routingPool,
52
- routingSchema: (0, routing_1.getRoutingSchema)(opts),
53
- tenantPool: (0, pg_cache_1.getPgPool)({ ...opts.pg, database: row.dbname }),
54
- databaseId: row.database_id,
55
- apiId: row.api_id,
56
- dbname: row.dbname
57
- });
50
+ const buildLoaderContext = (routingPool, opts, row) => {
51
+ // Scoped APIs leave dbname NULL when their schemas live in the serving
52
+ // database (pooled tenants); fall back to the server's own database.
53
+ const dbname = row.dbname || opts.pg?.database || '';
54
+ return {
55
+ routingPool,
56
+ routingSchema: (0, routing_1.getRoutingSchema)(opts),
57
+ tenantPool: (0, pg_cache_1.getPgPool)({ ...opts.pg, database: dbname }),
58
+ databaseId: row.database_id,
59
+ apiId: row.api_id,
60
+ dbname
61
+ };
62
+ };
58
63
  /**
59
64
  * Resolve all per-database module settings in parallel via the loader registry.
60
65
  * Each loader independently caches by databaseId — repeated calls are cheap.
@@ -234,6 +234,34 @@ const buildPreset = (pool, schemas, anonRole, roleName, databaseSettings, apiId,
234
234
  }
235
235
  return { pgSettings };
236
236
  }
237
+ // Private (in-cluster) surface: there is no token — identity
238
+ // arrives on the trusted internal X-* headers stamped by the
239
+ // dispatching worker/sync gateway (the same vocabulary as
240
+ // X-Database-Id above). Map it into per-request claims so writes
241
+ // made through this surface carry actor attribution. Never applied
242
+ // on the public surface, where client-supplied identity headers
243
+ // must not assert identity.
244
+ const headerActorId = req.get('X-Actor-Id');
245
+ if (req.api?.isPublic === false && headerActorId) {
246
+ const pgSettings = {
247
+ role: roleName,
248
+ 'jwt.claims.user_id': headerActorId,
249
+ 'jwt.claims.principal_id': headerActorId,
250
+ ...context
251
+ };
252
+ const headerEntityId = req.get('X-Entity-Id');
253
+ if (headerEntityId) {
254
+ pgSettings['jwt.claims.entity_id'] = headerEntityId;
255
+ }
256
+ const headerOrganizationId = req.get('X-Organization-Id');
257
+ if (headerOrganizationId) {
258
+ pgSettings['jwt.claims.organization_id'] = headerOrganizationId;
259
+ }
260
+ if (req.requestId) {
261
+ pgSettings['request.id'] = req.requestId;
262
+ }
263
+ return { pgSettings };
264
+ }
237
265
  }
238
266
  const anonSettings = {
239
267
  role: anonRole,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constructive-io/graphql-server",
3
- "version": "5.12.2",
3
+ "version": "5.13.0",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "Constructive GraphQL Server",
6
6
  "main": "index.js",
@@ -43,18 +43,18 @@
43
43
  "dependencies": {
44
44
  "@constructive-io/csrf": "^0.25.0",
45
45
  "@constructive-io/errors": "^0.7.1",
46
- "@constructive-io/express-context": "^0.22.1",
47
- "@constructive-io/graphql-env": "^3.27.1",
48
- "@constructive-io/graphql-types": "^3.26.1",
46
+ "@constructive-io/express-context": "^0.22.3",
47
+ "@constructive-io/graphql-env": "^3.27.3",
48
+ "@constructive-io/graphql-types": "^3.26.3",
49
49
  "@constructive-io/query-builder": "^3.8.0",
50
50
  "@constructive-io/s3-utils": "^2.28.0",
51
51
  "@constructive-io/url-domains": "^2.27.0",
52
52
  "@graphile-contrib/pg-many-to-many": "2.0.0-rc.2",
53
- "@pgpmjs/env": "^2.38.0",
53
+ "@pgpmjs/env": "^2.39.1",
54
54
  "@pgpmjs/logger": "^2.22.0",
55
- "@pgpmjs/server-utils": "^3.23.1",
56
- "@pgpmjs/types": "^2.46.0",
57
- "agentic-server": "0.20.1",
55
+ "@pgpmjs/server-utils": "^3.23.3",
56
+ "@pgpmjs/types": "^2.48.0",
57
+ "agentic-server": "0.20.3",
58
58
  "cors": "^2.8.6",
59
59
  "deepmerge": "^4.3.1",
60
60
  "express": "^5.2.1",
@@ -63,16 +63,16 @@
63
63
  "grafserv": "1.0.0",
64
64
  "graphile-build": "5.0.2",
65
65
  "graphile-build-pg": "5.0.2",
66
- "graphile-cache": "^4.8.1",
66
+ "graphile-cache": "^4.8.3",
67
67
  "graphile-config": "1.0.1",
68
- "graphile-function-bindings": "^1.9.2",
69
- "graphile-settings": "^6.9.2",
68
+ "graphile-function-bindings": "^1.9.4",
69
+ "graphile-settings": "^6.9.4",
70
70
  "graphile-utils": "5.0.1",
71
71
  "graphql": "16.13.0",
72
72
  "graphql-upload": "^13.0.0",
73
73
  "lru-cache": "^11.2.7",
74
74
  "pg": "^8.21.0",
75
- "pg-cache": "^3.23.1",
75
+ "pg-cache": "^3.23.3",
76
76
  "pg-env": "^1.26.0",
77
77
  "pg-query-context": "^2.27.0",
78
78
  "pg-sql2": "5.0.1",
@@ -88,10 +88,10 @@
88
88
  "@types/pg": "^8.20.0",
89
89
  "@types/request-ip": "^0.0.41",
90
90
  "cookie-parser": "^1.4.7",
91
- "graphile-test": "5.9.2",
91
+ "graphile-test": "5.9.4",
92
92
  "makage": "^0.3.0",
93
93
  "nodemon": "^3.1.14",
94
94
  "ts-node": "^10.9.2"
95
95
  },
96
- "gitHead": "5ef8341033a8fbd85841b770e996e468dd15333b"
96
+ "gitHead": "2832d5632d11eb22e1437df773706ac6a99175fe"
97
97
  }