@bigid/apps-infrastructure-node-js 0.2.0 → 1.180.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/.github/workflows/bigid_config +3 -0
- package/.github/workflows/npmjs_config +3 -0
- package/.github/workflows/{build-and-push.yml → private-registry-publish.yml} +5 -2
- package/.github/workflows/public-registry-publish.yml +14 -0
- package/CODEOWNERS +1 -0
- package/README.md +31 -2
- package/jsdoc.json +14 -0
- package/lib/abstractProviders/configureProvider.d.ts +10 -0
- package/lib/abstractProviders/configureProvider.js +19 -0
- package/lib/abstractProviders/index.d.ts +1 -0
- package/lib/abstractProviders/index.js +3 -1
- package/lib/abstractProviders/logsProvider.js +9 -1
- package/lib/abstractProviders/manifestProvider.d.ts +2 -1
- package/lib/dto/actionResponseDetails.d.ts +3 -1
- package/lib/dto/actionResponseDetails.js +2 -1
- package/lib/dto/executionContext.d.ts +8 -4
- package/lib/dto/index.d.ts +1 -1
- package/lib/dto/subExecutionItem.d.ts +7 -0
- package/lib/dto/subExecutionItem.js +12 -0
- package/lib/dto/tenantRegistration.d.ts +5 -0
- package/lib/dto/tenantRegistration.js +2 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.js +16 -2
- package/lib/server.d.ts +10 -6
- package/lib/server.js +14 -9
- package/lib/services/actionsHubService.d.ts +45 -0
- package/lib/services/actionsHubService.js +105 -0
- package/lib/services/batchProcessManager.d.ts +2 -0
- package/lib/services/batchProcessManager.js +45 -0
- package/lib/services/bigidProxyService.d.ts +18 -2
- package/lib/services/bigidProxyService.js +34 -14
- package/lib/services/dataSourceService.d.ts +4 -0
- package/lib/services/dataSourceService.js +26 -0
- package/lib/services/encryptionService.d.ts +1 -0
- package/lib/services/encryptionService.js +67 -0
- package/lib/services/index.d.ts +3 -0
- package/lib/services/index.js +12 -1
- package/lib/services/schedulerService.d.ts +11 -0
- package/lib/services/schedulerService.js +41 -0
- package/lib/utils/appLogger.d.ts +10 -1
- package/lib/utils/appLogger.js +38 -7
- package/lib/utils/index.d.ts +1 -1
- package/lib/utils/index.js +15 -3
- package/lib/utils/tokenUtil.d.ts +3 -0
- package/lib/utils/tokenUtil.js +62 -0
- package/package.json +11 -4
- package/src/abstractProviders/configureProvider.ts +15 -0
- package/src/abstractProviders/index.ts +1 -0
- package/src/abstractProviders/logsProvider.ts +11 -3
- package/src/abstractProviders/manifestProvider.ts +3 -1
- package/src/dto/actionResponseDetails.ts +5 -1
- package/src/dto/executionContext.ts +9 -4
- package/src/dto/index.ts +2 -2
- package/src/dto/subExecutionItem.ts +13 -0
- package/src/dto/tenantRegistration.ts +5 -0
- package/src/index.ts +17 -1
- package/src/server.ts +33 -20
- package/src/services/actionsHubService.ts +141 -0
- package/src/services/batchProcessManager.ts +39 -0
- package/src/services/bigidProxyService.ts +48 -24
- package/src/services/dataSourceService.ts +20 -0
- package/src/services/encryptionService.ts +44 -0
- package/src/services/index.ts +5 -1
- package/src/services/schedulerService.ts +39 -0
- package/src/utils/appLogger.ts +44 -6
- package/src/utils/index.ts +1 -1
- package/src/utils/tokenUtil.ts +65 -0
- package/.dcignore +0 -1547
- package/.idea/apps-infrastructure-node-js.iml +0 -9
- package/.idea/misc.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/prettier.xml +0 -7
- package/.idea/runConfigurations.xml +0 -10
- package/.idea/snyk.project.settings.xml +0 -6
- package/.idea/vcs.xml +0 -6
package/src/server.ts
CHANGED
|
@@ -1,36 +1,49 @@
|
|
|
1
|
-
import express, { NextFunction, Request, Response } from 'express';
|
|
2
|
-
import { ManifestProvider } from './abstractProviders
|
|
3
|
-
import {
|
|
4
|
-
import { IconsProviders } from './abstractProviders
|
|
1
|
+
import express, { Express, NextFunction, Request, Response } from 'express';
|
|
2
|
+
import { ManifestProvider } from './abstractProviders';
|
|
3
|
+
import { logInfo, logError } from './utils';
|
|
4
|
+
import { IconsProviders } from './abstractProviders';
|
|
5
5
|
import { ExecutionProvider, handleExecution } from './abstractProviders/executionProvider';
|
|
6
6
|
import { fetchLogs } from './abstractProviders/logsProvider';
|
|
7
7
|
import createError from 'http-errors';
|
|
8
|
+
import { ConfigureProvider, handleTenantConfigure } from './abstractProviders/configureProvider';
|
|
8
9
|
|
|
9
|
-
export
|
|
10
|
+
export type ServerInit = {
|
|
10
11
|
manifestController: ManifestProvider;
|
|
11
12
|
iconsController: IconsProviders;
|
|
12
|
-
executionController
|
|
13
|
+
executionController?: ExecutionProvider;
|
|
14
|
+
configureController?: ConfigureProvider;
|
|
13
15
|
serverPort?: number;
|
|
14
|
-
|
|
16
|
+
additionalEndpoints?: (app: Express) => void;
|
|
17
|
+
};
|
|
15
18
|
|
|
16
19
|
const app = express();
|
|
17
20
|
|
|
18
|
-
export const deployServer = (
|
|
21
|
+
export const deployServer = ({
|
|
22
|
+
manifestController,
|
|
23
|
+
iconsController,
|
|
24
|
+
executionController,
|
|
25
|
+
serverPort,
|
|
26
|
+
configureController,
|
|
27
|
+
additionalEndpoints,
|
|
28
|
+
}: ServerInit): void => {
|
|
19
29
|
app.use(express.json());
|
|
20
30
|
app.use(express.urlencoded({ extended: false }));
|
|
21
31
|
|
|
22
|
-
app.get('/assets/icon', (req, res) => res.sendFile(
|
|
23
|
-
app.get('/assets/sideBarIcon', (req, res) => res.sendFile(
|
|
24
|
-
app.get('/manifest',
|
|
25
|
-
app.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
app.get('/assets/icon', (req, res) => res.sendFile(iconsController.getIconPath()));
|
|
33
|
+
app.get('/assets/sideBarIcon', (req, res) => res.sendFile(iconsController.getSideBarIconPath()));
|
|
34
|
+
app.get('/manifest', manifestController.getManifest);
|
|
35
|
+
app.get('/logs', async (req: Request, res: Response) => await fetchLogs(req, res));
|
|
36
|
+
|
|
37
|
+
executionController &&
|
|
38
|
+
app.post('/execute', async (req: Request, res: Response) => await handleExecution(req, res, executionController));
|
|
39
|
+
configureController &&
|
|
40
|
+
app.post(
|
|
41
|
+
'/configure',
|
|
42
|
+
async (req: Request, res: Response) => await handleTenantConfigure(req, res, configureController),
|
|
43
|
+
);
|
|
44
|
+
additionalEndpoints && additionalEndpoints(app);
|
|
30
45
|
|
|
31
|
-
app.listen(process.env.PORT ||
|
|
32
|
-
appLogger.info(`Started server at port ${process.env.PORT || serverInit.serverPort}`),
|
|
33
|
-
);
|
|
46
|
+
app.listen(process.env.PORT || serverPort, () => logInfo(`Started server at port ${process.env.PORT || serverPort}`));
|
|
34
47
|
|
|
35
48
|
// catch 404 and forward to error handler
|
|
36
49
|
app.use((req: Request, res: Response, next: NextFunction) => next(createError(404)));
|
|
@@ -38,7 +51,7 @@ export const deployServer = (serverInit: ServerInit) => {
|
|
|
38
51
|
// error handler
|
|
39
52
|
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
|
|
40
53
|
const { message, status } = err;
|
|
41
|
-
|
|
54
|
+
logError(err);
|
|
42
55
|
res.locals.message = message;
|
|
43
56
|
res.locals.error = req.app.get('env') === 'development' ? err : {};
|
|
44
57
|
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { doCallToUrl, RequestMethod } from './bigidProxyService';
|
|
2
|
+
import { getAccessTokenFromRefreshToken } from '../utils/tokenUtil';
|
|
3
|
+
|
|
4
|
+
const REFRESH_TOKEN = process.env.BIGID_REFRESH_TOKEN;
|
|
5
|
+
|
|
6
|
+
const getBigidAccessToken = async (): Promise<string> => {
|
|
7
|
+
if (!REFRESH_TOKEN) {
|
|
8
|
+
throw new Error('BIGID_REFRESH_TOKEN must be supplied as an ENV var in order to call bigid actions hub');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return await getAccessTokenFromRefreshToken(REFRESH_TOKEN);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type Param = {
|
|
15
|
+
name: string;
|
|
16
|
+
value: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type Command = {
|
|
20
|
+
actionName: string;
|
|
21
|
+
command: string;
|
|
22
|
+
params: Param[];
|
|
23
|
+
id: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* use this method to get the currently registered action-center commands.
|
|
28
|
+
* the method is building an execution context for the api call, so you must provide REFRESH_TOKEN and BIGID_BASE_URL as ENV variables
|
|
29
|
+
*
|
|
30
|
+
* @return {Promise<Command[]>} array of the action-center register commands
|
|
31
|
+
*/
|
|
32
|
+
export const getCommandsRegistrations = async (): Promise<Command[]> => {
|
|
33
|
+
try {
|
|
34
|
+
const bigidToken = await getBigidAccessToken();
|
|
35
|
+
const {
|
|
36
|
+
data: { commands },
|
|
37
|
+
} = await doCallToUrl(
|
|
38
|
+
bigidToken,
|
|
39
|
+
RequestMethod.GET,
|
|
40
|
+
`${process.env.BIGID_BASE_URL}/api/v1/action-center/general-commands`,
|
|
41
|
+
);
|
|
42
|
+
return commands;
|
|
43
|
+
} catch (e) {
|
|
44
|
+
throw new Error(`Could not get commands registrations from bigid: ${e}`);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* use this method to execute an action-center commands.
|
|
50
|
+
* this method is used without endpoint for getting status messages, but rather return an execution id.
|
|
51
|
+
* the execution id can be used to poll the current execution status of the executed command.
|
|
52
|
+
*
|
|
53
|
+
* @param {string} actionName - the action name to be executed.
|
|
54
|
+
* @param {string} command - the command to be executed.
|
|
55
|
+
* @param {string} requestorAppName - the app name of the execution requestor.
|
|
56
|
+
* @param {string} webhookEndpoint - the endpoint of the requestor app to receive execution status messages.
|
|
57
|
+
* @param {Record<string, any>} params - the relevant params of the action to be executed.
|
|
58
|
+
* @return {Promise<string>} execution id of the executed action.
|
|
59
|
+
*/
|
|
60
|
+
export const executeCommand = async (
|
|
61
|
+
actionName: string,
|
|
62
|
+
command: string,
|
|
63
|
+
requestorAppName?: string,
|
|
64
|
+
webhookEndpoint?: string,
|
|
65
|
+
params?: Record<string, any>[],
|
|
66
|
+
): Promise<string> => {
|
|
67
|
+
try {
|
|
68
|
+
const bigidToken = await getBigidAccessToken();
|
|
69
|
+
const {
|
|
70
|
+
data: { executionId },
|
|
71
|
+
} = await doCallToUrl(
|
|
72
|
+
bigidToken,
|
|
73
|
+
RequestMethod.POST,
|
|
74
|
+
`${process.env.BIGID_BASE_URL}/api/v1/action-center/general-commands/execute`,
|
|
75
|
+
{
|
|
76
|
+
actionName,
|
|
77
|
+
command,
|
|
78
|
+
feedbackRequestorDetails:
|
|
79
|
+
requestorAppName && webhookEndpoint
|
|
80
|
+
? {
|
|
81
|
+
requestorAppName,
|
|
82
|
+
requestorFeedbackEndpoint: webhookEndpoint,
|
|
83
|
+
}
|
|
84
|
+
: null,
|
|
85
|
+
...(params && { params }),
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
return executionId;
|
|
89
|
+
} catch (e) {
|
|
90
|
+
throw new Error(`Could not execute ${command}: ${e}`);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* use this method to poll an execution status from the action-center.
|
|
96
|
+
*
|
|
97
|
+
* @param {string} execution - the execution id to poll status for.
|
|
98
|
+
* @return object with the updated status details.
|
|
99
|
+
*/
|
|
100
|
+
export const getExecutionStatus = async (executionId: string): Promise<any> => {
|
|
101
|
+
try {
|
|
102
|
+
const bigidToken = await getBigidAccessToken();
|
|
103
|
+
const { data } = await doCallToUrl(
|
|
104
|
+
bigidToken,
|
|
105
|
+
RequestMethod.GET,
|
|
106
|
+
`${process.env.BIGID_BASE_URL}/api/v1/action-center/general-commands/execute/${executionId}`,
|
|
107
|
+
);
|
|
108
|
+
return data;
|
|
109
|
+
} catch (e) {
|
|
110
|
+
throw new Error(`Could not get status for execution id ${executionId}: ${e}`);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* use this method to register application action as an action center command.
|
|
116
|
+
*
|
|
117
|
+
* @param {string} applicationName - the application name of the command to be registered.
|
|
118
|
+
* @param {string} actionName - the action name of the command to be registered.
|
|
119
|
+
* @param {string} command - the command to be registered.
|
|
120
|
+
*/
|
|
121
|
+
export const registerActionAsCommand = async (
|
|
122
|
+
applicationName: string,
|
|
123
|
+
actionName: string,
|
|
124
|
+
command: string,
|
|
125
|
+
): Promise<void> => {
|
|
126
|
+
try {
|
|
127
|
+
const bigidToken = await getBigidAccessToken();
|
|
128
|
+
await doCallToUrl(
|
|
129
|
+
bigidToken,
|
|
130
|
+
RequestMethod.POST,
|
|
131
|
+
`${process.env.BIGID_BASE_URL}/api/v1/action-center/general-commands/register`,
|
|
132
|
+
{
|
|
133
|
+
applicationName,
|
|
134
|
+
actionName,
|
|
135
|
+
command,
|
|
136
|
+
},
|
|
137
|
+
);
|
|
138
|
+
} catch (e) {
|
|
139
|
+
throw new Error(`Could not register ${command}: ${e}`);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { getAuth0Token, tokenExchange } from '../utils/tokenUtil';
|
|
2
|
+
import { getTenantRegistrations } from './bigidProxyService';
|
|
3
|
+
import { TenantRegistration } from '../dto/tenantRegistration';
|
|
4
|
+
import { logError, logInfo } from '../utils';
|
|
5
|
+
import { basename } from 'path';
|
|
6
|
+
|
|
7
|
+
export type BatchFunction = (tenantId: string, tenantDomain: string, tenantToken: string) => void;
|
|
8
|
+
const scriptName = basename(__filename).replace('.ts', '');
|
|
9
|
+
|
|
10
|
+
export const handleBatchProcess = async (callback: BatchFunction): Promise<void> => {
|
|
11
|
+
try {
|
|
12
|
+
logInfo(`Starting scheduled process: ${callback.name}`);
|
|
13
|
+
const auth0Token = await getAuth0Token();
|
|
14
|
+
const bigidToken = await tokenExchange(auth0Token);
|
|
15
|
+
const appRegistrations = await getTenantRegistrations(bigidToken);
|
|
16
|
+
appRegistrations.forEach(tenantRegistration => executeBatchForTenant(auth0Token, tenantRegistration, callback));
|
|
17
|
+
} catch (err: any) {
|
|
18
|
+
logError(`Problem occurred while fetching registrations info: ${err.message}`);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const executeBatchForTenant = async (
|
|
23
|
+
auth0Token: string,
|
|
24
|
+
tenantRegistration: TenantRegistration,
|
|
25
|
+
callback: BatchFunction,
|
|
26
|
+
): Promise<void> => {
|
|
27
|
+
const { tenantId, tenantDomain, companyName } = tenantRegistration;
|
|
28
|
+
try {
|
|
29
|
+
logInfo(`Fetching token for scheduled process`, { tenantId, functionName: executeBatchForTenant.name, scriptName });
|
|
30
|
+
const tenantToken = await tokenExchange(auth0Token, tenantId);
|
|
31
|
+
callback(tenantId, tenantDomain, tenantToken);
|
|
32
|
+
} catch (err: any) {
|
|
33
|
+
logError(`Problem occurred while starting scheduled process for tenant: ${companyName}. error: ${err.message}`, {
|
|
34
|
+
tenantId,
|
|
35
|
+
functionName: executeBatchForTenant.name,
|
|
36
|
+
scriptName,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
};
|
|
@@ -1,26 +1,39 @@
|
|
|
1
1
|
import { createReadStream } from 'fs';
|
|
2
2
|
import { Agent } from 'https';
|
|
3
|
-
import {
|
|
3
|
+
import { ActionResponseDetails, ExecutionContext } from '../dto';
|
|
4
4
|
import FormData from 'form-data';
|
|
5
5
|
|
|
6
6
|
import axios from 'axios';
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
import { logError, logInfo } from '../utils';
|
|
8
|
+
import { TenantRegistration } from '../dto/tenantRegistration';
|
|
9
|
+
|
|
10
|
+
export enum RequestMethod {
|
|
11
|
+
POST = 'post',
|
|
12
|
+
GET = 'get',
|
|
13
|
+
PUT = 'put',
|
|
14
|
+
PATCH = 'patch',
|
|
15
|
+
DELETE = 'delete',
|
|
16
|
+
}
|
|
16
17
|
|
|
17
|
-
const
|
|
18
|
+
export const getTenantRegistrations = async (bigidToken: string): Promise<TenantRegistration[]> => {
|
|
19
|
+
const {
|
|
20
|
+
data: {
|
|
21
|
+
data: { tenants },
|
|
22
|
+
},
|
|
23
|
+
} = await doCallToUrl(
|
|
24
|
+
bigidToken,
|
|
25
|
+
RequestMethod.GET,
|
|
26
|
+
`${process.env.BIGID_BASE_URL}/api/v1/tenant-service/applications/registrations`,
|
|
27
|
+
);
|
|
28
|
+
return tenants;
|
|
29
|
+
};
|
|
30
|
+
export const doCallToUrl = async (
|
|
18
31
|
bigidToken: string,
|
|
19
|
-
requestMethod:
|
|
32
|
+
requestMethod: RequestMethod,
|
|
20
33
|
endpoint: string,
|
|
21
|
-
bodyJson?:
|
|
34
|
+
bodyJson?: Record<string, any>,
|
|
22
35
|
) => {
|
|
23
|
-
|
|
36
|
+
logInfo(`--> bigid-proxy::callBigIdApi: [${requestMethod}] endpoint: ${endpoint}`);
|
|
24
37
|
try {
|
|
25
38
|
const headers = {
|
|
26
39
|
Accept: 'application/json, text/plain, */*',
|
|
@@ -43,18 +56,18 @@ const doCallToUrl = async (
|
|
|
43
56
|
|
|
44
57
|
const res = await axios(requestObj);
|
|
45
58
|
|
|
46
|
-
|
|
59
|
+
logInfo(`<-- bigid-proxy::callBigIdApi: ${endpoint} success`);
|
|
47
60
|
return res;
|
|
48
61
|
} catch (error: any) {
|
|
49
|
-
|
|
50
|
-
|
|
62
|
+
logInfo(`<-- bigid-proxy::callBigIdApi: error calling bigID on endpoint: ${endpoint}, error: ${error}`);
|
|
63
|
+
logError(error.message);
|
|
51
64
|
return error?.message;
|
|
52
65
|
}
|
|
53
66
|
};
|
|
54
67
|
|
|
55
68
|
async function callBigIdApi(
|
|
56
69
|
executionContext: ExecutionContext,
|
|
57
|
-
requestMethod:
|
|
70
|
+
requestMethod: RequestMethod,
|
|
58
71
|
endpoint: string,
|
|
59
72
|
bodyJson?: ActionResponseDetails,
|
|
60
73
|
useEndpointWithoutBigIdBasePath?: boolean,
|
|
@@ -93,11 +106,8 @@ export const executeHttpGet = async (executionContext: ExecutionContext, endpoin
|
|
|
93
106
|
/**
|
|
94
107
|
* the endpoint in BigID, used for POST requests. e.g - {BigIDBaseUrl}/scan
|
|
95
108
|
*/
|
|
96
|
-
export const executeHttpPost = async (
|
|
97
|
-
executionContext
|
|
98
|
-
endpoint: string,
|
|
99
|
-
actionResponseDetails: ActionResponseDetails,
|
|
100
|
-
) => await callBigIdApi(executionContext, RequestMethod.POST, endpoint, actionResponseDetails);
|
|
109
|
+
export const executeHttpPost = async (executionContext: ExecutionContext, endpoint: string, obj: any) =>
|
|
110
|
+
await callBigIdApi(executionContext, RequestMethod.POST, endpoint, obj);
|
|
101
111
|
|
|
102
112
|
/**
|
|
103
113
|
* the endpoint in BigID, used for POST requests. e.g - {BigIDBaseUrl}/scan
|
|
@@ -105,6 +115,12 @@ export const executeHttpPost = async (
|
|
|
105
115
|
export const executeHttpPut = async (executionContext: ExecutionContext, endpoint: string, obj: any) =>
|
|
106
116
|
await callBigIdApi(executionContext, RequestMethod.PUT, endpoint, obj);
|
|
107
117
|
|
|
118
|
+
/**
|
|
119
|
+
* the endpoint in BigID, used for DELETE requests.
|
|
120
|
+
*/
|
|
121
|
+
export const executeHttpDelete = async (executionContext: ExecutionContext, endpoint: string) =>
|
|
122
|
+
await callBigIdApi(executionContext, RequestMethod.DELETE, endpoint);
|
|
123
|
+
|
|
108
124
|
export const uploadAttachment = (executionContext: ExecutionContext, filePathToUpload: string) => {
|
|
109
125
|
const formData = new FormData();
|
|
110
126
|
formData.append('file', createReadStream(filePathToUpload));
|
|
@@ -133,6 +149,14 @@ export const getValueFromAppStorage = async (executionContext: ExecutionContext,
|
|
|
133
149
|
return data === 'Key not found' ? null : data.value;
|
|
134
150
|
};
|
|
135
151
|
|
|
152
|
+
export const deleteKeyFromAppStorage = async (executionContext: ExecutionContext, key: string) => {
|
|
153
|
+
await executeHttpDelete(executionContext, `tpa/${executionContext.tpaId}/storage/key/${key}`);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export const sendBiEvent = async (executionContext: ExecutionContext, eventType: string, data: Object) => {
|
|
157
|
+
return executeHttpPost(executionContext, 'bi-events', { event_type: eventType, data: data });
|
|
158
|
+
};
|
|
159
|
+
|
|
136
160
|
export const saveInStorage = async (
|
|
137
161
|
executionContext: ExecutionContext,
|
|
138
162
|
keyToStore: any,
|
|
@@ -145,4 +169,4 @@ export const saveInStorage = async (
|
|
|
145
169
|
value: valueToStore,
|
|
146
170
|
},
|
|
147
171
|
],
|
|
148
|
-
});
|
|
172
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { decrypt } from './encryptionService';
|
|
2
|
+
import { ExecutionContext } from '../dto';
|
|
3
|
+
import { executeHttpGet } from './bigidProxyService';
|
|
4
|
+
|
|
5
|
+
export const fetchDataSourceCredentials = async (
|
|
6
|
+
executionContext: ExecutionContext,
|
|
7
|
+
dataSourceName: string,
|
|
8
|
+
): Promise<{ [key: string]: string }> => {
|
|
9
|
+
const res = await executeHttpGet(
|
|
10
|
+
executionContext,
|
|
11
|
+
'/tpa/' + executionContext.tpaId + '/credentials/' + dataSourceName.replace(' ', '%20'),
|
|
12
|
+
);
|
|
13
|
+
const resData = res.data;
|
|
14
|
+
return Object.keys(resData).reduce((acc: { [key: string]: string }, key) => {
|
|
15
|
+
const credentialObject = resData[key];
|
|
16
|
+
const credentialValue = credentialObject.value?.toString();
|
|
17
|
+
acc[key] = credentialObject.encrypted ? decrypt(credentialValue) : credentialValue;
|
|
18
|
+
return acc;
|
|
19
|
+
}, {});
|
|
20
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as crypto from 'crypto';
|
|
2
|
+
|
|
3
|
+
type IvAndText = { iv: string | Buffer; cleanText: string | NodeJS.ArrayBufferView };
|
|
4
|
+
|
|
5
|
+
enum Defaults {
|
|
6
|
+
SHA_ALGORITHM = 'sha256',
|
|
7
|
+
AES_ALGORITHM = 'aes-256-cbc',
|
|
8
|
+
IV = '4e5Wa71fYoT7MFEX',
|
|
9
|
+
BASE64_ENC = 'base64',
|
|
10
|
+
UTF8_ENC = 'utf8',
|
|
11
|
+
DOLLAR_DEL = '$',
|
|
12
|
+
SECRET_KEY = 'SECRET_KEY',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const decrypt = (encryptedText: string): string => {
|
|
16
|
+
const ivAndText = getAndUseOldIvOrNew(encryptedText);
|
|
17
|
+
const decipher = crypto.createDecipheriv(Defaults.AES_ALGORITHM, makeKey(), ivAndText.iv);
|
|
18
|
+
let decryptedData = decipher.update(ivAndText.cleanText as string, Defaults.BASE64_ENC, Defaults.UTF8_ENC);
|
|
19
|
+
decryptedData += decipher.final(Defaults.UTF8_ENC);
|
|
20
|
+
return decryptedData;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const makeKey = (): Buffer => {
|
|
24
|
+
const md = crypto.createHash(Defaults.SHA_ALGORITHM);
|
|
25
|
+
try {
|
|
26
|
+
const secretKey = process.env.APPLICATION_CREDENTIALS_KEY ?? '';
|
|
27
|
+
const key = md.update(secretKey).digest();
|
|
28
|
+
return key;
|
|
29
|
+
} catch (error) {
|
|
30
|
+
throw new Error("Can't create a key. Check for APPLICATION_CREDENTIALS_KEY in your environment variables.");
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const getAndUseOldIvOrNew = (text: string | Buffer): IvAndText => {
|
|
35
|
+
const ivAndText: IvAndText = { iv: Defaults.IV, cleanText: text };
|
|
36
|
+
if (text.includes(Defaults.DOLLAR_DEL)) {
|
|
37
|
+
const [cipher_blob, ...cipher_blob2] = (text as string).split(Defaults.DOLLAR_DEL);
|
|
38
|
+
const cipherBlobPartTwo = cipher_blob2.join(Defaults.DOLLAR_DEL);
|
|
39
|
+
ivAndText.iv = Buffer.from(cipher_blob, Defaults.BASE64_ENC);
|
|
40
|
+
ivAndText.cleanText = Buffer.from(cipherBlobPartTwo, Defaults.BASE64_ENC);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return ivAndText;
|
|
44
|
+
};
|
package/src/services/index.ts
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { BatchFunction, handleBatchProcess } from './batchProcessManager';
|
|
2
|
+
import { Job, scheduleJob, scheduledJobs } from 'node-schedule';
|
|
3
|
+
import { getAccessTokenFromRefreshToken } from '../utils/tokenUtil';
|
|
4
|
+
|
|
5
|
+
const scheduleSingleTenantProcess = async (
|
|
6
|
+
batchUN: string,
|
|
7
|
+
cronExpression: string,
|
|
8
|
+
callback: BatchFunction,
|
|
9
|
+
): Promise<void> => {
|
|
10
|
+
if (!process.env.BIGID_BASE_URL || !process.env.BIGID_REFRESH_TOKEN)
|
|
11
|
+
throw new Error('Please make sure to fill out BIGID_BASE_URL && BIGID_REFRESH_TOKEN environment variables.');
|
|
12
|
+
|
|
13
|
+
const bigidAccessToken = await getAccessTokenFromRefreshToken(process.env.BIGID_REFRESH_TOKEN);
|
|
14
|
+
|
|
15
|
+
scheduleJob(batchUN, cronExpression, () =>
|
|
16
|
+
callback('SINGLE_TENANT', process.env.BIGID_BASE_URL as string, bigidAccessToken),
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* use this method to call a function execution according to a cron expression.
|
|
22
|
+
*
|
|
23
|
+
* @param batchUN - name for the job.
|
|
24
|
+
* @param cronExpression - the cron expression for the function call.
|
|
25
|
+
* @param callback - the function that will be called.
|
|
26
|
+
*/
|
|
27
|
+
export const scheduleFunction = (batchUN: string, cronExpression: string, callback: BatchFunction): void => {
|
|
28
|
+
process.env.MULTI_TENANT_MODE
|
|
29
|
+
? scheduleJob(batchUN, cronExpression, () => handleBatchProcess(callback))
|
|
30
|
+
: scheduleSingleTenantProcess(batchUN, cronExpression, callback);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const unscheduleFunction = (batchUN: string): void => {
|
|
34
|
+
const jobToCancel: Job = scheduledJobs[batchUN];
|
|
35
|
+
jobToCancel.cancel();
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const unscheduleAllFunctions = (): void =>
|
|
39
|
+
Object.keys(scheduledJobs).forEach(jobName => scheduledJobs[jobName].cancel());
|
package/src/utils/appLogger.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { configure, getLogger } from 'log4js';
|
|
2
2
|
import { LOGS_PATH } from './constants';
|
|
3
3
|
|
|
4
|
+
type LogMetadata = {
|
|
5
|
+
tenantId: string;
|
|
6
|
+
functionName: string;
|
|
7
|
+
scriptName: string;
|
|
8
|
+
};
|
|
9
|
+
const USER_LOG_BACKUPS = parseInt(process.env.LOG_BACKUPS + "") || 3;
|
|
10
|
+
const MAX_BACKUPS = 10;
|
|
11
|
+
|
|
4
12
|
configure({
|
|
5
13
|
appenders: {
|
|
6
14
|
console: { type: 'stdout', layout: { type: 'colored' } },
|
|
@@ -9,13 +17,43 @@ configure({
|
|
|
9
17
|
layout: { type: 'basic' },
|
|
10
18
|
filename: LOGS_PATH,
|
|
11
19
|
compress: true,
|
|
12
|
-
|
|
13
|
-
keepFileExt: true
|
|
14
|
-
}
|
|
20
|
+
numBackups: USER_LOG_BACKUPS > MAX_BACKUPS ? MAX_BACKUPS : USER_LOG_BACKUPS,
|
|
21
|
+
keepFileExt: true,
|
|
22
|
+
},
|
|
15
23
|
},
|
|
16
24
|
categories: {
|
|
17
|
-
default: { appenders: ['console', 'dateFile'], level: process.env.LOG_LEVEL || 'info' }
|
|
18
|
-
}
|
|
25
|
+
default: { appenders: ['console', 'dateFile'], level: process.env.LOG_LEVEL || 'info' },
|
|
26
|
+
},
|
|
19
27
|
});
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
const appLogger = getLogger();
|
|
30
|
+
|
|
31
|
+
const formatLog = (message: string, logMetadata?: LogMetadata): string => {
|
|
32
|
+
if (!logMetadata) return message;
|
|
33
|
+
|
|
34
|
+
const params = Object.entries(logMetadata)
|
|
35
|
+
.filter(([value]) => value)
|
|
36
|
+
.map(([key, value]) => `[${key}: ${value}]`)
|
|
37
|
+
.join(' ');
|
|
38
|
+
return `${params} ${message}`;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const logInfo = (message: string, logMetadata?: LogMetadata): void => {
|
|
42
|
+
const logMessage = formatLog(message, logMetadata);
|
|
43
|
+
appLogger.info(logMessage);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const logError = (message: string, logMetadata?: LogMetadata): void => {
|
|
47
|
+
const logMessage = formatLog(message, logMetadata);
|
|
48
|
+
appLogger.error(logMessage);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const logWarn = (message: string, logMetadata?: LogMetadata): void => {
|
|
52
|
+
const logMessage = formatLog(message, logMetadata);
|
|
53
|
+
appLogger.warn(logMessage);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const logDebug = (message: string, logMetadata?: LogMetadata): void => {
|
|
57
|
+
const logMessage = formatLog(message, logMetadata);
|
|
58
|
+
appLogger.debug(logMessage);
|
|
59
|
+
};
|
package/src/utils/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './appLogger';
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { Agent } from 'https';
|
|
3
|
+
import { doCallToUrl, RequestMethod } from '../services';
|
|
4
|
+
|
|
5
|
+
const auth0Payload = {
|
|
6
|
+
client_id: process.env.CLIENT_ID,
|
|
7
|
+
client_secret: process.env.CLIENT_SECRET,
|
|
8
|
+
audience: process.env.AUTH0_AUDIENCE || 'bigid-api-v1',
|
|
9
|
+
grant_type: 'client_credentials',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
let auth0Token: string;
|
|
13
|
+
let accessToken: string;
|
|
14
|
+
|
|
15
|
+
export const getAccessTokenFromRefreshToken = async (refreshToken: string) => {
|
|
16
|
+
if (canUseLocalToken(accessToken)) return accessToken;
|
|
17
|
+
const bigidBaseUrl = process.env.BIGID_BASE_URL;
|
|
18
|
+
const {
|
|
19
|
+
data: { systemToken },
|
|
20
|
+
} = await doCallToUrl(
|
|
21
|
+
refreshToken,
|
|
22
|
+
RequestMethod.GET,
|
|
23
|
+
new URL('api/v1/refresh-access-token', bigidBaseUrl).toString(),
|
|
24
|
+
);
|
|
25
|
+
accessToken = systemToken;
|
|
26
|
+
|
|
27
|
+
return accessToken;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const getAuth0Token = async (): Promise<string> => {
|
|
31
|
+
if (canUseLocalToken(auth0Token)) return auth0Token;
|
|
32
|
+
const auth0Domain = process.env.AUTH0_DOMAIN?.replace(/\/$/, '');
|
|
33
|
+
const {
|
|
34
|
+
data: { access_token },
|
|
35
|
+
} = await axios.post(`${auth0Domain}/oauth/token`, auth0Payload);
|
|
36
|
+
auth0Token = access_token;
|
|
37
|
+
|
|
38
|
+
return access_token;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const tokenExchange = async (auth0Token: string, tenantId?: string): Promise<string> => {
|
|
42
|
+
const {
|
|
43
|
+
data: { data },
|
|
44
|
+
} = await axios.post(
|
|
45
|
+
`${process.env.BIGID_BASE_URL}/api/v1/token-exchange`,
|
|
46
|
+
{ tenantId },
|
|
47
|
+
{
|
|
48
|
+
headers: { authorization: auth0Token },
|
|
49
|
+
httpsAgent: new Agent({
|
|
50
|
+
rejectUnauthorized: false,
|
|
51
|
+
}),
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
return data[0];
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const getTokenPayloadAsJson = (token: string): Record<string, any> =>
|
|
58
|
+
JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
|
|
59
|
+
|
|
60
|
+
const canUseLocalToken = (token: string): boolean => {
|
|
61
|
+
if (!token) return false;
|
|
62
|
+
|
|
63
|
+
const { exp } = getTokenPayloadAsJson(token);
|
|
64
|
+
return Date.now() < exp * 1000;
|
|
65
|
+
};
|