prostgles-server 4.2.167 → 4.2.169

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 (60) hide show
  1. package/dist/Auth/AuthHandler.d.ts.map +1 -1
  2. package/dist/Auth/AuthHandler.js +3 -2
  3. package/dist/Auth/AuthHandler.js.map +1 -1
  4. package/dist/DboBuilder/DboBuilder.js +1 -1
  5. package/dist/DboBuilder/DboBuilder.js.map +1 -1
  6. package/dist/DboBuilder/DboBuilderTypes.d.ts +2 -2
  7. package/dist/DboBuilder/DboBuilderTypes.d.ts.map +1 -1
  8. package/dist/DboBuilder/DboBuilderTypes.js.map +1 -1
  9. package/dist/DboBuilder/QueryStreamer.js +1 -1
  10. package/dist/DboBuilder/QueryStreamer.js.map +1 -1
  11. package/dist/DboBuilder/TableHandler/TableHandler.d.ts +1 -1
  12. package/dist/DboBuilder/TableHandler/TableHandler.d.ts.map +1 -1
  13. package/dist/DboBuilder/TableHandler/updateBatch.d.ts +1 -1
  14. package/dist/DboBuilder/TableHandler/updateBatch.d.ts.map +1 -1
  15. package/dist/DboBuilder/TableHandler/updateBatch.js +1 -1
  16. package/dist/DboBuilder/TableHandler/updateBatch.js.map +1 -1
  17. package/dist/DboBuilder/TableHandler/upsert.d.ts.map +1 -1
  18. package/dist/DboBuilder/TableHandler/upsert.js +3 -5
  19. package/dist/DboBuilder/TableHandler/upsert.js.map +1 -1
  20. package/dist/DboBuilder/ViewHandler/find.js +1 -1
  21. package/dist/DboBuilder/ViewHandler/find.js.map +1 -1
  22. package/dist/DboBuilder/dboBuilderUtils.d.ts +1 -2
  23. package/dist/DboBuilder/dboBuilderUtils.d.ts.map +1 -1
  24. package/dist/DboBuilder/dboBuilderUtils.js +11 -7
  25. package/dist/DboBuilder/dboBuilderUtils.js.map +1 -1
  26. package/dist/Prostgles.d.ts +1 -1
  27. package/dist/Prostgles.d.ts.map +1 -1
  28. package/dist/Prostgles.js +5 -5
  29. package/dist/Prostgles.js.map +1 -1
  30. package/dist/PublishParser/PublishParser.d.ts +1 -0
  31. package/dist/PublishParser/PublishParser.d.ts.map +1 -1
  32. package/dist/PublishParser/PublishParser.js +3 -3
  33. package/dist/PublishParser/PublishParser.js.map +1 -1
  34. package/dist/PublishParser/getSchemaFromPublish.d.ts +2 -1
  35. package/dist/PublishParser/getSchemaFromPublish.d.ts.map +1 -1
  36. package/dist/PublishParser/getSchemaFromPublish.js +4 -4
  37. package/dist/PublishParser/getSchemaFromPublish.js.map +1 -1
  38. package/dist/RestApi.js +1 -1
  39. package/dist/RestApi.js.map +1 -1
  40. package/dist/SyncReplication.js +1 -1
  41. package/dist/SyncReplication.js.map +1 -1
  42. package/dist/runClientRequest.d.ts.map +1 -1
  43. package/dist/runClientRequest.js +53 -28
  44. package/dist/runClientRequest.js.map +1 -1
  45. package/lib/Auth/AuthHandler.ts +3 -2
  46. package/lib/DboBuilder/DboBuilder.ts +1 -1
  47. package/lib/DboBuilder/DboBuilderTypes.ts +2 -1
  48. package/lib/DboBuilder/QueryStreamer.ts +1 -1
  49. package/lib/DboBuilder/TableHandler/TableHandler.ts +1 -1
  50. package/lib/DboBuilder/TableHandler/updateBatch.ts +2 -2
  51. package/lib/DboBuilder/TableHandler/upsert.ts +3 -4
  52. package/lib/DboBuilder/ViewHandler/find.ts +2 -2
  53. package/lib/DboBuilder/dboBuilderUtils.ts +15 -11
  54. package/lib/Prostgles.ts +6 -11
  55. package/lib/PublishParser/PublishParser.ts +2 -2
  56. package/lib/PublishParser/getSchemaFromPublish.ts +7 -6
  57. package/lib/RestApi.ts +1 -1
  58. package/lib/SyncReplication.ts +1 -1
  59. package/lib/runClientRequest.ts +65 -27
  60. package/package.json +2 -2
