@friggframework/core 2.0.0--canary.405.b81908d.0 → 2.0.0--canary.397.c07f148.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.
Files changed (100) hide show
  1. package/README.md +931 -50
  2. package/core/create-handler.js +1 -0
  3. package/credential/credential-repository.js +56 -0
  4. package/credential/use-cases/get-credential-for-user.js +21 -0
  5. package/credential/use-cases/update-authentication-status.js +15 -0
  6. package/encrypt/encrypt.js +27 -4
  7. package/handlers/app-definition-loader.js +38 -0
  8. package/handlers/app-handler-helpers.js +0 -3
  9. package/handlers/backend-utils.js +42 -44
  10. package/handlers/routers/auth.js +3 -14
  11. package/handlers/routers/integration-defined-routers.js +8 -5
  12. package/handlers/routers/user.js +25 -5
  13. package/handlers/workers/integration-defined-workers.js +6 -3
  14. package/index.js +1 -16
  15. package/integrations/index.js +0 -5
  16. package/integrations/integration-base.js +142 -46
  17. package/integrations/integration-repository.js +80 -0
  18. package/integrations/integration-router.js +303 -181
  19. package/integrations/options.js +1 -1
  20. package/integrations/tests/doubles/dummy-integration-class.js +90 -0
  21. package/integrations/tests/doubles/test-integration-repository.js +89 -0
  22. package/integrations/tests/use-cases/create-integration.test.js +124 -0
  23. package/integrations/tests/use-cases/delete-integration-for-user.test.js +143 -0
  24. package/integrations/tests/use-cases/get-integration-for-user.test.js +143 -0
  25. package/integrations/tests/use-cases/get-integration-instance.test.js +169 -0
  26. package/integrations/tests/use-cases/get-integrations-for-user.test.js +169 -0
  27. package/integrations/tests/use-cases/get-possible-integrations.test.js +188 -0
  28. package/integrations/tests/use-cases/update-integration-messages.test.js +142 -0
  29. package/integrations/tests/use-cases/update-integration-status.test.js +103 -0
  30. package/integrations/tests/use-cases/update-integration.test.js +134 -0
  31. package/integrations/use-cases/create-integration.js +71 -0
  32. package/integrations/use-cases/delete-integration-for-user.js +72 -0
  33. package/integrations/use-cases/get-integration-for-user.js +78 -0
  34. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  35. package/integrations/use-cases/get-integration-instance.js +82 -0
  36. package/integrations/use-cases/get-integrations-for-user.js +76 -0
  37. package/integrations/use-cases/get-possible-integrations.js +27 -0
  38. package/integrations/use-cases/index.js +11 -0
  39. package/integrations/use-cases/update-integration-messages.js +31 -0
  40. package/integrations/use-cases/update-integration-status.js +28 -0
  41. package/integrations/use-cases/update-integration.js +91 -0
  42. package/integrations/utils/map-integration-dto.js +36 -0
  43. package/{module-plugin → modules}/entity.js +1 -0
  44. package/{module-plugin → modules}/index.js +0 -8
  45. package/modules/module-factory.js +54 -0
  46. package/modules/module-repository.js +107 -0
  47. package/modules/module.js +218 -0
  48. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  49. package/{module-plugin → modules}/test/mock-api/definition.js +12 -8
  50. package/modules/tests/doubles/test-module-factory.js +16 -0
  51. package/modules/tests/doubles/test-module-repository.js +19 -0
  52. package/modules/use-cases/get-entities-for-user.js +32 -0
  53. package/modules/use-cases/get-entity-options-by-id.js +58 -0
  54. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  55. package/modules/use-cases/get-module-instance-from-type.js +31 -0
  56. package/modules/use-cases/get-module.js +56 -0
  57. package/modules/use-cases/process-authorization-callback.js +108 -0
  58. package/modules/use-cases/refresh-entity-options.js +58 -0
  59. package/modules/use-cases/test-module-auth.js +54 -0
  60. package/modules/utils/map-module-dto.js +18 -0
  61. package/package.json +5 -5
  62. package/syncs/sync.js +0 -1
  63. package/types/integrations/index.d.ts +2 -6
  64. package/types/module-plugin/index.d.ts +4 -56
  65. package/types/syncs/index.d.ts +0 -2
  66. package/user/tests/doubles/test-user-repository.js +72 -0
  67. package/user/tests/use-cases/create-individual-user.test.js +24 -0
  68. package/user/tests/use-cases/create-organization-user.test.js +28 -0
  69. package/user/tests/use-cases/create-token-for-user-id.test.js +19 -0
  70. package/user/tests/use-cases/get-user-from-bearer-token.test.js +64 -0
  71. package/user/tests/use-cases/login-user.test.js +140 -0
  72. package/user/use-cases/create-individual-user.js +61 -0
  73. package/user/use-cases/create-organization-user.js +47 -0
  74. package/user/use-cases/create-token-for-user-id.js +30 -0
  75. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  76. package/user/use-cases/login-user.js +122 -0
  77. package/user/user-repository.js +62 -0
  78. package/user/user.js +77 -0
  79. package/handlers/routers/HEALTHCHECK.md +0 -240
  80. package/handlers/routers/health.js +0 -451
  81. package/handlers/routers/health.test.js +0 -203
  82. package/handlers/routers/middleware/loadUser.js +0 -15
  83. package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
  84. package/integrations/create-frigg-backend.js +0 -31
  85. package/integrations/integration-factory.js +0 -251
  86. package/integrations/integration-user.js +0 -144
  87. package/integrations/test/integration-base.test.js +0 -144
  88. package/module-plugin/auther.js +0 -393
  89. package/module-plugin/entity-manager.js +0 -70
  90. package/module-plugin/manager.js +0 -169
  91. package/module-plugin/module-factory.js +0 -61
  92. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  93. /package/{module-plugin → modules}/credential.js +0 -0
  94. /package/{module-plugin → modules}/requester/api-key.js +0 -0
  95. /package/{module-plugin → modules}/requester/basic.js +0 -0
  96. /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
  97. /package/{module-plugin → modules}/requester/requester.js +0 -0
  98. /package/{module-plugin → modules}/requester/requester.test.js +0 -0
  99. /package/{module-plugin → modules}/test/auther.test.js +0 -0
  100. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -1,203 +0,0 @@
