dce-expresskit 4.0.0-beta-logreviewer.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/.eslintrc.js +93 -0
  2. package/LICENSE +21 -0
  3. package/README.md +17 -0
  4. package/genEncodedSecret.ts +107 -0
  5. package/genSalt.ts +15 -0
  6. package/lib/constants/LOG_REVIEW_PAGE_SIZE.d.ts +6 -0
  7. package/lib/constants/LOG_REVIEW_PAGE_SIZE.js +9 -0
  8. package/lib/constants/LOG_REVIEW_PAGE_SIZE.js.map +1 -0
  9. package/lib/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.d.ts +6 -0
  10. package/lib/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.js +13 -0
  11. package/lib/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.js.map +1 -0
  12. package/lib/constants/LOG_REVIEW_STATUS_ROUTE.d.ts +7 -0
  13. package/lib/constants/LOG_REVIEW_STATUS_ROUTE.js +14 -0
  14. package/lib/constants/LOG_REVIEW_STATUS_ROUTE.js.map +1 -0
  15. package/lib/constants/LOG_ROUTE_PATH.d.ts +6 -0
  16. package/lib/constants/LOG_ROUTE_PATH.js +13 -0
  17. package/lib/constants/LOG_ROUTE_PATH.js.map +1 -0
  18. package/lib/constants/ROUTE_PATH_PREFIX.d.ts +6 -0
  19. package/lib/constants/ROUTE_PATH_PREFIX.js +9 -0
  20. package/lib/constants/ROUTE_PATH_PREFIX.js.map +1 -0
  21. package/lib/errors/ErrorWithCode.d.ts +9 -0
  22. package/lib/errors/ErrorWithCode.js +33 -0
  23. package/lib/errors/ErrorWithCode.js.map +1 -0
  24. package/lib/helpers/addDBEditorEndpoints/generateEndpointPath.d.ts +9 -0
  25. package/lib/helpers/addDBEditorEndpoints/generateEndpointPath.js +17 -0
  26. package/lib/helpers/addDBEditorEndpoints/generateEndpointPath.js.map +1 -0
  27. package/lib/helpers/addDBEditorEndpoints/index.d.ts +41 -0
  28. package/lib/helpers/addDBEditorEndpoints/index.js +134 -0
  29. package/lib/helpers/addDBEditorEndpoints/index.js.map +1 -0
  30. package/lib/helpers/dataSigner.d.ts +40 -0
  31. package/lib/helpers/dataSigner.js +242 -0
  32. package/lib/helpers/dataSigner.js.map +1 -0
  33. package/lib/helpers/genRouteHandler.d.ts +75 -0
  34. package/lib/helpers/genRouteHandler.js +662 -0
  35. package/lib/helpers/genRouteHandler.js.map +1 -0
  36. package/lib/helpers/getLogReviewerLogs.d.ts +27 -0
  37. package/lib/helpers/getLogReviewerLogs.js +238 -0
  38. package/lib/helpers/getLogReviewerLogs.js.map +1 -0
  39. package/lib/helpers/handleError.d.ts +18 -0
  40. package/lib/helpers/handleError.js +51 -0
  41. package/lib/helpers/handleError.js.map +1 -0
  42. package/lib/helpers/handleSuccess.d.ts +8 -0
  43. package/lib/helpers/handleSuccess.js +20 -0
  44. package/lib/helpers/handleSuccess.js.map +1 -0
  45. package/lib/helpers/initCrossServerCredentialCollection.d.ts +11 -0
  46. package/lib/helpers/initCrossServerCredentialCollection.js +15 -0
  47. package/lib/helpers/initCrossServerCredentialCollection.js.map +1 -0
  48. package/lib/helpers/initLogCollection.d.ts +11 -0
  49. package/lib/helpers/initLogCollection.js +26 -0
  50. package/lib/helpers/initLogCollection.js.map +1 -0
  51. package/lib/helpers/initServer.d.ts +45 -0
  52. package/lib/helpers/initServer.js +292 -0
  53. package/lib/helpers/initServer.js.map +1 -0
  54. package/lib/helpers/parseUserAgent.d.ts +17 -0
  55. package/lib/helpers/parseUserAgent.js +108 -0
  56. package/lib/helpers/parseUserAgent.js.map +1 -0
  57. package/lib/helpers/visitEndpointOnAnotherServer/index.d.ts +18 -0
  58. package/lib/helpers/visitEndpointOnAnotherServer/index.js +89 -0
  59. package/lib/helpers/visitEndpointOnAnotherServer/index.js.map +1 -0
  60. package/lib/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.d.ts +23 -0
  61. package/lib/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.js +236 -0
  62. package/lib/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.js.map +1 -0
  63. package/lib/html/genErrorPage.d.ts +19 -0
  64. package/lib/html/genErrorPage.js +27 -0
  65. package/lib/html/genErrorPage.js.map +1 -0
  66. package/lib/html/genInfoPage.d.ts +13 -0
  67. package/lib/html/genInfoPage.js +16 -0
  68. package/lib/html/genInfoPage.js.map +1 -0
  69. package/lib/index.d.ts +11 -0
  70. package/lib/index.js +68 -0
  71. package/lib/index.js.map +1 -0
  72. package/lib/types/CrossServerCredential.d.ts +11 -0
  73. package/lib/types/CrossServerCredential.js +3 -0
  74. package/lib/types/CrossServerCredential.js.map +1 -0
  75. package/lib/types/ExpressKitErrorCode.d.ts +31 -0
  76. package/lib/types/ExpressKitErrorCode.js +38 -0
  77. package/lib/types/ExpressKitErrorCode.js.map +1 -0
  78. package/package.json +53 -0
  79. package/src/constants/LOG_REVIEW_PAGE_SIZE.ts +7 -0
  80. package/src/errors/ErrorWithCode.tsx +15 -0
  81. package/src/helpers/addDBEditorEndpoints/generateEndpointPath.ts +16 -0
  82. package/src/helpers/addDBEditorEndpoints/index.ts +130 -0
  83. package/src/helpers/dataSigner.ts +319 -0
  84. package/src/helpers/genRouteHandler.ts +920 -0
  85. package/src/helpers/getLogReviewerLogs.ts +259 -0
  86. package/src/helpers/handleError.ts +66 -0
  87. package/src/helpers/handleSuccess.ts +18 -0
  88. package/src/helpers/initCrossServerCredentialCollection.ts +19 -0
  89. package/src/helpers/initLogCollection.ts +30 -0
  90. package/src/helpers/initServer.ts +283 -0
  91. package/src/helpers/parseUserAgent.ts +108 -0
  92. package/src/helpers/visitEndpointOnAnotherServer/index.ts +70 -0
  93. package/src/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.ts +257 -0
  94. package/src/html/genErrorPage.ts +144 -0
  95. package/src/html/genInfoPage.ts +101 -0
  96. package/src/index.ts +125 -0
  97. package/src/types/CrossServerCredential.ts +16 -0
  98. package/src/types/ExpressKitErrorCode.ts +37 -0
  99. package/tsconfig.json +19 -0
