@friggframework/core 2.0.0--canary.684.0da024b.0 → 2.0.0--canary.679.a2bcc2f.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 (104) hide show
  1. package/CLAUDE.md +0 -2
  2. package/README.md +0 -105
  3. package/application/commands/integration-commands.js +2 -2
  4. package/application/commands/user-commands.js +5 -17
  5. package/credential/use-cases/credential-operations-for-user.js +25 -110
  6. package/credential/use-cases/reauthorize-credential-for-user.js +8 -15
  7. package/database/documentdb-utils.js +1 -6
  8. package/generated/prisma-mongodb/edge.js +4 -15
  9. package/generated/prisma-mongodb/index-browser.js +1 -12
  10. package/generated/prisma-mongodb/index.d.ts +86 -2027
  11. package/generated/prisma-mongodb/index.js +4 -15
  12. package/generated/prisma-mongodb/package.json +1 -1
  13. package/generated/prisma-mongodb/schema.prisma +0 -47
  14. package/generated/prisma-mongodb/wasm.js +4 -15
  15. package/generated/prisma-postgresql/edge.js +4 -15
  16. package/generated/prisma-postgresql/index-browser.js +1 -12
  17. package/generated/prisma-postgresql/index.d.ts +63 -2162
  18. package/generated/prisma-postgresql/index.js +4 -15
  19. package/generated/prisma-postgresql/package.json +1 -1
  20. package/generated/prisma-postgresql/schema.prisma +0 -44
  21. package/generated/prisma-postgresql/wasm.js +4 -15
  22. package/handlers/app-definition-loader.js +1 -15
  23. package/index.js +0 -18
  24. package/integrations/integration-router.js +73 -115
  25. package/integrations/repositories/integration-repository-documentdb.js +3 -35
  26. package/integrations/repositories/integration-repository-interface.js +1 -17
  27. package/integrations/repositories/integration-repository-mongo.js +76 -59
  28. package/integrations/repositories/integration-repository-postgres.js +76 -62
  29. package/integrations/tests/doubles/test-integration-repository.js +1 -12
  30. package/integrations/use-cases/create-integration.js +20 -55
  31. package/integrations/use-cases/get-integrations-for-user.js +40 -80
  32. package/integrations/use-cases/index.js +1 -5
  33. package/integrations/use-cases/integration-operations-for-user.js +24 -55
  34. package/management-api/management-api-config.js +0 -3
  35. package/management-api/openapi/components.js +0 -40
  36. package/management-api/openapi/v2-operations.js +1 -13
  37. package/management-api/v2/dependencies.js +15 -40
  38. package/management-api/v2/dto.js +0 -20
  39. package/management-api/v2/handlers/authorization.js +0 -1
  40. package/management-api/v2/handlers/credentials.js +2 -6
  41. package/management-api/v2/handlers/entities.js +7 -10
  42. package/management-api/v2/handlers/integrations.js +3 -6
  43. package/management-api/v2/request.js +1 -32
  44. package/modules/index.js +0 -8
  45. package/modules/repositories/module-repository-documentdb.js +4 -48
  46. package/modules/repositories/module-repository-interface.js +2 -19
  47. package/modules/repositories/module-repository-mongo.js +73 -65
  48. package/modules/repositories/module-repository-postgres.js +73 -64
  49. package/modules/use-cases/entity-operations-for-user.js +13 -50
  50. package/modules/use-cases/entity-types.js +7 -31
  51. package/modules/use-cases/execute-entity-proxy-request.js +0 -15
  52. package/modules/use-cases/get-entities-for-user.js +5 -16
  53. package/modules/use-cases/get-entity-module-for-user.js +3 -23
  54. package/modules/use-cases/get-entity-options-by-id.js +3 -9
  55. package/modules/use-cases/get-module.js +3 -9
  56. package/modules/use-cases/multi-step-authorization.js +12 -39
  57. package/modules/use-cases/process-authorization-callback.js +18 -52
  58. package/modules/use-cases/refresh-entity-options.js +3 -9
  59. package/modules/use-cases/test-module-auth.js +3 -9
  60. package/package.json +5 -6
  61. package/prisma-mongodb/schema.prisma +0 -47
  62. package/prisma-postgresql/schema.prisma +0 -44
  63. package/types/index.d.ts +0 -1
  64. package/types/modules/index.d.ts +0 -87
  65. package/user/repositories/user-repository-documentdb.js +2 -27
  66. package/user/repositories/user-repository-interface.js +0 -18
  67. package/user/repositories/user-repository-mongo.js +0 -24
  68. package/user/repositories/user-repository-postgres.js +0 -18
  69. package/user/use-cases/authenticate-user.js +1 -7
  70. package/user/use-cases/get-user-from-adopter-jwt.js +124 -95
  71. package/user/use-cases/get-user-from-x-frigg-headers.js +80 -83
  72. package/user/user.js +1 -64
  73. package/database/organization-where.js +0 -18
  74. package/database/unique-violation.js +0 -25
  75. package/integrations/repositories/integration-not-found.js +0 -14
  76. package/integrations/use-cases/resolve-integration-ownership.js +0 -177
  77. package/modules/entity-scope.js +0 -15
  78. package/modules/scope-config.js +0 -192
  79. package/modules/scope-policy.js +0 -112
  80. package/modules/scope-resolution.js +0 -288
  81. package/modules/scope-visibility.js +0 -239
  82. package/modules/tests/doubles/ownership-matching.js +0 -18
  83. package/modules/use-cases/authorize-entity-for-user.js +0 -61
  84. package/modules/use-cases/create-entity-from-credential.js +0 -113
  85. package/modules/use-cases/manage-access.js +0 -21
  86. package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +0 -26
  87. package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +0 -25
  88. package/types/user/index.d.ts +0 -134
  89. package/user/adopter-jwt-config.js +0 -177
  90. package/user/adopter-jwt-errors.js +0 -59
  91. package/user/adopter-token-verifier.js +0 -122
  92. package/user/app-user-helpers.js +0 -97
  93. package/user/create-user-authentication.js +0 -64
  94. package/user/membership.js +0 -51
  95. package/user/repositories/organization-membership-record.js +0 -19
  96. package/user/repositories/organization-membership-repository-documentdb.js +0 -106
  97. package/user/repositories/organization-membership-repository-factory.js +0 -42
  98. package/user/repositories/organization-membership-repository-interface.js +0 -57
  99. package/user/repositories/organization-membership-repository-mongo.js +0 -13
  100. package/user/repositories/organization-membership-repository-postgres.js +0 -29
  101. package/user/repositories/organization-membership-repository-prisma.js +0 -81
  102. package/user/tests/doubles/adopter-jwt-fixtures.js +0 -220
  103. package/user/use-cases/ensure-organization-membership.js +0 -92
  104. package/user/use-cases/find-individual-users-by-organization.js +0 -54
