@friggframework/core 2.0.0--canary.680.6bacd26.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/CHANGELOG.md +0 -11
- package/application/commands/integration-commands.js +8 -18
- package/application/index.js +2 -6
- 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/encryption/README.md +0 -2
- package/database/encryption/encryption-schema-registry.js +12 -41
- package/database/encryption/field-encryption-service.js +5 -69
- package/generated/prisma-mongodb/edge.js +4 -20
- package/generated/prisma-mongodb/index-browser.js +1 -17
- package/generated/prisma-mongodb/index.d.ts +95 -1543
- package/generated/prisma-mongodb/index.js +4 -20
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +0 -27
- package/generated/prisma-mongodb/wasm.js +4 -20
- package/generated/prisma-postgresql/edge.js +4 -20
- package/generated/prisma-postgresql/index-browser.js +1 -17
- package/generated/prisma-postgresql/index.d.ts +84 -1565
- package/generated/prisma-postgresql/index.js +4 -20
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +0 -26
- package/generated/prisma-postgresql/wasm.js +4 -20
- package/handlers/app-definition-loader.js +8 -19
- 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 +3 -11
- package/handlers/routers/health.js +4 -11
- package/handlers/workers/integration-defined-workers.js +2 -5
- package/index.js +2 -4
- package/integrations/index.js +4 -0
- package/integrations/integration-router.js +23 -16
- package/integrations/utils/map-integration-dto.js +15 -1
- package/modules/index.js +0 -6
- package/modules/requester/requester.js +2 -27
- package/package.json +5 -5
- package/prisma-mongodb/schema.prisma +0 -27
- package/prisma-postgresql/schema.prisma +0 -26
- package/types/index.d.ts +0 -1
- package/credential/use-cases/credential-operations-for-user.js +0 -117
- package/credential/use-cases/reauthorize-credential-for-user.js +0 -66
- package/database/encryption/test-support/fake-ciphertext.js +0 -15
- package/integrations/use-cases/integration-operations-for-user.js +0 -225
- 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 -82
- package/management-api/meta-router.js +0 -57
- package/management-api/openapi/build-openapi.js +0 -144
- package/management-api/openapi/components.js +0 -368
- package/management-api/openapi/v1-operations.js +0 -83
- package/management-api/openapi/v2-operations.js +0 -252
- package/management-api/route-registry.js +0 -294
- 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 -256
- package/management-api/v2/dto.js +0 -123
- package/management-api/v2/handlers/authorization.js +0 -93
- package/management-api/v2/handlers/credentials.js +0 -29
- package/management-api/v2/handlers/entities.js +0 -74
- package/management-api/v2/handlers/integrations.js +0 -125
- package/management-api/v2/handlers/proxy.js +0 -34
- package/management-api/v2/request.js +0 -54
- package/management-api/v2/test-support/fake-dependencies.js +0 -103
- package/modules/domain/authorization-session.js +0 -144
- 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/requester/proxy-request.js +0 -244
- package/modules/tests/doubles/in-memory-authorization-session-repository.js +0 -45
- package/modules/use-cases/entity-operations-for-user.js +0 -125
- package/modules/use-cases/entity-types.js +0 -115
- package/modules/use-cases/execute-entity-proxy-request.js +0 -239
- package/modules/use-cases/get-authorization-requirements.js +0 -100
- package/modules/use-cases/get-entity-module-for-user.js +0 -86
- package/modules/use-cases/multi-step-authorization.js +0 -302
- package/prisma-postgresql/migrations/20261003000000_add_authorization_session/migration.sql +0 -28
- package/types/modules/index.d.ts +0 -58
|
@@ -31,6 +31,9 @@ const {
|
|
|
31
31
|
GetIntegrationInstance,
|
|
32
32
|
} = require('./use-cases/get-integration-instance');
|
|
33
33
|
const { UpdateIntegration } = require('./use-cases/update-integration');
|
|
34
|
+
const {
|
|
35
|
+
getModulesDefinitionFromIntegrationClasses,
|
|
36
|
+
} = require('./utils/map-integration-dto');
|
|
34
37
|
const {
|
|
35
38
|
GetModuleInstanceFromType,
|
|
36
39
|
} = require('../modules/use-cases/get-module-instance-from-type');
|
|
@@ -71,13 +74,8 @@ const {
|
|
|
71
74
|
const log = getLogger('frigg.integrations');
|
|
72
75
|
|
|
73
76
|
function createIntegrationRouter() {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const {
|
|
77
|
-
integrations: integrationClasses,
|
|
78
|
-
userConfig,
|
|
79
|
-
moduleDefinitions,
|
|
80
|
-
} = loadAppDefinition();
|
|
77
|
+
const { integrations: integrationClasses, userConfig } =
|
|
78
|
+
loadAppDefinition();
|
|
81
79
|
const moduleRepository = createModuleRepository();
|
|
82
80
|
const integrationRepository = createIntegrationRepository();
|
|
83
81
|
const credentialRepository = createCredentialRepository();
|
|
@@ -110,7 +108,8 @@ function createIntegrationRouter() {
|
|
|
110
108
|
|
|
111
109
|
const moduleFactory = new ModuleFactory({
|
|
112
110
|
moduleRepository,
|
|
113
|
-
moduleDefinitions
|
|
111
|
+
moduleDefinitions:
|
|
112
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
114
113
|
});
|
|
115
114
|
const deleteIntegrationForUser = new DeleteIntegrationForUser({
|
|
116
115
|
integrationRepository,
|
|
@@ -137,7 +136,8 @@ function createIntegrationRouter() {
|
|
|
137
136
|
|
|
138
137
|
const getEntitiesForUser = new GetEntitiesForUser({
|
|
139
138
|
moduleRepository,
|
|
140
|
-
moduleDefinitions
|
|
139
|
+
moduleDefinitions:
|
|
140
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
141
141
|
});
|
|
142
142
|
|
|
143
143
|
const getIntegrationInstance = new GetIntegrationInstance({
|
|
@@ -153,31 +153,37 @@ function createIntegrationRouter() {
|
|
|
153
153
|
});
|
|
154
154
|
|
|
155
155
|
const getModuleInstanceFromType = new GetModuleInstanceFromType({
|
|
156
|
-
moduleDefinitions
|
|
156
|
+
moduleDefinitions:
|
|
157
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
157
158
|
});
|
|
158
159
|
|
|
159
160
|
const getEntityOptionsByType = new GetEntityOptionsByType({
|
|
160
|
-
moduleDefinitions
|
|
161
|
+
moduleDefinitions:
|
|
162
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
161
163
|
});
|
|
162
164
|
|
|
163
165
|
const testModuleAuth = new TestModuleAuth({
|
|
164
166
|
moduleRepository,
|
|
165
|
-
moduleDefinitions
|
|
167
|
+
moduleDefinitions:
|
|
168
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
166
169
|
});
|
|
167
170
|
|
|
168
171
|
const getModule = new GetModule({
|
|
169
172
|
moduleRepository,
|
|
170
|
-
moduleDefinitions
|
|
173
|
+
moduleDefinitions:
|
|
174
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
171
175
|
});
|
|
172
176
|
|
|
173
177
|
const getEntityOptionsById = new GetEntityOptionsById({
|
|
174
178
|
moduleRepository,
|
|
175
|
-
moduleDefinitions
|
|
179
|
+
moduleDefinitions:
|
|
180
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
176
181
|
});
|
|
177
182
|
|
|
178
183
|
const refreshEntityOptions = new RefreshEntityOptions({
|
|
179
184
|
moduleRepository,
|
|
180
|
-
moduleDefinitions
|
|
185
|
+
moduleDefinitions:
|
|
186
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
181
187
|
});
|
|
182
188
|
|
|
183
189
|
const getPossibleIntegrations = new GetPossibleIntegrations({
|
|
@@ -188,7 +194,8 @@ function createIntegrationRouter() {
|
|
|
188
194
|
moduleRepository,
|
|
189
195
|
credentialRepository,
|
|
190
196
|
integrationRepository,
|
|
191
|
-
moduleDefinitions
|
|
197
|
+
moduleDefinitions:
|
|
198
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
192
199
|
});
|
|
193
200
|
|
|
194
201
|
const router = express();
|
|
@@ -20,4 +20,18 @@ function mapIntegrationClassToIntegrationDTO(integration) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
const getModulesDefinitionFromIntegrationClasses = (integrationClasses) => {
|
|
24
|
+
return [
|
|
25
|
+
...new Set(
|
|
26
|
+
integrationClasses
|
|
27
|
+
.map((integration) =>
|
|
28
|
+
Object.values(integration.Definition.modules).map(
|
|
29
|
+
(module) => module.definition
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
.flat()
|
|
33
|
+
),
|
|
34
|
+
];
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
module.exports = { mapIntegrationClassToIntegrationDTO, getModulesDefinitionFromIntegrationClasses };
|
package/modules/index.js
CHANGED
|
@@ -4,10 +4,6 @@ const { OAuth2Requester } = require('./requester/oauth-2');
|
|
|
4
4
|
const { Requester } = require('./requester/requester');
|
|
5
5
|
const { ModuleConstants } = require('./ModuleConstants');
|
|
6
6
|
const { ModuleFactory } = require('./module-factory');
|
|
7
|
-
const {
|
|
8
|
-
getEntityTypeRegistry,
|
|
9
|
-
EntityRegistryError,
|
|
10
|
-
} = require('./entity-type-registry');
|
|
11
7
|
|
|
12
8
|
module.exports = {
|
|
13
9
|
ApiKeyRequester,
|
|
@@ -16,6 +12,4 @@ module.exports = {
|
|
|
16
12
|
Requester,
|
|
17
13
|
ModuleConstants,
|
|
18
14
|
ModuleFactory,
|
|
19
|
-
getEntityTypeRegistry,
|
|
20
|
-
EntityRegistryError,
|
|
21
15
|
};
|
|
@@ -8,7 +8,6 @@ const { getLogger } = require('../../logs');
|
|
|
8
8
|
const { redactUrl } = require('../../logs/redact');
|
|
9
9
|
const { toSanitizedSurrogate } = require('../../logs/serialize');
|
|
10
10
|
const { getLoggerScope } = require('../../logs/context');
|
|
11
|
-
const { proxyRequest } = require('./proxy-request');
|
|
12
11
|
|
|
13
12
|
const DEFAULT_REQUEST_TIMEOUT_MS = 60_000;
|
|
14
13
|
|
|
@@ -238,9 +237,7 @@ class Requester extends Delegate {
|
|
|
238
237
|
// recursion (with its own backoff sleeps) always gets a clean
|
|
239
238
|
// signal. Timer is cleared in the finally block regardless of
|
|
240
239
|
// outcome.
|
|
241
|
-
|
|
242
|
-
// Management API proxy passes what is left of its deadline).
|
|
243
|
-
const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs;
|
|
240
|
+
const timeoutMs = this.requestTimeoutMs;
|
|
244
241
|
const controller = timeoutMs > 0 ? new AbortController() : null;
|
|
245
242
|
const timeoutHandle = controller
|
|
246
243
|
? setTimeout(() => controller.abort(), timeoutMs)
|
|
@@ -278,11 +275,7 @@ class Requester extends Delegate {
|
|
|
278
275
|
// stall at batch scale.
|
|
279
276
|
const isTimeout =
|
|
280
277
|
e?.name === 'AbortError' || e?.type === 'aborted';
|
|
281
|
-
if (
|
|
282
|
-
e?.code === 'ECONNRESET' &&
|
|
283
|
-
!options.noRetry &&
|
|
284
|
-
attempt < this.backOff.length
|
|
285
|
-
) {
|
|
278
|
+
if (e?.code === 'ECONNRESET' && attempt < this.backOff.length) {
|
|
286
279
|
clearRequestTimer();
|
|
287
280
|
const delay = this.backOff[attempt] * 1000;
|
|
288
281
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
@@ -308,7 +301,6 @@ class Requester extends Delegate {
|
|
|
308
301
|
// If the status is retriable and there are back off requests left, retry the request
|
|
309
302
|
if (
|
|
310
303
|
(status === 429 || status >= 500) &&
|
|
311
|
-
!options.noRetry &&
|
|
312
304
|
attempt < this.backOff.length
|
|
313
305
|
) {
|
|
314
306
|
clearRequestTimer();
|
|
@@ -394,12 +386,6 @@ class Requester extends Delegate {
|
|
|
394
386
|
// If the error wasn't retried, throw. FetchError.create reads
|
|
395
387
|
// the response body (response.text()) — timer must still be
|
|
396
388
|
// alive to catch a stalled body stream.
|
|
397
|
-
// The proxy relays upstream errors to its caller instead of
|
|
398
|
-
// throwing; 401 keeps the refresh / invalidate handling above.
|
|
399
|
-
if (status >= 400 && options.returnErrorResponses) {
|
|
400
|
-
return response;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
389
|
if (status >= 400) {
|
|
404
390
|
this._logRequestFailed(encodedUrl, options, status);
|
|
405
391
|
const fetchError = await FetchError.create({
|
|
@@ -542,17 +528,6 @@ class Requester extends Delegate {
|
|
|
542
528
|
return res;
|
|
543
529
|
}
|
|
544
530
|
|
|
545
|
-
/**
|
|
546
|
-
* Generic, path-checked request for the Management API entity proxy
|
|
547
|
-
* (ADR-052 §8). The URL is always the module base URL plus a validated
|
|
548
|
-
* path; see ./proxy-request.js. Module methods are not involved.
|
|
549
|
-
*
|
|
550
|
-
* @param {Object} params - see proxyRequest()
|
|
551
|
-
*/
|
|
552
|
-
async _proxyRequest(params) {
|
|
553
|
-
return proxyRequest(this, params);
|
|
554
|
-
}
|
|
555
|
-
|
|
556
531
|
async _delete(options) {
|
|
557
532
|
const fetchOptions = {
|
|
558
533
|
method: 'DELETE',
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/core",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.
|
|
4
|
+
"version": "2.0.0--canary.663.8de669c.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@aws-sdk/client-apigatewaymanagementapi": "^3.588.0",
|
|
7
7
|
"@aws-sdk/client-kms": "^3.588.0",
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@friggframework/eslint-config": "2.0.0--canary.
|
|
51
|
-
"@friggframework/prettier-config": "2.0.0--canary.
|
|
52
|
-
"@friggframework/test": "2.0.0--canary.
|
|
50
|
+
"@friggframework/eslint-config": "2.0.0--canary.663.8de669c.0",
|
|
51
|
+
"@friggframework/prettier-config": "2.0.0--canary.663.8de669c.0",
|
|
52
|
+
"@friggframework/test": "2.0.0--canary.663.8de669c.0",
|
|
53
53
|
"@prisma/client": "^6.19.3",
|
|
54
54
|
"@types/lodash": "4.17.15",
|
|
55
55
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
@@ -89,5 +89,5 @@
|
|
|
89
89
|
"publishConfig": {
|
|
90
90
|
"access": "public"
|
|
91
91
|
},
|
|
92
|
-
"gitHead": "
|
|
92
|
+
"gitHead": "8de669cbe925abbcd0908be6e792e38e2a14ffd8"
|
|
93
93
|
}
|
|
@@ -453,30 +453,3 @@ model UsageCounter {
|
|
|
453
453
|
@@index([metric, integrationType, window])
|
|
454
454
|
@@map("UsageCounter")
|
|
455
455
|
}
|
|
456
|
-
|
|
457
|
-
// ============================================================================
|
|
458
|
-
// MANAGEMENT API MODELS
|
|
459
|
-
// ============================================================================
|
|
460
|
-
|
|
461
|
-
/// Multi-step authorization state for Management API v2 (/api/v2/authorize,
|
|
462
|
-
/// /api/v2/credentials/:id/reauthorize). Short-lived: expired documents are
|
|
463
|
-
/// removed opportunistically. stepData is encrypted at rest.
|
|
464
|
-
model AuthorizationSession {
|
|
465
|
-
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
466
|
-
sessionId String @unique
|
|
467
|
-
userId String
|
|
468
|
-
entityType String
|
|
469
|
-
credentialId String?
|
|
470
|
-
currentStep Int @default(1)
|
|
471
|
-
maxSteps Int
|
|
472
|
-
stepData Json @default("{}")
|
|
473
|
-
expiresAt DateTime
|
|
474
|
-
completed Boolean @default(false)
|
|
475
|
-
|
|
476
|
-
createdAt DateTime @default(now())
|
|
477
|
-
updatedAt DateTime @updatedAt
|
|
478
|
-
|
|
479
|
-
@@index([userId, entityType])
|
|
480
|
-
@@index([expiresAt])
|
|
481
|
-
@@map("AuthorizationSession")
|
|
482
|
-
}
|
|
@@ -434,29 +434,3 @@ model UsageCounter {
|
|
|
434
434
|
// Covers series() (metric + integrationType + window range).
|
|
435
435
|
@@index([metric, integrationType, window])
|
|
436
436
|
}
|
|
437
|
-
|
|
438
|
-
// ============================================================================
|
|
439
|
-
// MANAGEMENT API MODELS
|
|
440
|
-
// ============================================================================
|
|
441
|
-
|
|
442
|
-
/// Multi-step authorization state for Management API v2 (/api/v2/authorize,
|
|
443
|
-
/// /api/v2/credentials/:id/reauthorize). Short-lived: expired rows are removed
|
|
444
|
-
/// opportunistically. stepData is encrypted at rest.
|
|
445
|
-
model AuthorizationSession {
|
|
446
|
-
id Int @id @default(autoincrement())
|
|
447
|
-
sessionId String @unique
|
|
448
|
-
userId String
|
|
449
|
-
entityType String
|
|
450
|
-
credentialId String?
|
|
451
|
-
currentStep Int @default(1)
|
|
452
|
-
maxSteps Int
|
|
453
|
-
stepData Json @default("{}")
|
|
454
|
-
expiresAt DateTime
|
|
455
|
-
completed Boolean @default(false)
|
|
456
|
-
|
|
457
|
-
createdAt DateTime @default(now())
|
|
458
|
-
updatedAt DateTime @updatedAt
|
|
459
|
-
|
|
460
|
-
@@index([userId, entityType])
|
|
461
|
-
@@index([expiresAt])
|
|
462
|
-
}
|
package/types/index.d.ts
CHANGED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
const Boom = require('@hapi/boom');
|
|
2
|
-
const { userOwns } = require('../../modules/use-cases/get-entity-module-for-user');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Management API v2 credential operations. A credential has no module name
|
|
6
|
-
* of its own; its `type` and the entities that use it come from the
|
|
7
|
-
* caller's entities. A missing or foreign credential is a 404.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const credentialIdOf = (entity) => {
|
|
11
|
-
const ref = entity.credential ?? entity.credentialId;
|
|
12
|
-
if (ref === undefined || ref === null) return null;
|
|
13
|
-
if (typeof ref === 'object') {
|
|
14
|
-
const id = ref.id ?? ref._id;
|
|
15
|
-
return id === undefined || id === null ? null : String(id);
|
|
16
|
-
}
|
|
17
|
-
return String(ref);
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
/** Adds `type` and `entityIds` from the user's entities to each credential. */
|
|
21
|
-
function withEntityUsage(credentials, entities) {
|
|
22
|
-
const usage = new Map();
|
|
23
|
-
for (const entity of entities || []) {
|
|
24
|
-
const credentialId = credentialIdOf(entity);
|
|
25
|
-
if (!credentialId) continue;
|
|
26
|
-
const entry = usage.get(credentialId) || { type: null, entityIds: [] };
|
|
27
|
-
entry.type = entry.type || entity.moduleName || null;
|
|
28
|
-
entry.entityIds.push(String(entity.id));
|
|
29
|
-
usage.set(credentialId, entry);
|
|
30
|
-
}
|
|
31
|
-
return credentials.map((credential) => {
|
|
32
|
-
const entry = usage.get(String(credential.id)) || {
|
|
33
|
-
type: null,
|
|
34
|
-
entityIds: [],
|
|
35
|
-
};
|
|
36
|
-
return { ...credential, type: entry.type, entityIds: entry.entityIds };
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** The caller's credentials with their type and entity usage. */
|
|
41
|
-
class ListCredentialsForUser {
|
|
42
|
-
constructor({ credentialRepository, moduleRepository }) {
|
|
43
|
-
this.credentialRepository = credentialRepository;
|
|
44
|
-
this.moduleRepository = moduleRepository;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async execute(user) {
|
|
48
|
-
const userId = user.getId();
|
|
49
|
-
const [credentials, entities] = await Promise.all([
|
|
50
|
-
this.credentialRepository.findCredentialsByUserId(userId),
|
|
51
|
-
this.moduleRepository.findEntitiesByUserId(userId),
|
|
52
|
-
]);
|
|
53
|
-
return withEntityUsage(credentials || [], entities);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** One credential the caller owns, with its type and entity usage. */
|
|
58
|
-
class GetCredentialForUserV2 {
|
|
59
|
-
constructor({ credentialRepository, moduleRepository }) {
|
|
60
|
-
this.credentialRepository = credentialRepository;
|
|
61
|
-
this.moduleRepository = moduleRepository;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async execute(credentialId, user) {
|
|
65
|
-
let credential = null;
|
|
66
|
-
try {
|
|
67
|
-
credential = await this.credentialRepository.findCredentialById(
|
|
68
|
-
credentialId
|
|
69
|
-
);
|
|
70
|
-
} catch (error) {
|
|
71
|
-
if (!/Invalid ID|ObjectId|Malformed/i.test(error?.message || '')) {
|
|
72
|
-
throw error;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (!credential || !userOwns(user, credential.userId)) {
|
|
76
|
-
throw Boom.notFound(`Credential ${credentialId} not found`, {
|
|
77
|
-
code: 'CREDENTIAL_NOT_FOUND',
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
const entities = await this.moduleRepository.findEntitiesByUserId(
|
|
81
|
-
credential.userId
|
|
82
|
-
);
|
|
83
|
-
return withEntityUsage([credential], entities)[0];
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Deletes a credential the caller owns. The entities that used it stay, with
|
|
89
|
-
* their credential unset, so they show as needing re-authorization rather
|
|
90
|
-
* than disappearing from integrations.
|
|
91
|
-
*/
|
|
92
|
-
class DeleteCredentialForUser {
|
|
93
|
-
constructor({ credentialRepository, moduleRepository, getCredentialForUser }) {
|
|
94
|
-
this.credentialRepository = credentialRepository;
|
|
95
|
-
this.moduleRepository = moduleRepository;
|
|
96
|
-
this.getCredentialForUser = getCredentialForUser;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async execute(credentialId, user) {
|
|
100
|
-
const credential = await this.getCredentialForUser.execute(
|
|
101
|
-
credentialId,
|
|
102
|
-
user
|
|
103
|
-
);
|
|
104
|
-
for (const entityId of credential.entityIds) {
|
|
105
|
-
await this.moduleRepository.unsetCredential(entityId);
|
|
106
|
-
}
|
|
107
|
-
await this.credentialRepository.deleteCredentialById(credential.id);
|
|
108
|
-
return { id: String(credential.id), entityIds: credential.entityIds };
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
module.exports = {
|
|
113
|
-
ListCredentialsForUser,
|
|
114
|
-
GetCredentialForUserV2,
|
|
115
|
-
DeleteCredentialForUser,
|
|
116
|
-
withEntityUsage,
|
|
117
|
-
};
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
const Boom = require('@hapi/boom');
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Re-authorization of a credential the caller owns (Management API v2). It
|
|
5
|
-
* runs the same flow as /api/v2/authorize for the credential's entity type,
|
|
6
|
-
* with the session bound to the credential so it cannot be reused for
|
|
7
|
-
* anything else.
|
|
8
|
-
*/
|
|
9
|
-
async function credentialEntityType(getCredentialForUser, credentialId, user) {
|
|
10
|
-
const credential = await getCredentialForUser.execute(credentialId, user);
|
|
11
|
-
if (!credential.type) {
|
|
12
|
-
throw Boom.conflict(
|
|
13
|
-
`Credential ${credentialId} is not used by any entity, so its type is unknown; authorize a new connection instead`,
|
|
14
|
-
{ code: 'CREDENTIAL_TYPE_UNKNOWN' }
|
|
15
|
-
);
|
|
16
|
-
}
|
|
17
|
-
return { entityType: credential.type, credentialId: String(credential.id) };
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** GET /api/v2/credentials/:credentialId/reauthorize */
|
|
21
|
-
class GetCredentialReauthorization {
|
|
22
|
-
constructor({ getCredentialForUser, getAuthorizationStep }) {
|
|
23
|
-
this.getCredentialForUser = getCredentialForUser;
|
|
24
|
-
this.getAuthorizationStep = getAuthorizationStep;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async execute(credentialId, user, { step = 1, sessionId, state } = {}) {
|
|
28
|
-
const binding = await credentialEntityType(
|
|
29
|
-
this.getCredentialForUser,
|
|
30
|
-
credentialId,
|
|
31
|
-
user
|
|
32
|
-
);
|
|
33
|
-
return this.getAuthorizationStep.execute({
|
|
34
|
-
user,
|
|
35
|
-
...binding,
|
|
36
|
-
step,
|
|
37
|
-
sessionId,
|
|
38
|
-
state,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** POST /api/v2/credentials/:credentialId/reauthorize */
|
|
44
|
-
class ReauthorizeCredentialForUser {
|
|
45
|
-
constructor({ getCredentialForUser, submitAuthorizationStep }) {
|
|
46
|
-
this.getCredentialForUser = getCredentialForUser;
|
|
47
|
-
this.submitAuthorizationStep = submitAuthorizationStep;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async execute(credentialId, user, { data, step = 1, sessionId } = {}) {
|
|
51
|
-
const binding = await credentialEntityType(
|
|
52
|
-
this.getCredentialForUser,
|
|
53
|
-
credentialId,
|
|
54
|
-
user
|
|
55
|
-
);
|
|
56
|
-
return this.submitAuthorizationStep.execute({
|
|
57
|
-
user,
|
|
58
|
-
...binding,
|
|
59
|
-
data,
|
|
60
|
-
step,
|
|
61
|
-
sessionId,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
module.exports = { GetCredentialReauthorization, ReauthorizeCredentialForUser };
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test double for Cryptor output. Same shape as the real thing
|
|
3
|
-
* (`keyId:iv:ciphertext:encryptedKey`: base64, 16-byte hex, hex, base64) so
|
|
4
|
-
* FieldEncryptionService._isEncrypted accepts it, with the plaintext
|
|
5
|
-
* recoverable for the fake decrypt.
|
|
6
|
-
*/
|
|
7
|
-
const fakeCiphertext = (plain) =>
|
|
8
|
-
`a2V5SWQ=:${'0'.repeat(32)}:${Buffer.from(String(plain)).toString(
|
|
9
|
-
'hex'
|
|
10
|
-
)}:ZW5jS2V5`;
|
|
11
|
-
|
|
12
|
-
const fakePlaintext = (ciphertext) =>
|
|
13
|
-
Buffer.from(String(ciphertext).split(':')[2], 'hex').toString();
|
|
14
|
-
|
|
15
|
-
module.exports = { fakeCiphertext, fakePlaintext };
|