1
- process.env.HEALTH_API_KEY = 'test-api-key';
2
-
3
- jest.mock('mongoose', () => ({
4
- set: jest.fn(),
5
- connection: {
6
- readyState: 1,
7
- db: {
8
- admin: () => ({
9
- ping: jest.fn().mockResolvedValue(true)
10
- })
11
- }
12
- }
13
- }));
14
-
15
- jest.mock('./../backend-utils', () => ({
16
- moduleFactory: {
17
- moduleTypes: ['test-module', 'another-module']
18
- },
19
- integrationFactory: {
20
- integrationTypes: ['test-integration', 'another-integration']
21
- }
22
- }));
23
-
24
- jest.mock('./../app-handler-helpers', () => ({
25
- createAppHandler: jest.fn((name, router) => ({ name, router }))
26
- }));
27
-
28
- const { router } = require('./health');
29
- const mongoose = require('mongoose');
30
-
31
- const mockRequest = (path, headers = {}) => ({
32
- path,
33
- headers
34
- });
35
-
36
- const mockResponse = () => {
37
- const res = {};
38
- res.status = jest.fn().mockReturnValue(res);
39
- res.json = jest.fn().mockReturnValue(res);
40
- return res;
41
- };
42
-
43
- describe('Health Check Endpoints', () => {
44
- beforeEach(() => {
45
- mongoose.connection.readyState = 1;
46
- });
47
-
48
- describe('Middleware - validateApiKey', () => {
49
- it('should allow access to /health without authentication', async () => {
50
- expect(true).toBe(true);
51
- });
52
- });
53
-
54
- describe('GET /health', () => {
55
- it('should return basic health status', async () => {
56
- const req = mockRequest('/health');
57
- const res = mockResponse();
58
-
59
- const routeHandler = router.stack.find(layer =>
60
- layer.route && layer.route.path === '/health'
61
- ).route.stack[0].handle;
62
-
63
- await routeHandler(req, res);
64
-
65
- expect(res.status).toHaveBeenCalledWith(200);
66
- expect(res.json).toHaveBeenCalledWith({
67
- status: 'ok',
68
- timestamp: expect.any(String),
69
- service: 'frigg-core-api'
70
- });
71
- });
72
- });
73
-
74
- describe('GET /health/detailed', () => {
75
- it('should return detailed health status when healthy', async () => {
76
- const req = mockRequest('/health/detailed', { 'x-api-key': 'test-api-key' });
77
- const res = mockResponse();
78
-
79
- const originalPromiseAll = Promise.all;
80
- Promise.all = jest.fn().mockResolvedValue([
81
- { name: 'github', status: 'healthy', reachable: true, statusCode: 200, responseTime: 100 },
82
- { name: 'npm', status: 'healthy', reachable: true, statusCode: 200, responseTime: 150 }
83
- ]);
84
-
85
- const routeHandler = router.stack.find(layer =>
86
- layer.route && layer.route.path === '/health/detailed'
87
- ).route.stack[0].handle;
88
-
89
- await routeHandler(req, res);
90
-
91
- Promise.all = originalPromiseAll;
92
-
93
- expect(res.status).toHaveBeenCalledWith(200);
94
- expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
95
- status: 'healthy',
96
- service: 'frigg-core-api',
97
- timestamp: expect.any(String),
98
- checks: expect.objectContaining({
99
- database: expect.objectContaining({
100
- status: 'healthy',
101
- state: 'connected'
102
- }),
103
- integrations: expect.objectContaining({
104
- status: 'healthy'
105
- })
106
- }),
107
- responseTime: expect.any(Number)
108
- }));
109
-
110
- const response = res.json.mock.calls[0][0];
111
- expect(response).not.toHaveProperty('version');
112
- expect(response).not.toHaveProperty('uptime');
113
- expect(response.checks).not.toHaveProperty('memory');
114
- expect(response.checks.database).not.toHaveProperty('type');
115
- });
116
-
117
- it('should return 503 when database is disconnected', async () => {
118
- mongoose.connection.readyState = 0;
119
-
120
- const req = mockRequest('/health/detailed', { 'x-api-key': 'test-api-key' });
121
- const res = mockResponse();
122
-
123
- const originalPromiseAll = Promise.all;
124
- Promise.all = jest.fn().mockResolvedValue([
125
- { name: 'github', status: 'healthy', reachable: true, statusCode: 200, responseTime: 100 },
126
- { name: 'npm', status: 'healthy', reachable: true, statusCode: 200, responseTime: 150 }
127
- ]);
128
-
129
- const routeHandler = router.stack.find(layer =>
130
- layer.route && layer.route.path === '/health/detailed'
131
- ).route.stack[0].handle;
132
-
133
- await routeHandler(req, res);
134
-
135
- Promise.all = originalPromiseAll;
136
-
137
- expect(res.status).toHaveBeenCalledWith(503);
138
- expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
139
- status: 'unhealthy'
140
- }));
141
- });
142
- });
143
-
144
- describe('GET /health/live', () => {
145
- it('should return alive status', async () => {
146
- const req = mockRequest('/health/live', { 'x-api-key': 'test-api-key' });
147
- const res = mockResponse();
148
-
149
- const routeHandler = router.stack.find(layer =>
150
- layer.route && layer.route.path === '/health/live'
151
- ).route.stack[0].handle;
152
-
153
- routeHandler(req, res);
154
-
155
- expect(res.status).toHaveBeenCalledWith(200);
156
- expect(res.json).toHaveBeenCalledWith({
157
- status: 'alive',
158
- timestamp: expect.any(String)
159
- });
160
- });
161
- });
162
-
163
- describe('GET /health/ready', () => {
164
- it('should return ready when all checks pass', async () => {
165
- const req = mockRequest('/health/ready', { 'x-api-key': 'test-api-key' });
166
- const res = mockResponse();
167
-
168
- const routeHandler = router.stack.find(layer =>
169
- layer.route && layer.route.path === '/health/ready'
170
- ).route.stack[0].handle;
171
-
172
- await routeHandler(req, res);
173
-
174
- expect(res.status).toHaveBeenCalledWith(200);
175
- expect(res.json).toHaveBeenCalledWith({
176
- ready: true,
177
- timestamp: expect.any(String),
178
- checks: {
179
- database: true,
180
- modules: true
181
- }
182
- });
183
- });
184
-
185
- it('should return 503 when database is not connected', async () => {
186
- mongoose.connection.readyState = 0;
187
-
188
- const req = mockRequest('/health/ready', { 'x-api-key': 'test-api-key' });
189
- const res = mockResponse();
190
-
191
- const routeHandler = router.stack.find(layer =>
192
- layer.route && layer.route.path === '/health/ready'
193
- ).route.stack[0].handle;
194
-
195
- await routeHandler(req, res);
196
-
197
- expect(res.status).toHaveBeenCalledWith(503);
198
- expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
199
- ready: false
200
- }));
201
- });
202
- });
203
- });
@@ -1,15 +0,0 @@
1
- const catchAsyncError = require('express-async-handler');
2
- const { User } = require('../../backend-utils');
3
-
4
- module.exports = catchAsyncError(async (req, res, next) => {
5
- const authorizationHeader = req.headers.authorization;
6
-
7
- if (authorizationHeader) {
8
- // Removes "Bearer " and trims
9
- const token = authorizationHeader.split(' ')[1].trim();
10
- // Load user for later middleware/routes to use
11
- req.user = await User.newUser({ token });
12
- }
13
-
14
- return next();
15
- });
@@ -1,12 +0,0 @@
1
- const Boom = require('@hapi/boom');
2
-
3
- // CheckLoggedIn Middleware
4
- const requireLoggedInUser = (req, res, next) => {
5
- if (!req.user || !req.user.isLoggedIn()) {
6
- throw Boom.unauthorized('Invalid Token');
7
- }
8
-
9
- next();
10
- };
11
-
12
- module.exports = { requireLoggedInUser };
@@ -1,31 +0,0 @@
1
- const {IntegrationFactory, IntegrationHelper} = require('./integration-factory');
2
- const User = require('./integration-user');
3
-
4
- function createFriggBackend(appDefinition) {
5
- const {integrations = [], user=null} = appDefinition
6
- const integrationFactory = new IntegrationFactory(integrations);
7
- if (user) {
8
- if (user.usePassword) {
9
- User.usePassword = true;
10
- }
11
- if (user.primary === 'organization') {
12
- User.primary = User.OrganizationUser
13
- }
14
- if (user.individualUserRequired !== undefined) {
15
- User.individualUserRequired = user.individualUserRequired
16
- }
17
- if (user.organizationUserRequired !== undefined) {
18
- User.organizationUserRequired = user.organizationUserRequired
19
- }
20
-
21
- }
22
- const backend = {
23
- integrationFactory,
24
- moduleFactory: integrationFactory.moduleFactory,
25
- IntegrationHelper,
26
- User: User
27
- }
28
- return backend
29
- }
30
-
31
- module.exports = { createFriggBackend }
@@ -1,251 +0,0 @@
1
- const { ModuleFactory, Credential, Entity } = require('../module-plugin');
2
- const { IntegrationModel } = require('./integration-model');
3
- const _ = require('lodash');
4
-
5
- class IntegrationFactory {
6
- constructor(integrationClasses = []) {
7
- this.integrationClasses = integrationClasses;
8
- this.moduleFactory = new ModuleFactory(...this.getModules());
9
- this.integrationTypes = this.integrationClasses.map(
10
- (IntegrationClass) => IntegrationClass.getName()
11
- );
12
- this.getIntegrationDefinitions = this.integrationClasses.map(
13
- (IntegrationClass) => IntegrationClass.Definition
14
- );
15
- }
16
-
17
- async getIntegrationOptions() {
18
- const options = this.integrationClasses.map(
19
- (IntegrationClass) => IntegrationClass
20
- );
21
- return {
22
- entities: {
23
- options: options.map((IntegrationClass) =>
24
- IntegrationClass.getOptionDetails()
25
- ),
26
- authorized: [],
27
- },
28
- integrations: [],
29
- };
30
- }
31
-
32
- getModules() {
33
- return [
34
- ...new Set(
35
- this.integrationClasses
36
- .map((integration) =>
37
- Object.values(integration.Definition.modules).map(
38
- (module) => module.definition
39
- )
40
- )
41
- .flat()
42
- ),
43
- ];
44
- }
45
-
46
- getIntegrationClassByType(type) {
47
- const integrationClassIndex = this.integrationTypes.indexOf(type);
48
- return this.integrationClasses[integrationClassIndex];
49
- }
50
- getModuleTypesAndKeys(integrationClass) {
51
- const moduleTypesAndKeys = {};
52
- const moduleTypeCount = {};
53
-
54
- if (integrationClass && integrationClass.Definition.modules) {
55
- for (const [key, moduleClass] of Object.entries(
56
- integrationClass.Definition.modules
57
- )) {
58
- if (
59
- moduleClass &&
60
- typeof moduleClass.definition.getName === 'function'
61
- ) {
62
- const moduleType = moduleClass.definition.getName();
63
-
64
- // Check if this module type has already been seen
65
- if (moduleType in moduleTypesAndKeys) {
66
- throw new Error(
67
- `Duplicate module type "${moduleType}" found in integration class definition.`
68
- );
69
- }
70
-
71
- // Well how baout now
72
-
73
- moduleTypesAndKeys[moduleType] = key;
74
- moduleTypeCount[moduleType] =
75
- (moduleTypeCount[moduleType] || 0) + 1;
76
- }
77
- }
78
- }
79
-
80
- // Check for any module types with count > 1
81
- for (const [moduleType, count] of Object.entries(moduleTypeCount)) {
82
- if (count > 1) {
83
- throw new Error(
84
- `Multiple instances of module type "${moduleType}" found in integration class definition.`
85
- );
86
- }
87
- }
88
-
89
- return moduleTypesAndKeys;
90
- }
91
-
92
- async getInstanceFromIntegrationId(params) {
93
- const integrationRecord = await IntegrationHelper.getIntegrationById(
94
- params.integrationId
95
- );
96
- let { userId } = params;
97
- if (!integrationRecord) {
98
- throw new Error(
99
- `No integration found by the ID of ${params.integrationId}`
100
- );
101
- }
102
-
103
- if (!userId) {
104
- userId = integrationRecord.user._id.toString();
105
- } else if (userId.toString() !== integrationRecord.user.toString()) {
106
- throw new Error(
107
- `Integration ${
108
- params.integrationId
109
- } does not belong to User ${userId}, ${integrationRecord.user.toString()}`
110
- );
111
- }
112
-
113
- const integrationClass = this.getIntegrationClassByType(
114
- integrationRecord.config.type
115
- );
116
-
117
- const instance = new integrationClass({
118
- userId,
119
- integrationId: params.integrationId,
120
- });
121
-
122
- if (
123
- integrationRecord.entityReference &&
124
- Object.keys(integrationRecord.entityReference) > 0
125
- ) {
126
- // Use the specified entityReference to find the modules and load them according to their key
127
- // entityReference will be a map of entityIds with their corresponding desired key
128
- for (const [entityId, key] of Object.entries(
129
- integrationRecord.entityReference
130
- )) {
131
- const moduleInstance =
132
- await this.moduleFactory.getModuleInstanceFromEntityId(
133
- entityId,
134
- integrationRecord.user
135
- );
136
- instance[key] = moduleInstance;
137
- }
138
- } else {
139
- // for each entity, get the moduleinstance and load them according to their keys
140
- // If it's the first entity, load the moduleinstance into primary as well
141
- // If it's the second entity, load the moduleinstance into target as well
142
- const moduleTypesAndKeys =
143
- this.getModuleTypesAndKeys(integrationClass);
144
- for (let i = 0; i < integrationRecord.entities.length; i++) {
145
- const entityId = integrationRecord.entities[i];
146
- const moduleInstance =
147
- await this.moduleFactory.getModuleInstanceFromEntityId(
148
- entityId,
149
- integrationRecord.user
150
- );
151
- const moduleType = moduleInstance.getName();
152
- const key = moduleTypesAndKeys[moduleType];
153
- instance[key] = moduleInstance;
154
- if (i === 0) {
155
- instance.primary = moduleInstance;
156
- } else if (i === 1) {
157
- instance.target = moduleInstance;
158
- }
159
- }
160
- }
161
- instance.record = integrationRecord;
162
-
163
- try {
164
- const additionalUserActions =
165
- await instance.loadDynamicUserActions();
166
- instance.events = { ...instance.events, ...additionalUserActions };
167
- } catch (e) {
168
- instance.record.status = 'ERROR';
169
- instance.record.messages.errors.push(e);
170
- await instance.record.save();
171
- }
172
- // Register all of the event handlers
173
-
174
- await instance.registerEventHandlers();
175
- return instance;
176
- }
177
-
178
- async createIntegration(entities, userId, config) {
179
- const integrationRecord = await IntegrationModel.create({
180
- entities: entities,
181
- user: userId,
182
- config,
183
- version: '0.0.0',
184
- });
185
- return await this.getInstanceFromIntegrationId({
186
- integrationId: integrationRecord.id,
187
- userId,
188
- });
189
- }
190
- }
191
-
192
- const IntegrationHelper = {
193
- getFormattedIntegration: async function (integrationRecord) {
194
- const integrationObj = {
195
- id: integrationRecord.id,
196
- status: integrationRecord.status,
197
- config: integrationRecord.config,
198
- entities: [],
199
- version: integrationRecord.version,
200
- messages: integrationRecord.messages,
201
- };
202
- for (const entityId of integrationRecord.entities) {
203
- // Only return non-internal fields. Leverages "select" and "options" to non-excepted fields and a pure object.
204
- const entity = await Entity.findById(
205
- entityId,
206
- '-createdAt -updatedAt -user -credentials -credential -_id -__t -__v',
207
- { lean: true }
208
- );
209
- integrationObj.entities.push({
210
- id: entityId,
211
- ...entity,
212
- });
213
- }
214
- return integrationObj;
215
- },
216
-
217
- getIntegrationsForUserId: async function (userId) {
218
- const integrationList = await IntegrationModel.find({ user: userId });
219
- return await Promise.all(
220
- integrationList.map(
221
- async (integrationRecord) =>
222
- await IntegrationHelper.getFormattedIntegration(
223
- integrationRecord
224
- )
225
- )
226
- );
227
- },
228
-
229
- deleteIntegrationForUserById: async function (userId, integrationId) {
230
- const integrationList = await IntegrationModel.find({
231
- user: userId,
232
- _id: integrationId,
233
- });
234
- if (integrationList.length !== 1) {
235
- throw new Error(
236
- `Integration with id of ${integrationId} does not exist for this user`
237
- );
238
- }
239
- await IntegrationModel.deleteOne({ _id: integrationId });
240
- },
241
-
242
- getIntegrationById: async function (id) {
243
- return IntegrationModel.findById(id).populate('entities');
244
- },
245
-
246
- listCredentials: async function (options) {
247
- return Credential.find(options);
248
- },
249
- };
250
-
251
- module.exports = { IntegrationFactory, IntegrationHelper };
@@ -1,144 +0,0 @@
1
- const bcrypt = require('bcryptjs');
2
- const crypto = require('crypto');
3
- const { get } = require('../assertions');
4
- const { Token } = require('../database/models/Token');
5
- const { IndividualUser } = require('../database/models/IndividualUser');
6
- const { OrganizationUser } = require('../database/models/OrganizationUser');
7
- const Boom = require('@hapi/boom');
8
-
9
- class User {
10
- static IndividualUser = IndividualUser;
11
- static OrganizationUser = OrganizationUser;
12
- static Token = Token;
13
- static usePassword = false
14
- static primary = User.IndividualUser;
15
- static individualUserRequired = true;
16
- static organizationUserRequired = false;
17
-
18
- constructor() {
19
- this.user = null;
20
- this.individualUser = null;
21
- this.organizationUser = null;
22
- }
23
-
24
- getPrimaryUser() {
25
- if (User.primary === User.OrganizationUser) {
26
- return this.organizationUser;
27
- }
28
- return this.individualUser;
29
- }
30
-
31
- getUserId() {
32
- return this.getPrimaryUser()?.id;
33
- }
34
-
35
- isLoggedIn() {
36
- return Boolean(this.getUserId());
37
- }
38
-
39
- async createUserToken(minutes) {
40
- const rawToken = crypto.randomBytes(20).toString('hex');
41
- const createdToken = await User.Token.createTokenWithExpire(this.getUserId(), rawToken, 120);
42
- const tokenBuf = User.Token.createBase64BufferToken(createdToken, rawToken);
43
- return tokenBuf;
44
- }
45
-
46
- static async newUser(params={}) {
47
- const user = new User();
48
- const token = get(params, 'token', null);
49
- if (token) {
50
- const jsonToken = this.Token.getJSONTokenFromBase64BufferToken(token);
51
- const sessionToken = await this.Token.validateAndGetTokenFromJSONToken(jsonToken);
52
- if (this.primary === User.OrganizationUser) {
53
- user.organizationUser = await this.OrganizationUser.findById(sessionToken.user);
54
- } else {
55
- user.individualUser = await this.IndividualUser.findById(sessionToken.user);
56
- }
57
- }
58
- return user;
59
- }
60
-
61
- static async createIndividualUser(params) {
62
- const user = await this.newUser(params);
63
- let hashword;
64
- if (this.usePassword) {
65
- hashword = get(params, 'password');
66
- }
67
-
68
- const email = get(params, 'email', null);
69
- const username = get(params, 'username', null);
70
- if (!email && !username) {
71
- throw Boom.badRequest('email or username is required');
72
- }
73
-
74
- const appUserId = get(params, 'appUserId', null);
75
- const organizationUserId = get(params, 'organizationUserId', null);
76
-
77
- user.individualUser = await this.IndividualUser.create({
78
- email,
79
- username,
80
- hashword,
81
- appUserId,
82
- organizationUser: organizationUserId,
83
- });
84
- return user;
85
- }
86
-
87
- static async createOrganizationUser(params) {
88
- const user = await this.newUser(params);
89
- const name = get(params, 'name');
90
- const appOrgId = get(params, 'appOrgId');
91
- user.organizationUser = await this.OrganizationUser.create({
92
- name,
93
- appOrgId,
94
- });
95
- return user;
96
- }
97
-
98
- static async loginUser(params) {
99
- const user = await this.newUser(params);
100
-
101
- if (this.usePassword){
102
- const username = get(params, 'username');
103
- const password = get(params, 'password');
104
-
105
- const individualUser = await this.IndividualUser.findOne({username});
106
-
107
- if (!individualUser) {
108
- throw Boom.unauthorized('incorrect username or password');
109
- }
110
-
111
- const isValid = await bcrypt.compareSync(password, individualUser.hashword);
112
- if (!isValid) {
113
- throw Boom.unauthorized('incorrect username or password');
114
- }
115
- user.individualUser = individualUser;
116
- }
117
- else {
118
- const appUserId = get(params, 'appUserId', null);
119
- user.individualUser = await this.IndividualUser.getUserByAppUserId(
120
- appUserId
121
- );
122
- }
123
-
124
- const appOrgId = get(params, 'appOrgId', null);
125
- user.organizationUser = await this.OrganizationUser.getUserByAppOrgId(
126
- appOrgId
127
- );
128
-
129
- if (this.individualUserRequired) {
130
- if (!user.individualUser) {
131
- throw Boom.unauthorized('user not found');
132
- }
133
- }
134
-
135
- if (this.organizationUserRequired) {
136
- if (!user.organizationUser) {
137
- throw Boom.unauthorized(`org user ${appOrgId} not found`);
138
- }
139
- }
140
- return user;
141
- }
142
- }
143
-
144
- module.exports = User;