@friggframework/core 2.0.0-next.3 → 2.0.0-next.30
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/core/create-handler.js +2 -1
- package/database/models/WebsocketConnection.js +5 -0
- package/encrypt/encrypt.js +5 -33
- package/handlers/app-handler-helpers.js +59 -0
- package/handlers/backend-utils.js +85 -0
- package/handlers/routers/HEALTHCHECK.md +240 -0
- package/handlers/routers/auth.js +26 -0
- package/handlers/routers/health.js +451 -0
- package/handlers/routers/health.test.js +203 -0
- package/handlers/routers/integration-defined-routers.js +42 -0
- package/handlers/routers/middleware/loadUser.js +15 -0
- package/handlers/routers/middleware/requireLoggedInUser.js +12 -0
- package/handlers/routers/user.js +41 -0
- package/handlers/routers/websocket.js +55 -0
- package/handlers/workers/integration-defined-workers.js +24 -0
- package/index.js +4 -0
- package/integrations/integration-router.js +120 -96
- package/integrations/options.js +2 -3
- package/module-plugin/auther.js +97 -54
- package/module-plugin/requester/requester.js +1 -0
- package/package.json +17 -12
- package/utils/backend-path.js +38 -0
- package/utils/index.js +6 -0
package/module-plugin/auther.js
CHANGED
|
@@ -25,15 +25,14 @@
|
|
|
25
25
|
// 1. Add definition of expected params to API Class (or could just be credential?)
|
|
26
26
|
// 2.
|
|
27
27
|
|
|
28
|
-
|
|
29
28
|
const { Delegate } = require('../core');
|
|
30
29
|
const { get } = require('../assertions');
|
|
31
30
|
const _ = require('lodash');
|
|
32
|
-
const {flushDebugLog} = require('../logs');
|
|
31
|
+
const { flushDebugLog } = require('../logs');
|
|
33
32
|
const { Credential } = require('./credential');
|
|
34
33
|
const { Entity } = require('./entity');
|
|
35
34
|
const { mongoose } = require('../database/mongoose');
|
|
36
|
-
const {ModuleConstants} = require(
|
|
35
|
+
const { ModuleConstants } = require('./ModuleConstants');
|
|
37
36
|
|
|
38
37
|
class Auther extends Delegate {
|
|
39
38
|
static validateDefinition(definition) {
|
|
@@ -55,21 +54,35 @@ class Auther extends Delegate {
|
|
|
55
54
|
if (!definition.requiredAuthMethods) {
|
|
56
55
|
throw new Error('Auther definition requires requiredAuthMethods');
|
|
57
56
|
} else {
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
if (
|
|
58
|
+
definition.API.requesterType ===
|
|
59
|
+
ModuleConstants.authType.oauth2 &&
|
|
60
|
+
!definition.requiredAuthMethods.getToken
|
|
61
|
+
) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
'Auther definition requires requiredAuthMethods.getToken'
|
|
64
|
+
);
|
|
61
65
|
}
|
|
62
66
|
if (!definition.requiredAuthMethods.getEntityDetails) {
|
|
63
|
-
throw new Error(
|
|
67
|
+
throw new Error(
|
|
68
|
+
'Auther definition requires requiredAuthMethods.getEntityDetails'
|
|
69
|
+
);
|
|
64
70
|
}
|
|
65
71
|
if (!definition.requiredAuthMethods.getCredentialDetails) {
|
|
66
|
-
throw new Error(
|
|
72
|
+
throw new Error(
|
|
73
|
+
'Auther definition requires requiredAuthMethods.getCredentialDetails'
|
|
74
|
+
);
|
|
67
75
|
}
|
|
68
76
|
if (!definition.requiredAuthMethods.apiPropertiesToPersist) {
|
|
69
|
-
throw new Error(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
throw new Error(
|
|
78
|
+
'Auther definition requires requiredAuthMethods.apiPropertiesToPersist'
|
|
79
|
+
);
|
|
80
|
+
} else if (definition.Credential) {
|
|
81
|
+
for (const prop of definition.requiredAuthMethods
|
|
82
|
+
.apiPropertiesToPersist?.credential) {
|
|
83
|
+
if (
|
|
84
|
+
!definition.Credential.schema.paths.hasOwnProperty(prop)
|
|
85
|
+
) {
|
|
73
86
|
throw new Error(
|
|
74
87
|
`Auther definition requires Credential schema to have property ${prop}`
|
|
75
88
|
);
|
|
@@ -77,7 +90,9 @@ class Auther extends Delegate {
|
|
|
77
90
|
}
|
|
78
91
|
}
|
|
79
92
|
if (!definition.requiredAuthMethods.testAuthRequest) {
|
|
80
|
-
throw new Error(
|
|
93
|
+
throw new Error(
|
|
94
|
+
'Auther definition requires requiredAuthMethods.testAuth'
|
|
95
|
+
);
|
|
81
96
|
}
|
|
82
97
|
}
|
|
83
98
|
}
|
|
@@ -97,14 +112,17 @@ class Auther extends Delegate {
|
|
|
97
112
|
this.name = definition.moduleName;
|
|
98
113
|
this.modelName = definition.modelName;
|
|
99
114
|
this.apiClass = definition.API;
|
|
100
|
-
this.CredentialModel =
|
|
115
|
+
this.CredentialModel =
|
|
116
|
+
definition.Credential || this.getCredentialModel();
|
|
101
117
|
this.EntityModel = definition.Entity || this.getEntityModel();
|
|
102
118
|
}
|
|
103
119
|
|
|
104
120
|
static async getInstance(params) {
|
|
105
121
|
const instance = new this(params);
|
|
106
122
|
if (params.entityId) {
|
|
107
|
-
instance.entity = await instance.EntityModel.findById(
|
|
123
|
+
instance.entity = await instance.EntityModel.findById(
|
|
124
|
+
params.entityId
|
|
125
|
+
);
|
|
108
126
|
instance.credential = await instance.CredentialModel.findById(
|
|
109
127
|
instance.entity.credential
|
|
110
128
|
);
|
|
@@ -132,7 +150,7 @@ class Auther extends Delegate {
|
|
|
132
150
|
}
|
|
133
151
|
|
|
134
152
|
static getEntityModelFromDefinition(definition) {
|
|
135
|
-
const partialModule = new this({definition});
|
|
153
|
+
const partialModule = new this({ definition });
|
|
136
154
|
return partialModule.getEntityModel();
|
|
137
155
|
}
|
|
138
156
|
|
|
@@ -151,30 +169,38 @@ class Auther extends Delegate {
|
|
|
151
169
|
getEntityModel() {
|
|
152
170
|
if (!this.EntityModel) {
|
|
153
171
|
const prefix = this.modelName ?? _.upperFirst(this.getName());
|
|
154
|
-
const arrayToDefaultObject = (array, defaultValue) =>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
172
|
+
const arrayToDefaultObject = (array, defaultValue) =>
|
|
173
|
+
_.mapValues(_.keyBy(array), () => defaultValue);
|
|
174
|
+
const schema = new mongoose.Schema(
|
|
175
|
+
arrayToDefaultObject(this.apiPropertiesToPersist.entity, {
|
|
176
|
+
type: mongoose.Schema.Types.Mixed,
|
|
177
|
+
trim: true,
|
|
178
|
+
})
|
|
179
|
+
);
|
|
159
180
|
const name = `${prefix}Entity`;
|
|
160
181
|
this.EntityModel =
|
|
161
|
-
Entity.discriminators?.[name] ||
|
|
182
|
+
Entity.discriminators?.[name] ||
|
|
183
|
+
Entity.discriminator(name, schema);
|
|
162
184
|
}
|
|
163
185
|
return this.EntityModel;
|
|
164
186
|
}
|
|
165
187
|
|
|
166
188
|
getCredentialModel() {
|
|
167
189
|
if (!this.CredentialModel) {
|
|
168
|
-
const arrayToDefaultObject = (array, defaultValue) =>
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
190
|
+
const arrayToDefaultObject = (array, defaultValue) =>
|
|
191
|
+
_.mapValues(_.keyBy(array), () => defaultValue);
|
|
192
|
+
const schema = new mongoose.Schema(
|
|
193
|
+
arrayToDefaultObject(this.apiPropertiesToPersist.credential, {
|
|
194
|
+
type: mongoose.Schema.Types.Mixed,
|
|
195
|
+
trim: true,
|
|
196
|
+
lhEncrypt: true,
|
|
197
|
+
})
|
|
198
|
+
);
|
|
174
199
|
const prefix = this.modelName ?? _.upperFirst(this.getName());
|
|
175
200
|
const name = `${prefix}Credential`;
|
|
176
201
|
this.CredentialModel =
|
|
177
|
-
Credential.discriminators?.[name] ||
|
|
202
|
+
Credential.discriminators?.[name] ||
|
|
203
|
+
Credential.discriminator(name, schema);
|
|
178
204
|
}
|
|
179
205
|
return this.CredentialModel;
|
|
180
206
|
}
|
|
@@ -197,7 +223,10 @@ class Auther extends Delegate {
|
|
|
197
223
|
async validateAuthorizationRequirements() {
|
|
198
224
|
const requirements = await this.getAuthorizationRequirements();
|
|
199
225
|
let valid = true;
|
|
200
|
-
if (
|
|
226
|
+
if (
|
|
227
|
+
['oauth1', 'oauth2'].includes(requirements.type) &&
|
|
228
|
+
!requirements.url
|
|
229
|
+
) {
|
|
201
230
|
valid = false;
|
|
202
231
|
}
|
|
203
232
|
return valid;
|
|
@@ -238,20 +267,32 @@ class Auther extends Delegate {
|
|
|
238
267
|
throw new Error('Authorization failed');
|
|
239
268
|
}
|
|
240
269
|
const entityDetails = await this.getEntityDetails(
|
|
241
|
-
this.api,
|
|
270
|
+
this.api,
|
|
271
|
+
params,
|
|
272
|
+
tokenResponse,
|
|
273
|
+
this.userId
|
|
274
|
+
);
|
|
275
|
+
Object.assign(
|
|
276
|
+
entityDetails.details,
|
|
277
|
+
this.apiParamsFromEntity(this.api)
|
|
242
278
|
);
|
|
243
|
-
Object.assign(entityDetails.details, this.apiParamsFromEntity(this.api));
|
|
244
279
|
await this.findOrCreateEntity(entityDetails);
|
|
245
280
|
return {
|
|
246
281
|
credential_id: this.credential.id,
|
|
247
282
|
entity_id: this.entity.id,
|
|
248
283
|
type: this.getName(),
|
|
249
|
-
}
|
|
284
|
+
};
|
|
250
285
|
}
|
|
251
286
|
|
|
252
287
|
async onTokenUpdate() {
|
|
253
|
-
const credentialDetails = await this.getCredentialDetails(
|
|
254
|
-
|
|
288
|
+
const credentialDetails = await this.getCredentialDetails(
|
|
289
|
+
this.api,
|
|
290
|
+
this.userId
|
|
291
|
+
);
|
|
292
|
+
Object.assign(
|
|
293
|
+
credentialDetails.details,
|
|
294
|
+
this.apiParamsFromCredential(this.api)
|
|
295
|
+
);
|
|
255
296
|
credentialDetails.details.auth_is_valid = true;
|
|
256
297
|
await this.updateOrCreateCredential(credentialDetails);
|
|
257
298
|
}
|
|
@@ -259,11 +300,9 @@ class Auther extends Delegate {
|
|
|
259
300
|
async receiveNotification(notifier, delegateString, object = null) {
|
|
260
301
|
if (delegateString === this.api.DLGT_TOKEN_UPDATE) {
|
|
261
302
|
await this.onTokenUpdate();
|
|
262
|
-
}
|
|
263
|
-
else if (delegateString === this.api.DLGT_TOKEN_DEAUTHORIZED) {
|
|
303
|
+
} else if (delegateString === this.api.DLGT_TOKEN_DEAUTHORIZED) {
|
|
264
304
|
await this.deauthorize();
|
|
265
|
-
}
|
|
266
|
-
else if (delegateString === this.api.DLGT_INVALID_AUTH) {
|
|
305
|
+
} else if (delegateString === this.api.DLGT_INVALID_AUTH) {
|
|
267
306
|
await this.markCredentialsInvalid();
|
|
268
307
|
}
|
|
269
308
|
}
|
|
@@ -286,10 +325,10 @@ class Auther extends Delegate {
|
|
|
286
325
|
const search = await this.EntityModel.find(identifiers);
|
|
287
326
|
if (search.length > 1) {
|
|
288
327
|
throw new Error(
|
|
289
|
-
'Multiple entities found with the same identifiers: ' +
|
|
328
|
+
'Multiple entities found with the same identifiers: ' +
|
|
329
|
+
JSON.stringify(identifiers)
|
|
290
330
|
);
|
|
291
|
-
}
|
|
292
|
-
else if (search.length === 0) {
|
|
331
|
+
} else if (search.length === 0) {
|
|
293
332
|
this.entity = await this.EntityModel.create({
|
|
294
333
|
credential: this.credential.id,
|
|
295
334
|
...details,
|
|
@@ -308,25 +347,27 @@ class Auther extends Delegate {
|
|
|
308
347
|
const identifiers = get(credentialDetails, 'identifiers');
|
|
309
348
|
const details = get(credentialDetails, 'details');
|
|
310
349
|
|
|
311
|
-
if (!this.credential){
|
|
312
|
-
const credentialSearch = await this.CredentialModel.find(
|
|
350
|
+
if (!this.credential) {
|
|
351
|
+
const credentialSearch = await this.CredentialModel.find(
|
|
352
|
+
identifiers
|
|
353
|
+
);
|
|
313
354
|
if (credentialSearch.length > 1) {
|
|
314
|
-
throw new Error(
|
|
315
|
-
|
|
316
|
-
|
|
355
|
+
throw new Error(
|
|
356
|
+
`Multiple credentials found with same identifiers: ${identifiers}`
|
|
357
|
+
);
|
|
358
|
+
} else if (credentialSearch.length === 1) {
|
|
317
359
|
// found exactly one credential with these identifiers
|
|
318
360
|
this.credential = credentialSearch[0];
|
|
319
|
-
}
|
|
320
|
-
else {
|
|
361
|
+
} else {
|
|
321
362
|
// found no credential with these identifiers (match none for insert)
|
|
322
|
-
this.credential = {$exists: false};
|
|
363
|
+
this.credential = { $exists: false };
|
|
323
364
|
}
|
|
324
365
|
}
|
|
325
366
|
// update credential or create if none was found
|
|
326
367
|
this.credential = await this.CredentialModel.findOneAndUpdate(
|
|
327
|
-
{_id: this.credential},
|
|
328
|
-
{$set: {...identifiers, ...details}},
|
|
329
|
-
{useFindAndModify: true, new: true, upsert: true}
|
|
368
|
+
{ _id: this.credential },
|
|
369
|
+
{ $set: { ...identifiers, ...details } },
|
|
370
|
+
{ useFindAndModify: true, new: true, upsert: true }
|
|
330
371
|
);
|
|
331
372
|
}
|
|
332
373
|
|
|
@@ -340,7 +381,9 @@ class Auther extends Delegate {
|
|
|
340
381
|
async deauthorize() {
|
|
341
382
|
this.api = new this.apiClass();
|
|
342
383
|
if (this.entity?.credential) {
|
|
343
|
-
await this.CredentialModel.deleteOne({
|
|
384
|
+
await this.CredentialModel.deleteOne({
|
|
385
|
+
_id: this.entity.credential,
|
|
386
|
+
});
|
|
344
387
|
this.entity.credential = undefined;
|
|
345
388
|
await this.entity.save();
|
|
346
389
|
}
|
package/package.json
CHANGED
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/core",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0-next.
|
|
4
|
+
"version": "2.0.0-next.30",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@hapi/boom": "^10.0.1",
|
|
7
7
|
"aws-sdk": "^2.1200.0",
|
|
8
8
|
"bcryptjs": "^2.4.3",
|
|
9
|
+
"body-parser": "^1.20.2",
|
|
9
10
|
"common-tags": "^1.8.2",
|
|
10
|
-
"
|
|
11
|
+
"cors": "^2.8.5",
|
|
12
|
+
"dotenv": "^16.4.7",
|
|
13
|
+
"express": "^4.19.2",
|
|
11
14
|
"express-async-handler": "^1.2.0",
|
|
12
|
-
"
|
|
15
|
+
"form-data": "^4.0.0",
|
|
16
|
+
"fs-extra": "^11.2.0",
|
|
17
|
+
"lodash": "4.17.21",
|
|
13
18
|
"lodash.get": "^4.4.2",
|
|
14
19
|
"mongoose": "6.11.6",
|
|
15
|
-
"node-fetch": "^2.6.7"
|
|
20
|
+
"node-fetch": "^2.6.7",
|
|
21
|
+
"serverless-http": "^2.7.0",
|
|
22
|
+
"uuid": "^9.0.1"
|
|
16
23
|
},
|
|
17
24
|
"devDependencies": {
|
|
18
|
-
"@friggframework/eslint-config": "2.0.0-next.
|
|
19
|
-
"@friggframework/prettier-config": "2.0.0-next.
|
|
20
|
-
"@friggframework/test": "2.0.0-next.
|
|
21
|
-
"@types/lodash": "
|
|
25
|
+
"@friggframework/eslint-config": "2.0.0-next.30",
|
|
26
|
+
"@friggframework/prettier-config": "2.0.0-next.30",
|
|
27
|
+
"@friggframework/test": "2.0.0-next.30",
|
|
28
|
+
"@types/lodash": "4.17.15",
|
|
22
29
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
23
30
|
"chai": "^4.3.6",
|
|
24
31
|
"eslint": "^8.22.0",
|
|
@@ -26,9 +33,7 @@
|
|
|
26
33
|
"eslint-plugin-n": "^17.10.2",
|
|
27
34
|
"eslint-plugin-promise": "^7.0.0",
|
|
28
35
|
"jest": "^29.7.0",
|
|
29
|
-
"
|
|
30
|
-
"mongodb-memory-server": "^8.9.0",
|
|
31
|
-
"prettier": "^2.8.5",
|
|
36
|
+
"prettier": "^2.7.1",
|
|
32
37
|
"sinon": "^16.1.1",
|
|
33
38
|
"typescript": "^5.0.2"
|
|
34
39
|
},
|
|
@@ -48,5 +53,5 @@
|
|
|
48
53
|
},
|
|
49
54
|
"homepage": "https://github.com/friggframework/frigg#readme",
|
|
50
55
|
"description": "",
|
|
51
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "5c1c0838a8e2d0d77e557ce11d9ad0899c3f464d"
|
|
52
57
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const PACKAGE_JSON = 'package.json';
|
|
4
|
+
|
|
5
|
+
function findNearestBackendPackageJson() {
|
|
6
|
+
let currentDir = process.cwd();
|
|
7
|
+
|
|
8
|
+
// First check if we're in production by looking for package.json in the current directory
|
|
9
|
+
const rootPackageJson = path.join(currentDir, PACKAGE_JSON);
|
|
10
|
+
if (fs.existsSync(rootPackageJson)) {
|
|
11
|
+
// In production environment, check for index.js in the same directory
|
|
12
|
+
const indexJs = path.join(currentDir, 'index.js');
|
|
13
|
+
if (fs.existsSync(indexJs)) {
|
|
14
|
+
return rootPackageJson;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// If not found at root or not in production, look for it in the backend directory
|
|
19
|
+
while (currentDir !== path.parse(currentDir).root) {
|
|
20
|
+
const packageJsonPath = path.join(currentDir, 'backend', PACKAGE_JSON);
|
|
21
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
22
|
+
return packageJsonPath;
|
|
23
|
+
}
|
|
24
|
+
currentDir = path.dirname(currentDir);
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function validateBackendPath(backendPath) {
|
|
30
|
+
if (!backendPath) {
|
|
31
|
+
throw new Error('Could not find a backend package.json file.');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = {
|
|
36
|
+
findNearestBackendPackageJson,
|
|
37
|
+
validateBackendPath,
|
|
38
|
+
};
|