@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.
- package/CHANGELOG.md +3 -0
- package/CLAUDE.md +0 -28
- package/README.md +0 -105
- package/application/commands/integration-commands.js +2 -2
- package/application/commands/scheduler-commands.js +7 -11
- package/application/commands/user-commands.js +5 -17
- package/core/Worker.js +7 -31
- package/core/create-handler.js +0 -9
- package/credential/use-cases/credential-operations-for-user.js +25 -110
- package/credential/use-cases/reauthorize-credential-for-user.js +8 -15
- package/database/config.js +24 -48
- package/database/documentdb-encryption-service.js +0 -1
- package/database/documentdb-utils.js +1 -6
- package/database/encryption/encryption-config.js +44 -13
- package/database/encryption/encryption-schema-registry.js +31 -88
- package/database/prisma.js +2 -4
- package/generated/prisma-mongodb/edge.js +4 -27
- package/generated/prisma-mongodb/index-browser.js +1 -24
- package/generated/prisma-mongodb/index.d.ts +48 -3228
- package/generated/prisma-mongodb/index.js +4 -27
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +0 -65
- package/generated/prisma-mongodb/wasm.js +4 -27
- package/generated/prisma-postgresql/edge.js +4 -27
- package/generated/prisma-postgresql/index-browser.js +1 -24
- package/generated/prisma-postgresql/index.d.ts +51 -3503
- package/generated/prisma-postgresql/index.js +4 -27
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +0 -61
- package/generated/prisma-postgresql/wasm.js +4 -27
- package/handlers/app-definition-loader.js +12 -110
- package/handlers/routers/auth.js +12 -40
- package/handlers/routers/health.js +2 -4
- package/handlers/routers/integration-defined-routers.js +92 -144
- package/handlers/routers/integration-webhook-routers.js +64 -102
- package/index.js +1 -64
- package/infrastructure/scheduler/index.js +6 -19
- package/infrastructure/scheduler/scheduler-service-factory.js +7 -55
- package/integrations/index.js +0 -5
- package/integrations/integration-router.js +73 -115
- package/integrations/repositories/integration-repository-documentdb.js +3 -35
- package/integrations/repositories/integration-repository-interface.js +1 -17
- package/integrations/repositories/integration-repository-mongo.js +76 -59
- package/integrations/repositories/integration-repository-postgres.js +76 -62
- package/integrations/tests/doubles/test-integration-repository.js +1 -12
- package/integrations/use-cases/create-integration.js +20 -55
- package/integrations/use-cases/get-integrations-for-user.js +40 -80
- package/integrations/use-cases/index.js +1 -7
- package/integrations/use-cases/integration-operations-for-user.js +24 -55
- package/integrations/utils/map-integration-dto.js +1 -44
- package/management-api/management-api-config.js +2 -71
- package/management-api/meta-router.js +2 -21
- package/management-api/openapi/components.js +3 -50
- package/management-api/openapi/v2-operations.js +5 -17
- package/management-api/route-registry.js +6 -123
- package/management-api/v2/dependencies.js +15 -40
- package/management-api/v2/dto.js +0 -20
- package/management-api/v2/handlers/authorization.js +0 -1
- package/management-api/v2/handlers/credentials.js +2 -6
- package/management-api/v2/handlers/entities.js +7 -10
- package/management-api/v2/handlers/integrations.js +3 -6
- package/management-api/v2/request.js +1 -32
- package/modules/index.js +0 -8
- package/modules/module-factory.js +0 -12
- package/modules/module.js +2 -53
- package/modules/proxy/proxy-errors.js +1 -12
- package/modules/proxy/proxy-path.js +3 -7
- package/modules/proxy/proxy-policy.js +5 -34
- package/modules/repositories/module-repository-documentdb.js +6 -51
- package/modules/repositories/module-repository-interface.js +2 -19
- package/modules/repositories/module-repository-mongo.js +74 -67
- package/modules/repositories/module-repository-postgres.js +74 -66
- package/modules/repositories/module-repository.js +1 -2
- package/modules/requester/proxy-request.js +1 -2
- package/modules/requester/requester.js +3 -8
- package/modules/use-cases/entity-operations-for-user.js +13 -50
- package/modules/use-cases/entity-types.js +7 -31
- package/modules/use-cases/execute-entity-proxy-request.js +15 -107
- package/modules/use-cases/get-authorization-requirements.js +2 -2
- package/modules/use-cases/get-entities-for-user.js +13 -26
- package/modules/use-cases/get-entity-module-for-user.js +7 -34
- package/modules/use-cases/get-entity-options-by-id.js +4 -10
- package/modules/use-cases/get-entity-options-by-type.js +1 -1
- package/modules/use-cases/get-module-instance-from-type.js +1 -1
- package/modules/use-cases/get-module.js +4 -10
- package/modules/use-cases/multi-step-authorization.js +13 -40
- package/modules/use-cases/process-authorization-callback.js +19 -53
- package/modules/use-cases/refresh-entity-options.js +4 -10
- package/modules/use-cases/test-module-auth.js +4 -10
- package/package.json +5 -6
- package/prisma-mongodb/schema.prisma +0 -65
- package/prisma-postgresql/schema.prisma +0 -61
- package/queues/index.js +0 -25
- package/types/index.d.ts +0 -1
- package/types/modules/index.d.ts +1 -157
- package/user/repositories/user-repository-documentdb.js +2 -27
- package/user/repositories/user-repository-interface.js +0 -18
- package/user/repositories/user-repository-mongo.js +0 -24
- package/user/repositories/user-repository-postgres.js +0 -18
- package/user/use-cases/authenticate-user.js +1 -7
- package/user/use-cases/get-user-from-adopter-jwt.js +124 -95
- package/user/use-cases/get-user-from-x-frigg-headers.js +80 -83
- package/user/user.js +1 -64
- package/database/organization-where.js +0 -18
- package/database/unique-violation.js +0 -25
- package/extensions/bootstrap-runner.js +0 -43
- package/extensions/extension-loader.js +0 -230
- package/extensions/index.js +0 -44
- package/extensions/initialize-app.js +0 -123
- package/extensions/prisma-composer.js +0 -270
- package/extensions/route-mounter.js +0 -103
- package/extensions/schema-composer.js +0 -188
- package/infrastructure/scheduler/netlify-scheduler-adapter.js +0 -216
- package/infrastructure/scheduler/scheduled-job-repository.js +0 -118
- package/integrations/repositories/integration-not-found.js +0 -14
- package/integrations/schedule-config.js +0 -80
- package/integrations/use-cases/dispatch-ongoing-syncs.js +0 -100
- package/integrations/use-cases/resolve-integration-ownership.js +0 -177
- package/modules/entity-scope.js +0 -15
- package/modules/proxy/proxy-bases.js +0 -169
- package/modules/repositories/entity-not-found.js +0 -18
- package/modules/scope-config.js +0 -192
- package/modules/scope-policy.js +0 -112
- package/modules/scope-resolution.js +0 -288
- package/modules/scope-visibility.js +0 -247
- package/modules/tests/doubles/ownership-matching.js +0 -18
- package/modules/use-cases/authorize-entity-for-user.js +0 -61
- package/modules/use-cases/create-entity-from-credential.js +0 -113
- package/modules/use-cases/manage-access.js +0 -21
- package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +0 -26
- package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +0 -25
- package/prisma-postgresql/migrations/20261017000000_add_scheduled_job/migration.sql +0 -28
- package/providers/index.js +0 -23
- package/providers/resolve-provider.js +0 -93
- package/providers/runtime-detection.js +0 -124
- package/queues/providers/index.js +0 -11
- package/queues/providers/netlify-background-provider.js +0 -150
- package/queues/providers/qstash-queue-provider.js +0 -219
- package/queues/providers/sqs-queue-provider.js +0 -70
- package/queues/queue-client-interface.js +0 -55
- package/queues/queue-message-auth.js +0 -193
- package/queues/queue-provider-factory.js +0 -101
- package/queues/queue-provider.js +0 -65
- package/queues/sqs-queue-client.js +0 -81
- package/types/user/index.d.ts +0 -134
- package/user/adopter-jwt-config.js +0 -177
- package/user/adopter-jwt-errors.js +0 -59
- package/user/adopter-token-verifier.js +0 -122
- package/user/app-user-helpers.js +0 -97
- package/user/create-user-authentication.js +0 -64
- package/user/membership.js +0 -51
- package/user/repositories/organization-membership-record.js +0 -19
- package/user/repositories/organization-membership-repository-documentdb.js +0 -106
- package/user/repositories/organization-membership-repository-factory.js +0 -42
- package/user/repositories/organization-membership-repository-interface.js +0 -57
- package/user/repositories/organization-membership-repository-mongo.js +0 -13
- package/user/repositories/organization-membership-repository-postgres.js +0 -29
- package/user/repositories/organization-membership-repository-prisma.js +0 -81
- package/user/tests/doubles/adopter-jwt-fixtures.js +0 -220
- package/user/use-cases/ensure-organization-membership.js +0 -92
- 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 }
|
|
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;
|
|
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
|
|
163
|
+
400: ok('INVALID_PROXY_REQUEST', ref('ProxyError')),
|
|
173
164
|
401: ok('INVALID_CREDENTIALS or unauthenticated', ref('ProxyError')),
|
|
174
|
-
403: ok(
|
|
175
|
-
404: ok('Entity not found
|
|
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
|
-
|
|
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
|
|
323
|
-
*
|
|
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
|
|
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
|
|
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 {
|
|
33
|
-
const {
|
|
34
|
-
|
|
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
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
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
|
|
243
|
-
getEntityType: new GetEntityType({ entityTypeRegistry: registry
|
|
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({
|
package/management-api/v2/dto.js
CHANGED
|
@@ -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,
|
|
@@ -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(
|
|
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
|
|
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(
|
|
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
|
|
28
|
-
res.json({ types: await listEntityTypes.execute(
|
|
24
|
+
async listEntityTypes(_req, res) {
|
|
25
|
+
res.json({ types: await listEntityTypes.execute() });
|
|
29
26
|
},
|
|
30
27
|
|
|
31
|
-
async getEntityType(req, res
|
|
32
|
-
res.json(await getEntityType.execute(req.params.entityType
|
|
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
|
|
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(
|
|
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
|
-
|
|
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,
|