@friggframework/core 2.0.0--canary.707.df80bfd.0 → 2.0.0--canary.680.d670696.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.
Files changed (161) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/CLAUDE.md +0 -28
  3. package/README.md +0 -105
  4. package/application/commands/integration-commands.js +2 -2
  5. package/application/commands/scheduler-commands.js +7 -11
  6. package/application/commands/user-commands.js +5 -17
  7. package/core/Worker.js +7 -31
  8. package/core/create-handler.js +0 -9
  9. package/credential/use-cases/credential-operations-for-user.js +25 -110
  10. package/credential/use-cases/reauthorize-credential-for-user.js +8 -15
  11. package/database/config.js +24 -48
  12. package/database/documentdb-encryption-service.js +0 -1
  13. package/database/documentdb-utils.js +1 -6
  14. package/database/encryption/encryption-config.js +44 -13
  15. package/database/encryption/encryption-schema-registry.js +31 -88
  16. package/database/prisma.js +2 -4
  17. package/generated/prisma-mongodb/edge.js +4 -27
  18. package/generated/prisma-mongodb/index-browser.js +1 -24
  19. package/generated/prisma-mongodb/index.d.ts +48 -3228
  20. package/generated/prisma-mongodb/index.js +4 -27
  21. package/generated/prisma-mongodb/package.json +1 -1
  22. package/generated/prisma-mongodb/schema.prisma +0 -65
  23. package/generated/prisma-mongodb/wasm.js +4 -27
  24. package/generated/prisma-postgresql/edge.js +4 -27
  25. package/generated/prisma-postgresql/index-browser.js +1 -24
  26. package/generated/prisma-postgresql/index.d.ts +51 -3503
  27. package/generated/prisma-postgresql/index.js +4 -27
  28. package/generated/prisma-postgresql/package.json +1 -1
  29. package/generated/prisma-postgresql/schema.prisma +0 -61
  30. package/generated/prisma-postgresql/wasm.js +4 -27
  31. package/handlers/app-definition-loader.js +12 -110
  32. package/handlers/routers/auth.js +12 -40
  33. package/handlers/routers/health.js +2 -4
  34. package/handlers/routers/integration-defined-routers.js +92 -144
  35. package/handlers/routers/integration-webhook-routers.js +64 -102
  36. package/index.js +1 -64
  37. package/infrastructure/scheduler/index.js +6 -19
  38. package/infrastructure/scheduler/scheduler-service-factory.js +7 -55
  39. package/integrations/index.js +0 -5
  40. package/integrations/integration-router.js +73 -115
  41. package/integrations/repositories/integration-repository-documentdb.js +3 -35
  42. package/integrations/repositories/integration-repository-interface.js +1 -17
  43. package/integrations/repositories/integration-repository-mongo.js +76 -59
  44. package/integrations/repositories/integration-repository-postgres.js +76 -62
  45. package/integrations/tests/doubles/test-integration-repository.js +1 -12
  46. package/integrations/use-cases/create-integration.js +20 -55
  47. package/integrations/use-cases/get-integrations-for-user.js +40 -80
  48. package/integrations/use-cases/index.js +1 -7
  49. package/integrations/use-cases/integration-operations-for-user.js +24 -55
  50. package/integrations/utils/map-integration-dto.js +1 -44
  51. package/management-api/management-api-config.js +2 -71
  52. package/management-api/meta-router.js +2 -21
  53. package/management-api/openapi/components.js +3 -50
  54. package/management-api/openapi/v2-operations.js +5 -17
  55. package/management-api/route-registry.js +6 -123
  56. package/management-api/v2/dependencies.js +15 -40
  57. package/management-api/v2/dto.js +0 -20
  58. package/management-api/v2/handlers/authorization.js +0 -1
  59. package/management-api/v2/handlers/credentials.js +2 -6
  60. package/management-api/v2/handlers/entities.js +7 -10
  61. package/management-api/v2/handlers/integrations.js +3 -6
  62. package/management-api/v2/request.js +1 -32
  63. package/modules/index.js +0 -8
  64. package/modules/module-factory.js +0 -12
  65. package/modules/module.js +2 -53
  66. package/modules/proxy/proxy-errors.js +1 -12
  67. package/modules/proxy/proxy-path.js +3 -7
  68. package/modules/proxy/proxy-policy.js +5 -34
  69. package/modules/repositories/module-repository-documentdb.js +6 -51
  70. package/modules/repositories/module-repository-interface.js +2 -19
  71. package/modules/repositories/module-repository-mongo.js +74 -67
  72. package/modules/repositories/module-repository-postgres.js +74 -66
  73. package/modules/repositories/module-repository.js +1 -2
  74. package/modules/requester/proxy-request.js +1 -2
  75. package/modules/requester/requester.js +3 -8
  76. package/modules/use-cases/entity-operations-for-user.js +13 -50
  77. package/modules/use-cases/entity-types.js +7 -31
  78. package/modules/use-cases/execute-entity-proxy-request.js +15 -107
  79. package/modules/use-cases/get-authorization-requirements.js +2 -2
  80. package/modules/use-cases/get-entities-for-user.js +13 -26
  81. package/modules/use-cases/get-entity-module-for-user.js +7 -34
  82. package/modules/use-cases/get-entity-options-by-id.js +4 -10
  83. package/modules/use-cases/get-entity-options-by-type.js +1 -1
  84. package/modules/use-cases/get-module-instance-from-type.js +1 -1
  85. package/modules/use-cases/get-module.js +4 -10
  86. package/modules/use-cases/multi-step-authorization.js +13 -40
  87. package/modules/use-cases/process-authorization-callback.js +19 -53
  88. package/modules/use-cases/refresh-entity-options.js +4 -10
  89. package/modules/use-cases/test-module-auth.js +4 -10
  90. package/package.json +5 -6
  91. package/prisma-mongodb/schema.prisma +0 -65
  92. package/prisma-postgresql/schema.prisma +0 -61
  93. package/queues/index.js +0 -25
  94. package/types/index.d.ts +0 -1
  95. package/types/modules/index.d.ts +1 -157
  96. package/user/repositories/user-repository-documentdb.js +2 -27
  97. package/user/repositories/user-repository-interface.js +0 -18
  98. package/user/repositories/user-repository-mongo.js +0 -24
  99. package/user/repositories/user-repository-postgres.js +0 -18
  100. package/user/use-cases/authenticate-user.js +1 -7
  101. package/user/use-cases/get-user-from-adopter-jwt.js +124 -95
  102. package/user/use-cases/get-user-from-x-frigg-headers.js +80 -83
  103. package/user/user.js +1 -64
  104. package/database/organization-where.js +0 -18
  105. package/database/unique-violation.js +0 -25
  106. package/extensions/bootstrap-runner.js +0 -43
  107. package/extensions/extension-loader.js +0 -230
  108. package/extensions/index.js +0 -44
  109. package/extensions/initialize-app.js +0 -123
  110. package/extensions/prisma-composer.js +0 -270
  111. package/extensions/route-mounter.js +0 -103
  112. package/extensions/schema-composer.js +0 -188
  113. package/infrastructure/scheduler/netlify-scheduler-adapter.js +0 -216
  114. package/infrastructure/scheduler/scheduled-job-repository.js +0 -118
  115. package/integrations/repositories/integration-not-found.js +0 -14
  116. package/integrations/schedule-config.js +0 -80
  117. package/integrations/use-cases/dispatch-ongoing-syncs.js +0 -100
  118. package/integrations/use-cases/resolve-integration-ownership.js +0 -177
  119. package/modules/entity-scope.js +0 -15
  120. package/modules/proxy/proxy-bases.js +0 -169
  121. package/modules/repositories/entity-not-found.js +0 -18
  122. package/modules/scope-config.js +0 -192
  123. package/modules/scope-policy.js +0 -112
  124. package/modules/scope-resolution.js +0 -288
  125. package/modules/scope-visibility.js +0 -247
  126. package/modules/tests/doubles/ownership-matching.js +0 -18
  127. package/modules/use-cases/authorize-entity-for-user.js +0 -61
  128. package/modules/use-cases/create-entity-from-credential.js +0 -113
  129. package/modules/use-cases/manage-access.js +0 -21
  130. package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +0 -26
  131. package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +0 -25
  132. package/prisma-postgresql/migrations/20261017000000_add_scheduled_job/migration.sql +0 -28
  133. package/providers/index.js +0 -23
  134. package/providers/resolve-provider.js +0 -93
  135. package/providers/runtime-detection.js +0 -124
  136. package/queues/providers/index.js +0 -11
  137. package/queues/providers/netlify-background-provider.js +0 -150
  138. package/queues/providers/qstash-queue-provider.js +0 -219
  139. package/queues/providers/sqs-queue-provider.js +0 -70
  140. package/queues/queue-client-interface.js +0 -55
  141. package/queues/queue-message-auth.js +0 -193
  142. package/queues/queue-provider-factory.js +0 -101
  143. package/queues/queue-provider.js +0 -65
  144. package/queues/sqs-queue-client.js +0 -81
  145. package/types/user/index.d.ts +0 -134
  146. package/user/adopter-jwt-config.js +0 -177
  147. package/user/adopter-jwt-errors.js +0 -59
  148. package/user/adopter-token-verifier.js +0 -122
  149. package/user/app-user-helpers.js +0 -97
  150. package/user/create-user-authentication.js +0 -64
  151. package/user/membership.js +0 -51
  152. package/user/repositories/organization-membership-record.js +0 -19
  153. package/user/repositories/organization-membership-repository-documentdb.js +0 -106
  154. package/user/repositories/organization-membership-repository-factory.js +0 -42
  155. package/user/repositories/organization-membership-repository-interface.js +0 -57
  156. package/user/repositories/organization-membership-repository-mongo.js +0 -13
  157. package/user/repositories/organization-membership-repository-postgres.js +0 -29
  158. package/user/repositories/organization-membership-repository-prisma.js +0 -81
  159. package/user/tests/doubles/adopter-jwt-fixtures.js +0 -220
  160. package/user/use-cases/ensure-organization-membership.js +0 -92
  161. package/user/use-cases/find-individual-users-by-organization.js +0 -54
