@app-connect/core 1.5.8 → 1.6.4
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 +434 -434
- package/adapter/mock.js +76 -76
- package/adapter/registry.js +247 -247
- package/handlers/admin.js +62 -60
- package/handlers/auth.js +205 -156
- package/handlers/contact.js +274 -274
- package/handlers/disposition.js +193 -193
- package/handlers/log.js +612 -587
- package/handlers/user.js +101 -101
- package/index.js +1302 -1204
- package/jest.config.js +56 -56
- package/lib/analytics.js +52 -52
- package/lib/callLogComposer.js +550 -485
- package/lib/constants.js +8 -8
- package/lib/encode.js +30 -30
- package/lib/generalErrorMessage.js +41 -41
- package/lib/jwt.js +16 -16
- package/lib/oauth.js +34 -21
- package/lib/util.js +43 -40
- package/models/adminConfigModel.js +17 -17
- package/models/cacheModel.js +23 -23
- package/models/callLogModel.js +27 -27
- package/models/dynamo/lockSchema.js +24 -24
- package/models/dynamo/noteCacheSchema.js +30 -0
- package/models/messageLogModel.js +25 -25
- package/models/sequelize.js +16 -16
- package/models/userModel.js +38 -38
- package/package.json +67 -64
- package/releaseNotes.json +701 -605
- package/test/adapter/registry.test.js +270 -270
- package/test/handlers/auth.test.js +230 -230
- package/test/lib/jwt.test.js +161 -161
- package/test/setup.js +176 -176
package/handlers/admin.js
CHANGED
|
@@ -1,60 +1,62 @@
|
|
|
1
|
-
const axios = require('axios');
|
|
2
|
-
const { AdminConfigModel } = require('../models/adminConfigModel');
|
|
3
|
-
const adapterRegistry = require('../adapter/registry');
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
exports.
|
|
59
|
-
exports.
|
|
60
|
-
exports.
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const { AdminConfigModel } = require('../models/adminConfigModel');
|
|
3
|
+
const adapterRegistry = require('../adapter/registry');
|
|
4
|
+
const oauth = require('../lib/oauth');
|
|
5
|
+
|
|
6
|
+
async function validateAdminRole({ rcAccessToken }) {
|
|
7
|
+
const rcExtensionResponse = await axios.get(
|
|
8
|
+
'https://platform.ringcentral.com/restapi/v1.0/account/~/extension/~',
|
|
9
|
+
{
|
|
10
|
+
headers: {
|
|
11
|
+
Authorization: `Bearer ${rcAccessToken}`,
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
return {
|
|
15
|
+
isValidated: !!rcExtensionResponse.data?.permissions?.admin?.enabled || (!!process.env.ADMIN_EXTENSION_ID_DEV_PASS_LIST && process.env.ADMIN_EXTENSION_ID_DEV_PASS_LIST.split(',').includes(rcExtensionResponse.data.id.toString())),
|
|
16
|
+
rcAccountId: rcExtensionResponse.data.account.id
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function upsertAdminSettings({ hashedRcAccountId, adminSettings }) {
|
|
21
|
+
let existingAdminConfig = await AdminConfigModel.findByPk(hashedRcAccountId);
|
|
22
|
+
if (existingAdminConfig) {
|
|
23
|
+
await existingAdminConfig.update({
|
|
24
|
+
...adminSettings
|
|
25
|
+
});
|
|
26
|
+
} else {
|
|
27
|
+
await AdminConfigModel.create({
|
|
28
|
+
id: hashedRcAccountId,
|
|
29
|
+
...adminSettings
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function getAdminSettings({ hashedRcAccountId }) {
|
|
35
|
+
const existingAdminConfig = await AdminConfigModel.findByPk(hashedRcAccountId);
|
|
36
|
+
return existingAdminConfig;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function getServerLoggingSettings({ user }) {
|
|
40
|
+
const platformModule = adapterRegistry.getAdapter(user.platform);
|
|
41
|
+
if (platformModule.getServerLoggingSettings) {
|
|
42
|
+
const serverLoggingSettings = await platformModule.getServerLoggingSettings({ user });
|
|
43
|
+
return serverLoggingSettings;
|
|
44
|
+
}
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function updateServerLoggingSettings({ user, additionalFieldValues }) {
|
|
49
|
+
const platformModule = adapterRegistry.getAdapter(user.platform);
|
|
50
|
+
const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: user?.platformAdditionalInfo?.tokenUrl, hostname: user?.hostname })));
|
|
51
|
+
if (platformModule.updateServerLoggingSettings) {
|
|
52
|
+
const { successful, returnMessage } = await platformModule.updateServerLoggingSettings({ user, additionalFieldValues, oauthApp });
|
|
53
|
+
return { successful, returnMessage };
|
|
54
|
+
}
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
exports.validateAdminRole = validateAdminRole;
|
|
59
|
+
exports.upsertAdminSettings = upsertAdminSettings;
|
|
60
|
+
exports.getAdminSettings = getAdminSettings;
|
|
61
|
+
exports.getServerLoggingSettings = getServerLoggingSettings;
|
|
62
|
+
exports.updateServerLoggingSettings = updateServerLoggingSettings;
|
package/handlers/auth.js
CHANGED
|
@@ -1,156 +1,205 @@
|
|
|
1
|
-
const oauth = require('../lib/oauth');
|
|
2
|
-
const { UserModel } = require('../models/userModel');
|
|
3
|
-
const adapterRegistry = require('../adapter/registry');
|
|
4
|
-
const Op = require('sequelize').Op;
|
|
5
|
-
|
|
6
|
-
async function onOAuthCallback({ platform, hostname, tokenUrl, callbackUri, apiUrl, username, query }) {
|
|
7
|
-
const platformModule = adapterRegistry.getAdapter(platform);
|
|
8
|
-
const oauthInfo = await platformModule.getOauthInfo({ tokenUrl, hostname, rcAccountId: query.rcAccountId });
|
|
9
|
-
|
|
10
|
-
if (oauthInfo.failMessage) {
|
|
11
|
-
return {
|
|
12
|
-
userInfo: null,
|
|
13
|
-
returnMessage: {
|
|
14
|
-
messageType: 'danger',
|
|
15
|
-
message: oauthInfo.failMessage
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Some platforms require different oauth queries, this won't affect normal OAuth process unless CRM module implements getOverridingOAuthOption() method
|
|
21
|
-
let overridingOAuthOption = null;
|
|
22
|
-
if (platformModule.getOverridingOAuthOption != null) {
|
|
23
|
-
overridingOAuthOption = platformModule.getOverridingOAuthOption({ code: callbackUri.split('code=')[1] });
|
|
24
|
-
}
|
|
25
|
-
const oauthApp = oauth.getOAuthApp(oauthInfo);
|
|
26
|
-
const { accessToken, refreshToken, expires } = await oauthApp.code.getToken(callbackUri, overridingOAuthOption);
|
|
27
|
-
const authHeader = `Bearer ${accessToken}`;
|
|
28
|
-
const { successful, platformUserInfo, returnMessage } = await platformModule.getUserInfo({ authHeader, tokenUrl, apiUrl, hostname, username, callbackUri, query });
|
|
29
|
-
if (successful) {
|
|
30
|
-
|
|
31
|
-
platformUserInfo,
|
|
32
|
-
platform,
|
|
33
|
-
tokenUrl,
|
|
34
|
-
apiUrl,
|
|
35
|
-
username,
|
|
36
|
-
hostname: platformUserInfo?.overridingHostname ? platformUserInfo.overridingHostname : hostname,
|
|
37
|
-
accessToken,
|
|
38
|
-
refreshToken,
|
|
39
|
-
tokenExpiry: expires
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
userInfo,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return {
|
|
72
|
-
userInfo
|
|
73
|
-
returnMessage
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
1
|
+
const oauth = require('../lib/oauth');
|
|
2
|
+
const { UserModel } = require('../models/userModel');
|
|
3
|
+
const adapterRegistry = require('../adapter/registry');
|
|
4
|
+
const Op = require('sequelize').Op;
|
|
5
|
+
|
|
6
|
+
async function onOAuthCallback({ platform, hostname, tokenUrl, callbackUri, apiUrl, username, query }) {
|
|
7
|
+
const platformModule = adapterRegistry.getAdapter(platform);
|
|
8
|
+
const oauthInfo = await platformModule.getOauthInfo({ tokenUrl, hostname, rcAccountId: query.rcAccountId });
|
|
9
|
+
|
|
10
|
+
if (oauthInfo.failMessage) {
|
|
11
|
+
return {
|
|
12
|
+
userInfo: null,
|
|
13
|
+
returnMessage: {
|
|
14
|
+
messageType: 'danger',
|
|
15
|
+
message: oauthInfo.failMessage
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Some platforms require different oauth queries, this won't affect normal OAuth process unless CRM module implements getOverridingOAuthOption() method
|
|
21
|
+
let overridingOAuthOption = null;
|
|
22
|
+
if (platformModule.getOverridingOAuthOption != null) {
|
|
23
|
+
overridingOAuthOption = platformModule.getOverridingOAuthOption({ code: callbackUri.split('code=')[1] });
|
|
24
|
+
}
|
|
25
|
+
const oauthApp = oauth.getOAuthApp(oauthInfo);
|
|
26
|
+
const { accessToken, refreshToken, expires } = await oauthApp.code.getToken(callbackUri, overridingOAuthOption);
|
|
27
|
+
const authHeader = `Bearer ${accessToken}`;
|
|
28
|
+
const { successful, platformUserInfo, returnMessage } = await platformModule.getUserInfo({ authHeader, tokenUrl, apiUrl, hostname, username, callbackUri, query });
|
|
29
|
+
if (successful) {
|
|
30
|
+
let userInfo = await saveUserInfo({
|
|
31
|
+
platformUserInfo,
|
|
32
|
+
platform,
|
|
33
|
+
tokenUrl,
|
|
34
|
+
apiUrl,
|
|
35
|
+
username,
|
|
36
|
+
hostname: platformUserInfo?.overridingHostname ? platformUserInfo.overridingHostname : hostname,
|
|
37
|
+
accessToken,
|
|
38
|
+
refreshToken,
|
|
39
|
+
tokenExpiry: expires
|
|
40
|
+
});
|
|
41
|
+
if (platformModule.postSaveUserInfo) {
|
|
42
|
+
userInfo = await platformModule.postSaveUserInfo({ userInfo, oauthApp });
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
userInfo,
|
|
46
|
+
returnMessage
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
return {
|
|
51
|
+
userInfo: null,
|
|
52
|
+
returnMessage
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function onApiKeyLogin({ platform, hostname, apiKey, additionalInfo }) {
|
|
58
|
+
const platformModule = adapterRegistry.getAdapter(platform);
|
|
59
|
+
const basicAuth = platformModule.getBasicAuth({ apiKey });
|
|
60
|
+
const { successful, platformUserInfo, returnMessage } = await platformModule.getUserInfo({ authHeader: `Basic ${basicAuth}`, hostname, additionalInfo, apiKey });
|
|
61
|
+
if (successful) {
|
|
62
|
+
let userInfo = await saveUserInfo({
|
|
63
|
+
platformUserInfo,
|
|
64
|
+
platform,
|
|
65
|
+
hostname,
|
|
66
|
+
accessToken: platformUserInfo.overridingApiKey ?? apiKey
|
|
67
|
+
});
|
|
68
|
+
if (platformModule.postSaveUserInfo) {
|
|
69
|
+
userInfo = await platformModule.postSaveUserInfo({ userInfo });
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
userInfo,
|
|
73
|
+
returnMessage
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
return {
|
|
78
|
+
userInfo: null,
|
|
79
|
+
returnMessage
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function saveUserInfo({ platformUserInfo, platform, hostname, accessToken, refreshToken, tokenExpiry }) {
|
|
85
|
+
const id = platformUserInfo.id;
|
|
86
|
+
const name = platformUserInfo.name;
|
|
87
|
+
const existingUser = await UserModel.findByPk(id);
|
|
88
|
+
const timezoneName = platformUserInfo.timezoneName;
|
|
89
|
+
const timezoneOffset = platformUserInfo.timezoneOffset;
|
|
90
|
+
const platformAdditionalInfo = platformUserInfo.platformAdditionalInfo;
|
|
91
|
+
if (existingUser) {
|
|
92
|
+
await existingUser.update(
|
|
93
|
+
{
|
|
94
|
+
hostname,
|
|
95
|
+
timezoneName,
|
|
96
|
+
timezoneOffset,
|
|
97
|
+
accessToken,
|
|
98
|
+
refreshToken,
|
|
99
|
+
tokenExpiry,
|
|
100
|
+
platformAdditionalInfo: {
|
|
101
|
+
...existingUser.platformAdditionalInfo, // keep existing platformAdditionalInfo
|
|
102
|
+
...platformAdditionalInfo,
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// TEMP: replace user with old ID
|
|
109
|
+
if (id.endsWith(`-${platform}`)) {
|
|
110
|
+
const oldID = id.split('-');
|
|
111
|
+
const userWithOldID = await UserModel.findByPk(oldID[0]);
|
|
112
|
+
if (userWithOldID) {
|
|
113
|
+
await UserModel.create({
|
|
114
|
+
id,
|
|
115
|
+
hostname,
|
|
116
|
+
timezoneName,
|
|
117
|
+
timezoneOffset,
|
|
118
|
+
platform,
|
|
119
|
+
accessToken,
|
|
120
|
+
refreshToken,
|
|
121
|
+
tokenExpiry,
|
|
122
|
+
platformAdditionalInfo,
|
|
123
|
+
userSettings: userWithOldID.userSettings
|
|
124
|
+
});
|
|
125
|
+
await userWithOldID.destroy();
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
await UserModel.create({
|
|
129
|
+
id,
|
|
130
|
+
hostname,
|
|
131
|
+
timezoneName,
|
|
132
|
+
timezoneOffset,
|
|
133
|
+
platform,
|
|
134
|
+
accessToken,
|
|
135
|
+
refreshToken,
|
|
136
|
+
tokenExpiry,
|
|
137
|
+
platformAdditionalInfo,
|
|
138
|
+
userSettings: {}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
await UserModel.create({
|
|
144
|
+
id,
|
|
145
|
+
hostname,
|
|
146
|
+
timezoneName,
|
|
147
|
+
timezoneOffset,
|
|
148
|
+
platform,
|
|
149
|
+
accessToken,
|
|
150
|
+
refreshToken,
|
|
151
|
+
tokenExpiry,
|
|
152
|
+
platformAdditionalInfo,
|
|
153
|
+
userSettings: {}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
id,
|
|
159
|
+
name
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function getLicenseStatus({ userId, platform }) {
|
|
164
|
+
const platformModule = adapterRegistry.getAdapter(platform);
|
|
165
|
+
const licenseStatus = await platformModule.getLicenseStatus({ userId });
|
|
166
|
+
return licenseStatus;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Just for oauth ATM
|
|
170
|
+
async function authValidation({ platform, userId }) {
|
|
171
|
+
let existingUser = await UserModel.findOne({
|
|
172
|
+
where: {
|
|
173
|
+
[Op.and]: [
|
|
174
|
+
{
|
|
175
|
+
id: userId,
|
|
176
|
+
platform
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
if (existingUser) {
|
|
182
|
+
const platformModule = adapterRegistry.getAdapter(platform);
|
|
183
|
+
const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: existingUser?.platformAdditionalInfo?.tokenUrl, hostname: existingUser?.hostname })));
|
|
184
|
+
existingUser = await oauth.checkAndRefreshAccessToken(oauthApp, existingUser);
|
|
185
|
+
const { successful, returnMessage, status } = await platformModule.authValidation({ user: existingUser });
|
|
186
|
+
return {
|
|
187
|
+
successful,
|
|
188
|
+
returnMessage,
|
|
189
|
+
status,
|
|
190
|
+
failReason: successful ? '' : 'CRM. API failed'
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
return {
|
|
195
|
+
successful: false,
|
|
196
|
+
status: 404,
|
|
197
|
+
failReason: 'App Connect. User not found in database'
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
exports.onOAuthCallback = onOAuthCallback;
|
|
203
|
+
exports.onApiKeyLogin = onApiKeyLogin;
|
|
204
|
+
exports.authValidation = authValidation;
|
|
205
|
+
exports.getLicenseStatus = getLicenseStatus;
|