prostgles-server 2.0.264 → 2.0.267

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/lib/DboBuilder.ts CHANGED
@@ -8,7 +8,7 @@ import * as Bluebird from "bluebird";
8
8
  // declare global { export interface Promise<T> extends Bluebird<T> {} }
9
9
 
10
10
  import * as pgPromise from 'pg-promise';
11
- const { ParameterizedQuery: PQ } = require('pg-promise');
11
+ import { canRunSQL, runSQL } from "./DboBuilder/runSQL";
12
12
  import pg = require('pg-promise/typescript/pg-subset');
13
13
  import {
14
14
  ColumnInfo, ValidatedColumnInfo, FieldFilter, SelectParams, SubscribeParams,
@@ -1091,6 +1091,14 @@ export class ViewHandler {
1091
1091
  }
1092
1092
 
1093
1093
  if(returnQuery) return (_query as unknown as any[]);
1094
+
1095
+ if(returnType === "statement"){
1096
+ if(!(await canRunSQL(this.dboBuilder.prostgles, localParams))){
1097
+ throw `Not allowed: {returnType: "statement"} requires sql privileges `
1098
+ }
1099
+ return _query as unknown as any[];
1100
+ }
1101
+
1094
1102
  if(["row", "value"].includes(returnType!)) {
1095
1103
  return (this.t || this.db).oneOrNone(_query).then(data => {
1096
1104
  return (data && returnType === "value")? Object.values(data)[0] : data;
@@ -1247,7 +1255,7 @@ export class ViewHandler {
1247
1255
  /* Local update allow all. TODO -> FIX THIS */
1248
1256
  if(!ff && !tableRule) filterFields = "*";
1249
1257
 
1250
- const parseFullFilter = async (f: any, parentFilter: any = null, checkForeignTablePublished = true): Promise<string> => {
1258
+ const parseFullFilter = async (f: any, parentFilter: any = null, isForcedFilterBypass: boolean): Promise<string> => {
1251
1259
  if(!f) throw "Invalid/missing group filter provided";
1252
1260
  let result = "";
1253
1261
  let keys = getKeys(f);
@@ -1262,21 +1270,21 @@ export class ViewHandler {
1262
1270
 
1263
1271
  if(group && group.length){
1264
1272
  const operand = $and? " AND " : " OR ";
1265
- let conditions = (await Promise.all(group.map(async gf => await parseFullFilter(gf, group, checkForeignTablePublished)))).filter(c => c);
1273
+ let conditions = (await Promise.all(group.map(async gf => await parseFullFilter(gf, group, isForcedFilterBypass)))).filter(c => c);
1266
1274
  if(conditions && conditions.length){
1267
1275
  if(conditions.length === 1) return conditions.join(operand);
1268
1276
  else return ` ( ${conditions.sort().join(operand)} ) `;
1269
1277
  }
1270
1278
  } else if(!group) {
1271
1279
 
1280
+ /** forcedFilters do not get checked against publish and are treated as server-side requests */
1272
1281
  result = await this.getCondition({
1273
1282
  filter: { ...f },
1274
1283
  select,
1275
1284
  allowed_colnames: this.parseFieldFilter(filterFields),
1276
1285
  tableAlias,
1277
- localParams,
1278
- tableRules: tableRule,
1279
- checkForeignTablePublished
1286
+ localParams: isForcedFilterBypass? undefined : localParams,
1287
+ tableRules: isForcedFilterBypass? undefined : tableRule
1280
1288
  });
1281
1289
  }
1282
1290
  return result;
@@ -1286,8 +1294,8 @@ export class ViewHandler {
1286
1294
 
1287
1295
 
1288
1296
  /* A forced filter condition will not check if the existsJoined filter tables have been published */
1289
- const forcedFilterCond = forcedFilter? await parseFullFilter(forcedFilter, null, false) : undefined;
1290
- const filterCond = await parseFullFilter(filter, null);
1297
+ const forcedFilterCond = forcedFilter? await parseFullFilter(forcedFilter, null, true) : undefined;
1298
+ const filterCond = await parseFullFilter(filter, null, false);
1291
1299
  let cond = [
1292
1300
  forcedFilterCond, filterCond
1293
1301
  ].filter(c => c).join(" AND ");
@@ -1300,7 +1308,7 @@ export class ViewHandler {
1300
1308
  return { where: cond || "", filter: finalFilter };
1301
1309
  }
1302
1310
 
1303
- async prepareExistCondition(eConfig: ExistsFilterConfig, localParams: LocalParams | undefined, checkForeignTablePublished = true): Promise<string> {
1311
+ async prepareExistCondition(eConfig: ExistsFilterConfig, localParams: LocalParams | undefined): Promise<string> {
1304
1312
  let res = "";
1305
1313
  const thisTable = this.name;
1306
1314
  const isNotExists = ["$notExists", "$notExistsJoined"].includes(eConfig.existType);
@@ -1377,15 +1385,15 @@ export class ViewHandler {
1377
1385
 
1378
1386
  let finalWhere = "";
1379
1387
 
1380
- console.error("NEED TO ENSURE PUBLISH FILTERS BYPASS THE CHECKS HERE")
1381
1388
  let t2Rules: TableRule | undefined = undefined,
1382
1389
  forcedFilter: AnyObject | undefined,
1383
1390
  filterFields: FieldFilter | undefined,
1384
1391
  tableAlias;
1385
1392
 
1386
- /* Check if allowed to view data */
1387
- if(localParams && checkForeignTablePublished && (localParams.socket || localParams.httpReq) && this.dboBuilder.publishParser){
1388
- /* Need to think about joining through dissallowed tables */
1393
+ /* Check if allowed to view data - forcedFilters will bypass this check through isForcedFilterBypass */
1394
+ if(localParams && (!localParams?.socket && !localParams?.httpReq)) throw "Unexpected: localParams missing socket/httpReq"
1395
+ if(localParams && (localParams.socket || localParams.httpReq) && this.dboBuilder.publishParser){
1396
+
1389
1397
  t2Rules = await this.dboBuilder.publishParser.getValidatedRequestRuleWusr({ tableName: t2, command: "find", localParams }) as TableRule;
1390
1398
  if(!t2Rules || !t2Rules.select) throw "Dissallowed";
1391
1399
  ({ forcedFilter, filterFields } = t2Rules.select);
@@ -1420,8 +1428,8 @@ export class ViewHandler {
1420
1428
  * { fff: 2 } => "fff" = 2
1421
1429
  * { fff: { $ilike: 'abc' } } => "fff" ilike 'abc'
1422
1430
  */
1423
- async getCondition(params: { filter: any, select?: SelectItem[], allowed_colnames: string[], tableAlias?: string, localParams?: LocalParams, tableRules?: TableRule, checkForeignTablePublished: boolean }){
1424
- const { filter, select, allowed_colnames, tableAlias, localParams, tableRules, checkForeignTablePublished = true } = params;
1431
+ async getCondition(params: { filter: any, select?: SelectItem[], allowed_colnames: string[], tableAlias?: string, localParams?: LocalParams, tableRules?: TableRule }){
1432
+ const { filter, select, allowed_colnames, tableAlias, localParams, tableRules } = params;
1425
1433
 
1426
1434
 
1427
1435
  let data = { ... (filter as any) } as any ;
@@ -1491,7 +1499,7 @@ export class ViewHandler {
1491
1499
 
1492
1500
  let existsCond = "";
1493
1501
  if(existsKeys.length){
1494
- existsCond = (await Promise.all(existsKeys.map(async k => await this.prepareExistCondition(k, localParams, checkForeignTablePublished)))).join(" AND ");
1502
+ existsCond = (await Promise.all(existsKeys.map(async k => await this.prepareExistCondition(k, localParams)))).join(" AND ");
1495
1503
  }
1496
1504
 
1497
1505
  /* Computed field queries */
@@ -2439,13 +2447,9 @@ export class TableHandler extends ViewHandler {
2439
2447
 
2440
2448
  }
2441
2449
 
2442
- let DATA_TYPES: {oid: string, typname: PG_COLUMN_UDT_DATA_TYPE }[] | undefined;
2443
- let USER_TABLES: { relid: string; relname: string; }[] | undefined;
2444
-
2445
2450
  import { JOIN_TYPES } from "./Prostgles";
2446
2451
  import { BasicSession } from "./AuthHandler";
2447
2452
  import { DBOFullyTyped, getDBSchema } from "./DBSchemaBuilder";
2448
- import { bool } from "aws-sdk/clients/signer";
2449
2453
 
2450
2454
  export class DboBuilder {
2451
2455
  tablesOrViews?: TableSchema[]; //TableSchema TableOrViewInfo
@@ -2651,107 +2655,9 @@ export class DboBuilder {
2651
2655
  return this.joinPaths;
2652
2656
  }
2653
2657
 
2654
- runSQL = async (query: string, params: any, options: SQLOptions | undefined, localParams?: LocalParams) => {
2655
-
2656
- /** Cache types */
2657
- DATA_TYPES ??= await this.db.any("SELECT oid, typname FROM pg_type") ?? [];
2658
- USER_TABLES ??= await this.db.any("SELECT relid, relname FROM pg_catalog.pg_statio_user_tables") ?? [];
2659
-
2660
- const canRunSQL = async (localParams?: LocalParams) => {
2661
- if(!localParams?.socket || !localParams?.httpReq) return true;
2662
-
2663
- const { socket } = localParams;
2664
- const publishParams = await this.prostgles.publishParser!.getPublishParams({ socket });
2665
- let res = await this.prostgles.opts.publishRawSQL?.(publishParams);
2666
- return Boolean(res && typeof res === "boolean" || res === "*");
2667
- }
2668
-
2669
- if(!(await canRunSQL(localParams))) throw "Not allowed to run SQL";
2670
-
2671
- const { returnType, allowListen }: SQLOptions = options || ({} as any);
2672
- const { socket } = localParams || {};
2673
-
2674
- const db = localParams?.tx?.t || this.db;
2675
- if(returnType === "noticeSubscription"){
2676
- if(!socket) throw "Only allowed with client socket"
2677
- return await this.prostgles.dbEventsManager?.addNotice(socket);
2678
- } else if(returnType === "statement"){
2679
- try {
2680
- return pgp.as.format(query, params);
2681
- } catch (err){
2682
- throw (err as any).toString();
2683
- }
2684
- } else if(db) {
2685
-
2686
- let finalQuery = query + "";
2687
- if(returnType === "arrayMode" && !["listen ", "notify "].find(c => query.toLowerCase().trim().startsWith(c))){
2688
- finalQuery = new PQ({ text: pgp.as.format(query, params), rowMode: "array" });
2689
- }
2690
-
2691
- let _qres = await db.result(finalQuery, params)
2692
- const { fields, rows, command } = _qres;
2693
-
2694
- /**
2695
- * Fallback for watchSchema in case not superuser and cannot add db event listener
2696
- */
2697
- const { watchSchema, watchSchemaType } = this.prostgles?.opts || {};
2698
-
2699
- if(
2700
- watchSchema &&
2701
- (!this.prostgles.isSuperUser || watchSchemaType === "prostgles_queries")
2702
- ){
2703
- if(["CREATE", "ALTER", "DROP"].includes(command)){
2704
- this.prostgles.onSchemaChange({ command, query })
2705
- } else if(query) {
2706
- const cleanedQuery = query.toLowerCase().replace(/\s\s+/g, ' ');
2707
- if(PubSubManager.SCHEMA_ALTERING_QUERIES.some(q => cleanedQuery.includes(q.toLowerCase()))){
2708
- this.prostgles.onSchemaChange({ command, query })
2709
- }
2710
- }
2711
- }
2712
-
2713
- if(command === "LISTEN"){
2714
- if(!allowListen) throw new Error(`Your query contains a LISTEN command. Set { allowListen: true } to get subscription hooks. Or ignore this message`)
2715
- if(!socket) throw "Only allowed with client socket"
2716
- return await this.prostgles.dbEventsManager?.addNotify(query, socket);
2717
-
2718
- } else if(returnType === "rows") {
2719
- return rows;
2720
-
2721
- } else if(returnType === "row") {
2722
- return rows[0];
2723
-
2724
- } else if(returnType === "value") {
2725
- return Object.values(rows?.[0] || {})?.[0];
2726
-
2727
- } else if(returnType === "values") {
2728
- return rows.map(r => Object.values(r[0]));
2729
-
2730
- } else {
2731
-
2732
- let qres: SQLResult<typeof returnType> = {
2733
- duration: 0,
2734
- ..._qres,
2735
- fields: fields?.map(f => {
2736
- const dataType = DATA_TYPES!.find(dt => +dt.oid === +f.dataTypeID)?.typname ?? "text",
2737
- tableName = USER_TABLES!.find(t => +t.relid === +f.tableID),
2738
- tsDataType = postgresToTsType(dataType);
2739
-
2740
- return {
2741
- ...f,
2742
- tsDataType,
2743
- dataType,
2744
- udt_name: dataType,
2745
- tableName: tableName?.relname
2746
- }
2747
- }) ?? []
2748
- };
2749
- return qres;
2750
- }
2751
-
2752
- } else console.error("db missing");
2658
+ private runSQL = async (query: string, params: any, options: SQLOptions | undefined, localParams?: LocalParams) => {
2659
+ return runSQL.bind(this)(query, params, options, localParams);
2753
2660
  }
2754
-
2755
2661
  async build(): Promise<DBHandlerServer>{
2756
2662
 
2757
2663
  this.tablesOrViews = await getTablesForSchemaPostgresSQL(this.db);
@@ -2820,101 +2726,6 @@ export class DboBuilder {
2820
2726
  if(!this.dbo.sql){
2821
2727
 
2822
2728
  this.dbo.sql = this.runSQL;
2823
- // this.dbo.sql = async (query: string, params: any, options: SQLOptions | undefined, localParams?: LocalParams) => {
2824
-
2825
- // const canRunSQL = async (localParams?: LocalParams) => {
2826
- // if(!localParams?.socket || !localParams?.httpReq) return true;
2827
-
2828
- // const { socket } = localParams;
2829
- // const publishParams = await this.prostgles.publishParser!.getPublishParams({ socket });
2830
- // let res = await this.prostgles.opts.publishRawSQL?.(publishParams);
2831
- // return Boolean(res && typeof res === "boolean" || res === "*");
2832
- // }
2833
-
2834
- // if(!(await canRunSQL(localParams))) throw "Not allowed to run SQL";
2835
-
2836
- // const { returnType, allowListen }: SQLOptions = options || ({} as any);
2837
- // const { socket } = localParams || {};
2838
-
2839
- // if(returnType === "noticeSubscription"){
2840
- // if(!socket) throw "Only allowed with client socket"
2841
- // return await this.prostgles.dbEventsManager?.addNotice(socket);
2842
- // } else if(returnType === "statement"){
2843
- // try {
2844
- // return pgp.as.format(query, params);
2845
- // } catch (err){
2846
- // throw (err as any).toString();
2847
- // }
2848
- // } else if(this.db) {
2849
-
2850
- // let finalQuery = query + "";
2851
- // if(returnType === "arrayMode" && !["listen ", "notify "].find(c => query.toLowerCase().trim().startsWith(c))){
2852
- // finalQuery = new PQ({ text: pgp.as.format(query, params), rowMode: "array" });
2853
- // }
2854
-
2855
- // let _qres = await this.db.result(finalQuery, params)
2856
- // const { fields, rows, command } = _qres;
2857
-
2858
- // /**
2859
- // * Fallback for watchSchema in case not superuser and cannot add db event listener
2860
- // */
2861
- // const { watchSchema, watchSchemaType } = this.prostgles?.opts || {};
2862
-
2863
- // if(
2864
- // watchSchema &&
2865
- // (!this.prostgles.isSuperUser || watchSchemaType === "prostgles_queries")
2866
- // ){
2867
- // if(["CREATE", "ALTER", "DROP"].includes(command)){
2868
- // this.prostgles.onSchemaChange({ command, query })
2869
- // } else if(query) {
2870
- // const cleanedQuery = query.toLowerCase().replace(/\s\s+/g, ' ');
2871
- // if(PubSubManager.SCHEMA_ALTERING_QUERIES.some(q => cleanedQuery.includes(q.toLowerCase()))){
2872
- // this.prostgles.onSchemaChange({ command, query })
2873
- // }
2874
- // }
2875
- // }
2876
-
2877
- // if(command === "LISTEN"){
2878
- // if(!allowListen) throw new Error(`Your query contains a LISTEN command. Set { allowListen: true } to get subscription hooks. Or ignore this message`)
2879
- // if(!socket) throw "Only allowed with client socket"
2880
- // return await this.prostgles.dbEventsManager?.addNotify(query, socket);
2881
-
2882
- // } else if(returnType === "rows") {
2883
- // return rows;
2884
-
2885
- // } else if(returnType === "row") {
2886
- // return rows[0];
2887
-
2888
- // } else if(returnType === "value") {
2889
- // return Object.values(rows?.[0] || {})?.[0];
2890
-
2891
- // } else if(returnType === "values") {
2892
- // return rows.map(r => Object.values(r[0]));
2893
-
2894
- // } else {
2895
-
2896
- // let qres: SQLResult<typeof returnType> = {
2897
- // duration: 0,
2898
- // ..._qres,
2899
- // fields: fields?.map(f => {
2900
- // const dataType = DATA_TYPES.find(dt => +dt.oid === +f.dataTypeID)?.typname ?? "text",
2901
- // tableName = USER_TABLES.find(t => +t.relid === +f.tableID),
2902
- // tsDataType = postgresToTsType(dataType);
2903
-
2904
- // return {
2905
- // ...f,
2906
- // tsDataType,
2907
- // dataType,
2908
- // udt_name: dataType,
2909
- // tableName: tableName?.relname
2910
- // }
2911
- // }) ?? []
2912
- // };
2913
- // return qres;
2914
- // }
2915
-
2916
- // } else console.error("db missing");
2917
- // }
2918
2729
  } else {
2919
2730
  console.warn(`Could not create dbo.sql handler because there is already a table named "sql"`)
2920
2731
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prostgles-server",
3
- "version": "2.0.264",
3
+ "version": "2.0.267",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -31,7 +31,7 @@
31
31
  "check-disk-space": "^3.3.1",
32
32
  "file-type": "^17.1.4",
33
33
  "pg-promise": "^10.11.1",
34
- "prostgles-types": "^1.5.170",
34
+ "prostgles-types": "^1.5.174",
35
35
  "sharp": "^0.30.7"
36
36
  },
37
37
  "devDependencies": {
@@ -1 +1 @@
1
- 14126
1
+ 14047
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "../..": {
23
23
  "name": "prostgles-server",
24
- "version": "2.0.263",
24
+ "version": "2.0.266",
25
25
  "license": "MIT",
26
26
  "dependencies": {
27
27
  "@aws-sdk/client-s3": "^3.121.0",
@@ -31,7 +31,7 @@
31
31
  "check-disk-space": "^3.3.1",
32
32
  "file-type": "^17.1.4",
33
33
  "pg-promise": "^10.11.1",
34
- "prostgles-types": "^1.5.170",
34
+ "prostgles-types": "^1.5.174",
35
35
  "sharp": "^0.30.7"
36
36
  },
37
37
  "devDependencies": {
@@ -1375,7 +1375,7 @@
1375
1375
  "check-disk-space": "^3.3.1",
1376
1376
  "file-type": "^17.1.4",
1377
1377
  "pg-promise": "^10.11.1",
1378
- "prostgles-types": "^1.5.170",
1378
+ "prostgles-types": "^1.5.174",
1379
1379
  "sharp": "^0.30.7",
1380
1380
  "typescript": "^4.7.4"
1381
1381
  }