@@ -2,17 +2,9 @@
2
2
  const {
3
3
  mapIntegrationClassToIntegrationDTO,
4
4
  } = require('../utils/map-integration-dto');
5
- const {
6
- describeRecord,
7
- visibilityClauses,
8
- matchesListFilters,
9
- } = require('../../modules/scope-visibility');
10
- const { DEFAULT_SCOPE_POLICY } = require('../../modules/scope-policy');
11
5
 
12
6
  /**
13
- * Use case for retrieving the integrations a caller may see: the ones they own
14
- * or their active organization owns, created in that organization (ADR-056
15
- * §3.6), each with `tenancy` ({scope, owner, organizationId, access}).
7
+ * Use case for retrieving all integrations for a specific user.
16
8
  * @class GetIntegrationsForUser
17
9
  */
18
10
  class GetIntegrationsForUser {
@@ -23,16 +15,13 @@ class GetIntegrationsForUser {
23
15
  * @param {Array<import('../integration').Integration>} params.integrationClasses - Array of available integration classes.
24
16
  * @param {import('../../modules/module-factory').ModuleFactory} params.moduleFactory - Service for module instantiation and management.
25
17
  * @param {import('../../modules/repositories/module-repository-interface').ModuleRepositoryInterface} params.moduleRepository - Repository for module and entity data operations.
26
- * @param {Object} [params.scopePolicy] - from createScopePolicy
27
18
  */
28
19
  constructor({
29
20
  integrationRepository,
30
21
  integrationClasses,
31
22
  moduleFactory,
32
23
  moduleRepository,
33
- scopePolicy,
34
24
  }) {
35
- this.scopePolicy = scopePolicy || DEFAULT_SCOPE_POLICY;
36
25
  /**
37
26
  * @type {import('../repositories/integration-repository-interface').IntegrationRepositoryInterface}
38
27
  */
@@ -43,86 +32,57 @@ class GetIntegrationsForUser {
43
32
  }
44
33
 
45
34
  /**
35
+ * Executes the retrieval of all integrations for a user.
46
36
  * @async
47
- * @param {Object} user - authenticated User
48
- * @param {{scope?: string, owner?: string}} [filters] - `?scope=` and `?owner=`
49
- * @returns {Promise<Object[]>} Array of integration DTOs.
37
+ * @param {string} userId - ID of the user whose integrations to retrieve.
38
+ * @returns {Promise<Object[]>} Array of integration DTOs for the specified user.
50
39
  */
51
- async execute(user, filters = {}) {
52
- const ctx = this.scopePolicy.accessContext(user);
53
- const records = await this.integrationRepository.findIntegrationsByOwnership(
54
- visibilityClauses(ctx, 'integration')
55
- );
40
+ async execute(userId) {
41
+ const integrationRecords =
42
+ await this.integrationRepository.findIntegrationsByUserId(userId);
56
43
 
57
44
  const integrations = [];
58
- for (const record of records) {
59
- // The query is the filter; the same rule again means a repository
60
- // that returns too much cannot widen the list.
61
- const tenancy = describeRecord(record, ctx, 'integration');
62
- if (!tenancy || !matchesListFilters(tenancy, filters)) continue;
63
- integrations.push({ ...(await this.toDto(record)), tenancy });
64
- }
65
- return integrations;
66
- }
67
45
 
68
- /** Hydrates one stored integration record into its DTO. */
69
- async toDto(integrationRecord) {
70
- const entities = await this.moduleRepository.findEntitiesByIds(
71
- integrationRecord.entitiesIds
72
- );
73
-
74
- const integrationClass = this.integrationClasses.find(
75
- (integrationClass) =>
76
- integrationClass.Definition.name ===
77
- integrationRecord.config.type
78
- );
46
+ for (const integrationRecord of integrationRecords) {
47
+ const entities = await this.moduleRepository.findEntitiesByIds(
48
+ integrationRecord.entitiesIds
49
+ );
79
50
 
80
- const modules = [];
81
- for (const entity of entities) {
82
- const moduleInstance = await this.moduleFactory.getModuleInstance(
83
- entity.id,
84
- integrationRecord.userId
51
+ const integrationClass = this.integrationClasses.find(
52
+ (integrationClass) =>
53
+ integrationClass.Definition.name ===
54
+ integrationRecord.config.type
85
55
  );
86
- modules.push(moduleInstance);
87
- }
88
56
 
89
- return mapIntegrationClassToIntegrationDTO({
90
- id: integrationRecord.id,
91
- userId: integrationRecord.userId,
92
- entities: entities,
93
- config: integrationRecord.config,
94
- status: integrationRecord.status,
95
- version: integrationRecord.version,
96
- messages: integrationRecord.messages || { errors: [], warnings: [] },
97
- modules,
98
- options: integrationClass.getOptionDetails(),
99
- });
100
- }
101
- }
57
+ const modules = [];
58
+ for (const entity of entities) {
59
+ const moduleInstance =
60
+ await this.moduleFactory.getModuleInstance(
61
+ entity.id,
62
+ integrationRecord.userId
63
+ );
64
+ modules.push(moduleInstance);
65
+ }
102
66
 
103
- /**
104
- * Every integration one user id owns, with no organization rules: the lookup
105
- * behind the app-level `friggCommands.findIntegrationsByUserId`, which has a
106
- * user id and no request.
107
- */
108
- class GetIntegrationsByUserId {
109
- /** @param {ConstructorParameters<typeof GetIntegrationsForUser>[0]} params */
110
- constructor(params) {
111
- this.integrationRepository = params.integrationRepository;
112
- this.hydrator = new GetIntegrationsForUser(params);
113
- }
67
+ const integrationData = {
68
+ id: integrationRecord.id,
69
+ userId: integrationRecord.userId,
70
+ entities: entities,
71
+ config: integrationRecord.config,
72
+ status: integrationRecord.status,
73
+ version: integrationRecord.version,
74
+ messages: integrationRecord.messages || { errors: [], warnings: [] },
75
+ modules,
76
+ options: integrationClass.getOptionDetails(),
77
+ };
114
78
 
115
- /** @param {string} userId */
116
- async execute(userId) {
117
- const records = await this.integrationRepository.findIntegrationsByUserId(
118
- userId
119
- );
120
- const integrations = [];
121
- for (const record of records) {
122
- integrations.push(await this.hydrator.toDto(record));
79
+ integrations.push(
80
+ mapIntegrationClassToIntegrationDTO(integrationData)
81
+ );
123
82
  }
83
+
124
84
  return integrations;
125
85
  }
126
86
  }
127
87
 
128
- module.exports = { GetIntegrationsForUser, GetIntegrationsByUserId };
88
+ module.exports = { GetIntegrationsForUser };
@@ -1,7 +1,4 @@
1
- const {
2
- GetIntegrationsForUser,
3
- GetIntegrationsByUserId,
4
- } = require('./get-integrations-for-user');
1
+ const { GetIntegrationsForUser } = require('./get-integrations-for-user');
5
2
  const { DeleteIntegrationForUser } = require('./delete-integration-for-user');
6
3
  const { CreateIntegration } = require('./create-integration');
7
4
  const { GetIntegration } = require('./get-integration');
@@ -12,7 +9,6 @@ const { GetProcess } = require('./get-process');
12
9
 
13
10
  module.exports = {
14
11
  GetIntegrationsForUser,
15
- GetIntegrationsByUserId,
16
12
  DeleteIntegrationForUser,
17
13
  CreateIntegration,
18
14
  GetIntegration,
@@ -1,15 +1,11 @@
1
1
  const Boom = require('@hapi/boom');
2
- const { describeRecord } = require('../../modules/scope-visibility');
3
- const { DEFAULT_SCOPE_POLICY } = require('../../modules/scope-policy');
4
- const { assertCanManage } = require('../../modules/use-cases/manage-access');
5
- const { isIntegrationNotFound } = require('../repositories/integration-not-found');
2
+ const { userOwns } = require('../../modules/use-cases/get-entity-module-for-user');
6
3
 
7
4
  /**
8
- * Management API v2 integration operations. They put an access check
9
- * (missing or unavailable integration = 404) in front of the existing
10
- * integration use cases, and pass the record's owner id on. Access follows
11
- * the owner and the organization the integration was created in (ADR-056
12
- * §3.6), not the owner alone.
5
+ * Management API v2 integration operations. They put an ownership check
6
+ * (missing or foreign integration = 404) in front of the existing
7
+ * integration use cases, and pass the record's owner id on, so the
8
+ * organisation user model works the same as for entities.
13
9
  */
14
10
 
15
11
  const notFound = (integrationId) =>
@@ -17,61 +13,40 @@ const notFound = (integrationId) =>
17
13
  code: 'INTEGRATION_NOT_FOUND',
18
14
  });
19
15
 
20
- /**
21
- * The integration record, or 404 when it is missing or the caller may not
22
- * use it: not theirs or their active organization's, or created in another
23
- * organization.
24
- */
16
+ /** The integration record, or 404 when it is missing or not the caller's. */
25
17
  class GetOwnedIntegrationRecord {
26
- constructor({ integrationRepository, scopePolicy }) {
18
+ constructor({ integrationRepository }) {
27
19
  this.integrationRepository = integrationRepository;
28
- this.scopePolicy = scopePolicy || DEFAULT_SCOPE_POLICY;
29
20
  }
30
21
 
31
- /** `{ scope, owner, organizationId, access }` of a record to this caller, or null. */
32
- describe(record, user) {
33
- return describeRecord(record, this.scopePolicy.accessContext(user), 'integration');
34
- }
35
-
36
- /**
37
- * @param {string} integrationId
38
- * @param {Object} user - authenticated User
39
- * @param {{manage?: boolean}} [options] - `manage: true` for changing or
40
- * deleting: a person may use an organization's integration but only an
41
- * admin may change it (403 ORG_ADMIN_REQUIRED)
42
- */
43
- async execute(integrationId, user, { manage = false } = {}) {
22
+ async execute(integrationId, user) {
44
23
  let record = null;
45
24
  try {
46
25
  record = await this.integrationRepository.findIntegrationById(
47
26
  integrationId
48
27
  );
49
28
  } catch (error) {
50
- if (
51
- /Invalid ID|ObjectId|Malformed/i.test(error?.message || '') ||
52
- isIntegrationNotFound(error)
53
- ) {
29
+ if (/Invalid ID|ObjectId|Malformed/i.test(error?.message || '')) {
54
30
  throw notFound(integrationId);
55
31
  }
56
32
  throw error;
57
33
  }
58
- const tenancy = record && this.describe(record, user);
59
- if (!tenancy) throw notFound(integrationId);
60
- if (manage) assertCanManage(tenancy.access, `integration ${integrationId}`);
34
+ if (!record || !userOwns(user, record.userId)) {
35
+ throw notFound(integrationId);
36
+ }
61
37
  return record;
62
38
  }
63
39
  }
64
40
 
65
41
  /**
66
- * Creates (or reuses) an integration between entities the caller may use.
67
- * The entities' access, the binding scopes and the integration type are
68
- * checked before anything is written, and the integration is owned and
69
- * paired with an organization by `resolveIntegrationOwnership` (§3, §3.6).
42
+ * Creates (or reuses) an integration between entities the caller owns.
43
+ * Unlike v1, the entities' ownership and the integration type are checked
44
+ * before anything is written.
70
45
  */
71
46
  class CreateIntegrationForUser {
72
- constructor({ createIntegration, resolveIntegrationOwnership, integrationClasses }) {
47
+ constructor({ createIntegration, getEntityModuleForUser, integrationClasses }) {
73
48
  this.createIntegration = createIntegration;
74
- this.resolveIntegrationOwnership = resolveIntegrationOwnership;
49
+ this.getEntityModuleForUser = getEntityModuleForUser;
75
50
  this.integrationClasses = integrationClasses || [];
76
51
  }
77
52
 
@@ -99,17 +74,13 @@ class CreateIntegrationForUser {
99
74
  details: { field: 'entities' },
100
75
  });
101
76
  }
102
- const { ownerId, organizationId, adoptUnscoped } =
103
- await this.resolveIntegrationOwnership.execute({
104
- entityIds: entities,
105
- config,
106
- user,
107
- });
77
+ for (const entityId of entities) {
78
+ await this.getEntityModuleForUser.loadOwnedEntity(String(entityId), user);
79
+ }
108
80
  return this.createIntegration.execute(
109
81
  entities.map(String),
110
- ownerId,
111
- config,
112
- { organizationId, adoptUnscoped }
82
+ user.getId(),
83
+ config
113
84
  );
114
85
  }
115
86
  }
@@ -146,8 +117,7 @@ class UpdateIntegrationForUser {
146
117
  }
147
118
  const record = await this.getOwnedIntegrationRecord.execute(
148
119
  integrationId,
149
- user,
150
- { manage: true }
120
+ user
151
121
  );
152
122
  return this.updateIntegration.execute(record.id, record.userId, config);
153
123
  }
@@ -163,8 +133,7 @@ class RemoveIntegrationForUser {
163
133
  async execute(integrationId, user) {
164
134
  const record = await this.getOwnedIntegrationRecord.execute(
165
135
  integrationId,
166
- user,
167
- { manage: true }
136
+ user
168
137
  );
169
138
  await this.deleteIntegrationForUser.execute(record.id, record.userId);
170
139
  }
@@ -16,9 +16,6 @@
16
16
  * },
17
17
  * }
18
18
  * ```
19
- *
20
- * TODO(F6): `proxy.scopes` (ADR-056 §3.5) is not read yet; see
21
- * ExecuteEntityProxyRequest.
22
19
  */
23
20
 
24
21
  // ADR-052 §7. API Gateway HTTP APIs time out at 30s and Lambda responses cap
@@ -8,27 +8,6 @@ const ref = (name) => ({ $ref: `#/components/schemas/${name}` });
8
8
 
9
9
  const nullable = (schema) => ({ ...schema, nullable: true });
10
10
 
11
- // Whose a record is and what the caller may do with it (ADR-056 §3.4). Added to
12
- // entities, credentials and integrations by the list and get use cases.
13
- const tenancyProperties = {
14
- scope: { type: 'string', enum: ['individual', 'organization'], description: 'Whose it is. `global` records are not shown to tenants.' },
15
- owner: {
16
- type: 'object',
17
- properties: { type: { type: 'string', enum: ['individual', 'organization'] }, id: { type: 'string' } },
18
- description: "The user that owns it: the person, or the organization's user",
19
- },
20
- organizationId: nullable({
21
- type: 'string',
22
- description:
23
- 'The organization it belongs to. Null for an individual entity connected outside an organization, or under ownership.individualAcross "all".',
24
- }),
25
- access: {
26
- type: 'string',
27
- enum: ['manage', 'use', 'view'],
28
- description: 'manage (create, reauthorize, delete), use (bind it, proxy through it) or view (metadata only)',
29
- },
30
- };
31
-
32
11
  const errorBody = {
33
12
  type: 'object',
34
13
  required: ['error'],
@@ -90,7 +69,6 @@ const SCHEMAS = {
90
69
  config: { type: 'object', additionalProperties: true },
91
70
  version: nullable({ type: 'string' }),
92
71
  messages: { type: 'object', additionalProperties: true },
93
- ...tenancyProperties,
94
72
  },
95
73
  },
96
74
  ListIntegrationsResponse: {
@@ -151,7 +129,6 @@ const SCHEMAS = {
151
129
  credentialId: nullable({ type: 'string' }),
152
130
  userId: nullable({ type: 'string' }),
153
131
  authIsValid: nullable({ type: 'boolean' }),
154
- ...tenancyProperties,
155
132
  },
156
133
  },
157
134
  ListEntitiesResponse: {
@@ -176,16 +153,6 @@ const SCHEMAS = {
176
153
  isMultiStep: { type: 'boolean' },
177
154
  stepCount: { type: 'integer', minimum: 1 },
178
155
  capabilities: {},
179
- scope: {
180
- type: 'object',
181
- description:
182
- 'The scope a new connection of this type gets and the scopes a request may choose (ADR-056 §3.1). `global` is never offered.',
183
- properties: {
184
- default: { type: 'string', enum: ['individual', 'organization', 'global'] },
185
- allow: { type: 'array', items: { type: 'string', enum: ['individual', 'organization'] } },
186
- source: { type: 'string', enum: ['binding', 'integration', 'entityType', 'app', 'default'] },
187
- },
188
- },
189
156
  },
190
157
  },
191
158
  ListEntityTypesResponse: {
@@ -206,7 +173,6 @@ const SCHEMAS = {
206
173
  authIsValid: nullable({ type: 'boolean' }),
207
174
  entityIds: { type: 'array', items: { type: 'string' } },
208
175
  entityCount: { type: 'integer', minimum: 0 },
209
- ...tenancyProperties,
210
176
  createdAt: nullable({ type: 'string', format: 'date-time' }),
211
177
  updatedAt: nullable({ type: 'string', format: 'date-time' }),
212
178
  data: {
@@ -231,12 +197,6 @@ const SCHEMAS = {
231
197
  data: { type: 'object', additionalProperties: true, description: 'OAuth callback params or step form data' },
232
198
  step: { type: 'integer', minimum: 1, default: 1 },
233
199
  sessionId: { type: 'string', description: 'Required for step 2 and later' },
234
- scope: {
235
- type: 'string',
236
- enum: ['individual', 'organization'],
237
- description:
238
- "The scope to connect at, from the entity type's allow list (ADR-056 §3.1). Omit to take the type's default. `global` is refused (403).",
239
- },
240
200
  },
241
201
  },
242
202
  ReauthorizeRequest: {
@@ -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: {
@@ -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: {
@@ -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
  },