@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
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
const { Router } = require('express');
|
|
2
|
-
const Boom = require('@hapi/boom');
|
|
3
|
-
const { getLogger } = require('../logs');
|
|
4
|
-
const { getTelemetry } = require('../telemetry/telemetry-runtime');
|
|
5
|
-
const {
|
|
6
|
-
MANAGEMENT_API_VERSIONS,
|
|
7
|
-
V1_MIGRATION_DOCS_URL,
|
|
8
|
-
V1_ROUTES,
|
|
9
|
-
} = require('./route-registry');
|
|
10
|
-
|
|
11
|
-
const log = getLogger('frigg.api');
|
|
12
|
-
|
|
13
|
-
const V1_LINK_HEADER = `<${V1_MIGRATION_DOCS_URL}>; rel="deprecation"; type="text/html"`;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* RFC 9745 `Deprecation` value: an RFC 9651 structured-field date, i.e. `@`
|
|
17
|
-
* followed by seconds since the epoch.
|
|
18
|
-
* @param {string} isoDate - YYYY-MM-DD
|
|
19
|
-
*/
|
|
20
|
-
function deprecationHeaderValue(isoDate) {
|
|
21
|
-
return `@${Math.floor(Date.parse(`${isoDate}T00:00:00Z`) / 1000)}`;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const registerOn = (router, { method, path }, handler) => {
|
|
25
|
-
const verb = method === 'ANY' ? 'all' : method.toLowerCase();
|
|
26
|
-
router[verb](path, handler);
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
function setDeprecationHeaders(res) {
|
|
30
|
-
const { deprecatedAt, sunset } = MANAGEMENT_API_VERSIONS['1'];
|
|
31
|
-
res.set('Deprecation', deprecationHeaderValue(deprecatedAt));
|
|
32
|
-
if (sunset) {
|
|
33
|
-
// RFC 8594: an HTTP-date.
|
|
34
|
-
res.set('Sunset', new Date(`${sunset}T00:00:00Z`).toUTCString());
|
|
35
|
-
}
|
|
36
|
-
res.append('Link', V1_LINK_HEADER);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Marks every v1 response as deprecated and records the call (ADR-053 §6):
|
|
41
|
-
* `Deprecation` + `Link: rel="deprecation"` headers, one ADR-048 log record
|
|
42
|
-
* and one ADR-011 counter increment per call. Mounted ahead of the frozen v1
|
|
43
|
-
* router; it calls `next()` so the v1 handler answers unchanged.
|
|
44
|
-
*
|
|
45
|
-
* @param {Object} [params]
|
|
46
|
-
* @param {Object} [params.telemetry] - defaults to the process telemetry.
|
|
47
|
-
*/
|
|
48
|
-
function createV1DeprecationRouter({ telemetry } = {}) {
|
|
49
|
-
const router = Router();
|
|
50
|
-
|
|
51
|
-
for (const route of V1_ROUTES) {
|
|
52
|
-
registerOn(router, route, (req, res, next) => {
|
|
53
|
-
// One signal per request, even if two v1 patterns match it.
|
|
54
|
-
if (res.locals.friggV1Deprecated) return next();
|
|
55
|
-
res.locals.friggV1Deprecated = true;
|
|
56
|
-
setDeprecationHeaders(res);
|
|
57
|
-
const method = req.method;
|
|
58
|
-
log.info('Deprecated Management API v1 route called', {
|
|
59
|
-
eventName: 'frigg.api.deprecated_route',
|
|
60
|
-
route: route.path,
|
|
61
|
-
method,
|
|
62
|
-
client: req.get('Frigg-Client'),
|
|
63
|
-
});
|
|
64
|
-
try {
|
|
65
|
-
(telemetry || getTelemetry()).count('frigg.api.v1.requests', 1, {
|
|
66
|
-
route: route.path,
|
|
67
|
-
method,
|
|
68
|
-
});
|
|
69
|
-
} catch {
|
|
70
|
-
// Telemetry must never break a request.
|
|
71
|
-
}
|
|
72
|
-
next();
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return router;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Answers the v1 routes with 410 Gone when the app sets
|
|
81
|
-
* `managementApi.v1: false`. Only the registry's v1 routes are claimed, so
|
|
82
|
-
* the OAuth redirect and adopter routes keep working.
|
|
83
|
-
*/
|
|
84
|
-
function createV1DisabledRouter() {
|
|
85
|
-
const router = Router();
|
|
86
|
-
for (const route of V1_ROUTES) {
|
|
87
|
-
registerOn(router, route, (_req, res, next) => {
|
|
88
|
-
res.append('Link', V1_LINK_HEADER);
|
|
89
|
-
next(
|
|
90
|
-
Boom.resourceGone(
|
|
91
|
-
'Management API v1 is disabled for this app. Use /api/v2; see the migration guide.',
|
|
92
|
-
{
|
|
93
|
-
code: 'API_VERSION_DISABLED',
|
|
94
|
-
details: { migrationGuide: V1_MIGRATION_DOCS_URL },
|
|
95
|
-
}
|
|
96
|
-
)
|
|
97
|
-
);
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
// A disabled v1 answers in the v2 error shape: there is no v1 contract
|
|
101
|
-
// left to honour, and the body should point at the replacement.
|
|
102
|
-
router.use((err, req, res, next) => {
|
|
103
|
-
if (err?.data?.code !== 'API_VERSION_DISABLED') return next(err);
|
|
104
|
-
res.status(410).json({
|
|
105
|
-
error: {
|
|
106
|
-
code: 'API_VERSION_DISABLED',
|
|
107
|
-
message: err.message,
|
|
108
|
-
details: err.data.details,
|
|
109
|
-
},
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
return router;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
module.exports = {
|
|
116
|
-
createV1DeprecationRouter,
|
|
117
|
-
createV1DisabledRouter,
|
|
118
|
-
deprecationHeaderValue,
|
|
119
|
-
V1_LINK_HEADER,
|
|
120
|
-
};
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
const express = require('express');
|
|
2
|
-
const Boom = require('@hapi/boom');
|
|
3
|
-
const catchAsyncError = require('express-async-handler');
|
|
4
|
-
const { API_VERSION_HEADER } = require('../error-response');
|
|
5
|
-
const { listV2Routes } = require('../route-registry');
|
|
6
|
-
const { resolveManagementApiConfig } = require('../management-api-config');
|
|
7
|
-
const { createIntegrationHandlers } = require('./handlers/integrations');
|
|
8
|
-
const { createEntityHandlers } = require('./handlers/entities');
|
|
9
|
-
const { createCredentialHandlers } = require('./handlers/credentials');
|
|
10
|
-
const { createAuthorizationHandlers } = require('./handlers/authorization');
|
|
11
|
-
const { createProxyHandlers } = require('./handlers/proxy');
|
|
12
|
-
|
|
13
|
-
/** Every v2 handler, keyed by the registry's operationId. */
|
|
14
|
-
function createV2Handlers(dependencies) {
|
|
15
|
-
return {
|
|
16
|
-
...createIntegrationHandlers(dependencies),
|
|
17
|
-
...createEntityHandlers(dependencies),
|
|
18
|
-
...createCredentialHandlers(dependencies),
|
|
19
|
-
...createAuthorizationHandlers(dependencies),
|
|
20
|
-
...(dependencies.executeEntityProxyRequest
|
|
21
|
-
? createProxyHandlers(dependencies)
|
|
22
|
-
: {}),
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Management API v2 router (ADR-053). Routes come from the registry, in
|
|
28
|
-
* registry order; each authenticates the caller with the same
|
|
29
|
-
* AuthenticateUser as v1 (bearer token, x-frigg headers, adopter JWT) and then
|
|
30
|
-
* runs the handler for its operationId. Every response under /api/v2 carries
|
|
31
|
-
* `Frigg-API-Version: 2`; an unknown /api/v2 path is a v2 404.
|
|
32
|
-
*
|
|
33
|
-
* @param {Object} [params]
|
|
34
|
-
* @param {Object} [params.appDefinition]
|
|
35
|
-
* @param {Object} [params.config] - resolveManagementApiConfig() output.
|
|
36
|
-
* @param {Object} [params.dependencies] - use cases and authenticateUser;
|
|
37
|
-
* built from the app definition when omitted.
|
|
38
|
-
*/
|
|
39
|
-
function createManagementApiV2Router({
|
|
40
|
-
appDefinition = {},
|
|
41
|
-
config,
|
|
42
|
-
dependencies,
|
|
43
|
-
} = {}) {
|
|
44
|
-
const resolvedConfig =
|
|
45
|
-
config || resolveManagementApiConfig(appDefinition.managementApi);
|
|
46
|
-
const deps =
|
|
47
|
-
dependencies ||
|
|
48
|
-
require('./dependencies').buildV2Dependencies({
|
|
49
|
-
appDefinition,
|
|
50
|
-
config: resolvedConfig,
|
|
51
|
-
});
|
|
52
|
-
const handlers = createV2Handlers(deps);
|
|
53
|
-
|
|
54
|
-
const router = express.Router();
|
|
55
|
-
|
|
56
|
-
router.use('/api/v2', (_req, res, next) => {
|
|
57
|
-
res.set(API_VERSION_HEADER, '2');
|
|
58
|
-
next();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
for (const route of listV2Routes({ proxy: resolvedConfig.proxy.enable })) {
|
|
62
|
-
const handler = handlers[route.operationId];
|
|
63
|
-
if (!handler) continue;
|
|
64
|
-
router[route.method.toLowerCase()](
|
|
65
|
-
route.path,
|
|
66
|
-
catchAsyncError(async (req, res) => {
|
|
67
|
-
const user = await deps.authenticateUser.execute(req);
|
|
68
|
-
if (!user) throw Boom.unauthorized('Authentication required');
|
|
69
|
-
await handler(req, res, user);
|
|
70
|
-
})
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
router.use('/api/v2', (req, _res, next) => {
|
|
75
|
-
next(
|
|
76
|
-
Boom.notFound(
|
|
77
|
-
`No Management API v2 route for ${req.method} ${req.baseUrl}${req.path}`
|
|
78
|
-
)
|
|
79
|
-
);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
return router;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
module.exports = { createManagementApiV2Router, createV2Handlers };
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wires the repositories and use cases behind Management API v2. Kept apart
|
|
3
|
-
* from the router so tests can hand the router doubles instead.
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* @param {Object} params
|
|
7
|
-
* @param {Object} params.appDefinition - the loaded app definition. Its
|
|
8
|
-
* `entityTypeRegistry` (built once by loadAppDefinition) is used.
|
|
9
|
-
* @param {Object} params.config
|
|
10
|
-
* @param {Map} [params.entityTypeRegistry] - overrides the registry on the
|
|
11
|
-
* app definition. When neither exists (a raw definition in a test), it is
|
|
12
|
-
* built here from the definition.
|
|
13
|
-
*/
|
|
14
|
-
function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
|
|
15
|
-
const {
|
|
16
|
-
createIntegrationRepository,
|
|
17
|
-
} = require('../../integrations/repositories/integration-repository-factory');
|
|
18
|
-
const {
|
|
19
|
-
createCredentialRepository,
|
|
20
|
-
} = require('../../credential/repositories/credential-repository-factory');
|
|
21
|
-
const {
|
|
22
|
-
createModuleRepository,
|
|
23
|
-
} = require('../../modules/repositories/module-repository-factory');
|
|
24
|
-
const {
|
|
25
|
-
createUserRepository,
|
|
26
|
-
} = require('../../user/repositories/user-repository-factory');
|
|
27
|
-
const { ModuleFactory } = require('../../modules/module-factory');
|
|
28
|
-
const {
|
|
29
|
-
getEntityTypeRegistry,
|
|
30
|
-
getEntityTypeDefinitions,
|
|
31
|
-
} = require('../../modules/entity-type-registry');
|
|
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');
|
|
35
|
-
const {
|
|
36
|
-
AuthenticateWithSharedSecret,
|
|
37
|
-
} = require('../../user/use-cases/authenticate-with-shared-secret');
|
|
38
|
-
const { AuthenticateUser } = require('../../user/use-cases/authenticate-user');
|
|
39
|
-
const { CreateIntegration } = require('../../integrations/use-cases/create-integration');
|
|
40
|
-
const { UpdateIntegration } = require('../../integrations/use-cases/update-integration');
|
|
41
|
-
const {
|
|
42
|
-
DeleteIntegrationForUser,
|
|
43
|
-
} = require('../../integrations/use-cases/delete-integration-for-user');
|
|
44
|
-
const {
|
|
45
|
-
GetIntegrationsForUser,
|
|
46
|
-
} = require('../../integrations/use-cases/get-integrations-for-user');
|
|
47
|
-
const {
|
|
48
|
-
GetIntegrationInstance,
|
|
49
|
-
} = require('../../integrations/use-cases/get-integration-instance');
|
|
50
|
-
const {
|
|
51
|
-
GetPossibleIntegrations,
|
|
52
|
-
} = require('../../integrations/use-cases/get-possible-integrations');
|
|
53
|
-
const integrationOps = require('../../integrations/use-cases/integration-operations-for-user');
|
|
54
|
-
const {
|
|
55
|
-
GetEntityModuleForUser,
|
|
56
|
-
} = require('../../modules/use-cases/get-entity-module-for-user');
|
|
57
|
-
const entityOps = require('../../modules/use-cases/entity-operations-for-user');
|
|
58
|
-
const { ListEntityTypes, GetEntityType } = require('../../modules/use-cases/entity-types');
|
|
59
|
-
const credentialOps = require('../../credential/use-cases/credential-operations-for-user');
|
|
60
|
-
const {
|
|
61
|
-
GetCredentialReauthorization,
|
|
62
|
-
ReauthorizeCredentialForUser,
|
|
63
|
-
} = require('../../credential/use-cases/reauthorize-credential-for-user');
|
|
64
|
-
const {
|
|
65
|
-
GetAuthorizationStep,
|
|
66
|
-
SubmitAuthorizationStep,
|
|
67
|
-
} = require('../../modules/use-cases/multi-step-authorization');
|
|
68
|
-
const {
|
|
69
|
-
ProcessAuthorizationCallback,
|
|
70
|
-
} = require('../../modules/use-cases/process-authorization-callback');
|
|
71
|
-
const {
|
|
72
|
-
ExecuteEntityProxyRequest,
|
|
73
|
-
} = require('../../modules/use-cases/execute-entity-proxy-request');
|
|
74
|
-
const {
|
|
75
|
-
createAuthorizationSessionRepository,
|
|
76
|
-
} = require('../../modules/repositories/authorization-session-repository-factory');
|
|
77
|
-
const {
|
|
78
|
-
GetAuthorizationRequirements,
|
|
79
|
-
} = require('../../modules/use-cases/get-authorization-requirements');
|
|
80
|
-
|
|
81
|
-
const integrationClasses = appDefinition.integrations || [];
|
|
82
|
-
const userConfig = appDefinition.userConfig || {};
|
|
83
|
-
// The registry holds entity types registered on the app as well as those
|
|
84
|
-
// integrations bind (ADR-059), so v2 authorize/entities accept both.
|
|
85
|
-
const registry =
|
|
86
|
-
entityTypeRegistry ??
|
|
87
|
-
appDefinition.entityTypeRegistry ??
|
|
88
|
-
getEntityTypeRegistry(appDefinition);
|
|
89
|
-
const moduleDefinitions = getEntityTypeDefinitions(registry);
|
|
90
|
-
|
|
91
|
-
const moduleRepository = createModuleRepository();
|
|
92
|
-
const integrationRepository = createIntegrationRepository();
|
|
93
|
-
const credentialRepository = createCredentialRepository();
|
|
94
|
-
const userRepository = createUserRepository();
|
|
95
|
-
const authorizationSessionRepository = createAuthorizationSessionRepository();
|
|
96
|
-
|
|
97
|
-
const authenticateUser = new AuthenticateUser({
|
|
98
|
-
getUserFromBearerToken: new GetUserFromBearerToken({ userRepository, userConfig }),
|
|
99
|
-
getUserFromXFriggHeaders: new GetUserFromXFriggHeaders({ userRepository, userConfig }),
|
|
100
|
-
getUserFromAdopterJwt: new GetUserFromAdopterJwt({ userRepository, userConfig }),
|
|
101
|
-
authenticateWithSharedSecret: new AuthenticateWithSharedSecret(),
|
|
102
|
-
userConfig,
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
const moduleFactory = new ModuleFactory({ moduleRepository, moduleDefinitions });
|
|
106
|
-
const getIntegrationInstance = new GetIntegrationInstance({
|
|
107
|
-
integrationRepository,
|
|
108
|
-
integrationClasses,
|
|
109
|
-
moduleFactory,
|
|
110
|
-
});
|
|
111
|
-
const getEntityModuleForUser = new GetEntityModuleForUser({
|
|
112
|
-
moduleRepository,
|
|
113
|
-
moduleDefinitions,
|
|
114
|
-
});
|
|
115
|
-
const getCredentialForUser = new credentialOps.GetCredentialForUserV2({
|
|
116
|
-
credentialRepository,
|
|
117
|
-
moduleRepository,
|
|
118
|
-
});
|
|
119
|
-
const getAuthorizationRequirements = new GetAuthorizationRequirements({
|
|
120
|
-
moduleDefinitions,
|
|
121
|
-
});
|
|
122
|
-
const getAuthorizationStep = new GetAuthorizationStep({
|
|
123
|
-
moduleDefinitions,
|
|
124
|
-
getAuthorizationRequirements,
|
|
125
|
-
authorizationSessionRepository,
|
|
126
|
-
});
|
|
127
|
-
const submitAuthorizationStep = new SubmitAuthorizationStep({
|
|
128
|
-
moduleDefinitions,
|
|
129
|
-
getAuthorizationRequirements,
|
|
130
|
-
authorizationSessionRepository,
|
|
131
|
-
processAuthorizationCallback: new ProcessAuthorizationCallback({
|
|
132
|
-
moduleRepository,
|
|
133
|
-
credentialRepository,
|
|
134
|
-
integrationRepository,
|
|
135
|
-
moduleDefinitions,
|
|
136
|
-
}),
|
|
137
|
-
moduleRepository,
|
|
138
|
-
credentialRepository,
|
|
139
|
-
});
|
|
140
|
-
const getOwnedIntegrationRecord = new integrationOps.GetOwnedIntegrationRecord({
|
|
141
|
-
integrationRepository,
|
|
142
|
-
});
|
|
143
|
-
const getIntegrationInstanceForUser = new integrationOps.GetIntegrationInstanceForUser({
|
|
144
|
-
getOwnedIntegrationRecord,
|
|
145
|
-
getIntegrationInstance,
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
return {
|
|
149
|
-
config,
|
|
150
|
-
moduleDefinitions,
|
|
151
|
-
repositories: {
|
|
152
|
-
moduleRepository,
|
|
153
|
-
integrationRepository,
|
|
154
|
-
credentialRepository,
|
|
155
|
-
userRepository,
|
|
156
|
-
authorizationSessionRepository,
|
|
157
|
-
},
|
|
158
|
-
authenticateUser,
|
|
159
|
-
|
|
160
|
-
// Integrations
|
|
161
|
-
getIntegrationsForUser: new GetIntegrationsForUser({
|
|
162
|
-
integrationRepository,
|
|
163
|
-
integrationClasses,
|
|
164
|
-
moduleFactory,
|
|
165
|
-
moduleRepository,
|
|
166
|
-
}),
|
|
167
|
-
getPossibleIntegrations: new GetPossibleIntegrations({ integrationClasses }),
|
|
168
|
-
createIntegrationForUser: new integrationOps.CreateIntegrationForUser({
|
|
169
|
-
createIntegration: new CreateIntegration({
|
|
170
|
-
integrationRepository,
|
|
171
|
-
integrationClasses,
|
|
172
|
-
moduleFactory,
|
|
173
|
-
}),
|
|
174
|
-
getEntityModuleForUser,
|
|
175
|
-
integrationClasses,
|
|
176
|
-
}),
|
|
177
|
-
getIntegrationInstanceForUser,
|
|
178
|
-
updateIntegrationForUser: new integrationOps.UpdateIntegrationForUser({
|
|
179
|
-
getOwnedIntegrationRecord,
|
|
180
|
-
updateIntegration: new UpdateIntegration({
|
|
181
|
-
integrationRepository,
|
|
182
|
-
integrationClasses,
|
|
183
|
-
moduleFactory,
|
|
184
|
-
}),
|
|
185
|
-
}),
|
|
186
|
-
removeIntegrationForUser: new integrationOps.RemoveIntegrationForUser({
|
|
187
|
-
getOwnedIntegrationRecord,
|
|
188
|
-
deleteIntegrationForUser: new DeleteIntegrationForUser({
|
|
189
|
-
integrationRepository,
|
|
190
|
-
integrationClasses,
|
|
191
|
-
moduleFactory,
|
|
192
|
-
}),
|
|
193
|
-
}),
|
|
194
|
-
sendIntegrationEventForUser: new integrationOps.SendIntegrationEventForUser({
|
|
195
|
-
getIntegrationInstanceForUser,
|
|
196
|
-
}),
|
|
197
|
-
runIntegrationActionForUser: new integrationOps.RunIntegrationActionForUser({
|
|
198
|
-
getIntegrationInstanceForUser,
|
|
199
|
-
}),
|
|
200
|
-
testIntegrationAuthForUser: new integrationOps.TestIntegrationAuthForUser({
|
|
201
|
-
getIntegrationInstanceForUser,
|
|
202
|
-
}),
|
|
203
|
-
|
|
204
|
-
// Entities
|
|
205
|
-
getEntityModuleForUser,
|
|
206
|
-
listEntitiesForUser: new entityOps.ListEntitiesForUser({ moduleRepository }),
|
|
207
|
-
getEntityForUser: new entityOps.GetEntityForUser({ getEntityModuleForUser }),
|
|
208
|
-
deleteEntityForUser: new entityOps.DeleteEntityForUser({
|
|
209
|
-
getEntityModuleForUser,
|
|
210
|
-
moduleRepository,
|
|
211
|
-
integrationRepository,
|
|
212
|
-
}),
|
|
213
|
-
testEntityAuthForUser: new entityOps.TestEntityAuthForUser({ getEntityModuleForUser }),
|
|
214
|
-
getEntityOptionsForUser: new entityOps.GetEntityOptionsForUser({ getEntityModuleForUser }),
|
|
215
|
-
refreshEntityOptionsForUser: new entityOps.RefreshEntityOptionsForUser({
|
|
216
|
-
getEntityModuleForUser,
|
|
217
|
-
}),
|
|
218
|
-
listEntityTypes: new ListEntityTypes({ entityTypeRegistry: registry }),
|
|
219
|
-
getEntityType: new GetEntityType({ entityTypeRegistry: registry }),
|
|
220
|
-
getAuthorizationRequirements,
|
|
221
|
-
|
|
222
|
-
// Credentials
|
|
223
|
-
listCredentialsForUser: new credentialOps.ListCredentialsForUser({
|
|
224
|
-
credentialRepository,
|
|
225
|
-
moduleRepository,
|
|
226
|
-
}),
|
|
227
|
-
getCredentialForUser,
|
|
228
|
-
deleteCredentialForUser: new credentialOps.DeleteCredentialForUser({
|
|
229
|
-
credentialRepository,
|
|
230
|
-
moduleRepository,
|
|
231
|
-
getCredentialForUser,
|
|
232
|
-
}),
|
|
233
|
-
getCredentialReauthorization: new GetCredentialReauthorization({
|
|
234
|
-
getCredentialForUser,
|
|
235
|
-
getAuthorizationStep,
|
|
236
|
-
}),
|
|
237
|
-
reauthorizeCredentialForUser: new ReauthorizeCredentialForUser({
|
|
238
|
-
getCredentialForUser,
|
|
239
|
-
submitAuthorizationStep,
|
|
240
|
-
}),
|
|
241
|
-
|
|
242
|
-
// Authorization (multi-step)
|
|
243
|
-
getAuthorizationStep,
|
|
244
|
-
submitAuthorizationStep,
|
|
245
|
-
|
|
246
|
-
// Entity proxy (ADR-052): only built when the app enables it.
|
|
247
|
-
...(config.proxy.enable && {
|
|
248
|
-
executeEntityProxyRequest: new ExecuteEntityProxyRequest({
|
|
249
|
-
getEntityModuleForUser,
|
|
250
|
-
proxyConfig: config.proxy,
|
|
251
|
-
}),
|
|
252
|
-
}),
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
module.exports = { buildV2Dependencies };
|
package/management-api/v2/dto.js
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Response shapes for Management API v2. Use cases return domain records;
|
|
3
|
-
* these functions decide what leaves the server. Credential material never
|
|
4
|
-
* does (see maskCredential).
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const idOf = (value) => {
|
|
8
|
-
if (value === undefined || value === null) return null;
|
|
9
|
-
if (typeof value === 'object') {
|
|
10
|
-
const id = value.id ?? value._id;
|
|
11
|
-
return id === undefined || id === null ? null : String(id);
|
|
12
|
-
}
|
|
13
|
-
return String(value);
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
/** An entity (connected account). */
|
|
17
|
-
function toEntityDto(entity) {
|
|
18
|
-
const credential =
|
|
19
|
-
entity.credential && typeof entity.credential === 'object'
|
|
20
|
-
? entity.credential
|
|
21
|
-
: null;
|
|
22
|
-
return {
|
|
23
|
-
id: String(entity.id),
|
|
24
|
-
type: entity.moduleName ?? null,
|
|
25
|
-
name: entity.name ?? null,
|
|
26
|
-
externalId: entity.externalId ?? null,
|
|
27
|
-
credentialId: idOf(entity.credential ?? entity.credentialId),
|
|
28
|
-
userId: idOf(entity.userId ?? entity.user),
|
|
29
|
-
authIsValid:
|
|
30
|
-
typeof credential?.authIsValid === 'boolean'
|
|
31
|
-
? credential.authIsValid
|
|
32
|
-
: null,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** An integration, from a hydrated instance or a stored DTO. */
|
|
37
|
-
function toIntegrationDto(integration) {
|
|
38
|
-
return {
|
|
39
|
-
id: String(integration.id),
|
|
40
|
-
entities: integration.entities ?? [],
|
|
41
|
-
status: integration.status ?? null,
|
|
42
|
-
config: integration.config ?? {},
|
|
43
|
-
version: integration.version ?? null,
|
|
44
|
-
messages: integration.messages ?? {},
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Fields every repository adds around the stored data; everything else on a
|
|
49
|
-
// credential record came from the provider and is treated as secret.
|
|
50
|
-
const CREDENTIAL_METADATA = new Set([
|
|
51
|
-
'id',
|
|
52
|
-
'_id',
|
|
53
|
-
'userId',
|
|
54
|
-
'user',
|
|
55
|
-
'externalId',
|
|
56
|
-
'authIsValid',
|
|
57
|
-
'createdAt',
|
|
58
|
-
'updatedAt',
|
|
59
|
-
'type',
|
|
60
|
-
'entityIds',
|
|
61
|
-
'status',
|
|
62
|
-
]);
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Masks one stored credential value. Strings keep at most their last four
|
|
66
|
-
* characters, and only when long enough that four characters reveal nothing
|
|
67
|
-
* useful; objects and arrays are hidden whole. Numbers, booleans and null
|
|
68
|
-
* (expiry times, flags) are not secret and pass through.
|
|
69
|
-
*/
|
|
70
|
-
function maskValue(value) {
|
|
71
|
-
if (value === null || typeof value === 'number' || typeof value === 'boolean') {
|
|
72
|
-
return value;
|
|
73
|
-
}
|
|
74
|
-
if (typeof value === 'string') {
|
|
75
|
-
return value.length >= 16 ? `****${value.slice(-4)}` : '****';
|
|
76
|
-
}
|
|
77
|
-
return '****';
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const toIso = (value) => {
|
|
81
|
-
if (!value) return null;
|
|
82
|
-
const date = value instanceof Date ? value : new Date(value);
|
|
83
|
-
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* A credential with every provider field masked. The keys stay visible so a
|
|
88
|
-
* client can tell an OAuth credential from an API-key one; the values never
|
|
89
|
-
* leave the server.
|
|
90
|
-
*/
|
|
91
|
-
function maskCredential(credential) {
|
|
92
|
-
const data = {};
|
|
93
|
-
for (const [key, value] of Object.entries(credential)) {
|
|
94
|
-
if (CREDENTIAL_METADATA.has(key) || value === undefined) continue;
|
|
95
|
-
data[key] = maskValue(value);
|
|
96
|
-
}
|
|
97
|
-
const entityIds = Array.isArray(credential.entityIds)
|
|
98
|
-
? credential.entityIds.map(String)
|
|
99
|
-
: [];
|
|
100
|
-
return {
|
|
101
|
-
id: String(credential.id),
|
|
102
|
-
type: credential.type ?? null,
|
|
103
|
-
externalId: credential.externalId ?? null,
|
|
104
|
-
userId: idOf(credential.userId),
|
|
105
|
-
authIsValid:
|
|
106
|
-
typeof credential.authIsValid === 'boolean'
|
|
107
|
-
? credential.authIsValid
|
|
108
|
-
: null,
|
|
109
|
-
entityIds,
|
|
110
|
-
entityCount: entityIds.length,
|
|
111
|
-
createdAt: toIso(credential.createdAt),
|
|
112
|
-
updatedAt: toIso(credential.updatedAt),
|
|
113
|
-
data,
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
module.exports = {
|
|
118
|
-
toEntityDto,
|
|
119
|
-
toIntegrationDto,
|
|
120
|
-
maskCredential,
|
|
121
|
-
maskValue,
|
|
122
|
-
idOf,
|
|
123
|
-
};
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
const { toEntityDto, maskCredential } = require('../dto');
|
|
2
|
-
const { objectBody, requireFields, parseStep, optionalString } = require('../request');
|
|
3
|
-
|
|
4
|
-
/** The v2 body for a submitted step: masked records when complete. */
|
|
5
|
-
function toAuthorizationResponse(result) {
|
|
6
|
-
if (result.status !== 'complete') return result;
|
|
7
|
-
return {
|
|
8
|
-
status: 'complete',
|
|
9
|
-
entity: result.entity ? toEntityDto(result.entity) : null,
|
|
10
|
-
credential: result.credential ? maskCredential(result.credential) : null,
|
|
11
|
-
...(result.previousCredentialId && {
|
|
12
|
-
previousCredentialId: result.previousCredentialId,
|
|
13
|
-
}),
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Management API v2 authorization handlers (multi-step authorize and
|
|
19
|
-
* credential re-authorization), keyed by operationId.
|
|
20
|
-
*/
|
|
21
|
-
function createAuthorizationHandlers(useCases) {
|
|
22
|
-
const {
|
|
23
|
-
getAuthorizationStep,
|
|
24
|
-
submitAuthorizationStep,
|
|
25
|
-
getCredentialReauthorization,
|
|
26
|
-
reauthorizeCredentialForUser,
|
|
27
|
-
} = useCases;
|
|
28
|
-
|
|
29
|
-
const stepQuery = (req) => ({
|
|
30
|
-
step: parseStep(req.query.step),
|
|
31
|
-
sessionId: optionalString(req.query.sessionId, 'sessionId'),
|
|
32
|
-
state: optionalString(req.query.state, 'state'),
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const stepBody = (req) => {
|
|
36
|
-
const body = objectBody(req);
|
|
37
|
-
return {
|
|
38
|
-
body,
|
|
39
|
-
step: parseStep(body.step),
|
|
40
|
-
sessionId: optionalString(body.sessionId, 'sessionId'),
|
|
41
|
-
};
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
async getAuthorizationRequirements(req, res, user) {
|
|
46
|
-
const entityType = optionalString(req.query.entityType, 'entityType');
|
|
47
|
-
requireFields({ entityType }, ['entityType']);
|
|
48
|
-
res.json(
|
|
49
|
-
await getAuthorizationStep.execute({
|
|
50
|
-
user,
|
|
51
|
-
entityType,
|
|
52
|
-
...stepQuery(req),
|
|
53
|
-
})
|
|
54
|
-
);
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
async authorize(req, res, user) {
|
|
58
|
-
const { body, step, sessionId } = stepBody(req);
|
|
59
|
-
requireFields(body, ['entityType', 'data']);
|
|
60
|
-
const result = await submitAuthorizationStep.execute({
|
|
61
|
-
user,
|
|
62
|
-
entityType: optionalString(body.entityType, 'entityType'),
|
|
63
|
-
data: body.data,
|
|
64
|
-
step,
|
|
65
|
-
sessionId,
|
|
66
|
-
});
|
|
67
|
-
res.json(toAuthorizationResponse(result));
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
async getCredentialReauthorizeRequirements(req, res, user) {
|
|
71
|
-
res.json(
|
|
72
|
-
await getCredentialReauthorization.execute(
|
|
73
|
-
req.params.credentialId,
|
|
74
|
-
user,
|
|
75
|
-
stepQuery(req)
|
|
76
|
-
)
|
|
77
|
-
);
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
async reauthorizeCredential(req, res, user) {
|
|
81
|
-
const { body, step, sessionId } = stepBody(req);
|
|
82
|
-
requireFields(body, ['data']);
|
|
83
|
-
const result = await reauthorizeCredentialForUser.execute(
|
|
84
|
-
req.params.credentialId,
|
|
85
|
-
user,
|
|
86
|
-
{ data: body.data, step, sessionId }
|
|
87
|
-
);
|
|
88
|
-
res.json(toAuthorizationResponse(result));
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
module.exports = { createAuthorizationHandlers, toAuthorizationResponse };
|