@friggframework/core 2.0.0--canary.686.3e27a92.0 → 2.0.0--canary.663.8de669c.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 -28
- package/README.md +0 -105
- package/application/commands/integration-commands.js +10 -20
- package/application/commands/scheduler-commands.js +7 -11
- package/application/commands/user-commands.js +5 -17
- package/application/index.js +2 -6
- package/core/Worker.js +7 -31
- package/core/create-handler.js +0 -9
- package/credential/repositories/credential-repository-documentdb.js +0 -34
- package/credential/repositories/credential-repository-interface.js +0 -15
- package/credential/repositories/credential-repository-mongo.js +0 -23
- package/credential/repositories/credential-repository-postgres.js +0 -28
- package/database/config.js +24 -48
- package/database/documentdb-utils.js +1 -6
- package/database/encryption/encryption-config.js +31 -10
- package/database/encryption/encryption-schema-registry.js +12 -93
- package/database/prisma.js +2 -4
- package/generated/prisma-mongodb/edge.js +4 -43
- package/generated/prisma-mongodb/index-browser.js +1 -40
- package/generated/prisma-mongodb/index.d.ts +111 -4739
- package/generated/prisma-mongodb/index.js +4 -43
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +0 -92
- package/generated/prisma-mongodb/wasm.js +4 -43
- package/generated/prisma-postgresql/edge.js +4 -43
- package/generated/prisma-postgresql/index-browser.js +1 -40
- package/generated/prisma-postgresql/index.d.ts +33 -4966
- package/generated/prisma-postgresql/index.js +4 -43
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +0 -87
- package/generated/prisma-postgresql/wasm.js +4 -43
- package/handlers/app-definition-loader.js +19 -121
- package/handlers/app-handler-helpers.js +3 -25
- package/handlers/backend-utils.js +25 -31
- package/handlers/middleware/admin-auth.js +1 -14
- package/handlers/routers/auth.js +10 -46
- package/handlers/routers/health.js +4 -13
- package/handlers/routers/integration-defined-routers.js +92 -144
- package/handlers/routers/integration-webhook-routers.js +64 -102
- package/handlers/workers/integration-defined-workers.js +2 -5
- package/index.js +1 -65
- package/infrastructure/scheduler/index.js +6 -19
- package/infrastructure/scheduler/scheduler-service-factory.js +7 -55
- package/integrations/index.js +0 -1
- package/integrations/integration-router.js +96 -131
- 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/utils/map-integration-dto.js +14 -43
- package/modules/index.js +0 -14
- package/modules/module-factory.js +0 -12
- package/modules/module.js +2 -53
- 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/requester.js +2 -27
- package/modules/use-cases/get-entities-for-user.js +13 -26
- 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/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 -92
- package/prisma-postgresql/schema.prisma +0 -87
- package/queues/index.js +0 -25
- package/types/index.d.ts +0 -2
- 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/credential/use-cases/credential-operations-for-user.js +0 -202
- package/credential/use-cases/reauthorize-credential-for-user.js +0 -73
- 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 -187
- package/extensions/index.js +0 -41
- package/extensions/initialize-app.js +0 -123
- 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/integration-operations-for-user.js +0 -256
- package/integrations/use-cases/resolve-integration-ownership.js +0 -177
- package/management-api/create-management-api-router.js +0 -49
- package/management-api/error-response.js +0 -116
- package/management-api/management-api-config.js +0 -151
- package/management-api/meta-router.js +0 -76
- package/management-api/openapi/build-openapi.js +0 -144
- package/management-api/openapi/components.js +0 -408
- package/management-api/openapi/v1-operations.js +0 -83
- package/management-api/openapi/v2-operations.js +0 -264
- package/management-api/route-registry.js +0 -411
- package/management-api/test-support/http-client.js +0 -39
- package/management-api/use-cases/get-management-api-meta.js +0 -57
- package/management-api/v1-deprecation.js +0 -120
- package/management-api/v2/create-v2-router.js +0 -85
- package/management-api/v2/dependencies.js +0 -281
- package/management-api/v2/dto.js +0 -143
- package/management-api/v2/handlers/authorization.js +0 -94
- package/management-api/v2/handlers/credentials.js +0 -33
- package/management-api/v2/handlers/entities.js +0 -77
- package/management-api/v2/handlers/integrations.js +0 -128
- package/management-api/v2/handlers/proxy.js +0 -34
- package/management-api/v2/request.js +0 -85
- package/management-api/v2/test-support/fake-dependencies.js +0 -103
- package/modules/domain/authorization-session.js +0 -144
- package/modules/entity-scope.js +0 -15
- package/modules/entity-type-registry.js +0 -311
- package/modules/proxy/proxy-errors.js +0 -20
- package/modules/proxy/proxy-path.js +0 -179
- package/modules/proxy/proxy-policy.js +0 -248
- package/modules/repositories/authorization-session-repository-documentdb.js +0 -112
- package/modules/repositories/authorization-session-repository-factory.js +0 -31
- package/modules/repositories/authorization-session-repository-interface.js +0 -47
- package/modules/repositories/authorization-session-repository-prisma.js +0 -87
- package/modules/repositories/entity-not-found.js +0 -18
- package/modules/requester/proxy-request.js +0 -244
- 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/in-memory-authorization-session-repository.js +0 -45
- 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/entity-operations-for-user.js +0 -162
- package/modules/use-cases/entity-types.js +0 -139
- package/modules/use-cases/execute-entity-proxy-request.js +0 -286
- package/modules/use-cases/get-authorization-requirements.js +0 -100
- package/modules/use-cases/get-entity-module-for-user.js +0 -113
- package/modules/use-cases/manage-access.js +0 -21
- package/modules/use-cases/multi-step-authorization.js +0 -329
- package/prisma-postgresql/migrations/20261003000000_add_authorization_session/migration.sql +0 -28
- 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/modules/index.d.ts +0 -197
- 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
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
const Boom = require('@hapi/boom');
|
|
2
|
-
const { Module } = require('../module');
|
|
3
|
-
|
|
4
|
-
const sameId = (a, b) =>
|
|
5
|
-
a !== undefined && a !== null && b !== undefined && b !== null &&
|
|
6
|
-
String(a) === String(b);
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Creates (or finds) the entity for a credential the caller already holds:
|
|
10
|
-
* the v1 `POST /api/entity`. The owner and organization follow the scope of
|
|
11
|
-
* `entityType`, exactly as an authorization does (ADR-056 §3.2, §3.6), so the
|
|
12
|
-
* owner is decided by `AuthorizeEntityForUser.resolveOwnership` and never by
|
|
13
|
-
* `user.getId()`. Whatever that resolution refuses (a missing identity, and
|
|
14
|
-
* an organization-level connection by a non-admin) is refused here too.
|
|
15
|
-
*
|
|
16
|
-
* The credential must belong to that owner: an organization entity is built
|
|
17
|
-
* on the organization's credential, an individual one on the person's own.
|
|
18
|
-
*/
|
|
19
|
-
class CreateEntityFromCredential {
|
|
20
|
-
/**
|
|
21
|
-
* @param {Object} params
|
|
22
|
-
* @param {import('./authorize-entity-for-user').AuthorizeEntityForUser} params.authorizeEntityForUser
|
|
23
|
-
* @param {import('./process-authorization-callback').ProcessAuthorizationCallback} params.processAuthorizationCallback
|
|
24
|
-
* @param {Object} params.credentialRepository
|
|
25
|
-
* @param {Array<Object>} params.moduleDefinitions
|
|
26
|
-
* @param {Function} [params.createModule] - builds a Module; injectable for tests
|
|
27
|
-
*/
|
|
28
|
-
constructor({
|
|
29
|
-
authorizeEntityForUser,
|
|
30
|
-
processAuthorizationCallback,
|
|
31
|
-
credentialRepository,
|
|
32
|
-
moduleDefinitions,
|
|
33
|
-
createModule,
|
|
34
|
-
}) {
|
|
35
|
-
this.authorizeEntityForUser = authorizeEntityForUser;
|
|
36
|
-
this.processAuthorizationCallback = processAuthorizationCallback;
|
|
37
|
-
this.credentialRepository = credentialRepository;
|
|
38
|
-
this.moduleDefinitions = moduleDefinitions || [];
|
|
39
|
-
this.createModule = createModule || ((params) => Module.create(params));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @param {Object} params
|
|
44
|
-
* @param {Object} params.user - authenticated User
|
|
45
|
-
* @param {string} params.entityType
|
|
46
|
-
* @param {string} params.credentialId
|
|
47
|
-
* @returns {Promise<Object>} the persisted entity
|
|
48
|
-
*/
|
|
49
|
-
async execute({ user, entityType, credentialId }) {
|
|
50
|
-
const definition = this.moduleDefinitions.find(
|
|
51
|
-
(def) =>
|
|
52
|
-
def.moduleName === entityType ||
|
|
53
|
-
(typeof def.getName === 'function' && def.getName() === entityType)
|
|
54
|
-
);
|
|
55
|
-
if (!definition) {
|
|
56
|
-
throw Boom.badRequest(`Unknown entity type ${entityType}`, {
|
|
57
|
-
code: 'ENTITY_TYPE_NOT_FOUND',
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const ownership = this.authorizeEntityForUser.resolveOwnership({
|
|
62
|
-
user,
|
|
63
|
-
entityType,
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const credential =
|
|
67
|
-
await this.credentialRepository.findCredentialById(credentialId);
|
|
68
|
-
// A missing credential and someone else's are the same answer, so ids
|
|
69
|
-
// cannot be probed.
|
|
70
|
-
if (!credential || !sameId(credential.userId, ownership.ownerId)) {
|
|
71
|
-
throw Boom.badRequest('Invalid credential ID', {
|
|
72
|
-
code: 'INVALID_CREDENTIAL',
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Built for the owner, with the credential loaded (the repository
|
|
77
|
-
// returns its tokens flat; a Module reads them from `data`), so the
|
|
78
|
-
// API can call the provider and a token refresh during
|
|
79
|
-
// getEntityDetails updates the owner's credential.
|
|
80
|
-
const { id, userId, externalId, authIsValid, ...data } = credential;
|
|
81
|
-
const module = await this.createModule({
|
|
82
|
-
userId: ownership.ownerId,
|
|
83
|
-
definition,
|
|
84
|
-
entity: {
|
|
85
|
-
credential: { id, userId, externalId, authIsValid, data },
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
const entityDetails =
|
|
89
|
-
await definition.requiredAuthMethods.getEntityDetails(
|
|
90
|
-
module.api,
|
|
91
|
-
null,
|
|
92
|
-
null,
|
|
93
|
-
ownership.ownerId
|
|
94
|
-
);
|
|
95
|
-
Object.assign(
|
|
96
|
-
entityDetails.details,
|
|
97
|
-
module.apiParamsFromEntity(module.api)
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
return this.processAuthorizationCallback.findOrCreateEntity(
|
|
101
|
-
entityDetails,
|
|
102
|
-
definition.moduleName,
|
|
103
|
-
credential.id,
|
|
104
|
-
{
|
|
105
|
-
scope: ownership.scope,
|
|
106
|
-
organizationId: ownership.organizationId,
|
|
107
|
-
legacyOrgId: ownership.legacyOrgId,
|
|
108
|
-
}
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
module.exports = { CreateEntityFromCredential };
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
const Boom = require('@hapi/boom');
|
|
2
|
-
const {
|
|
3
|
-
describeRecord,
|
|
4
|
-
visibilityClauses,
|
|
5
|
-
matchesListFilters,
|
|
6
|
-
} = require('../scope-visibility');
|
|
7
|
-
const { DEFAULT_SCOPE_POLICY } = require('../scope-policy');
|
|
8
|
-
const { assertCanManage } = require('./manage-access');
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Management API v2 entity operations. Each one starts from
|
|
12
|
-
* GetEntityModuleForUser, so ownership is checked the same way everywhere
|
|
13
|
-
* (missing or foreign entity = 404).
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* The entities the caller may see in the active organization (ADR-056 §3.4,
|
|
18
|
-
* §3.6): the organization's own, and the caller's individual ones by the
|
|
19
|
-
* visibility rule. Each carries `tenancy` ({scope, owner, organizationId,
|
|
20
|
-
* access}); the stored entity is otherwise untouched.
|
|
21
|
-
*/
|
|
22
|
-
class ListEntitiesForUser {
|
|
23
|
-
constructor({ moduleRepository, scopePolicy }) {
|
|
24
|
-
this.moduleRepository = moduleRepository;
|
|
25
|
-
this.scopePolicy = scopePolicy || DEFAULT_SCOPE_POLICY;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* @param {Object} user - authenticated User
|
|
30
|
-
* @param {{scope?: string, owner?: string}} [filters] - `?scope=` and `?owner=`
|
|
31
|
-
*/
|
|
32
|
-
async execute(user, filters = {}) {
|
|
33
|
-
const ctx = this.scopePolicy.accessContext(user);
|
|
34
|
-
const entities = await this.moduleRepository.findEntitiesByOwnership(
|
|
35
|
-
visibilityClauses(ctx, 'entity')
|
|
36
|
-
);
|
|
37
|
-
const listed = [];
|
|
38
|
-
for (const entity of Array.isArray(entities) ? entities : []) {
|
|
39
|
-
// The query is the filter; this is the same rule applied again,
|
|
40
|
-
// so a repository that returns too much cannot widen the list.
|
|
41
|
-
const tenancy = describeRecord(entity, ctx, 'entity');
|
|
42
|
-
if (tenancy && matchesListFilters(tenancy, filters)) {
|
|
43
|
-
listed.push({ ...entity, tenancy });
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return listed;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** One entity the caller may use, with its `tenancy`. */
|
|
51
|
-
class GetEntityForUser {
|
|
52
|
-
constructor({ getEntityModuleForUser }) {
|
|
53
|
-
this.getEntityModuleForUser = getEntityModuleForUser;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async execute(entityId, user) {
|
|
57
|
-
const entity = await this.getEntityModuleForUser.loadOwnedEntity(
|
|
58
|
-
entityId,
|
|
59
|
-
user
|
|
60
|
-
);
|
|
61
|
-
return {
|
|
62
|
-
...entity,
|
|
63
|
-
tenancy: this.getEntityModuleForUser.describe(entity, user),
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** Runs the module's testAuthRequest against the stored credential. */
|
|
69
|
-
class TestEntityAuthForUser {
|
|
70
|
-
constructor({ getEntityModuleForUser }) {
|
|
71
|
-
this.getEntityModuleForUser = getEntityModuleForUser;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** @returns {Promise<boolean>} */
|
|
75
|
-
async execute(entityId, user) {
|
|
76
|
-
const { module } = await this.getEntityModuleForUser.execute(
|
|
77
|
-
entityId,
|
|
78
|
-
user
|
|
79
|
-
);
|
|
80
|
-
return Boolean(await module.testAuth());
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** The module's entity options (e.g. objects a user can pick). */
|
|
85
|
-
class GetEntityOptionsForUser {
|
|
86
|
-
constructor({ getEntityModuleForUser }) {
|
|
87
|
-
this.getEntityModuleForUser = getEntityModuleForUser;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async execute(entityId, user) {
|
|
91
|
-
const { module } = await this.getEntityModuleForUser.execute(
|
|
92
|
-
entityId,
|
|
93
|
-
user
|
|
94
|
-
);
|
|
95
|
-
return module.getEntityOptions();
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/** Refreshes the module's entity options with caller input, then returns them. */
|
|
100
|
-
class RefreshEntityOptionsForUser {
|
|
101
|
-
constructor({ getEntityModuleForUser }) {
|
|
102
|
-
this.getEntityModuleForUser = getEntityModuleForUser;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async execute(entityId, user, options = {}) {
|
|
106
|
-
const { module } = await this.getEntityModuleForUser.execute(
|
|
107
|
-
entityId,
|
|
108
|
-
user
|
|
109
|
-
);
|
|
110
|
-
return module.refreshEntityOptions(options);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Deletes an entity the caller may manage (the owner; for an organization
|
|
116
|
-
* entity, an organization admin). Refuses with 409 while integrations still
|
|
117
|
-
* use it, so an integration never points at a missing entity. The credential
|
|
118
|
-
* is left alone (others may share it); delete it through /api/v2/credentials.
|
|
119
|
-
*/
|
|
120
|
-
class DeleteEntityForUser {
|
|
121
|
-
constructor({ getEntityModuleForUser, moduleRepository, integrationRepository }) {
|
|
122
|
-
this.getEntityModuleForUser = getEntityModuleForUser;
|
|
123
|
-
this.moduleRepository = moduleRepository;
|
|
124
|
-
this.integrationRepository = integrationRepository;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
async execute(entityId, user) {
|
|
128
|
-
const entity = await this.getEntityModuleForUser.loadOwnedEntity(
|
|
129
|
-
entityId,
|
|
130
|
-
user
|
|
131
|
-
);
|
|
132
|
-
assertCanManage(
|
|
133
|
-
this.getEntityModuleForUser.describe(entity, user)?.access,
|
|
134
|
-
`entity ${entity.id}`
|
|
135
|
-
);
|
|
136
|
-
const integrations =
|
|
137
|
-
(await this.integrationRepository.findIntegrationsByEntityId(
|
|
138
|
-
entity.id
|
|
139
|
-
)) || [];
|
|
140
|
-
if (integrations.length > 0) {
|
|
141
|
-
throw Boom.conflict(
|
|
142
|
-
`Entity ${entity.id} is used by ${integrations.length} integration(s); delete them first`,
|
|
143
|
-
{
|
|
144
|
-
code: 'ENTITY_IN_USE',
|
|
145
|
-
details: {
|
|
146
|
-
integrationIds: integrations.map((i) => String(i.id)),
|
|
147
|
-
},
|
|
148
|
-
}
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
await this.moduleRepository.deleteEntity(entity.id);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
module.exports = {
|
|
156
|
-
ListEntitiesForUser,
|
|
157
|
-
GetEntityForUser,
|
|
158
|
-
TestEntityAuthForUser,
|
|
159
|
-
GetEntityOptionsForUser,
|
|
160
|
-
RefreshEntityOptionsForUser,
|
|
161
|
-
DeleteEntityForUser,
|
|
162
|
-
};
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
const Boom = require('@hapi/boom');
|
|
2
|
-
const { ModuleConstants } = require('../ModuleConstants');
|
|
3
|
-
const { usedByOf } = require('../entity-type-registry');
|
|
4
|
-
|
|
5
|
-
const AUTH_TYPE_BY_REQUESTER = {
|
|
6
|
-
[ModuleConstants.authType.oauth2]: 'oauth2',
|
|
7
|
-
[ModuleConstants.authType.oauth1]: 'oauth1',
|
|
8
|
-
[ModuleConstants.authType.apiKey]: 'api-key',
|
|
9
|
-
[ModuleConstants.authType.basic]: 'basic',
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const callIfFunction = (target, name) =>
|
|
13
|
-
typeof target?.[name] === 'function' ? target[name]() : undefined;
|
|
14
|
-
|
|
15
|
-
/** Number of authorization steps a module declares (1 unless multi-step). */
|
|
16
|
-
function getAuthStepCount(definition) {
|
|
17
|
-
const count = callIfFunction(definition, 'getAuthStepCount');
|
|
18
|
-
return Number.isInteger(count) && count > 0 ? count : 1;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Public description of an API module (an "entity type"). Reads only the
|
|
23
|
-
* definition: display metadata, auth type and step count. Never credentials
|
|
24
|
-
* or env values.
|
|
25
|
-
*/
|
|
26
|
-
function describeEntityType(definition, typeName = definition.moduleName) {
|
|
27
|
-
const stepCount = getAuthStepCount(definition);
|
|
28
|
-
const display = definition.display || {};
|
|
29
|
-
const description = {
|
|
30
|
-
type: typeName,
|
|
31
|
-
name:
|
|
32
|
-
callIfFunction(definition, 'getDisplayName') ??
|
|
33
|
-
display.label ??
|
|
34
|
-
display.name ??
|
|
35
|
-
typeName,
|
|
36
|
-
authType:
|
|
37
|
-
callIfFunction(definition, 'getAuthType') ??
|
|
38
|
-
AUTH_TYPE_BY_REQUESTER[definition.API?.requesterType] ??
|
|
39
|
-
'custom',
|
|
40
|
-
isMultiStep: stepCount > 1,
|
|
41
|
-
stepCount,
|
|
42
|
-
};
|
|
43
|
-
const text = callIfFunction(definition, 'getDescription') ?? display.description;
|
|
44
|
-
if (text) description.description = text;
|
|
45
|
-
const capabilities = callIfFunction(definition, 'getCapabilities');
|
|
46
|
-
if (capabilities !== undefined) description.capabilities = capabilities;
|
|
47
|
-
return description;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function findDefinition(moduleDefinitions, entityType) {
|
|
51
|
-
const definition = moduleDefinitions.find(
|
|
52
|
-
(def) => def.moduleName === entityType
|
|
53
|
-
);
|
|
54
|
-
if (!definition) {
|
|
55
|
-
throw Boom.notFound(`Entity type '${entityType}' not found`, {
|
|
56
|
-
code: 'ENTITY_TYPE_NOT_FOUND',
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
return definition;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* With a scope policy the entry also says which scope a new connection gets
|
|
64
|
-
* and which a request may choose (ADR-056 §3.1), so a UI can offer the choice.
|
|
65
|
-
* `global` is never offered: a tenant request cannot create one.
|
|
66
|
-
*/
|
|
67
|
-
const scopeOf = (scopePolicy, name, user) => {
|
|
68
|
-
if (!scopePolicy) return {};
|
|
69
|
-
const { scope, source, allow } = scopePolicy.resolveScope({
|
|
70
|
-
entityType: name,
|
|
71
|
-
user,
|
|
72
|
-
});
|
|
73
|
-
return {
|
|
74
|
-
scope: {
|
|
75
|
-
default: scope,
|
|
76
|
-
allow: allow.filter((value) => value !== 'global'),
|
|
77
|
-
source,
|
|
78
|
-
},
|
|
79
|
-
};
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const describeEntry = (name, entry, scopePolicy, user) => ({
|
|
83
|
-
// The registry key is the name the API uses, whatever the definition spells.
|
|
84
|
-
...describeEntityType(entry.definition, name),
|
|
85
|
-
usedBy: usedByOf(entry),
|
|
86
|
-
...scopeOf(scopePolicy, name, user),
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
function requireRegistry(entityTypeRegistry) {
|
|
90
|
-
if (!(entityTypeRegistry instanceof Map)) {
|
|
91
|
-
throw new TypeError('entityTypeRegistry is required (see getEntityTypeRegistry)');
|
|
92
|
-
}
|
|
93
|
-
return entityTypeRegistry;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Lists the entity types this app can authorize: registry entries plus every
|
|
98
|
-
* module an integration binds (ADR-059). `usedBy` names the integrations that
|
|
99
|
-
* bind each type; it is empty for a registry-only type.
|
|
100
|
-
*/
|
|
101
|
-
class ListEntityTypes {
|
|
102
|
-
constructor({ entityTypeRegistry, scopePolicy }) {
|
|
103
|
-
this.registry = requireRegistry(entityTypeRegistry);
|
|
104
|
-
this.scopePolicy = scopePolicy;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/** @param {{user?: Object}} [params] - the caller, for the level-6 scope default */
|
|
108
|
-
async execute({ user } = {}) {
|
|
109
|
-
return [...this.registry]
|
|
110
|
-
.map(([name, entry]) => describeEntry(name, entry, this.scopePolicy, user))
|
|
111
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** Describes one entity type, looked up by its registry key; 404 when absent. */
|
|
116
|
-
class GetEntityType {
|
|
117
|
-
constructor({ entityTypeRegistry, scopePolicy }) {
|
|
118
|
-
this.registry = requireRegistry(entityTypeRegistry);
|
|
119
|
-
this.scopePolicy = scopePolicy;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async execute(entityType, { user } = {}) {
|
|
123
|
-
const entry = this.registry.get(entityType);
|
|
124
|
-
if (!entry) {
|
|
125
|
-
throw Boom.notFound(`Entity type '${entityType}' not found`, {
|
|
126
|
-
code: 'ENTITY_TYPE_NOT_FOUND',
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
return describeEntry(entityType, entry, this.scopePolicy, user);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
module.exports = {
|
|
134
|
-
ListEntityTypes,
|
|
135
|
-
GetEntityType,
|
|
136
|
-
describeEntityType,
|
|
137
|
-
getAuthStepCount,
|
|
138
|
-
findDefinition,
|
|
139
|
-
};
|