@@ -34,11 +34,6 @@ const stepQuery = [
34
34
  query('step', 'Step number, 1-based (default 1)', { type: 'integer', minimum: 1 }),
35
35
  query('sessionId', 'Session from step 1; required for later steps'),
36
36
  ];
37
- // ADR-056 §3.4: the same two filters on every list that mixes ownership.
38
- const listFilters = [
39
- query('scope', 'Only records of this scope', { type: 'string', enum: ['individual', 'organization'] }),
40
- query('owner', 'Only the caller\'s own (`me`) or the active organization\'s (`org`)', { type: 'string', enum: ['me', 'org'] }),
41
- ];
42
37
  const anyJson = { description: 'Defined by the integration or module' };
43
38
 
44
39
  const V2_OPERATIONS = {
@@ -46,8 +41,7 @@ const V2_OPERATIONS = {
46
41
  listIntegrations: {
47
42
  summary: "List the caller's integrations",
48
43
  description:
49
- 'Returns only `{ integrations }`: the caller\'s and the active organization\'s, created in that organization (ADR-056 §3.6). Each carries `scope`, `owner`, `organizationId` and `access`. Integration types are at /api/v2/integrations/options and connected accounts at /api/v2/entities.',
50
- parameters: listFilters,
44
+ 'Returns only `{ integrations }`. Integration types are at /api/v2/integrations/options and connected accounts at /api/v2/entities.',
51
45
  responses: { 200: ok('Integrations', ref('ListIntegrationsResponse')), ...errors('Unauthorized') },
52
46
  },
53
47
  listIntegrationOptions: {
@@ -114,9 +108,6 @@ const V2_OPERATIONS = {
114
108
  // Entities
115
109
  listEntities: {
116
110
  summary: "List the caller's connected entities",
117
- description:
118
- "The active organization's entities and the caller's individual ones (ADR-056 §3.6): an individual entity belongs to the organization it was connected in, unless `ownership.individualAcross` is `all`. Each carries `scope`, `owner`, `organizationId` and `access`.",
119
- parameters: listFilters,
120
111
  responses: { 200: ok('Entities', ref('ListEntitiesResponse')), ...errors('Unauthorized') },
121
112
  },
122
113
  listEntityTypes: {
@@ -165,14 +156,14 @@ const V2_OPERATIONS = {
165
156
  proxyEntityRequest: {
166
157
  summary: "Call the entity's API through Frigg (beta)",
167
158
  description:
168
- "ADR-052. Off unless the app sets managementApi.proxy.enable. Path-only input relative to the module's base URL; `base` optionally names one of the module's several base URLs (default `default`; a name, never a URL), and allow-list rules match per base. Deny by default, only operations in the module's (or the app's) allow-list. ADR-056 §3.5: the caller needs use access to the entity in the active organization (an entity they cannot see there is 404), and the entity's scope must be in managementApi.proxy.scopes (default individual only; 403 PROXY_NOT_ALLOWED with details.reason 'scope', or 'access' for view-only access). The HTTP status follows the upstream status; upstream 5xx is 502 UPSTREAM_ERROR, 429 keeps Retry-After.",
159
+ "ADR-052. Off unless the app sets managementApi.proxy.enable. Path-only input relative to the module's base URL; deny by default, only operations in the module's (or the app's) allow-list. The HTTP status follows the upstream status; upstream 5xx is 502 UPSTREAM_ERROR, 429 keeps Retry-After.",
169
160
  requestBody: body(ref('ProxyRequest')),
170
161
  responses: {
171
162
  200: ok('Upstream answer (the status mirrors the upstream status)', ref('ProxyResponse')),
172
- 400: ok('INVALID_PROXY_REQUEST, or PROXY_UNKNOWN_BASE (`base` is not one of the module\'s base names; the message lists them)', ref('ProxyError')),
163
+ 400: ok('INVALID_PROXY_REQUEST', ref('ProxyError')),
173
164
  401: ok('INVALID_CREDENTIALS or unauthenticated', ref('ProxyError')),
174
- 403: ok("PROXY_NOT_ALLOWED: not in the allow-list, or details.reason 'scope' / 'access'", ref('ProxyError')),
175
- 404: ok('Entity not found, or not visible in the active organization', ref('Error')),
165
+ 403: ok('PROXY_NOT_ALLOWED', ref('ProxyError')),
166
+ 404: ok('Entity not found', ref('Error')),
176
167
  413: ok('PAYLOAD_TOO_LARGE', ref('ProxyError')),
177
168
  415: ok('UNSUPPORTED_MEDIA_TYPE', ref('ProxyError')),
178
169
  429: ok('RATE_LIMITED (Retry-After passed through)', ref('ProxyError')),
@@ -184,9 +175,6 @@ const V2_OPERATIONS = {
184
175
  // Credentials
185
176
  listCredentials: {
186
177
  summary: "List the caller's credentials (masked)",
187
- description:
188
- 'A credential is listed through the entities that use it that the caller may see, so each organization lists only its own connections; one no entity uses is listed to its owner. Each carries `scope`, `owner`, `organizationId` and `access`, taken from who owns the credential.',
189
- parameters: listFilters,
190
178
  responses: { 200: ok('Credentials', ref('ListCredentialsResponse')), ...errors('Unauthorized') },
191
179
  },
192
180
  getCredential: {
@@ -50,16 +50,8 @@ const RESERVED_V2_SEGMENTS = Object.freeze(['reports']);
50
50
  const V1_MIGRATION_DOCS_URL =
51
51
  'https://docs.friggframework.org/api/migrate-v1-v2';
52
52
 
53
- /**
54
- * Functions (devtools names) that serve registered routes. `queueWorker` is
55
- * the HTTP queue worker of hosts that deliver queue messages over HTTP
56
- * (provider-netlify's worker-background); AWS delivers through SQS.
57
- */
58
- const FUNCTIONS = Object.freeze({
59
- AUTH: 'auth',
60
- HEALTH: 'health',
61
- QUEUE_WORKER: 'queueWorker',
62
- });
53
+ /** Lambda functions (devtools names) that serve Management API routes. */
54
+ const FUNCTIONS = Object.freeze({ AUTH: 'auth', HEALTH: 'health' });
63
55
 
64
56
  const route = (version, fn) => (definition) =>
65
57
  Object.freeze({ version, function: fn, ...definition });
@@ -188,91 +180,6 @@ const AUTH_EXTRA_ROUTES = Object.freeze(
188
180
  ].map(route(null, FUNCTIONS.AUTH))
189
181
  );
190
182
 
191
- /**
192
- * Unversioned routes the DB-free health Lambda serves next to /api/meta.
193
- * GET /api/docs redirects to the current OpenAPI document; the docs
194
- * themselves live under /api/meta (ADR-053).
195
- */
196
- const DOCS_ROUTES = Object.freeze(
197
- [
198
- {
199
- operationId: 'redirectToOpenApi',
200
- method: 'GET',
201
- path: '/api/docs',
202
- },
203
- ].map(route(null, FUNCTIONS.HEALTH))
204
- );
205
-
206
- /**
207
- * Admin routes live under /admin (as core's /admin/db-migrate and the
208
- * admin-scripts /admin/scripts do), never under the public /api prefix. A
209
- * core extension (appDefinition.coreExtensions[].routes.path) mounts its admin
210
- * API at /admin/<name>, served by the auth function behind the admin API key;
211
- * these segments belong to other functions and cannot be taken.
212
- */
213
- const ADMIN_ROUTE_PREFIX = '/admin/';
214
- const RESERVED_ADMIN_SEGMENTS = Object.freeze(['scripts', 'db-migrate', 'queue']);
215
-
216
- /**
217
- * The queue delivery endpoint of hosts that deliver queue messages over HTTP
218
- * (QStash posts here on Netlify). Every message must be signed: the worker
219
- * verifies it before parsing anything. Not part of the public /api surface.
220
- */
221
- const HTTP_QUEUE_ROUTES = Object.freeze(
222
- [
223
- {
224
- operationId: 'deliverQueueMessage',
225
- method: 'POST',
226
- path: '/admin/queue',
227
- },
228
- ].map(route(null, FUNCTIONS.QUEUE_WORKER))
229
- );
230
-
231
- /**
232
- * Why a core extension route path is not acceptable, or null when it is:
233
- * `/admin/<segment>` (letters, digits, `-` and `_`, one or more segments), not
234
- * a reserved segment.
235
- *
236
- * @param {*} path
237
- * @returns {string|null}
238
- */
239
- function coreExtensionRoutePathError(path) {
240
- if (typeof path !== 'string' || !path.startsWith(ADMIN_ROUTE_PREFIX)) {
241
- return `must start with ${ADMIN_ROUTE_PREFIX} (admin routes are not served under /api)`;
242
- }
243
- if (!/^\/admin(\/[A-Za-z0-9_-]+)+$/.test(path)) {
244
- return 'must be /admin/<segment>[/<segment>...] using letters, digits, - and _';
245
- }
246
- const segment = path.split('/')[2];
247
- if (RESERVED_ADMIN_SEGMENTS.includes(segment)) {
248
- return `/admin/${segment} is reserved (${RESERVED_ADMIN_SEGMENTS.map((s) => `/admin/${s}`).join(', ')})`;
249
- }
250
- return null;
251
- }
252
-
253
- /**
254
- * Route records for the core extensions' admin routes: each path and
255
- * everything under it, on the auth function (core's auth router mounts the
256
- * extension routes behind the admin API key).
257
- *
258
- * @param {Array<Object>} [coreExtensions] - appDefinition.coreExtensions (plain data;
259
- * only `routes.path` is read)
260
- * @returns {Array<Object>}
261
- */
262
- function listCoreExtensionRoutes(coreExtensions = []) {
263
- if (!Array.isArray(coreExtensions)) return [];
264
- const out = [];
265
- for (const ext of coreExtensions) {
266
- const path = ext?.routes?.path;
267
- if (typeof path !== 'string' || coreExtensionRoutePathError(path)) continue;
268
- out.push(
269
- Object.freeze({ version: null, function: FUNCTIONS.AUTH, method: 'ANY', path, extension: ext.name }),
270
- Object.freeze({ version: null, function: FUNCTIONS.AUTH, method: 'ANY', path: `${path}/{proxy+}`, extension: ext.name })
271
- );
272
- }
273
- return out;
274
- }
275
-
276
183
  /**
277
184
  * The v2 routes an app serves. Routes tied to an opt-in capability (the entity
278
185
  * proxy, ADR-052) are left out unless the app enables it.
@@ -299,7 +206,6 @@ function listRoutes({ v1 = true, proxy = false } = {}) {
299
206
  ...listV2Routes({ proxy }),
300
207
  ...META_ROUTES,
301
208
  ...AUTH_EXTRA_ROUTES,
302
- ...DOCS_ROUTES,
303
209
  ];
304
210
  }
305
211
 
@@ -319,25 +225,18 @@ function resourceRoot(record) {
319
225
  }
320
226
 
321
227
  /**
322
- * Gateway routes derived from the registry: the Management API, its
323
- * discovery and docs routes, the core extensions' admin routes and, for
324
- * hosts that deliver queue messages over HTTP, the queue endpoint. Each
325
- * Management API resource root gets an exact route when the root itself is
228
+ * API Gateway HTTP API routes for the Management API, derived from the
229
+ * registry. Each resource root gets an exact route when the root itself is
326
230
  * registered and a greedy `{proxy+}` route when deeper paths are. The auth
327
231
  * function uses `ANY` (as it always has, so CORS preflights reach Express);
328
232
  * the health function only serves GET.
329
233
  *
330
- * devtools turns these into API Gateway HTTP API events; provider-netlify
331
- * into netlify.toml redirects.
332
- *
333
234
  * @param {Object} [options]
334
235
  * @param {boolean} [options.v1=true]
335
236
  * @param {boolean} [options.proxy=false]
336
- * @param {Array<Object>} [options.coreExtensions=[]] - appDefinition.coreExtensions
337
- * @param {boolean} [options.httpQueue=false] - include the HTTP queue route
338
237
  * @returns {Array<{function: string, method: string, path: string}>}
339
238
  */
340
- function getGatewayRoutes({ v1 = true, proxy = false, coreExtensions = [], httpQueue = false } = {}) {
239
+ function getGatewayRoutes({ v1 = true, proxy = false } = {}) {
341
240
  const out = [];
342
241
  const seen = new Set();
343
242
  const add = (fn, method, path) => {
@@ -366,7 +265,7 @@ function getGatewayRoutes({ v1 = true, proxy = false, coreExtensions = [], httpQ
366
265
 
367
266
  // Extra routes get an exact route unless a greedy route on the same
368
267
  // function already covers them.
369
- for (const record of [...AUTH_EXTRA_ROUTES, ...DOCS_ROUTES]) {
268
+ for (const record of AUTH_EXTRA_ROUTES) {
370
269
  const covered = greedyRoots.has(
371
270
  `${record.function} ${resourceRoot(record)}`
372
271
  );
@@ -375,16 +274,6 @@ function getGatewayRoutes({ v1 = true, proxy = false, coreExtensions = [], httpQ
375
274
  }
376
275
  }
377
276
 
378
- for (const record of listCoreExtensionRoutes(coreExtensions)) {
379
- add(record.function, record.method, record.path);
380
- }
381
-
382
- if (httpQueue) {
383
- for (const record of HTTP_QUEUE_ROUTES) {
384
- add(record.function, record.method, record.path);
385
- }
386
- }
387
-
388
277
  return out;
389
278
  }
390
279
 
@@ -398,12 +287,6 @@ module.exports = {
398
287
  V2_ROUTES,
399
288
  META_ROUTES,
400
289
  AUTH_EXTRA_ROUTES,
401
- DOCS_ROUTES,
402
- HTTP_QUEUE_ROUTES,
403
- ADMIN_ROUTE_PREFIX,
404
- RESERVED_ADMIN_SEGMENTS,
405
- coreExtensionRoutePathError,
406
- listCoreExtensionRoutes,
407
290
  listRoutes,
408
291
  listV2Routes,
409
292
  getGatewayRoutes,
@@ -29,18 +29,13 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
29
29
  getEntityTypeRegistry,
30
30
  getEntityTypeDefinitions,
31
31
  } = require('../../modules/entity-type-registry');
32
- const { createScopePolicy } = require('../../modules/scope-policy');
33
- const {
34
- AuthorizeEntityForUser,
35
- } = require('../../modules/use-cases/authorize-entity-for-user');
36
- const {
37
- ResolveIntegrationOwnership,
38
- } = require('../../integrations/use-cases/resolve-integration-ownership');
32
+ const { GetUserFromBearerToken } = require('../../user/use-cases/get-user-from-bearer-token');
33
+ const { GetUserFromXFriggHeaders } = require('../../user/use-cases/get-user-from-x-frigg-headers');
34
+ const { GetUserFromAdopterJwt } = require('../../user/use-cases/get-user-from-adopter-jwt');
39
35
  const {
40
36
  AuthenticateWithSharedSecret,
41
37
  } = require('../../user/use-cases/authenticate-with-shared-secret');
42
38
  const { AuthenticateUser } = require('../../user/use-cases/authenticate-user');
43
- const { buildUserAuthentication } = require('../../user/create-user-authentication');
44
39
  const { CreateIntegration } = require('../../integrations/use-cases/create-integration');
45
40
  const { UpdateIntegration } = require('../../integrations/use-cases/update-integration');
46
41
  const {
@@ -92,11 +87,6 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
92
87
  appDefinition.entityTypeRegistry ??
93
88
  getEntityTypeRegistry(appDefinition);
94
89
  const moduleDefinitions = getEntityTypeDefinitions(registry);
95
- // Ownership by scope (ADR-056 §3): one object every use case asks.
96
- const scopePolicy = createScopePolicy({
97
- ownership: appDefinition.ownership,
98
- entityTypeRegistry: registry,
99
- });
100
90
 
101
91
  const moduleRepository = createModuleRepository();
102
92
  const integrationRepository = createIntegrationRepository();
@@ -105,7 +95,9 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
105
95
  const authorizationSessionRepository = createAuthorizationSessionRepository();
106
96
 
107
97
  const authenticateUser = new AuthenticateUser({
108
- ...buildUserAuthentication({ userConfig, userRepository }),
98
+ getUserFromBearerToken: new GetUserFromBearerToken({ userRepository, userConfig }),
99
+ getUserFromXFriggHeaders: new GetUserFromXFriggHeaders({ userRepository, userConfig }),
100
+ getUserFromAdopterJwt: new GetUserFromAdopterJwt({ userRepository, userConfig }),
109
101
  authenticateWithSharedSecret: new AuthenticateWithSharedSecret(),
110
102
  userConfig,
111
103
  });
@@ -119,12 +111,10 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
119
111
  const getEntityModuleForUser = new GetEntityModuleForUser({
120
112
  moduleRepository,
121
113
  moduleDefinitions,
122
- scopePolicy,
123
114
  });
124
115
  const getCredentialForUser = new credentialOps.GetCredentialForUserV2({
125
116
  credentialRepository,
126
117
  moduleRepository,
127
- scopePolicy,
128
118
  });
129
119
  const getAuthorizationRequirements = new GetAuthorizationRequirements({
130
120
  moduleDefinitions,
@@ -138,22 +128,17 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
138
128
  moduleDefinitions,
139
129
  getAuthorizationRequirements,
140
130
  authorizationSessionRepository,
141
- authorizeEntityForUser: new AuthorizeEntityForUser({
142
- scopePolicy,
143
- processAuthorizationCallback: new ProcessAuthorizationCallback({
144
- moduleRepository,
145
- credentialRepository,
146
- integrationRepository,
147
- moduleDefinitions,
148
- }),
131
+ processAuthorizationCallback: new ProcessAuthorizationCallback({
132
+ moduleRepository,
133
+ credentialRepository,
134
+ integrationRepository,
135
+ moduleDefinitions,
149
136
  }),
150
137
  moduleRepository,
151
138
  credentialRepository,
152
- scopePolicy,
153
139
  });
154
140
  const getOwnedIntegrationRecord = new integrationOps.GetOwnedIntegrationRecord({
155
141
  integrationRepository,
156
- scopePolicy,
157
142
  });
158
143
  const getIntegrationInstanceForUser = new integrationOps.GetIntegrationInstanceForUser({
159
144
  getOwnedIntegrationRecord,
@@ -163,7 +148,6 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
163
148
  return {
164
149
  config,
165
150
  moduleDefinitions,
166
- scopePolicy,
167
151
  repositories: {
168
152
  moduleRepository,
169
153
  integrationRepository,
@@ -179,7 +163,6 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
179
163
  integrationClasses,
180
164
  moduleFactory,
181
165
  moduleRepository,
182
- scopePolicy,
183
166
  }),
184
167
  getPossibleIntegrations: new GetPossibleIntegrations({ integrationClasses }),
185
168
  createIntegrationForUser: new integrationOps.CreateIntegrationForUser({
@@ -188,11 +171,7 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
188
171
  integrationClasses,
189
172
  moduleFactory,
190
173
  }),
191
- resolveIntegrationOwnership: new ResolveIntegrationOwnership({
192
- moduleRepository,
193
- integrationClasses,
194
- scopePolicy,
195
- }),
174
+ getEntityModuleForUser,
196
175
  integrationClasses,
197
176
  }),
198
177
  getIntegrationInstanceForUser,
@@ -224,10 +203,7 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
224
203
 
225
204
  // Entities
226
205
  getEntityModuleForUser,
227
- listEntitiesForUser: new entityOps.ListEntitiesForUser({
228
- moduleRepository,
229
- scopePolicy,
230
- }),
206
+ listEntitiesForUser: new entityOps.ListEntitiesForUser({ moduleRepository }),
231
207
  getEntityForUser: new entityOps.GetEntityForUser({ getEntityModuleForUser }),
232
208
  deleteEntityForUser: new entityOps.DeleteEntityForUser({
233
209
  getEntityModuleForUser,
@@ -239,15 +215,14 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
239
215
  refreshEntityOptionsForUser: new entityOps.RefreshEntityOptionsForUser({
240
216
  getEntityModuleForUser,
241
217
  }),
242
- listEntityTypes: new ListEntityTypes({ entityTypeRegistry: registry, scopePolicy }),
243
- getEntityType: new GetEntityType({ entityTypeRegistry: registry, scopePolicy }),
218
+ listEntityTypes: new ListEntityTypes({ entityTypeRegistry: registry }),
219
+ getEntityType: new GetEntityType({ entityTypeRegistry: registry }),
244
220
  getAuthorizationRequirements,
245
221
 
246
222
  // Credentials
247
223
  listCredentialsForUser: new credentialOps.ListCredentialsForUser({
248
224
  credentialRepository,
249
225
  moduleRepository,
250
- scopePolicy,
251
226
  }),
252
227
  getCredentialForUser,
253
228
  deleteCredentialForUser: new credentialOps.DeleteCredentialForUser({
@@ -13,20 +13,6 @@ const idOf = (value) => {
13
13
  return String(value);
14
14
  };
15
15
 
16
- /**
17
- * `{ scope, owner, organizationId, access }` (ADR-056 §3.4): whose a record is
18
- * and what the caller may do with it. Empty when the use case did not say.
19
- */
20
- function tenancyFields(tenancy) {
21
- if (!tenancy) return {};
22
- return {
23
- scope: tenancy.scope,
24
- owner: tenancy.owner,
25
- organizationId: tenancy.organizationId ?? null,
26
- access: tenancy.access,
27
- };
28
- }
29
-
30
16
  /** An entity (connected account). */
31
17
  function toEntityDto(entity) {
32
18
  const credential =
@@ -44,7 +30,6 @@ function toEntityDto(entity) {
44
30
  typeof credential?.authIsValid === 'boolean'
45
31
  ? credential.authIsValid
46
32
  : null,
47
- ...tenancyFields(entity.tenancy),
48
33
  };
49
34
  }
50
35
 
@@ -57,7 +42,6 @@ function toIntegrationDto(integration) {
57
42
  config: integration.config ?? {},
58
43
  version: integration.version ?? null,
59
44
  messages: integration.messages ?? {},
60
- ...tenancyFields(integration.tenancy),
61
45
  };
62
46
  }
63
47
 
@@ -75,8 +59,6 @@ const CREDENTIAL_METADATA = new Set([
75
59
  'type',
76
60
  'entityIds',
77
61
  'status',
78
- // Added by the credential use cases (ADR-056 §3.4); never provider data.
79
- 'tenancy',
80
62
  ]);
81
63
 
82
64
  /**
@@ -128,13 +110,11 @@ function maskCredential(credential) {
128
110
  entityCount: entityIds.length,
129
111
  createdAt: toIso(credential.createdAt),
130
112
  updatedAt: toIso(credential.updatedAt),
131
- ...tenancyFields(credential.tenancy),
132
113
  data,
133
114
  };
134
115
  }
135
116
 
136
117
  module.exports = {
137
- tenancyFields,
138
118
  toEntityDto,
139
119
  toIntegrationDto,
140
120
  maskCredential,
@@ -63,7 +63,6 @@ function createAuthorizationHandlers(useCases) {
63
63
  data: body.data,
64
64
  step,
65
65
  sessionId,
66
- scope: optionalString(body.scope, 'scope'),
67
66
  });
68
67
  res.json(toAuthorizationResponse(result));
69
68
  },
@@ -1,5 +1,4 @@
1
1
  const { maskCredential } = require('../dto');
2
- const { parseListFilters } = require('../request');
3
2
 
4
3
  /** Management API v2 credential handlers, keyed by operationId. */
5
4
  function createCredentialHandlers(useCases) {
@@ -7,11 +6,8 @@ function createCredentialHandlers(useCases) {
7
6
  useCases;
8
7
 
9
8
  return {
10
- async listCredentials(req, res, user) {
11
- const credentials = await listCredentialsForUser.execute(
12
- user,
13
- parseListFilters(req.query)
14
- );
9
+ async listCredentials(_req, res, user) {
10
+ const credentials = await listCredentialsForUser.execute(user);
15
11
  res.json({ credentials: credentials.map(maskCredential) });
16
12
  },
17
13
 
@@ -1,5 +1,5 @@
1
1
  const { toEntityDto } = require('../dto');
2
- const { objectBody, parseStep, parseListFilters } = require('../request');
2
+ const { objectBody, parseStep } = require('../request');
3
3
 
4
4
  /** Management API v2 entity and entity-type handlers, keyed by operationId. */
5
5
  function createEntityHandlers(useCases) {
@@ -16,20 +16,17 @@ function createEntityHandlers(useCases) {
16
16
  } = useCases;
17
17
 
18
18
  return {
19
- async listEntities(req, res, user) {
20
- const entities = await listEntitiesForUser.execute(
21
- user,
22
- parseListFilters(req.query)
23
- );
19
+ async listEntities(_req, res, user) {
20
+ const entities = await listEntitiesForUser.execute(user);
24
21
  res.json({ entities: entities.map(toEntityDto) });
25
22
  },
26
23
 
27
- async listEntityTypes(_req, res, user) {
28
- res.json({ types: await listEntityTypes.execute({ user }) });
24
+ async listEntityTypes(_req, res) {
25
+ res.json({ types: await listEntityTypes.execute() });
29
26
  },
30
27
 
31
- async getEntityType(req, res, user) {
32
- res.json(await getEntityType.execute(req.params.entityType, { user }));
28
+ async getEntityType(req, res) {
29
+ res.json(await getEntityType.execute(req.params.entityType));
33
30
  },
34
31
 
35
32
  // Describes a step's requirements without starting an authorization
@@ -1,5 +1,5 @@
1
1
  const { toIntegrationDto } = require('../dto');
2
- const { objectBody, optionalString, parseListFilters } = require('../request');
2
+ const { objectBody, optionalString } = require('../request');
3
3
 
4
4
  /**
5
5
  * Management API v2 integration handlers, keyed by registry operationId.
@@ -27,11 +27,8 @@ function createIntegrationHandlers(useCases) {
27
27
  );
28
28
 
29
29
  return {
30
- async listIntegrations(req, res, user) {
31
- const integrations = await getIntegrationsForUser.execute(
32
- user,
33
- parseListFilters(req.query)
34
- );
30
+ async listIntegrations(_req, res, user) {
31
+ const integrations = await getIntegrationsForUser.execute(user.getId());
35
32
  res.json({ integrations: integrations.map(toIntegrationDto) });
36
33
  },
37
34
 
@@ -1,5 +1,4 @@
1
1
  const Boom = require('@hapi/boom');
2
- const { LIST_OWNERS } = require('../../modules/scope-visibility');
3
2
 
4
3
  /** The JSON body as an object, or a 400 VALIDATION_ERROR. */
5
4
  function objectBody(req) {
@@ -52,34 +51,4 @@ function optionalString(value, name) {
52
51
  return value;
53
52
  }
54
53
 
55
- const LIST_SCOPES = ['individual', 'organization'];
56
-
57
- /**
58
- * `?scope=individual|organization` and `?owner=me|org` on a list (ADR-056
59
- * §3.4). Anything else is a 400.
60
- */
61
- function parseListFilters(query = {}) {
62
- const scope = optionalString(query.scope, 'scope');
63
- const owner = optionalString(query.owner, 'owner');
64
- if (scope !== undefined && !LIST_SCOPES.includes(scope)) {
65
- throw Boom.badRequest(`scope must be one of ${LIST_SCOPES.join(', ')}`, {
66
- code: 'VALIDATION_ERROR',
67
- details: { field: 'scope' },
68
- });
69
- }
70
- if (owner !== undefined && !Object.hasOwn(LIST_OWNERS, owner)) {
71
- throw Boom.badRequest('owner must be me or org', {
72
- code: 'VALIDATION_ERROR',
73
- details: { field: 'owner' },
74
- });
75
- }
76
- return { scope, owner };
77
- }
78
-
79
- module.exports = {
80
- objectBody,
81
- requireFields,
82
- parseStep,
83
- optionalString,
84
- parseListFilters,
85
- };
54
+ module.exports = { objectBody, requireFields, parseStep, optionalString };
package/modules/index.js CHANGED
@@ -8,11 +8,6 @@ const {
8
8
  getEntityTypeRegistry,
9
9
  EntityRegistryError,
10
10
  } = require('./entity-type-registry');
11
- const {
12
- resolveScope,
13
- listBindingScopes,
14
- OwnershipConfigError,
15
- } = require('./entity-scope');
16
11
 
17
12
  module.exports = {
18
13
  ApiKeyRequester,
@@ -23,7 +18,4 @@ module.exports = {
23
18
  ModuleFactory,
24
19
  getEntityTypeRegistry,
25
20
  EntityRegistryError,
26
- resolveScope,
27
- listBindingScopes,
28
- OwnershipConfigError,
29
21
  };
@@ -45,18 +45,6 @@ class ModuleFactory {
45
45
  );
46
46
  }
47
47
 
48
- // definition.env may be an async function (e.g. the db-credentials
49
- // extension's bootstrap loads OAuth app credentials from the
50
- // database); resolve it first. A static env keeps the plain
51
- // constructor, exactly as before.
52
- if (typeof moduleDefinition.env === 'function') {
53
- return await Module.create({
54
- userId,
55
- entity,
56
- definition: moduleDefinition,
57
- });
58
- }
59
-
60
48
  return new Module({
61
49
  userId,
62
50
  entity,