@@ -0,0 +1,130 @@
1
+ // Import express
2
+ import express from 'express';
3
+
4
+ // Import dce-reactkit
5
+ import {
6
+ ParamType,
7
+ } from 'dce-reactkit';
8
+
9
+ // Import shared helpers
10
+ import genRouteHandler from '../genRouteHandler';
11
+ import generateEndpointPath from './generateEndpointPath';
12
+
13
+ /**
14
+ * Interface for a collection in the database
15
+ * @author Yuen Ler Chow
16
+ */
17
+ type DCEMangoCollection = {
18
+ /**
19
+ * Find all items in the collection that match the filter query
20
+ * @param filterQuery query for the filter
21
+ * @returns list of items that match the filter query
22
+ */
23
+ find: (filterQuery: any) => Promise<any[]>,
24
+ /**
25
+ * Insert an item into the collection
26
+ * @param item the item to insert
27
+ */
28
+ insert: (item: any) => Promise<void>,
29
+ /**
30
+ * Delete an item in the collection
31
+ * @param id the id of the item to delete
32
+ */
33
+ delete: (filterQuery: { id: string }) => Promise<void>,
34
+ };
35
+
36
+ /**
37
+ * Add all routes for the DBEditor
38
+ * @author Yuen Ler Chow
39
+ * @param opts object containing all arguments
40
+ * @param opts.app express app to add routes too
41
+ * @param opts.collectionName the name of the collection
42
+ * @param opts.adminsOnly true if the endpoint is for admins only
43
+ * @param opts.collection dce-mango db collection
44
+ */
45
+ const addDBEditorEndpoints = (
46
+ opts: {
47
+ app: express.Application,
48
+ collectionName: string,
49
+ adminsOnly: boolean,
50
+ collection: DCEMangoCollection,
51
+ },
52
+ ) => {
53
+ const {
54
+ app,
55
+ collectionName,
56
+ adminsOnly,
57
+ collection,
58
+ } = opts;
59
+
60
+ // Generate the endpoint path
61
+ const endpointPath = generateEndpointPath(collectionName, adminsOnly);
62
+
63
+ /**
64
+ * List all items in the collection
65
+ * @author Yuen Ler Chow
66
+ * @returns {any[]} the list of items in the collection
67
+ */
68
+ app.get(
69
+ endpointPath,
70
+ genRouteHandler({
71
+ paramTypes: {
72
+ filterQuery: ParamType.JSONOptional,
73
+ },
74
+ handler: async ({ params }) => {
75
+ const filterQuery = params.filterQuery ?? {};
76
+ const categories = await collection.find(filterQuery);
77
+ return categories;
78
+ },
79
+ }),
80
+ );
81
+
82
+ /**
83
+ * Create a new item in the collection
84
+ * @author Yuen Ler Chow
85
+ * @param {any} item the item to create
86
+ */
87
+ app.post(
88
+ endpointPath,
89
+ genRouteHandler({
90
+ paramTypes: {
91
+ item: ParamType.JSON,
92
+ },
93
+ handler: async ({
94
+ params,
95
+ }) => {
96
+ // Destructure params
97
+ const {
98
+ item,
99
+ } = params;
100
+
101
+ await collection.insert(item);
102
+ },
103
+ }),
104
+ );
105
+
106
+ /**
107
+ * Remove an item from the collection by id
108
+ * @author Yuen Ler Chow
109
+ */
110
+ app.delete(
111
+ `${endpointPath}/:id`,
112
+ genRouteHandler({
113
+ paramTypes: {
114
+ id: ParamType.String,
115
+ },
116
+ handler: async ({
117
+ params,
118
+ }) => {
119
+ // Destructure params
120
+ const {
121
+ id,
122
+ } = params;
123
+
124
+ await collection.delete({ id });
125
+ },
126
+ }),
127
+ );
128
+ };
129
+
130
+ export default addDBEditorEndpoints;
@@ -0,0 +1,319 @@
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
+
176
+ // Return the augmented params
177
+ return augmentedParams;
178
+ };
179
+
180
+ /**
181
+ * Validate a signed request. Throws an error if invalid
182
+ * @author Gabe Abrams
183
+ * @param opts object containing all arguments
184
+ * @param opts.method the method of the data validate
185
+ * @param opts.path the http request path to validate
186
+ * @param opts.scope the name of the scope to validate
187
+ * @param opts.params the request data to validate
188
+ * @returns parsed and validated params
189
+ */
190
+ export const validateSignedRequest = async (
191
+ opts: {
192
+ method: string,
193
+ path: string,
194
+ scope: string,
195
+ params: { [key: string]: any },
196
+ },
197
+ ) => {
198
+ /* ---------- Collect Info ---------- */
199
+
200
+ // Get the signature
201
+ if (!opts.params.oauth_signature) {
202
+ throw new ErrorWithCode(
203
+ 'Could not validate a cross-server request there was no oauth signature.',
204
+ ExpressKitErrorCode.CrossServerMissingSignedRequestInfo,
205
+ );
206
+ }
207
+ const signature = opts.params.oauth_signature;
208
+
209
+ // Get the timestamp
210
+ if (
211
+ // No timestamp
212
+ !opts.params.oauth_timestamp
213
+ // Invalid timestamp
214
+ || Number.isNaN(Number.parseInt(opts.params.oauth_timestamp, 10))
215
+ ) {
216
+ throw new ErrorWithCode(
217
+ 'Could not validate a cross-server request there was no valid oauth timestamp.',
218
+ ExpressKitErrorCode.CrossServerMissingSignedRequestInfo,
219
+ );
220
+ }
221
+ const timestamp = Number.parseInt(opts.params.oauth_timestamp, 10);
222
+
223
+ // Get the key
224
+ if (!opts.params.oauth_consumer_key) {
225
+ throw new ErrorWithCode(
226
+ 'Could not validate a cross-server request there was no oauth consumer key.',
227
+ ExpressKitErrorCode.CrossServerMissingSignedRequestInfo,
228
+ );
229
+ }
230
+ const key = opts.params.oauth_consumer_key;
231
+
232
+ // Get the rest of the info
233
+ const {
234
+ method,
235
+ path,
236
+ params,
237
+ scope,
238
+ } = opts;
239
+
240
+ /* ------- Look Up Credential ------- */
241
+
242
+ // Get the cross-server credential collection
243
+ const crossServerCredentialCollection = internalGetCrossServerCredentialCollection();
244
+ if (!crossServerCredentialCollection) {
245
+ throw new ErrorWithCode(
246
+ 'Could not validate a cross-server request because the cross-server credential collection was not ready in time.',
247
+ ExpressKitErrorCode.SignedRequestInvalidCollection,
248
+ );
249
+ }
250
+
251
+ // Get the cross-server credential
252
+ const crossServerCredentialMatches: CrossServerCredential[] = await crossServerCredentialCollection.find({ key });
253
+ if (!crossServerCredentialMatches || crossServerCredentialMatches.length === 0) {
254
+ throw new ErrorWithCode(
255
+ 'Could not validate a cross-server request because the credential was not found.',
256
+ ExpressKitErrorCode.SignedRequestInvalidCredential,
257
+ );
258
+ }
259
+ const crossServerCredential = crossServerCredentialMatches[0];
260
+
261
+ // Make sure the scope is included
262
+ const allowedScopes = crossServerCredential.scopes;
263
+ if (!allowedScopes || !Array.isArray(allowedScopes)) {
264
+ throw new ErrorWithCode(
265
+ 'Could not validate a cross-server request because the credential does not have access to any scopes.',
266
+ ExpressKitErrorCode.SignedRequestInvalidScope,
267
+ );
268
+
269
+ }
270
+ if (!allowedScopes.includes(scope)) {
271
+ throw new ErrorWithCode(
272
+ 'Could not validate a cross-server request because the required scope was not approved for the credential.',
273
+ ExpressKitErrorCode.SignedRequestInvalidScope,
274
+ );
275
+ }
276
+
277
+ // Decode the secret
278
+ const secret = await decrypt(crossServerCredential.encodedeSecret);
279
+
280
+ /* -------- Verify Signature -------- */
281
+
282
+ // Curate what goes into the params
283
+ const paramsToSign: {
284
+ [key: string]: any,
285
+ } = {
286
+ ...params,
287
+ };
288
+ Object.keys(paramsToSign).forEach((key) => {
289
+ // Delete oauth params
290
+ if (key.startsWith('oauth_')) {
291
+ delete paramsToSign[key];
292
+ }
293
+ });
294
+
295
+ // Generate a new signature to compare
296
+ const expectedSignature = await genSignature({
297
+ method,
298
+ path,
299
+ params: paramsToSign,
300
+ secret,
301
+ });
302
+
303
+ // Make sure the signatures match
304
+ if (signature !== expectedSignature) {
305
+ throw new ErrorWithCode(
306
+ 'Could not validate a cross-server request because the signature did not match.',
307
+ ExpressKitErrorCode.SignedRequestInvalidSignature,
308
+ );
309
+ }
310
+
311
+ // Make sure the timestamp was recent enough
312
+ const elapsedMs = Math.abs(Date.now() - timestamp);
313
+ if (elapsedMs > MINUTE_IN_MS) {
314
+ throw new ErrorWithCode(
315
+ 'Could not validate a cross-server request because the request was too old.',
316
+ ExpressKitErrorCode.SignedRequestInvalidTimestamp,
317
+ );
318
+ }
319
+ };