dce-expresskit 4.0.0-beta.2
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/.eslintrc.js +93 -0
- package/LICENSE +21 -0
- package/README.md +17 -0
- package/genEncodedSecret.ts +84 -0
- package/lib/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.d.ts +6 -0
- package/lib/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.js +13 -0
- package/lib/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.js.map +1 -0
- package/lib/constants/LOG_REVIEW_STATUS_ROUTE.d.ts +7 -0
- package/lib/constants/LOG_REVIEW_STATUS_ROUTE.js +14 -0
- package/lib/constants/LOG_REVIEW_STATUS_ROUTE.js.map +1 -0
- package/lib/constants/LOG_ROUTE_PATH.d.ts +6 -0
- package/lib/constants/LOG_ROUTE_PATH.js +13 -0
- package/lib/constants/LOG_ROUTE_PATH.js.map +1 -0
- package/lib/constants/ROUTE_PATH_PREFIX.d.ts +6 -0
- package/lib/constants/ROUTE_PATH_PREFIX.js +9 -0
- package/lib/constants/ROUTE_PATH_PREFIX.js.map +1 -0
- package/lib/errors/ErrorWithCode.d.ts +9 -0
- package/lib/errors/ErrorWithCode.js +33 -0
- package/lib/errors/ErrorWithCode.js.map +1 -0
- package/lib/helpers/addDBEditorEndpoints/generateEndpointPath.d.ts +9 -0
- package/lib/helpers/addDBEditorEndpoints/generateEndpointPath.js +17 -0
- package/lib/helpers/addDBEditorEndpoints/generateEndpointPath.js.map +1 -0
- package/lib/helpers/addDBEditorEndpoints/index.d.ts +41 -0
- package/lib/helpers/addDBEditorEndpoints/index.js +134 -0
- package/lib/helpers/addDBEditorEndpoints/index.js.map +1 -0
- package/lib/helpers/dataSigner.d.ts +40 -0
- package/lib/helpers/dataSigner.js +231 -0
- package/lib/helpers/dataSigner.js.map +1 -0
- package/lib/helpers/genRouteHandler.d.ts +75 -0
- package/lib/helpers/genRouteHandler.js +661 -0
- package/lib/helpers/genRouteHandler.js.map +1 -0
- package/lib/helpers/handleError.d.ts +18 -0
- package/lib/helpers/handleError.js +51 -0
- package/lib/helpers/handleError.js.map +1 -0
- package/lib/helpers/handleSuccess.d.ts +8 -0
- package/lib/helpers/handleSuccess.js +20 -0
- package/lib/helpers/handleSuccess.js.map +1 -0
- package/lib/helpers/initCrossServerCredentialCollection.d.ts +11 -0
- package/lib/helpers/initCrossServerCredentialCollection.js +15 -0
- package/lib/helpers/initCrossServerCredentialCollection.js.map +1 -0
- package/lib/helpers/initLogCollection.d.ts +11 -0
- package/lib/helpers/initLogCollection.js +26 -0
- package/lib/helpers/initLogCollection.js.map +1 -0
- package/lib/helpers/initServer.d.ts +43 -0
- package/lib/helpers/initServer.js +297 -0
- package/lib/helpers/initServer.js.map +1 -0
- package/lib/helpers/parseUserAgent.d.ts +17 -0
- package/lib/helpers/parseUserAgent.js +108 -0
- package/lib/helpers/parseUserAgent.js.map +1 -0
- package/lib/helpers/visitEndpointOnAnotherServer/index.d.ts +18 -0
- package/lib/helpers/visitEndpointOnAnotherServer/index.js +156 -0
- package/lib/helpers/visitEndpointOnAnotherServer/index.js.map +1 -0
- package/lib/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.d.ts +23 -0
- package/lib/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.js +168 -0
- package/lib/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.js.map +1 -0
- package/lib/html/genErrorPage.d.ts +19 -0
- package/lib/html/genErrorPage.js +27 -0
- package/lib/html/genErrorPage.js.map +1 -0
- package/lib/html/genInfoPage.d.ts +13 -0
- package/lib/html/genInfoPage.js +16 -0
- package/lib/html/genInfoPage.js.map +1 -0
- package/lib/index.d.ts +11 -0
- package/lib/index.js +68 -0
- package/lib/index.js.map +1 -0
- package/lib/types/CrossServerCredential.d.ts +11 -0
- package/lib/types/CrossServerCredential.js +3 -0
- package/lib/types/CrossServerCredential.js.map +1 -0
- package/lib/types/ExpressKitErrorCode.d.ts +31 -0
- package/lib/types/ExpressKitErrorCode.js +38 -0
- package/lib/types/ExpressKitErrorCode.js.map +1 -0
- package/package.json +52 -0
- package/src/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.ts +9 -0
- package/src/constants/LOG_REVIEW_STATUS_ROUTE.ts +10 -0
- package/src/constants/LOG_ROUTE_PATH.ts +9 -0
- package/src/constants/ROUTE_PATH_PREFIX.ts +7 -0
- package/src/errors/ErrorWithCode.tsx +15 -0
- package/src/helpers/addDBEditorEndpoints/generateEndpointPath.ts +16 -0
- package/src/helpers/addDBEditorEndpoints/index.ts +130 -0
- package/src/helpers/dataSigner.ts +296 -0
- package/src/helpers/genRouteHandler.ts +914 -0
- package/src/helpers/handleError.ts +66 -0
- package/src/helpers/handleSuccess.ts +18 -0
- package/src/helpers/initCrossServerCredentialCollection.ts +19 -0
- package/src/helpers/initLogCollection.ts +31 -0
- package/src/helpers/initServer.ts +284 -0
- package/src/helpers/parseUserAgent.ts +108 -0
- package/src/helpers/visitEndpointOnAnotherServer/index.ts +157 -0
- package/src/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.ts +164 -0
- package/src/html/genErrorPage.ts +144 -0
- package/src/html/genInfoPage.ts +101 -0
- package/src/index.ts +125 -0
- package/src/types/CrossServerCredential.ts +16 -0
- package/src/types/ExpressKitErrorCode.ts +37 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
// Import dce-reactkit
|
|
2
|
+
import {
|
|
3
|
+
ErrorWithCode,
|
|
4
|
+
MINUTE_IN_MS,
|
|
5
|
+
} from 'dce-reactkit';
|
|
6
|
+
|
|
7
|
+
// Import oauth
|
|
8
|
+
import oauth from 'oauth-signature';
|
|
9
|
+
|
|
10
|
+
// Import crypto
|
|
11
|
+
import crypto from 'crypto';
|
|
12
|
+
|
|
13
|
+
// Import shared helpers
|
|
14
|
+
import { internalGetCrossServerCredentialCollection } from './initServer';
|
|
15
|
+
|
|
16
|
+
// Import shared types
|
|
17
|
+
import ExpressKitErrorCode from '../types/ExpressKitErrorCode';
|
|
18
|
+
import CrossServerCredential from '../types/CrossServerCredential';
|
|
19
|
+
|
|
20
|
+
/*------------------------------------------------------------------------*/
|
|
21
|
+
/* ------------------------------- Helpers ------------------------------ */
|
|
22
|
+
/*------------------------------------------------------------------------*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Generate an oauth signature
|
|
26
|
+
* @author Gabe Abrams
|
|
27
|
+
* @param opts object containing all arguments
|
|
28
|
+
* @param opts.method the http method
|
|
29
|
+
* @param opts.path the http request path
|
|
30
|
+
* @param opts.params the data in the body to sign
|
|
31
|
+
* @param opts.secret the secret to sign with
|
|
32
|
+
* @return the signature
|
|
33
|
+
*/
|
|
34
|
+
const genSignature = async (
|
|
35
|
+
opts: {
|
|
36
|
+
method?: string,
|
|
37
|
+
path?: string,
|
|
38
|
+
params?: { [key: string]: any },
|
|
39
|
+
secret: string,
|
|
40
|
+
},
|
|
41
|
+
): Promise<string> => {
|
|
42
|
+
// Destructure opts
|
|
43
|
+
const {
|
|
44
|
+
method,
|
|
45
|
+
path,
|
|
46
|
+
params,
|
|
47
|
+
secret,
|
|
48
|
+
} = opts;
|
|
49
|
+
|
|
50
|
+
// Order the params alphabetically by key
|
|
51
|
+
const keys = Object.keys(params ?? {});
|
|
52
|
+
keys.sort();
|
|
53
|
+
const orderedParams: {
|
|
54
|
+
[key: string]: any,
|
|
55
|
+
} = {};
|
|
56
|
+
keys.forEach((key) => {
|
|
57
|
+
// Skip oauth_signature
|
|
58
|
+
if (key === 'oauth_signature') {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Add the param
|
|
63
|
+
orderedParams[key] = (params ?? {})[key];
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Generate the signature
|
|
67
|
+
return decodeURIComponent(oauth.generate(
|
|
68
|
+
method ?? 'GET',
|
|
69
|
+
path ?? 'no-path',
|
|
70
|
+
orderedParams,
|
|
71
|
+
secret,
|
|
72
|
+
));
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Decrypt an encrypted string using a secret
|
|
77
|
+
* @author Gabe Abrams
|
|
78
|
+
* @param str the encrypted string
|
|
79
|
+
* @return the decrypted string
|
|
80
|
+
*/
|
|
81
|
+
const decrypt = async (
|
|
82
|
+
encryptedPack: string,
|
|
83
|
+
): Promise<string> => {
|
|
84
|
+
// Decryption process based on:
|
|
85
|
+
// https://medium.com/@tony.infisical/guide-to-nodes-crypto-module-for-encryption-decryption-65c077176980
|
|
86
|
+
|
|
87
|
+
// Get the encryption secret
|
|
88
|
+
const { DCEKIT_CRED_ENCODING_SALT } = process.env;
|
|
89
|
+
if (!DCEKIT_CRED_ENCODING_SALT) {
|
|
90
|
+
throw new ErrorWithCode(
|
|
91
|
+
'Could not decrypt a string because the encryption salt was not set.',
|
|
92
|
+
ExpressKitErrorCode.CrossServerNoCredentialEncodingSalt,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Separate encrypted pack
|
|
97
|
+
const {
|
|
98
|
+
ciphertext,
|
|
99
|
+
iv,
|
|
100
|
+
tag,
|
|
101
|
+
} = JSON.parse(decodeURIComponent(encryptedPack));
|
|
102
|
+
|
|
103
|
+
// Parse the encrypted data
|
|
104
|
+
const decipher = crypto.createDecipheriv(
|
|
105
|
+
'aes-256-gcm',
|
|
106
|
+
Buffer.from(DCEKIT_CRED_ENCODING_SALT, 'base64'),
|
|
107
|
+
Buffer.from(iv, 'base64'),
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// Set the authentication tag
|
|
111
|
+
decipher.setAuthTag(Buffer.from(tag, 'base64'));
|
|
112
|
+
|
|
113
|
+
// Decrypt the string
|
|
114
|
+
let str = decipher.update(ciphertext, 'base64', 'utf8');
|
|
115
|
+
str += decipher.final('utf8');
|
|
116
|
+
|
|
117
|
+
// Return the decrypted string
|
|
118
|
+
return str;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/*------------------------------------------------------------------------*/
|
|
122
|
+
/* ------------------------------- Signing ------------------------------ */
|
|
123
|
+
/*------------------------------------------------------------------------*/
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Sign a request and get the new request params
|
|
127
|
+
* @author Gabe Abrams
|
|
128
|
+
* @param opts object containing all arguments
|
|
129
|
+
* @param opts.method the method to sign
|
|
130
|
+
* @param opts.path the http request path
|
|
131
|
+
* @param opts.params the data in the body to sign
|
|
132
|
+
* @param opts.key the dcekit key to sign with
|
|
133
|
+
* @param opts.secret the dcekit secret to sign with
|
|
134
|
+
* @return augmented params for the request, including a signature, timestamp, and key
|
|
135
|
+
*/
|
|
136
|
+
export const signRequest = async (
|
|
137
|
+
opts: {
|
|
138
|
+
method: string,
|
|
139
|
+
path: string,
|
|
140
|
+
params: { [key: string]: any },
|
|
141
|
+
key: string,
|
|
142
|
+
secret: string,
|
|
143
|
+
},
|
|
144
|
+
): Promise<{ [key: string]: any }> => {
|
|
145
|
+
// Destructure opts
|
|
146
|
+
const method = opts.method.toUpperCase();
|
|
147
|
+
const {
|
|
148
|
+
path,
|
|
149
|
+
params,
|
|
150
|
+
key,
|
|
151
|
+
secret,
|
|
152
|
+
} = opts;
|
|
153
|
+
|
|
154
|
+
// Augment the params
|
|
155
|
+
const augmentedParams: {
|
|
156
|
+
[key: string]: any,
|
|
157
|
+
} = {
|
|
158
|
+
...params,
|
|
159
|
+
oauth_consumer_key: key,
|
|
160
|
+
oauth_nonce: Math.random().toString(36),
|
|
161
|
+
oauth_timestamp: Date.now(),
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// Generate a signature
|
|
165
|
+
const signature = await genSignature({
|
|
166
|
+
method,
|
|
167
|
+
path,
|
|
168
|
+
params,
|
|
169
|
+
secret,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Add signature to the augmented params
|
|
173
|
+
augmentedParams.oauth_signature = signature;
|
|
174
|
+
|
|
175
|
+
// Return the augmented params
|
|
176
|
+
return augmentedParams;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Validate a signed request. Throws an error if invalid
|
|
181
|
+
* @author Gabe Abrams
|
|
182
|
+
* @param opts object containing all arguments
|
|
183
|
+
* @param opts.method the method of the data validate
|
|
184
|
+
* @param opts.path the http request path to validate
|
|
185
|
+
* @param opts.scope the name of the scope to validate
|
|
186
|
+
* @param opts.params the request data to validate
|
|
187
|
+
* @returns parsed and validated params
|
|
188
|
+
*/
|
|
189
|
+
export const validateSignedRequest = async (
|
|
190
|
+
opts: {
|
|
191
|
+
method: string,
|
|
192
|
+
path: string,
|
|
193
|
+
scope: string,
|
|
194
|
+
params: { [key: string]: any },
|
|
195
|
+
},
|
|
196
|
+
) => {
|
|
197
|
+
/* ---------- Collect Info ---------- */
|
|
198
|
+
|
|
199
|
+
// Get the signature
|
|
200
|
+
if (!opts.params.oauth_signature) {
|
|
201
|
+
throw new ErrorWithCode(
|
|
202
|
+
'Could not validate a cross-server request there was no oauth signature.',
|
|
203
|
+
ExpressKitErrorCode.CrossServerMissingSignedRequestInfo,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
const signature = opts.params.oauth_signature;
|
|
207
|
+
|
|
208
|
+
// Get the timestamp
|
|
209
|
+
if (
|
|
210
|
+
// No timestamp
|
|
211
|
+
!opts.params.oauth_timestamp
|
|
212
|
+
// Invalid timestamp
|
|
213
|
+
|| Number.isNaN(Number.parseInt(opts.params.oauth_timestamp, 10))
|
|
214
|
+
) {
|
|
215
|
+
throw new ErrorWithCode(
|
|
216
|
+
'Could not validate a cross-server request there was no valid oauth timestamp.',
|
|
217
|
+
ExpressKitErrorCode.CrossServerMissingSignedRequestInfo,
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
const timestamp = Number.parseInt(opts.params.oauth_timestamp, 10);
|
|
221
|
+
|
|
222
|
+
// Get the key
|
|
223
|
+
if (!opts.params.oauth_consumer_key) {
|
|
224
|
+
throw new ErrorWithCode(
|
|
225
|
+
'Could not validate a cross-server request there was no oauth consumer key.',
|
|
226
|
+
ExpressKitErrorCode.CrossServerMissingSignedRequestInfo,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
const key = opts.params.oauth_consumer_key;
|
|
230
|
+
|
|
231
|
+
// Get the rest of the info
|
|
232
|
+
const {
|
|
233
|
+
method,
|
|
234
|
+
path,
|
|
235
|
+
params,
|
|
236
|
+
} = opts;
|
|
237
|
+
|
|
238
|
+
/* ------- Look Up Credential ------- */
|
|
239
|
+
|
|
240
|
+
// Get the cross-server credential collection
|
|
241
|
+
const crossServerCredentialCollection = internalGetCrossServerCredentialCollection();
|
|
242
|
+
if (!crossServerCredentialCollection) {
|
|
243
|
+
throw new ErrorWithCode(
|
|
244
|
+
'Could not validate a cross-server request because the cross-server credential collection was not ready in time.',
|
|
245
|
+
ExpressKitErrorCode.SignedRequestInvalidCollection,
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Get the cross-server credential
|
|
250
|
+
const crossServerCredential: CrossServerCredential = await crossServerCredentialCollection.find({ key });
|
|
251
|
+
if (!crossServerCredential) {
|
|
252
|
+
throw new ErrorWithCode(
|
|
253
|
+
'Could not validate a cross-server request because the credential was not found.',
|
|
254
|
+
ExpressKitErrorCode.SignedRequestInvalidCredential,
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Make sure the scope is included
|
|
259
|
+
const allowedScopes = crossServerCredential.scopes;
|
|
260
|
+
if (!allowedScopes.includes(opts.scope)) {
|
|
261
|
+
throw new ErrorWithCode(
|
|
262
|
+
'Could not validate a cross-server request because the scope was not included.',
|
|
263
|
+
ExpressKitErrorCode.SignedRequestInvalidScope,
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Decode the secret
|
|
268
|
+
const secret = await decrypt(crossServerCredential.encodedeSecret);
|
|
269
|
+
|
|
270
|
+
/* -------- Verify Signature -------- */
|
|
271
|
+
|
|
272
|
+
// Generate a new signature to compare
|
|
273
|
+
const expectedSignature = await genSignature({
|
|
274
|
+
method,
|
|
275
|
+
path,
|
|
276
|
+
params,
|
|
277
|
+
secret,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Make sure the signatures match
|
|
281
|
+
if (signature !== expectedSignature) {
|
|
282
|
+
throw new ErrorWithCode(
|
|
283
|
+
'Could not validate a cross-server request because the signature did not match.',
|
|
284
|
+
ExpressKitErrorCode.SignedRequestInvalidSignature,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Make sure the timestamp was recent enough
|
|
289
|
+
const elapsedMs = Math.abs(Date.now() - timestamp);
|
|
290
|
+
if (elapsedMs < MINUTE_IN_MS) {
|
|
291
|
+
throw new ErrorWithCode(
|
|
292
|
+
'Could not validate a cross-server request because the request was too old.',
|
|
293
|
+
ExpressKitErrorCode.SignedRequestInvalidTimestamp,
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
};
|