@app-connect/core 1.7.25 → 1.7.26
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/.env.test +5 -5
- package/README.md +441 -441
- package/connector/developerPortal.js +31 -31
- package/connector/mock.js +84 -77
- package/connector/proxy/engine.js +164 -164
- package/connector/proxy/index.js +500 -500
- package/connector/registry.js +252 -252
- package/docs/README.md +50 -50
- package/docs/architecture.md +93 -93
- package/docs/connectors.md +116 -116
- package/docs/handlers.md +125 -125
- package/docs/libraries.md +101 -101
- package/docs/models.md +144 -144
- package/docs/routes.md +115 -115
- package/docs/tests.md +73 -73
- package/handlers/admin.js +523 -523
- package/handlers/appointment.js +193 -0
- package/handlers/auth.js +296 -296
- package/handlers/calldown.js +99 -99
- package/handlers/contact.js +280 -280
- package/handlers/disposition.js +82 -80
- package/handlers/log.js +984 -973
- package/handlers/managedAuth.js +446 -446
- package/handlers/plugin.js +208 -208
- package/handlers/user.js +142 -142
- package/index.js +3140 -2652
- package/jest.config.js +56 -56
- package/lib/analytics.js +54 -54
- package/lib/authSession.js +109 -109
- package/lib/cacheCleanup.js +21 -0
- package/lib/callLogComposer.js +898 -898
- package/lib/callLogLookup.js +34 -0
- package/lib/constants.js +8 -8
- package/lib/debugTracer.js +177 -177
- package/lib/encode.js +30 -30
- package/lib/errorHandler.js +218 -206
- package/lib/generalErrorMessage.js +41 -41
- package/lib/jwt.js +18 -18
- package/lib/logger.js +190 -190
- package/lib/migrateCallLogsSchema.js +116 -0
- package/lib/ringcentral.js +266 -266
- package/lib/s3ErrorLogReport.js +65 -65
- package/lib/sharedSMSComposer.js +471 -471
- package/lib/util.js +67 -67
- package/mcp/README.md +412 -395
- package/mcp/lib/validator.js +91 -91
- package/mcp/mcpHandler.js +425 -425
- package/mcp/tools/cancelAppointment.js +101 -0
- package/mcp/tools/checkAuthStatus.js +105 -105
- package/mcp/tools/confirmAppointment.js +101 -0
- package/mcp/tools/createAppointment.js +157 -0
- package/mcp/tools/createCallLog.js +327 -316
- package/mcp/tools/createContact.js +117 -117
- package/mcp/tools/createMessageLog.js +287 -287
- package/mcp/tools/doAuth.js +60 -60
- package/mcp/tools/findContactByName.js +93 -93
- package/mcp/tools/findContactByPhone.js +101 -101
- package/mcp/tools/getCallLog.js +111 -102
- package/mcp/tools/getGoogleFilePicker.js +99 -99
- package/mcp/tools/getHelp.js +43 -43
- package/mcp/tools/getPublicConnectors.js +94 -94
- package/mcp/tools/getSessionInfo.js +90 -90
- package/mcp/tools/index.js +51 -41
- package/mcp/tools/listAppointments.js +163 -0
- package/mcp/tools/logout.js +96 -96
- package/mcp/tools/rcGetCallLogs.js +65 -65
- package/mcp/tools/updateAppointment.js +154 -0
- package/mcp/tools/updateCallLog.js +130 -126
- package/mcp/ui/App/App.tsx +358 -358
- package/mcp/ui/App/components/AuthInfoForm.tsx +113 -113
- package/mcp/ui/App/components/AuthSuccess.tsx +22 -22
- package/mcp/ui/App/components/ConnectorList.tsx +82 -82
- package/mcp/ui/App/components/DebugPanel.tsx +43 -43
- package/mcp/ui/App/components/OAuthConnect.tsx +270 -270
- package/mcp/ui/App/lib/callTool.ts +130 -130
- package/mcp/ui/App/lib/debugLog.ts +41 -41
- package/mcp/ui/App/lib/developerPortal.ts +111 -111
- package/mcp/ui/App/main.css +5 -5
- package/mcp/ui/App/root.tsx +13 -13
- package/mcp/ui/index.html +13 -13
- package/mcp/ui/package-lock.json +6356 -6356
- package/mcp/ui/package.json +25 -25
- package/mcp/ui/tsconfig.json +26 -26
- package/mcp/ui/vite.config.ts +16 -16
- package/models/accountDataModel.js +33 -33
- package/models/adminConfigModel.js +35 -35
- package/models/cacheModel.js +30 -26
- package/models/callDownListModel.js +34 -34
- package/models/callLogModel.js +33 -27
- package/models/dynamo/connectorSchema.js +146 -146
- package/models/dynamo/lockSchema.js +24 -24
- package/models/dynamo/noteCacheSchema.js +29 -29
- package/models/llmSessionModel.js +17 -17
- package/models/messageLogModel.js +25 -25
- package/models/sequelize.js +16 -16
- package/models/userModel.js +45 -45
- package/package.json +1 -1
- package/releaseNotes.json +1093 -1081
- package/test/connector/proxy/engine.test.js +126 -126
- package/test/connector/proxy/index.test.js +279 -279
- package/test/connector/proxy/sample.json +161 -161
- package/test/connector/registry.test.js +415 -415
- package/test/handlers/admin.test.js +616 -616
- package/test/handlers/auth.test.js +1018 -1018
- package/test/handlers/contact.test.js +1014 -1014
- package/test/handlers/log.test.js +1298 -1160
- package/test/handlers/managedAuth.test.js +457 -457
- package/test/handlers/plugin.test.js +380 -380
- package/test/index.test.js +105 -105
- package/test/lib/cacheCleanup.test.js +42 -0
- package/test/lib/callLogComposer.test.js +1231 -1231
- package/test/lib/debugTracer.test.js +328 -328
- package/test/lib/jwt.test.js +176 -176
- package/test/lib/logger.test.js +206 -206
- package/test/lib/oauth.test.js +359 -359
- package/test/lib/ringcentral.test.js +467 -467
- package/test/lib/sharedSMSComposer.test.js +1084 -1084
- package/test/lib/util.test.js +329 -329
- package/test/mcp/tools/checkAuthStatus.test.js +83 -83
- package/test/mcp/tools/createCallLog.test.js +436 -436
- package/test/mcp/tools/createContact.test.js +58 -58
- package/test/mcp/tools/createMessageLog.test.js +595 -595
- package/test/mcp/tools/doAuth.test.js +113 -113
- package/test/mcp/tools/findContactByName.test.js +275 -275
- package/test/mcp/tools/findContactByPhone.test.js +296 -296
- package/test/mcp/tools/getCallLog.test.js +298 -298
- package/test/mcp/tools/getGoogleFilePicker.test.js +281 -281
- package/test/mcp/tools/getPublicConnectors.test.js +107 -107
- package/test/mcp/tools/getSessionInfo.test.js +127 -127
- package/test/mcp/tools/logout.test.js +233 -233
- package/test/mcp/tools/rcGetCallLogs.test.js +56 -56
- package/test/mcp/tools/updateCallLog.test.js +360 -360
- package/test/models/accountDataModel.test.js +98 -98
- package/test/models/dynamo/connectorSchema.test.js +189 -189
- package/test/models/models.test.js +568 -539
- package/test/routes/managedAuthRoutes.test.js +104 -104
- package/test/setup.js +178 -178
package/handlers/auth.js
CHANGED
|
@@ -1,296 +1,296 @@
|
|
|
1
|
-
const oauth = require('../lib/oauth');
|
|
2
|
-
const { UserModel } = require('../models/userModel');
|
|
3
|
-
const connectorRegistry = require('../connector/registry');
|
|
4
|
-
const Op = require('sequelize').Op;
|
|
5
|
-
const { RingCentral } = require('../lib/ringcentral');
|
|
6
|
-
const adminCore = require('./admin');
|
|
7
|
-
const { Connector } = require('../models/dynamo/connectorSchema');
|
|
8
|
-
const { handleDatabaseError } = require('../lib/errorHandler');
|
|
9
|
-
const managedAuthCore = require('./managedAuth');
|
|
10
|
-
const { getHashValue } = require('../lib/util');
|
|
11
|
-
|
|
12
|
-
async function onOAuthCallback({ platform, hostname, tokenUrl, query, hashedRcExtensionId, isFromMCP = false }) {
|
|
13
|
-
const callbackUri = query.callbackUri;
|
|
14
|
-
const apiUrl = query.apiUrl;
|
|
15
|
-
const username = query.username;
|
|
16
|
-
const proxyId = query.proxyId;
|
|
17
|
-
const userEmail = query.userEmail;
|
|
18
|
-
const platformModule = connectorRegistry.getConnector(platform);
|
|
19
|
-
let proxyConfig = null;
|
|
20
|
-
if (proxyId) {
|
|
21
|
-
proxyConfig = await Connector.getProxyConfig(proxyId);
|
|
22
|
-
}
|
|
23
|
-
const oauthInfo = await platformModule.getOauthInfo({ tokenUrl, hostname, rcAccountId: query.rcAccountId, proxyId, proxyConfig, userEmail, isFromMCP });
|
|
24
|
-
if (oauthInfo.failMessage) {
|
|
25
|
-
return {
|
|
26
|
-
userInfo: null,
|
|
27
|
-
returnMessage: {
|
|
28
|
-
messageType: 'danger',
|
|
29
|
-
message: oauthInfo.failMessage
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Some platforms require different oauth queries, this won't affect normal OAuth process unless CRM module implements getOverridingOAuthOption() method
|
|
35
|
-
let overridingOAuthOption = null;
|
|
36
|
-
if (platformModule.getOverridingOAuthOption != null) {
|
|
37
|
-
const code = new URL(callbackUri).searchParams.get('code');
|
|
38
|
-
overridingOAuthOption = platformModule.getOverridingOAuthOption({ code });
|
|
39
|
-
}
|
|
40
|
-
const oauthApp = oauth.getOAuthApp(oauthInfo);
|
|
41
|
-
const { accessToken, refreshToken, expires, data } = await oauthApp.code.getToken(callbackUri, overridingOAuthOption);
|
|
42
|
-
const authHeader = `Bearer ${accessToken}`;
|
|
43
|
-
const { successful, platformUserInfo, returnMessage } = await platformModule.getUserInfo({ authHeader, tokenUrl, apiUrl, hostname, platform, username, callbackUri, query, proxyId, proxyConfig, userEmail, data });
|
|
44
|
-
|
|
45
|
-
if (successful) {
|
|
46
|
-
let userInfo = null;
|
|
47
|
-
try {
|
|
48
|
-
userInfo = await saveUserInfo({
|
|
49
|
-
platformUserInfo,
|
|
50
|
-
platform,
|
|
51
|
-
tokenUrl,
|
|
52
|
-
apiUrl,
|
|
53
|
-
username,
|
|
54
|
-
hostname: platformUserInfo?.overridingHostname ? platformUserInfo.overridingHostname : hostname,
|
|
55
|
-
accessToken,
|
|
56
|
-
refreshToken,
|
|
57
|
-
tokenExpiry: isNaN(expires) ? null : expires,
|
|
58
|
-
rcAccountId: query?.rcAccountId,
|
|
59
|
-
hashedRcExtensionId,
|
|
60
|
-
proxyId
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
catch (error) {
|
|
64
|
-
return handleDatabaseError(error, 'Error saving user info');
|
|
65
|
-
}
|
|
66
|
-
if (platformModule.postSaveUserInfo) {
|
|
67
|
-
userInfo = await platformModule.postSaveUserInfo({ userInfo, oauthApp });
|
|
68
|
-
}
|
|
69
|
-
return {
|
|
70
|
-
userInfo,
|
|
71
|
-
returnMessage
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
return {
|
|
76
|
-
userInfo: null,
|
|
77
|
-
returnMessage
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async function onApiKeyLogin({ platform, hostname, apiKey, proxyId, rcAccountId, rcExtensionId, connectorId, isPrivate, hashedRcExtensionId, additionalInfo }) {
|
|
83
|
-
const platformModule = connectorRegistry.getConnector(platform);
|
|
84
|
-
let resolvedAdditionalInfo = {
|
|
85
|
-
...(additionalInfo ?? {})
|
|
86
|
-
};
|
|
87
|
-
if (resolvedAdditionalInfo.apiKey === undefined && apiKey !== undefined) {
|
|
88
|
-
resolvedAdditionalInfo.apiKey = apiKey;
|
|
89
|
-
}
|
|
90
|
-
let resolvedApiKey = apiKey;
|
|
91
|
-
let managedFieldDefinitions = [];
|
|
92
|
-
if (rcAccountId) {
|
|
93
|
-
managedFieldDefinitions = await managedAuthCore.getManagedFieldDefinitions({ rcAccountId, platform, connectorId, isPrivate });
|
|
94
|
-
const shouldFallbackToManualAuth = managedFieldDefinitions.length > 0
|
|
95
|
-
&& await managedAuthCore.hasManagedAuthLoginFailure({ rcAccountId, platform, rcExtensionId });
|
|
96
|
-
const managedAuthResult = await managedAuthCore.resolveApiKeyLoginFields({
|
|
97
|
-
platform,
|
|
98
|
-
rcAccountId,
|
|
99
|
-
rcExtensionId,
|
|
100
|
-
connectorId,
|
|
101
|
-
isPrivate,
|
|
102
|
-
apiKey,
|
|
103
|
-
additionalInfo: resolvedAdditionalInfo,
|
|
104
|
-
preferSubmittedValuesForManagedFields: shouldFallbackToManualAuth
|
|
105
|
-
});
|
|
106
|
-
resolvedAdditionalInfo = managedAuthResult.resolvedAdditionalInfo;
|
|
107
|
-
resolvedApiKey = managedAuthResult.resolvedApiKey;
|
|
108
|
-
const missingRequiredFieldConsts = managedAuthResult.missingRequiredFieldConsts;
|
|
109
|
-
if (missingRequiredFieldConsts.length > 0) {
|
|
110
|
-
return {
|
|
111
|
-
userInfo: null,
|
|
112
|
-
returnMessage: {
|
|
113
|
-
messageType: 'warning',
|
|
114
|
-
message: 'Missing required authentication fields.',
|
|
115
|
-
ttl: 3000,
|
|
116
|
-
missingRequiredFieldConsts
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
const basicAuth = platformModule.getBasicAuth({ apiKey: resolvedApiKey });
|
|
122
|
-
const { successful, platformUserInfo, returnMessage } = await platformModule.getUserInfo({
|
|
123
|
-
authHeader: `Basic ${basicAuth}`,
|
|
124
|
-
hostname,
|
|
125
|
-
platform,
|
|
126
|
-
additionalInfo: resolvedAdditionalInfo,
|
|
127
|
-
apiKey: resolvedApiKey,
|
|
128
|
-
proxyId
|
|
129
|
-
});
|
|
130
|
-
if (successful) {
|
|
131
|
-
await managedAuthCore.clearManagedAuthLoginFailure({ rcAccountId, platform, rcExtensionId });
|
|
132
|
-
let userInfo = null;
|
|
133
|
-
try {
|
|
134
|
-
userInfo = await saveUserInfo({
|
|
135
|
-
platformUserInfo,
|
|
136
|
-
platform,
|
|
137
|
-
hostname,
|
|
138
|
-
proxyId,
|
|
139
|
-
hashedRcExtensionId,
|
|
140
|
-
rcAccountId,
|
|
141
|
-
accessToken: platformUserInfo.overridingApiKey ?? resolvedApiKey
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
catch (error) {
|
|
145
|
-
return handleDatabaseError(error, 'Error saving user info');
|
|
146
|
-
}
|
|
147
|
-
if (platformModule.postSaveUserInfo) {
|
|
148
|
-
userInfo = await platformModule.postSaveUserInfo({ userInfo });
|
|
149
|
-
}
|
|
150
|
-
return {
|
|
151
|
-
userInfo,
|
|
152
|
-
returnMessage
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
if (managedFieldDefinitions.length > 0) {
|
|
156
|
-
await managedAuthCore.markManagedAuthLoginFailure({ rcAccountId, platform, rcExtensionId });
|
|
157
|
-
}
|
|
158
|
-
return {
|
|
159
|
-
userInfo: null,
|
|
160
|
-
returnMessage
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
async function saveUserInfo({ platformUserInfo, platform, hostname, accessToken, refreshToken, tokenExpiry, rcAccountId, hashedRcExtensionId, proxyId }) {
|
|
165
|
-
const id = platformUserInfo.id;
|
|
166
|
-
const name = platformUserInfo.name;
|
|
167
|
-
const existingUser = await UserModel.findByPk(id);
|
|
168
|
-
const timezoneName = platformUserInfo.timezoneName;
|
|
169
|
-
const timezoneOffset = platformUserInfo.timezoneOffset;
|
|
170
|
-
const platformAdditionalInfo = platformUserInfo.platformAdditionalInfo || {};
|
|
171
|
-
platformAdditionalInfo.proxyId = proxyId;
|
|
172
|
-
if (existingUser) {
|
|
173
|
-
try {
|
|
174
|
-
await existingUser.update(
|
|
175
|
-
{
|
|
176
|
-
platform,
|
|
177
|
-
hostname,
|
|
178
|
-
timezoneName,
|
|
179
|
-
timezoneOffset,
|
|
180
|
-
accessToken,
|
|
181
|
-
refreshToken,
|
|
182
|
-
tokenExpiry,
|
|
183
|
-
rcAccountId,
|
|
184
|
-
hashedRcExtensionId,
|
|
185
|
-
platformAdditionalInfo: {
|
|
186
|
-
...existingUser.platformAdditionalInfo, // keep existing platformAdditionalInfo
|
|
187
|
-
...platformAdditionalInfo,
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
catch (error) {
|
|
193
|
-
return handleDatabaseError(error, 'Error saving user info');
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
try {
|
|
198
|
-
await UserModel.create({
|
|
199
|
-
id,
|
|
200
|
-
hostname,
|
|
201
|
-
timezoneName,
|
|
202
|
-
timezoneOffset,
|
|
203
|
-
platform,
|
|
204
|
-
accessToken,
|
|
205
|
-
refreshToken,
|
|
206
|
-
tokenExpiry,
|
|
207
|
-
rcAccountId,
|
|
208
|
-
hashedRcExtensionId,
|
|
209
|
-
platformAdditionalInfo,
|
|
210
|
-
userSettings: {}
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
catch (error) {
|
|
214
|
-
return handleDatabaseError(error, 'Error saving user info');
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
return {
|
|
218
|
-
id,
|
|
219
|
-
name
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
async function getLicenseStatus({ userId, platform }) {
|
|
224
|
-
const user = await UserModel.findByPk(userId);
|
|
225
|
-
if (!user) {
|
|
226
|
-
return {
|
|
227
|
-
isLicenseValid: false,
|
|
228
|
-
licenseStatus: 'Invalid (User not found)',
|
|
229
|
-
licenseStatusDescription: ''
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
const platformModule = connectorRegistry.getConnector(platform);
|
|
233
|
-
const licenseStatus = await platformModule.getLicenseStatus({ userId, platform, user });
|
|
234
|
-
return licenseStatus;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Just for oauth ATM
|
|
238
|
-
async function authValidation({ platform, userId }) {
|
|
239
|
-
let existingUser = await UserModel.findOne({
|
|
240
|
-
where: {
|
|
241
|
-
[Op.and]: [
|
|
242
|
-
{
|
|
243
|
-
id: userId,
|
|
244
|
-
platform
|
|
245
|
-
}
|
|
246
|
-
]
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
if (existingUser) {
|
|
250
|
-
const platformModule = connectorRegistry.getConnector(platform);
|
|
251
|
-
const proxyId = existingUser?.platformAdditionalInfo?.proxyId;
|
|
252
|
-
const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: existingUser?.platformAdditionalInfo?.tokenUrl, hostname: existingUser?.hostname, proxyId })));
|
|
253
|
-
existingUser = await oauth.checkAndRefreshAccessToken(oauthApp, existingUser);
|
|
254
|
-
const { successful, returnMessage, status } = await platformModule.authValidation({ user: existingUser });
|
|
255
|
-
return {
|
|
256
|
-
successful,
|
|
257
|
-
returnMessage,
|
|
258
|
-
status,
|
|
259
|
-
failReason: successful ? '' : 'CRM. API failed'
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
else {
|
|
263
|
-
return {
|
|
264
|
-
successful: false,
|
|
265
|
-
status: 404,
|
|
266
|
-
failReason: 'App Connect. User not found in database'
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Ringcentral
|
|
272
|
-
async function onRingcentralOAuthCallback({ code, rcAccountId }) {
|
|
273
|
-
if (!process.env.RINGCENTRAL_SERVER || !process.env.RINGCENTRAL_CLIENT_ID || !process.env.RINGCENTRAL_CLIENT_SECRET) {
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
const rcSDK = new RingCentral({
|
|
277
|
-
server: process.env.RINGCENTRAL_SERVER,
|
|
278
|
-
clientId: process.env.RINGCENTRAL_CLIENT_ID,
|
|
279
|
-
clientSecret: process.env.RINGCENTRAL_CLIENT_SECRET,
|
|
280
|
-
redirectUri: `${process.env.APP_SERVER}/ringcentral/oauth/callback`
|
|
281
|
-
});
|
|
282
|
-
const { access_token, refresh_token, expire_time } = await rcSDK.generateToken({ code });
|
|
283
|
-
const hashedRcAccountId = getHashValue(rcAccountId, process.env.HASH_KEY);
|
|
284
|
-
await adminCore.updateAdminRcTokens({
|
|
285
|
-
hashedRcAccountId,
|
|
286
|
-
adminAccessToken: access_token,
|
|
287
|
-
adminRefreshToken: refresh_token,
|
|
288
|
-
adminTokenExpiry: expire_time
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
exports.onOAuthCallback = onOAuthCallback;
|
|
293
|
-
exports.onApiKeyLogin = onApiKeyLogin;
|
|
294
|
-
exports.authValidation = authValidation;
|
|
295
|
-
exports.getLicenseStatus = getLicenseStatus;
|
|
296
|
-
exports.onRingcentralOAuthCallback = onRingcentralOAuthCallback;
|
|
1
|
+
const oauth = require('../lib/oauth');
|
|
2
|
+
const { UserModel } = require('../models/userModel');
|
|
3
|
+
const connectorRegistry = require('../connector/registry');
|
|
4
|
+
const Op = require('sequelize').Op;
|
|
5
|
+
const { RingCentral } = require('../lib/ringcentral');
|
|
6
|
+
const adminCore = require('./admin');
|
|
7
|
+
const { Connector } = require('../models/dynamo/connectorSchema');
|
|
8
|
+
const { handleDatabaseError } = require('../lib/errorHandler');
|
|
9
|
+
const managedAuthCore = require('./managedAuth');
|
|
10
|
+
const { getHashValue } = require('../lib/util');
|
|
11
|
+
|
|
12
|
+
async function onOAuthCallback({ platform, hostname, tokenUrl, query, hashedRcExtensionId, isFromMCP = false }) {
|
|
13
|
+
const callbackUri = query.callbackUri;
|
|
14
|
+
const apiUrl = query.apiUrl;
|
|
15
|
+
const username = query.username;
|
|
16
|
+
const proxyId = query.proxyId;
|
|
17
|
+
const userEmail = query.userEmail;
|
|
18
|
+
const platformModule = connectorRegistry.getConnector(platform);
|
|
19
|
+
let proxyConfig = null;
|
|
20
|
+
if (proxyId) {
|
|
21
|
+
proxyConfig = await Connector.getProxyConfig(proxyId);
|
|
22
|
+
}
|
|
23
|
+
const oauthInfo = await platformModule.getOauthInfo({ tokenUrl, hostname, rcAccountId: query.rcAccountId, proxyId, proxyConfig, userEmail, isFromMCP });
|
|
24
|
+
if (oauthInfo.failMessage) {
|
|
25
|
+
return {
|
|
26
|
+
userInfo: null,
|
|
27
|
+
returnMessage: {
|
|
28
|
+
messageType: 'danger',
|
|
29
|
+
message: oauthInfo.failMessage
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Some platforms require different oauth queries, this won't affect normal OAuth process unless CRM module implements getOverridingOAuthOption() method
|
|
35
|
+
let overridingOAuthOption = null;
|
|
36
|
+
if (platformModule.getOverridingOAuthOption != null) {
|
|
37
|
+
const code = new URL(callbackUri).searchParams.get('code');
|
|
38
|
+
overridingOAuthOption = platformModule.getOverridingOAuthOption({ code });
|
|
39
|
+
}
|
|
40
|
+
const oauthApp = oauth.getOAuthApp(oauthInfo);
|
|
41
|
+
const { accessToken, refreshToken, expires, data } = await oauthApp.code.getToken(callbackUri, overridingOAuthOption);
|
|
42
|
+
const authHeader = `Bearer ${accessToken}`;
|
|
43
|
+
const { successful, platformUserInfo, returnMessage } = await platformModule.getUserInfo({ authHeader, tokenUrl, apiUrl, hostname, platform, username, callbackUri, query, proxyId, proxyConfig, userEmail, data });
|
|
44
|
+
|
|
45
|
+
if (successful) {
|
|
46
|
+
let userInfo = null;
|
|
47
|
+
try {
|
|
48
|
+
userInfo = await saveUserInfo({
|
|
49
|
+
platformUserInfo,
|
|
50
|
+
platform,
|
|
51
|
+
tokenUrl,
|
|
52
|
+
apiUrl,
|
|
53
|
+
username,
|
|
54
|
+
hostname: platformUserInfo?.overridingHostname ? platformUserInfo.overridingHostname : hostname,
|
|
55
|
+
accessToken,
|
|
56
|
+
refreshToken,
|
|
57
|
+
tokenExpiry: isNaN(expires) ? null : expires,
|
|
58
|
+
rcAccountId: query?.rcAccountId,
|
|
59
|
+
hashedRcExtensionId,
|
|
60
|
+
proxyId
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
return handleDatabaseError(error, 'Error saving user info');
|
|
65
|
+
}
|
|
66
|
+
if (platformModule.postSaveUserInfo) {
|
|
67
|
+
userInfo = await platformModule.postSaveUserInfo({ userInfo, oauthApp });
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
userInfo,
|
|
71
|
+
returnMessage
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
return {
|
|
76
|
+
userInfo: null,
|
|
77
|
+
returnMessage
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function onApiKeyLogin({ platform, hostname, apiKey, proxyId, rcAccountId, rcExtensionId, connectorId, isPrivate, hashedRcExtensionId, additionalInfo }) {
|
|
83
|
+
const platformModule = connectorRegistry.getConnector(platform);
|
|
84
|
+
let resolvedAdditionalInfo = {
|
|
85
|
+
...(additionalInfo ?? {})
|
|
86
|
+
};
|
|
87
|
+
if (resolvedAdditionalInfo.apiKey === undefined && apiKey !== undefined) {
|
|
88
|
+
resolvedAdditionalInfo.apiKey = apiKey;
|
|
89
|
+
}
|
|
90
|
+
let resolvedApiKey = apiKey;
|
|
91
|
+
let managedFieldDefinitions = [];
|
|
92
|
+
if (rcAccountId) {
|
|
93
|
+
managedFieldDefinitions = await managedAuthCore.getManagedFieldDefinitions({ rcAccountId, platform, connectorId, isPrivate });
|
|
94
|
+
const shouldFallbackToManualAuth = managedFieldDefinitions.length > 0
|
|
95
|
+
&& await managedAuthCore.hasManagedAuthLoginFailure({ rcAccountId, platform, rcExtensionId });
|
|
96
|
+
const managedAuthResult = await managedAuthCore.resolveApiKeyLoginFields({
|
|
97
|
+
platform,
|
|
98
|
+
rcAccountId,
|
|
99
|
+
rcExtensionId,
|
|
100
|
+
connectorId,
|
|
101
|
+
isPrivate,
|
|
102
|
+
apiKey,
|
|
103
|
+
additionalInfo: resolvedAdditionalInfo,
|
|
104
|
+
preferSubmittedValuesForManagedFields: shouldFallbackToManualAuth
|
|
105
|
+
});
|
|
106
|
+
resolvedAdditionalInfo = managedAuthResult.resolvedAdditionalInfo;
|
|
107
|
+
resolvedApiKey = managedAuthResult.resolvedApiKey;
|
|
108
|
+
const missingRequiredFieldConsts = managedAuthResult.missingRequiredFieldConsts;
|
|
109
|
+
if (missingRequiredFieldConsts.length > 0) {
|
|
110
|
+
return {
|
|
111
|
+
userInfo: null,
|
|
112
|
+
returnMessage: {
|
|
113
|
+
messageType: 'warning',
|
|
114
|
+
message: 'Missing required authentication fields.',
|
|
115
|
+
ttl: 3000,
|
|
116
|
+
missingRequiredFieldConsts
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const basicAuth = platformModule.getBasicAuth({ apiKey: resolvedApiKey });
|
|
122
|
+
const { successful, platformUserInfo, returnMessage } = await platformModule.getUserInfo({
|
|
123
|
+
authHeader: `Basic ${basicAuth}`,
|
|
124
|
+
hostname,
|
|
125
|
+
platform,
|
|
126
|
+
additionalInfo: resolvedAdditionalInfo,
|
|
127
|
+
apiKey: resolvedApiKey,
|
|
128
|
+
proxyId
|
|
129
|
+
});
|
|
130
|
+
if (successful) {
|
|
131
|
+
await managedAuthCore.clearManagedAuthLoginFailure({ rcAccountId, platform, rcExtensionId });
|
|
132
|
+
let userInfo = null;
|
|
133
|
+
try {
|
|
134
|
+
userInfo = await saveUserInfo({
|
|
135
|
+
platformUserInfo,
|
|
136
|
+
platform,
|
|
137
|
+
hostname,
|
|
138
|
+
proxyId,
|
|
139
|
+
hashedRcExtensionId,
|
|
140
|
+
rcAccountId,
|
|
141
|
+
accessToken: platformUserInfo.overridingApiKey ?? resolvedApiKey
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
return handleDatabaseError(error, 'Error saving user info');
|
|
146
|
+
}
|
|
147
|
+
if (platformModule.postSaveUserInfo) {
|
|
148
|
+
userInfo = await platformModule.postSaveUserInfo({ userInfo });
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
userInfo,
|
|
152
|
+
returnMessage
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (managedFieldDefinitions.length > 0) {
|
|
156
|
+
await managedAuthCore.markManagedAuthLoginFailure({ rcAccountId, platform, rcExtensionId });
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
userInfo: null,
|
|
160
|
+
returnMessage
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function saveUserInfo({ platformUserInfo, platform, hostname, accessToken, refreshToken, tokenExpiry, rcAccountId, hashedRcExtensionId, proxyId }) {
|
|
165
|
+
const id = platformUserInfo.id;
|
|
166
|
+
const name = platformUserInfo.name;
|
|
167
|
+
const existingUser = await UserModel.findByPk(id);
|
|
168
|
+
const timezoneName = platformUserInfo.timezoneName;
|
|
169
|
+
const timezoneOffset = platformUserInfo.timezoneOffset;
|
|
170
|
+
const platformAdditionalInfo = platformUserInfo.platformAdditionalInfo || {};
|
|
171
|
+
platformAdditionalInfo.proxyId = proxyId;
|
|
172
|
+
if (existingUser) {
|
|
173
|
+
try {
|
|
174
|
+
await existingUser.update(
|
|
175
|
+
{
|
|
176
|
+
platform,
|
|
177
|
+
hostname,
|
|
178
|
+
timezoneName,
|
|
179
|
+
timezoneOffset,
|
|
180
|
+
accessToken,
|
|
181
|
+
refreshToken,
|
|
182
|
+
tokenExpiry,
|
|
183
|
+
rcAccountId,
|
|
184
|
+
hashedRcExtensionId,
|
|
185
|
+
platformAdditionalInfo: {
|
|
186
|
+
...existingUser.platformAdditionalInfo, // keep existing platformAdditionalInfo
|
|
187
|
+
...platformAdditionalInfo,
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
return handleDatabaseError(error, 'Error saving user info');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
try {
|
|
198
|
+
await UserModel.create({
|
|
199
|
+
id,
|
|
200
|
+
hostname,
|
|
201
|
+
timezoneName,
|
|
202
|
+
timezoneOffset,
|
|
203
|
+
platform,
|
|
204
|
+
accessToken,
|
|
205
|
+
refreshToken,
|
|
206
|
+
tokenExpiry,
|
|
207
|
+
rcAccountId,
|
|
208
|
+
hashedRcExtensionId,
|
|
209
|
+
platformAdditionalInfo,
|
|
210
|
+
userSettings: {}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
return handleDatabaseError(error, 'Error saving user info');
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
id,
|
|
219
|
+
name
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function getLicenseStatus({ userId, platform }) {
|
|
224
|
+
const user = await UserModel.findByPk(userId);
|
|
225
|
+
if (!user) {
|
|
226
|
+
return {
|
|
227
|
+
isLicenseValid: false,
|
|
228
|
+
licenseStatus: 'Invalid (User not found)',
|
|
229
|
+
licenseStatusDescription: ''
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const platformModule = connectorRegistry.getConnector(platform);
|
|
233
|
+
const licenseStatus = await platformModule.getLicenseStatus({ userId, platform, user });
|
|
234
|
+
return licenseStatus;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Just for oauth ATM
|
|
238
|
+
async function authValidation({ platform, userId }) {
|
|
239
|
+
let existingUser = await UserModel.findOne({
|
|
240
|
+
where: {
|
|
241
|
+
[Op.and]: [
|
|
242
|
+
{
|
|
243
|
+
id: userId,
|
|
244
|
+
platform
|
|
245
|
+
}
|
|
246
|
+
]
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
if (existingUser) {
|
|
250
|
+
const platformModule = connectorRegistry.getConnector(platform);
|
|
251
|
+
const proxyId = existingUser?.platformAdditionalInfo?.proxyId;
|
|
252
|
+
const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: existingUser?.platformAdditionalInfo?.tokenUrl, hostname: existingUser?.hostname, proxyId })));
|
|
253
|
+
existingUser = await oauth.checkAndRefreshAccessToken(oauthApp, existingUser);
|
|
254
|
+
const { successful, returnMessage, status } = await platformModule.authValidation({ user: existingUser });
|
|
255
|
+
return {
|
|
256
|
+
successful,
|
|
257
|
+
returnMessage,
|
|
258
|
+
status,
|
|
259
|
+
failReason: successful ? '' : 'CRM. API failed'
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
return {
|
|
264
|
+
successful: false,
|
|
265
|
+
status: 404,
|
|
266
|
+
failReason: 'App Connect. User not found in database'
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Ringcentral
|
|
272
|
+
async function onRingcentralOAuthCallback({ code, rcAccountId }) {
|
|
273
|
+
if (!process.env.RINGCENTRAL_SERVER || !process.env.RINGCENTRAL_CLIENT_ID || !process.env.RINGCENTRAL_CLIENT_SECRET) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
const rcSDK = new RingCentral({
|
|
277
|
+
server: process.env.RINGCENTRAL_SERVER,
|
|
278
|
+
clientId: process.env.RINGCENTRAL_CLIENT_ID,
|
|
279
|
+
clientSecret: process.env.RINGCENTRAL_CLIENT_SECRET,
|
|
280
|
+
redirectUri: `${process.env.APP_SERVER}/ringcentral/oauth/callback`
|
|
281
|
+
});
|
|
282
|
+
const { access_token, refresh_token, expire_time } = await rcSDK.generateToken({ code });
|
|
283
|
+
const hashedRcAccountId = getHashValue(rcAccountId, process.env.HASH_KEY);
|
|
284
|
+
await adminCore.updateAdminRcTokens({
|
|
285
|
+
hashedRcAccountId,
|
|
286
|
+
adminAccessToken: access_token,
|
|
287
|
+
adminRefreshToken: refresh_token,
|
|
288
|
+
adminTokenExpiry: expire_time
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
exports.onOAuthCallback = onOAuthCallback;
|
|
293
|
+
exports.onApiKeyLogin = onApiKeyLogin;
|
|
294
|
+
exports.authValidation = authValidation;
|
|
295
|
+
exports.getLicenseStatus = getLicenseStatus;
|
|
296
|
+
exports.onRingcentralOAuthCallback = onRingcentralOAuthCallback;
|