@@ -3,9 +3,8 @@ const bodyParser = require("body-parser");
3
3
const { createProxyMiddleware } = require ( "http-proxy-middleware" ) ;
4
4
5
5
const mysql = require ( "mysql" ) ;
6
- const { Pool, Client } = require ( "pg" ) ;
7
-
8
- const { parse } = require ( "graphql" ) ;
6
+ const Pool = require ( "pg-pool" ) ;
7
+ const fs = require ( "fs" ) ;
9
8
10
9
// Create Express Server
11
10
const app = express ( ) ;
@@ -35,6 +34,10 @@ const config = {
35
34
ENV : process . env . ENV || "NA" ,
36
35
} ;
37
36
37
+ if ( fs . existsSync ( "./config/permitted-routes.json" ) ) {
38
+ config . PERMITTED_ROUTES = JSON . parse ( fs . readFileSync ( "./config/permitted-routes.json" ) ) ;
39
+ }
40
+
38
41
// #endregion
39
42
40
43
// #region LOGGING
@@ -155,11 +158,11 @@ async function fetchAPIKeysInfo(key) {
155
158
if ( process . env . DB_TYPE === "mysql" ) {
156
159
params = [ key ] ;
157
160
query =
158
- "select * from prefect_api_keys where api_key=? and CURDATE() < key_expr_dt" ;
161
+ "SELECT * FROM prefect_api_keys WHERE api_key=? AND CURDATE() < key_expr_dt" ;
159
162
} else {
160
163
params = [ key ] ;
161
164
query =
162
- "select * from prefect_api_keys where api_key=$1 and CURRENT_TIMESTAMP < key_expr_dt" ;
165
+ "SELECT * FROM prefect_api_keys WHERE api_key=$1 AND CURRENT_TIMESTAMP < key_expr_dt" ;
163
166
}
164
167
165
168
dbConn . query ( query , params , ( error , results ) => {
@@ -177,49 +180,41 @@ async function fetchAPIKeysInfo(key) {
177
180
} ) ;
178
181
}
179
182
180
- const isMutationBlocked = ( op , acl ) => {
181
- // If public access is allowed, no need to check ACL
182
- if ( config . ALLOW_PUBLIC_ACCESS ) {
183
+ const checkRoutes = ( url , routes ) => {
184
+ if ( ! routes || ! routes . length ) {
183
185
return false ;
184
186
}
185
187
186
- if ( op && op . query ) {
187
- const parsedQuery = parse ( op . query ) ;
188
- const operationType = parsedQuery . definitions [ 0 ] . operation ;
189
- // Operation Names are symbolic and currently Prefect CLI does not
190
- // use them. So use the Selection Fields to determine the operation type
191
- // const operationName = parsedQuery.definitions[0].name
192
- // ? parsedQuery.definitions[0].name.value
193
- // : parsedQuery.definitions[0].selectionSet.selections[0].name.value;
194
- const operationName =
195
- parsedQuery . definitions [ 0 ] . selectionSet . selections [ 0 ] . name . value ;
196
-
197
- if (
198
- acl . ops &&
199
- ( acl . ops . includes ( `${ operationType } /*` ) ||
200
- acl . ops . includes ( `${ operationType } /${ operationName } ` ) )
201
- ) {
202
- // Do not block
203
- console . log ( `ALLOWED ${ operationType } Op Name: ` , operationName ) ;
204
- if ( operationType === "mutation" ) {
205
- console . warn (
206
- `PREFECT_AUDIT_TRAIL: ${ operationType } ${ operationName } allowed for ${ acl . user_id } ` ,
207
- op
208
- ) ;
209
- }
210
- return false ;
211
- } else {
212
- // Block
213
- console . warn (
214
- `PREFECT_AUDIT_TRAIL: BLOCKED ${ operationType } ${ operationName } for ${ acl . user_id } ` ,
215
- op
216
- ) ;
188
+ regexRoutes = routes . map ( route => route . replace ( / \* / g, "[^ ]*" ) ) ;
189
+
190
+ for ( let i = 0 ; i < regexRoutes . length ; i ++ ) {
191
+ const match = url . match ( regexRoutes [ i ] ) ;
192
+
193
+ if ( match ) {
217
194
return true ;
218
195
}
219
- } else {
196
+ }
197
+
198
+ return false ;
199
+ } ;
200
+
201
+ const allowPassthrough = ( url , method , acl ) => {
202
+ // check config for public access
203
+ if ( config . ALLOW_PUBLIC_ACCESS ) {
204
+ return true ;
205
+ }
206
+
207
+ // check permitted routes
208
+ if ( config . PERMITTED_ROUTES && config . PERMITTED_ROUTES [ method ] ?. length && checkRoutes ( url , config . PERMITTED_ROUTES [ method ] ) ) {
220
209
return true ;
221
210
}
222
- return true ;
211
+
212
+ // check acl
213
+ if ( checkRoutes ( url , acl ?. ops ) ) {
214
+ return true ;
215
+ }
216
+
217
+ return false ;
223
218
} ;
224
219
// #endregion
225
220
@@ -253,16 +248,16 @@ app.use(async (req, res, next) => {
253
248
} else {
254
249
acl = {
255
250
user_id : "Anonymous" ,
256
- ops : [ "mutation/none" , "query/*" ] ,
251
+ ops : [ ] ,
257
252
} ;
258
253
}
259
254
}
260
255
console . log ( "ACL: " , acl ) ;
261
256
req . acl = acl ;
262
257
}
263
258
264
- if ( ! req . acl && ! config . ALLOW_PUBLIC_ACCESS ) {
265
- res . status ( 401 ) . send ( "Unauthorized" ) ;
259
+ if ( ! allowPassthrough ( req . url , req . method , req . acl ) ) {
260
+ return res . status ( 401 ) . send ( "Unauthorized" ) ;
266
261
} else {
267
262
next ( ) ;
268
263
}
@@ -297,9 +292,6 @@ app.use(
297
292
console . log ( "URL" , req . url ) ;
298
293
console . log ( "Headers:" , req . headers ) ;
299
294
console . log ( "Body:" , req . body ) ;
300
- if ( ! req . body || ! Object . keys ( req . body ) . length ) {
301
- return res . status ( 400 ) . send ( "Invalid Request" ) ;
302
- }
303
295
304
296
if (
305
297
req . url === "/" &&
@@ -331,43 +323,13 @@ app.use(
331
323
} ) ;
332
324
} else {
333
325
// Proxy request to end point
334
- const contentType = proxyReq . getHeader ( "Content-Type" ) ;
335
326
const writeBody = ( bodyData ) => {
336
327
proxyReq . setHeader ( "Content-Length" , Buffer . byteLength ( bodyData ) ) ;
337
328
proxyReq . write ( bodyData ) ;
338
329
} ;
339
330
340
- if ( contentType . includes ( "application/json" ) ) {
341
- let ops = req . body ;
342
- let filteredOps = [ ] ;
343
-
344
- if ( Array . isArray ( ops ) ) {
345
- for ( let op of ops ) {
346
- if ( isMutationBlocked ( op , req . acl ) ) {
347
- // console.log("Mutation filtered");
348
- } else {
349
- filteredOps . push ( op ) ;
350
- }
351
- }
352
- if ( filteredOps . length > 0 ) {
353
- writeBody ( JSON . stringify ( filteredOps ) ) ;
354
- } else if ( filteredOps . length === 1 ) {
355
- writeBody ( JSON . stringify ( filteredOps [ 0 ] ) ) ;
356
- } else {
357
- return res . status ( 401 ) . send ( "Unauthorized" ) ;
358
- }
359
- } else {
360
- let op = ops ;
361
- if ( isMutationBlocked ( op , req . acl ) ) {
362
- // console.log("Mutation filtered");
363
- return res . status ( 401 ) . send ( "Unauthorized" ) ;
364
- }
365
- writeBody ( JSON . stringify ( ops ) ) ;
366
- }
367
- }
368
-
369
- if ( contentType === "application/x-www-form-urlencoded" ) {
370
- writeBody ( querystring . stringify ( req . body ) ) ;
331
+ if ( req . body ) {
332
+ writeBody ( JSON . stringify ( req . body ) ) ;
371
333
}
372
334
}
373
335
} ,
0 commit comments