@@ -49,19 +49,22 @@ export const getErrorAsObject = (rawError: any, includeStack = false) => {
49
49
 
50
50
  type GetSerializedClientErrorFromPGErrorArgs = {
51
51
  type: "sql";
52
+ localParams: LocalParams | undefined;
52
53
  } | {
53
54
  type: "tableMethod";
54
55
  localParams: LocalParams | undefined;
55
56
  view: ViewHandler | Partial<TableHandler> | undefined;
56
57
  allowedKeys?: string[];
57
- canRunSql?: boolean;
58
58
  } | {
59
59
  type: "method";
60
60
  localParams: LocalParams | undefined;
61
61
  allowedKeys?: string[];
62
62
  view?: undefined;
63
- canRunSql?: boolean;
64
- }
63
+ };
64
+
65
+ const sensitiveErrorKeys = ["hint", "detail", "context"] as const;
66
+ const otherKeys = ["column", "code", "code_info", "table", "constraint", "severity", "message", "name"] as const;
67
+
65
68
  export function getSerializedClientErrorFromPGError(rawError: any, args: GetSerializedClientErrorFromPGErrorArgs): AnyObject {
66
69
  const err = getErrorAsObject(rawError);
67
70
  if(err.code) {
@@ -71,18 +74,19 @@ export function getSerializedClientErrorFromPGError(rawError: any, args: GetSeri
71
74
  console.trace(err)
72
75
  }
73
76
 
74
- const isServerSideRequest = args.type !== "sql" && !args.localParams;
75
- if(args.type === "sql" || isServerSideRequest){
77
+ const isServerSideRequest = !args.localParams;
78
+ //TODO: add a rawSQL check for HTTP requests
79
+ const showFullError = isServerSideRequest || args.type === "sql" || args.localParams?.socket?.prostgles?.rawSQL;
80
+ if(showFullError){
76
81
  return err;
77
82
  }
78
83
  const { view, allowedKeys } = args;
79
84
 
80
-
81
- const sensitiveErrorKeys = ["hint", "detail", "context"];
82
- const otherKeys = ["column", "code", "code_info", "table", "constraint", "severity", "message", "name"];
83
- const finalKeys = otherKeys
84
- .concat(allowedKeys ?? [])
85
- .concat(args.canRunSql? sensitiveErrorKeys : []);
85
+ const finalKeys = [
86
+ ...otherKeys,
87
+ ...(allowedKeys ?? []),
88
+ ...(showFullError? sensitiveErrorKeys : [])
89
+ ];
86
90
 
87
91
  const errObject = pickKeys(err, finalKeys);
88
92
  if (view?.dboBuilder?.constraints && errObject.constraint && !errObject.column) {
package/lib/Prostgles.ts CHANGED
@@ -24,13 +24,12 @@ export type PGP = pgPromise.IMain<{}, pg.IClient>;
24
24
  import {
25
25
  CHANNELS,
26
26
  ClientSchema,
27
- DBSchemaTable,
28
- SQLRequest, TableSchemaForClient,
27
+ SQLRequest,
29
28
  isObject, omitKeys, tryCatch
30
29
  } from "prostgles-types";
31
30
  import { DBEventsManager } from "./DBEventsManager";
32
31
  import { PublishParser } from "./PublishParser/PublishParser";
33
- export { sendEmail, getOrSetTransporter } from "./Auth/sendEmail";
32
+ export { getOrSetTransporter, sendEmail } from "./Auth/sendEmail";
34
33
 
35
34
  export type DB = pgPromise.IDatabase<{}, pg.IClient>;
36
35
  export type DBorTx = DB | pgPromise.ITask<{}>
@@ -62,7 +61,6 @@ const DEFAULT_KEYWORDS = {
62
61
 
63
62
  import { randomUUID } from "crypto";
64
63
  import * as fs from 'fs';
65
- import { sendEmail } from "./Auth/sendEmail";
66
64
 
67
65
  export class Prostgles {
68
66
  /**
@@ -376,10 +374,7 @@ export class Prostgles {
376
374
  if(!this.authHandler) throw "this.authHandler missing";
377
375
  const userData = await this.authHandler.getClientInfo(clientInfo);
378
376
  const { publishParser } = this;
379
- let fullSchema: {
380
- schema: TableSchemaForClient;
381
- tables: DBSchemaTable[];
382
- } | undefined;
377
+ let fullSchema: Awaited<ReturnType<PublishParser["getSchemaFromPublish"]>> | undefined;
383
378
  let publishValidationError;
384
379
 
385
380
  try {
@@ -395,7 +390,7 @@ export class Prostgles {
395
390
  rawSQL = allowed;
396
391
  }
397
392
 
398
- const { schema, tables } = fullSchema ?? { schema: {}, tables: [] };
393
+ const { schema, tables, tableSchemaErrors } = fullSchema ?? { schema: {}, tables: [], tableSchemaErrors: {} };
399
394
  const joinTables2: string[][] = [];
400
395
  if (this.opts.joins) {
401
396
  const _joinTables2 = this.dboBuilder.getAllJoinPaths()
@@ -433,6 +428,7 @@ export class Prostgles {
433
428
  tableSchema: tables,
434
429
  rawSQL,
435
430
  joinTables: joinTables2,
431
+ tableSchemaErrors,
436
432
  auth,
437
433
  version,
438
434
  err: publishValidationError? "Server Error: User publish validation failed." : undefined
@@ -460,8 +456,7 @@ export class Prostgles {
460
456
 
461
457
  try {
462
458
  const clientSchema = await this.getClientSchema({ socket });
463
- socket.prostgles = socket.prostgles || {};
464
- socket.prostgles.schema = clientSchema.schema;
459
+ socket.prostgles = clientSchema;
465
460
  if (clientSchema.rawSQL) {
466
461
  socket.removeAllListeners(CHANNELS.SQL)
467
462
  socket.on(CHANNELS.SQL, async ({ query, params, options }: SQLRequest, cb = (..._callback: any) => { /* Empty */ }) => {
@@ -108,9 +108,9 @@ export class PublishParser {
108
108
  if (!this.publish) throw "publish is missing";
109
109
 
110
110
  /* Get any publish errors for socket */
111
- const schm = localParams?.socket?.prostgles?.schema?.[tableName]?.[command];
111
+ const errorInfo = localParams?.socket?.prostgles?.tableSchemaErrors?.[tableName]?.[command];
112
112
 
113
- if (schm && schm.err) throw schm.err;
113
+ if (errorInfo) throw errorInfo.error;
114
114
 
115
115
  const table_rule = await this.getTableRules({ tableName, localParams }, clientInfo);
116
116
  if (!table_rule) throw { stack: ["getValidatedRequestRule()"], message: "Invalid or disallowed table: " + tableName };
@@ -1,4 +1,4 @@
1
- import { DBSchemaTable, MethodKey, TableInfo, TableSchemaForClient, getKeys, pickKeys } from "prostgles-types";
1
+ import { DBSchemaTable, MethodKey, TableInfo, TableSchemaErrors, TableSchemaForClient, getKeys, pickKeys } from "prostgles-types";
2
2
  import { AuthResult, ExpressReq } from "../Auth/AuthTypes";
3
3
  import { getErrorAsObject, PRGLIOSocket } from "../DboBuilder/DboBuilder";
4
4
  import { PublishObject, PublishParser } from "./PublishParser"
@@ -13,8 +13,10 @@ type Args = ({
13
13
  }) & {
14
14
  userData: AuthResult | undefined;
15
15
  }
16
- export async function getSchemaFromPublish(this: PublishParser, { userData, ...clientReq }: Args): Promise<{ schema: TableSchemaForClient; tables: DBSchemaTable[] }> {
16
+
17
+ export async function getSchemaFromPublish(this: PublishParser, { userData, ...clientReq }: Args): Promise<{ schema: TableSchemaForClient; tables: DBSchemaTable[]; tableSchemaErrors: TableSchemaErrors }> {
17
18
  const schema: TableSchemaForClient = {};
19
+ const tableSchemaErrors: TableSchemaErrors = {};
18
20
  let tables: DBSchemaTable[] = []
19
21
 
20
22
  try {
@@ -92,9 +94,8 @@ export async function getSchemaFromPublish(this: PublishParser, { userData, ...c
92
94
  }
93
95
 
94
96
  } catch (e) {
95
- tableSchema[method] = {
96
- err: "Internal publish error. Check server logs"
97
- };
97
+ tableSchemaErrors[tableName] ??= {};
98
+ tableSchemaErrors[tableName]![method] = { error: "Internal publish error. Check server logs" };
98
99
 
99
100
  throw {
100
101
  ...getErrorAsObject(e),
@@ -137,5 +138,5 @@ export async function getSchemaFromPublish(this: PublishParser, { userData, ...c
137
138
  }
138
139
 
139
140
  tables = tables.sort((a, b) => a.name.localeCompare(b.name));
140
- return { schema, tables };
141
+ return { schema, tables, tableSchemaErrors };
141
142
  }
package/lib/RestApi.ts CHANGED
@@ -92,7 +92,7 @@ export class RestApi {
92
92
  const data = await runClientSqlRequest.bind(this.prostgles)({ type: "http", httpReq: req, query, args, options });
93
93
  res.json(data);
94
94
  } catch(rawError){
95
- const error = getSerializedClientErrorFromPGError(rawError, { type: "sql" });
95
+ const error = getSerializedClientErrorFromPGError(rawError, { type: "sql", localParams: { httpReq: req } });
96
96
  res.status(400).json({ error });
97
97
  }
98
98
  }
@@ -219,7 +219,7 @@ export async function syncData (this: PubSubManager, sync: SyncParams, clientDat
219
219
 
220
220
  updateData.push([syncSafeFilter, omitKeys(upd, id_fields)])
221
221
  }));
222
- await tbl.updateBatch(updateData, { fixIssues: true }, table_rules);
222
+ await tbl.updateBatch(updateData, { fixIssues: true }, undefined, table_rules);
223
223
  } else {
224
224
  updates = [];
225
225
  }
@@ -1,9 +1,13 @@
1
- import { AnyObject, TableHandler, UserLike, getKeys, pickKeys } from "prostgles-types";
1
+ import { AnyObject,
2
+ TableHandler,
3
+ UserLike, getKeys, pickKeys } from "prostgles-types";
2
4
  import { ExpressReq } from "./Auth/AuthTypes";
3
5
  import { LocalParams, PRGLIOSocket } from "./DboBuilder/DboBuilder";
4
6
  import { parseFieldFilter } from "./DboBuilder/ViewHandler/parseFieldFilter";
5
7
  import { canRunSQL } from "./DboBuilder/runSQL";
6
8
  import { Prostgles } from "./Prostgles";
9
+ import { TableHandler as TableHandlerServer } from "./DboBuilder/TableHandler/TableHandler";
10
+ import { TableRule } from "./PublishParser/publishTypesAndUtils";
7
11
 
8
12
  type ReqInfo = {
9
13
  type: "socket";
@@ -19,6 +23,27 @@ type ReqInfoClient = {
19
23
  } | {
20
24
  httpReq: ExpressReq;
21
25
  }
26
+
27
+ const TABLE_METHODS = {
28
+ find: 1,
29
+ findOne: 1,
30
+ count: 1,
31
+ size: 1,
32
+ update: 1,
33
+ updateBatch: 1,
34
+ delete: 1,
35
+ upsert: 1,
36
+ insert: 1,
37
+ subscribe: 1,
38
+ subscribeOne: 1,
39
+ getColumns: 1,
40
+ getInfo: 1,
41
+ sync: 1,
42
+ } as const satisfies Record<(keyof (TableHandler & Pick<TableHandlerServer, "sync">)), 1>;
43
+
44
+ const TABLE_METHODS_KEYS = getKeys(TABLE_METHODS);
45
+ const SOCKET_ONLY_COMMANDS = ["subscribe", "subscribeOne", "sync"] as const satisfies typeof TABLE_METHODS_KEYS;
46
+
22
47
  type Args = ReqInfo & {
23
48
  tableName: string;
24
49
  command: string;
@@ -26,7 +51,6 @@ type Args = ReqInfo & {
26
51
  param2: any;
27
52
  param3: any;
28
53
  };
29
- const SOCKET_ONLY_COMMANDS = ["subscribe", "subscribeOne", "sync"];
30
54
 
31
55
  const getReqInfoClient = (reqInfo: ReqInfo): ReqInfoClient => {
32
56
  if(reqInfo.type === "socket"){
@@ -34,43 +58,57 @@ const getReqInfoClient = (reqInfo: ReqInfo): ReqInfoClient => {
34
58
  }
35
59
  return { httpReq: reqInfo.httpReq };
36
60
  }
61
+
62
+ type TableMethodFunctionWithRulesAndLocalParams = ((arg1: any, arg2: any, arg3: any, tableRule: TableRule, localParams: LocalParams) => any);
63
+
37
64
  export const runClientRequest = async function(this: Prostgles, args: Args){
38
65
  /* Channel name will only include client-sent params so we ignore table_rules enforced params */
39
66
  if ((args.type === "socket" && !args.socket) || (args.type === "http" && !args.httpReq) || !this.authHandler || !this.publishParser || !this.dbo) {
40
67
  throw "socket/httpReq or authhandler missing";
41
68
  }
42
69
 
43
- const { tableName, command, param1, param2, param3 } = args;
44
-
45
- if(args.type !== "socket" && SOCKET_ONLY_COMMANDS.includes(command)){
70
+ const { tableName, command: nonValidatedCommand, param1, param2, param3 } = args;
71
+ if(!TABLE_METHODS_KEYS.some(v => v === nonValidatedCommand)){
72
+ throw `Invalid command: ${nonValidatedCommand}. Expecting one of: ${TABLE_METHODS_KEYS};`
73
+ }
74
+ const command = nonValidatedCommand as keyof TableHandler;
75
+ if(args.type !== "socket" && SOCKET_ONLY_COMMANDS.some(v => v === command)){
46
76
  throw "The following commands cannot be completed over a non-websocket connection: " + SOCKET_ONLY_COMMANDS;
47
77
  }
78
+
48
79
  const reqInfo = getReqInfoClient(args);
49
80
  const clientInfo = await this.authHandler.getClientInfo(args);
50
- const valid_table_command_rules = await this.publishParser.getValidatedRequestRule({ tableName, command, localParams: reqInfo }, clientInfo);
51
- if (valid_table_command_rules) {
52
- const sessionUser: UserLike | undefined = !clientInfo?.user? undefined : {
53
- ...parseFieldFilter(clientInfo.sessionFields ?? [] as any, false, Object.keys(clientInfo.user)),
54
- ...pickKeys(clientInfo.user, ["id", "type"]) as UserLike,
55
- }
56
- const localParams: LocalParams = { ...reqInfo, isRemoteRequest: { user: sessionUser } }
57
- if(param3 && (param3 as LocalParams).returnQuery){
58
- const isAllowed = await canRunSQL(this, localParams);
59
- if(isAllowed){
60
- localParams.returnQuery = (param3 as LocalParams).returnQuery;
61
- } else {
62
- throw "Must be allowed to run sql to use returnQuery";
63
- }
64
- }
65
- const tableHandler = this.dbo[tableName];
66
- if(!tableHandler || !tableHandler.column_names) throw `Invalid tableName ${tableName} provided`;
67
- const method = tableHandler[command as keyof TableHandler];
68
- if(!method) throw `Invalid command ${command} provided`;
69
- //@ts-ignore
70
- return this.dbo[tableName][command](param1, param2, param3, valid_table_command_rules, localParams);
71
- } else {
81
+ const validRules = await this.publishParser.getValidatedRequestRule({ tableName, command, localParams: reqInfo }, clientInfo);
82
+ if (!validRules) {
72
83
  throw `Invalid OR disallowed request: ${tableName}.${command} `;
73
84
  }
85
+
86
+ const sessionUser: UserLike | undefined = !clientInfo?.user? undefined : {
87
+ ...parseFieldFilter(clientInfo.sessionFields ?? [] as any, false, Object.keys(clientInfo.user)),
88
+ ...pickKeys(clientInfo.user, ["id", "type"]) as UserLike,
89
+ }
90
+ const localParams: LocalParams = { ...reqInfo, isRemoteRequest: { user: sessionUser } }
91
+ if(param3 && (param3 as LocalParams).returnQuery){
92
+ const isAllowed = await canRunSQL(this, localParams);
93
+ if(isAllowed){
94
+ localParams.returnQuery = (param3 as LocalParams).returnQuery;
95
+ } else {
96
+ throw "Must be allowed to run sql to use returnQuery";
97
+ }
98
+ }
99
+ const tableHandler = this.dbo[tableName];
100
+ if(!tableHandler || !tableHandler.column_names) throw `Invalid tableName ${tableName} provided`;
101
+
102
+ /**
103
+ * satisfies check is used to ensure rules arguments are correctly passed to each method
104
+ */
105
+ const tableCommand = tableHandler[command] satisfies undefined | TableMethodFunctionWithRulesAndLocalParams;
106
+ if(!tableCommand) throw `Invalid or disallowed command provided: ${command}`;
107
+ //TODO: why does this not work?
108
+ // const result = await (tableCommand as TableMethodFunctionWithRulesAndLocalParams)(param1, param2, param3, validRules, localParams);
109
+ // return result;
110
+ //@ts-ignore
111
+ return this.dbo[tableName][command](param1, param2, param3, validRules, localParams);
74
112
  }
75
113
 
76
114
  export const clientCanRunSqlRequest = async function(this: Prostgles, args: ReqInfo){
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prostgles-server",
3
- "version": "4.2.167",
3
+ "version": "4.2.169",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -54,7 +54,7 @@
54
54
  "pg": "^8.11.5",
55
55
  "pg-cursor": "^2.11.0",
56
56
  "pg-promise": "^11.9.1",
57
- "prostgles-types": "^4.0.107"
57
+ "prostgles-types": "^4.0.109"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@types/express": "^4.17.21",