@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.
- package/CLAUDE.md +0 -2
- package/README.md +0 -105
- package/application/commands/integration-commands.js +2 -2
- package/application/commands/user-commands.js +5 -17
- package/credential/use-cases/credential-operations-for-user.js +25 -110
- package/credential/use-cases/reauthorize-credential-for-user.js +8 -15
- package/database/documentdb-utils.js +1 -6
- package/generated/prisma-mongodb/edge.js +4 -15
- package/generated/prisma-mongodb/index-browser.js +1 -12
- package/generated/prisma-mongodb/index.d.ts +86 -2027
- package/generated/prisma-mongodb/index.js +4 -15
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +0 -47
- package/generated/prisma-mongodb/wasm.js +4 -15
- package/generated/prisma-postgresql/edge.js +4 -15
- package/generated/prisma-postgresql/index-browser.js +1 -12
- package/generated/prisma-postgresql/index.d.ts +63 -2162
- package/generated/prisma-postgresql/index.js +4 -15
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +0 -44
- package/generated/prisma-postgresql/wasm.js +4 -15
- package/handlers/app-definition-loader.js +1 -15
- package/index.js +0 -18
- 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 -5
- package/integrations/use-cases/integration-operations-for-user.js +24 -55
- package/management-api/management-api-config.js +0 -3
- package/management-api/openapi/components.js +0 -40
- package/management-api/openapi/v2-operations.js +1 -13
- 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/repositories/module-repository-documentdb.js +4 -48
- package/modules/repositories/module-repository-interface.js +2 -19
- package/modules/repositories/module-repository-mongo.js +73 -65
- package/modules/repositories/module-repository-postgres.js +73 -64
- 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 +0 -15
- package/modules/use-cases/get-entities-for-user.js +5 -16
- package/modules/use-cases/get-entity-module-for-user.js +3 -23
- package/modules/use-cases/get-entity-options-by-id.js +3 -9
- package/modules/use-cases/get-module.js +3 -9
- package/modules/use-cases/multi-step-authorization.js +12 -39
- package/modules/use-cases/process-authorization-callback.js +18 -52
- package/modules/use-cases/refresh-entity-options.js +3 -9
- package/modules/use-cases/test-module-auth.js +3 -9
- package/package.json +5 -6
- package/prisma-mongodb/schema.prisma +0 -47
- package/prisma-postgresql/schema.prisma +0 -44
- package/types/index.d.ts +0 -1
- package/types/modules/index.d.ts +0 -87
- 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/integrations/repositories/integration-not-found.js +0 -14
- package/integrations/use-cases/resolve-integration-ownership.js +0 -177
- package/modules/entity-scope.js +0 -15
- 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 -239
- 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/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
|
@@ -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
|
|
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 {
|
|
48
|
-
* @
|
|
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(
|
|
52
|
-
const
|
|
53
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
|
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 {
|
|
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
|
|
9
|
-
* (missing or
|
|
10
|
-
* integration use cases, and pass the record's owner id on
|
|
11
|
-
*
|
|
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
|
|
18
|
+
constructor({ integrationRepository }) {
|
|
27
19
|
this.integrationRepository = integrationRepository;
|
|
28
|
-
this.scopePolicy = scopePolicy || DEFAULT_SCOPE_POLICY;
|
|
29
20
|
}
|
|
30
21
|
|
|
31
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
|
67
|
-
*
|
|
68
|
-
*
|
|
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,
|
|
47
|
+
constructor({ createIntegration, getEntityModuleForUser, integrationClasses }) {
|
|
73
48
|
this.createIntegration = createIntegration;
|
|
74
|
-
this.
|
|
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
|
|
103
|
-
await this.
|
|
104
|
-
|
|
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
|
-
|
|
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
|
}
|
|
@@ -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 }
|
|
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 {
|
|
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,
|