@galadrim/galadmin 0.1.228 → 0.1.232

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.
@@ -360,6 +360,7 @@ export type AuthRole = {
360
360
  useArgon2?: boolean;
361
361
  };
362
362
  export type Config = {
363
+ enriched?: boolean;
363
364
  name: string;
364
365
  sessionSecret: string;
365
366
  useMysql2?: boolean;
@@ -74,7 +74,7 @@ class dbManager {
74
74
  port: config.mysql.port || 3306,
75
75
  charset: 'UTF8MB4_GENERAL_CI',
76
76
  dateStrings: true,
77
- multipleStatements: true
77
+ multipleStatements: config.mysql.multipleStatements === true
78
78
  });
79
79
  this.dbType = "MYSQL";
80
80
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dbManager.js","sourceRoot":"","sources":["../../src/utils/dbManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dbManager.js","sourceRoot":"","sources":["../../src/utils/dbManager.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@galadrim/galadmin",
3
- "version": "0.1.228",
3
+ "version": "0.1.232",
4
4
  "main": "dist/index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -41,7 +41,8 @@
41
41
  "@types/express": "^4.17.13",
42
42
  "@types/express-session": "1.17.0",
43
43
  "@types/mime": "^2.0.3",
44
- "@types/node": "^16.18.11"
44
+ "@types/node": "^16.18.11",
45
+ "typescript": "^5.9.3"
45
46
  },
46
47
  "repository": {
47
48
  "type": "git",
package/src/api.ts CHANGED
@@ -23,13 +23,150 @@ function generateUniqueIdForObject(obj) {
23
23
  return hash;
24
24
  }
25
25
 
26
- export default (db, config, user) => {
26
+ declare global {
27
+ namespace Express {
28
+ interface Request {
29
+ config?: any;
30
+ }
31
+ }
32
+ }
33
+
34
+ export default (db, config) => {
27
35
  const app = express();
28
36
 
37
+ app.use((req, res, next) => {
38
+ req.config = {
39
+ ...config,
40
+ views: config.views
41
+ .filter(x => !x.roles
42
+ || (req.session.user && ([...(x.roles || []), ...(x.readRoles || [])].includes(req.session.user.role)))
43
+ )
44
+ .map(view => {
45
+ const readOnly = req.session.user && view.roles && !view.roles.includes(req.session.user.role)
46
+ if (readOnly) {
47
+ return {
48
+ ...view,
49
+ canEdit: false,
50
+ canCreate: false,
51
+ canDelete: false,
52
+ clickToEdit: false
53
+ }
54
+ } else {
55
+ return view;
56
+ }
57
+ })
58
+ .map(view => {
59
+ if (view.type === "table") {
60
+ return {
61
+ ...view,
62
+ columns: view.columns.filter(column => !column.roles || (req.session.user && column.roles.includes(req.session.user.role)))
63
+ }
64
+ } else {
65
+ return view;
66
+ }
67
+ })
68
+ }
69
+ next();
70
+ });
71
+
29
72
  app.use(async (req, res, next) => {
30
- if (!config.enriched) {
31
- await enrichConfig(config, db);
73
+ if (!req.config.enriched) {
74
+ await enrichConfig(req.config, db);
75
+ }
76
+ next();
77
+ });
78
+
79
+ // ---------------------------------------------------------------------------
80
+ // SECURITY: server-side authorization.
81
+ //
82
+ // The config-filtering middleware above only decides which views the *client*
83
+ // is shown, and sets canEdit/canCreate/canDelete flags on that served config.
84
+ // Nothing enforced those decisions server-side, so a session with the lowest
85
+ // role could POST directly to /updateRecord, /deleteRecord, /customQuery, …
86
+ // and act on any view — including read-only ones — as the DB user (root).
87
+ // This block re-checks every sensitive route against the view's declared roles.
88
+ // ---------------------------------------------------------------------------
89
+ const ROOT_ROLE = "root";
90
+ const roleOf = (req) => req.session && req.session.user && req.session.user.role;
91
+ // Write access = the view's `roles` (root always allowed).
92
+ const canWriteView = (view, role) =>
93
+ role === ROOT_ROLE || !view || !view.roles || view.roles.includes(role);
94
+
95
+ const viewFromRequest = (req) => {
96
+ const idx =
97
+ req.body && req.body.viewIndex !== undefined
98
+ ? req.body.viewIndex
99
+ : req.query && req.query.viewIndex !== undefined
100
+ ? req.query.viewIndex
101
+ : undefined;
102
+ if (idx === undefined || !req.config || !req.config.views) return undefined;
103
+ return req.config.views[idx];
104
+ };
105
+
106
+ // Routes that mutate data and reference their target view via viewIndex
107
+ // (body for POST bodies, query for /uploadFile). Import routes carry the
108
+ // viewIndex inside a multipart body and are guarded inline in their handlers.
109
+ const MUTATING_VIEW_ROUTES = new Set([
110
+ "/updateRecord",
111
+ "/createRecord",
112
+ "/deleteRecord",
113
+ "/deleteRecords",
114
+ "/deleteRecordFromPanel",
115
+ "/customAction",
116
+ "/uploadFile",
117
+ ]);
118
+ // /duplicateRecord references its table via req.body.table (not a viewIndex),
119
+ // so it is guarded inline in its handler instead of by the generic middleware.
120
+
121
+ // Column names are interpolated into INSERT/UPDATE statements; only accept plain
122
+ // SQL identifiers so a crafted key cannot break out of the column list.
123
+ const areSafeColumnNames = (data) =>
124
+ !data ||
125
+ Object.keys(data).every((k) => /^[A-Za-z0-9_]+$/.test(k));
126
+
127
+ // Tables the current role is allowed to write, gathered from its writable views
128
+ // and their creatable panels — used to bound /duplicateRecord's target table.
129
+ const writableTables = (req) => {
130
+ const role = roleOf(req);
131
+ const tables = new Set<string>();
132
+ for (const view of req.config?.views || []) {
133
+ if (!canWriteView(view, role)) continue;
134
+ if (view.tableName) tables.add(view.tableName);
135
+ for (const panel of view.recordViewPanels || []) {
136
+ if (panel.tableName) tables.add(panel.tableName);
137
+ if (panel.createConfig?.tableName) tables.add(panel.createConfig.tableName);
138
+ }
139
+ }
140
+ return tables;
141
+ };
142
+
143
+ // Reject client-supplied SQL fragments (the `filters` array is concatenated
144
+ // raw into the WHERE clause). With multipleStatements off this already blocks
145
+ // stacked writes; the pattern also blocks UNION/comment/file exfil breakouts.
146
+ const looksLikeSqlBreakout = (frag) =>
147
+ typeof frag === "string" &&
148
+ /(;|--|\/\*|\bunion\b|\binto\s+(out|dump)file\b|\bsleep\s*\(|\bbenchmark\s*\(|\bload_file\s*\()/i.test(
149
+ frag
150
+ );
151
+
152
+ app.use((req, res, next) => {
153
+ const role = roleOf(req);
154
+
155
+ // 1) Validate any raw filter fragments on read endpoints.
156
+ if (req.body && Array.isArray(req.body.filters)) {
157
+ if (req.body.filters.some(looksLikeSqlBreakout)) {
158
+ return res.status(400).send({ error: "Invalid filter" });
159
+ }
160
+ }
161
+
162
+ // 2) Enforce write authorization on mutating routes.
163
+ if (MUTATING_VIEW_ROUTES.has(req.path)) {
164
+ const view = viewFromRequest(req);
165
+ if (!view || !canWriteView(view, role)) {
166
+ return res.sendStatus(403);
167
+ }
32
168
  }
169
+
33
170
  next();
34
171
  });
35
172
 
@@ -124,7 +261,7 @@ export default (db, config, user) => {
124
261
 
125
262
  app.get("/config", async (req, res) => {
126
263
  try {
127
- const { mysql, auth, sessionSecret, ...safeConfig } = config;
264
+ const { mysql, auth, sessionSecret, ...safeConfig } = req.config;
128
265
  res.send({
129
266
  config: serializeJs(safeConfig),
130
267
  });
@@ -201,7 +338,7 @@ export default (db, config, user) => {
201
338
  };
202
339
 
203
340
  app.post("/foreignSelectRecords", async (req, res, next) => {
204
- const view = config.views[req.body.viewIndex];
341
+ const view = req.config.views[req.body.viewIndex];
205
342
  const column = [
206
343
  ...view.columns,
207
344
  ...(view.recordViewPanels
@@ -222,7 +359,7 @@ export default (db, config, user) => {
222
359
  ? `${column.searchableName} LIKE '${column.wholeSearch ? "%" : ""}${queryEscaped}%'`
223
360
  : null,
224
361
  column.filtersFromUser
225
- ? column.filtersFromUser(user.userId, user.role)
362
+ ? column.filtersFromUser(req.session.user.userId, req.session.user.role)
226
363
  : null,
227
364
  ]),
228
365
  join: column.join,
@@ -256,7 +393,7 @@ export default (db, config, user) => {
256
393
  });
257
394
 
258
395
  app.post("/records", async function (req, res, next) {
259
- const view = config.views[req.body.viewIndex];
396
+ const view = req.config.views[req.body.viewIndex];
260
397
  const isDesc = req.body.isDesc === null ? view.isDesc : req.body.isDesc;
261
398
  const subview =
262
399
  view.subviews && req.body.subviewIndex !== undefined
@@ -277,7 +414,7 @@ export default (db, config, user) => {
277
414
  subview?.filters,
278
415
  ...req.body.filters,
279
416
  view.filtersFromUser
280
- ? view.filtersFromUser(user.userId, user.role)
417
+ ? view.filtersFromUser(req.session.user.userId, req.session.user.role)
281
418
  : null,
282
419
  ]),
283
420
  having: subview?.having,
@@ -352,11 +489,11 @@ export default (db, config, user) => {
352
489
  " = " +
353
490
  (typeof data[key] === "string"
354
491
  ? "'" +
355
- data[key]
356
- .replace(/'/g, "\\'")
357
- .replace(/\n/g, "")
358
- .replace(/\r/g, "") +
359
- "'"
492
+ data[key]
493
+ .replace(/'/g, "\\'")
494
+ .replace(/\n/g, "")
495
+ .replace(/\r/g, "") +
496
+ "'"
360
497
  : typeof data[key] === "object"
361
498
  ? JSON.stringify(data[key])
362
499
  : data[key]) +
@@ -400,7 +537,7 @@ export default (db, config, user) => {
400
537
  }
401
538
 
402
539
  app.post("/recordsExport", async function (req, res) {
403
- const view = config.views[req.body.viewIndex];
540
+ const view = req.config.views[req.body.viewIndex];
404
541
  const orderBy = req.body.orderBy || view.orderBy;
405
542
  const isDesc = req.body.isDesc === null ? view.isDesc : req.body.isDesc;
406
543
  const subview =
@@ -462,7 +599,7 @@ export default (db, config, user) => {
462
599
  return res.status(500).send(err);
463
600
  }
464
601
  // Get viewIndex in fields
465
- const view = config.views[fields.viewIndex[0]];
602
+ const view = req.config.views[fields.viewIndex[0]];
466
603
  if (typeof view.parseImportFile !== "function") {
467
604
  return res
468
605
  .status(500)
@@ -553,7 +690,7 @@ export default (db, config, user) => {
553
690
  return res.status(500).send(err);
554
691
  }
555
692
  // Get viewIndex and panelIndex in fields
556
- const view = config.views[fields.viewIndex[0]];
693
+ const view = req.config.views[fields.viewIndex[0]];
557
694
  const panel = view.recordViewPanels[fields.panelIndex[0]];
558
695
  if (typeof panel.parseImportFile !== "function") {
559
696
  return res
@@ -634,13 +771,13 @@ export default (db, config, user) => {
634
771
  });
635
772
 
636
773
  app.get("/record", async function (req, res) {
637
- const view = config.views[req.query.viewIndex as string];
774
+ const view = req.config.views[req.query.viewIndex as string];
638
775
  const { err, rows } = await db.query(
639
776
  "SELECT " +
640
- (view.selectExpression || "*") +
641
- " FROM ?? " +
642
- (view.join || "") +
643
- " WHERE ?? = ?",
777
+ (view.selectExpression || "*") +
778
+ " FROM ?? " +
779
+ (view.join || "") +
780
+ " WHERE ?? = ?",
644
781
  [
645
782
  view.tableName,
646
783
  view.tableName + "." + view.primaryId,
@@ -665,7 +802,10 @@ export default (db, config, user) => {
665
802
  });
666
803
 
667
804
  app.post("/updateRecord", async function (req, res, next) {
668
- const view = config.views[req.body.viewIndex];
805
+ const view = req.config.views[req.body.viewIndex];
806
+ if (!areSafeColumnNames(req.body.data)) {
807
+ return res.status(400).send({ error: "Invalid column name" });
808
+ }
669
809
  for (let key in req.body.data) {
670
810
  if (
671
811
  req.body.data[key] == "NULL" ||
@@ -703,7 +843,7 @@ export default (db, config, user) => {
703
843
  data: req.body.data,
704
844
  previousData: req.body.previousData,
705
845
  });
706
- } catch (e) {}
846
+ } catch (e) { }
707
847
  }
708
848
  if (view.editCallbackFunction) {
709
849
  await view.editCallbackFunction({
@@ -715,20 +855,20 @@ export default (db, config, user) => {
715
855
  });
716
856
 
717
857
  app.post("/customAction", async function (req, res, next) {
718
- const view = config.views[req.body.viewIndex];
858
+ const view = req.config.views[req.body.viewIndex];
719
859
  const action = view.customActions[req.body.actionIndex];
720
860
  for (const recordId of req.body.selectedRecordIds) {
721
861
  try {
722
862
  const { rows } = await db.query(
723
863
  "SELECT " +
724
- (view.selectExpression || "*") +
725
- " FROM ?? " +
726
- (view.join || "") +
727
- " WHERE ?? = ?",
864
+ (view.selectExpression || "*") +
865
+ " FROM ?? " +
866
+ (view.join || "") +
867
+ " WHERE ?? = ?",
728
868
  [view.tableName, view.tableName + "." + view.primaryId, recordId]
729
869
  );
730
870
  const record = rows[0];
731
- const actionResult = await action.action(record, user.userId);
871
+ const actionResult = await action.action(record, req.session.user.userId);
732
872
  if (actionResult) {
733
873
  return res.send({
734
874
  success: true,
@@ -750,13 +890,13 @@ export default (db, config, user) => {
750
890
  });
751
891
 
752
892
  app.post("/deleteRecord", async function (req, res) {
753
- const view = config.views[req.body.viewIndex];
893
+ const view = req.config.views[req.body.viewIndex];
754
894
  const { rows, err: err2 } = await db.query(
755
895
  "SELECT " +
756
- (view.selectExpression || "*") +
757
- " FROM ?? " +
758
- (view.join || "") +
759
- " WHERE ?? = ?",
896
+ (view.selectExpression || "*") +
897
+ " FROM ?? " +
898
+ (view.join || "") +
899
+ " WHERE ?? = ?",
760
900
  [
761
901
  view.tableName,
762
902
  view.tableName + "." + view.primaryId,
@@ -783,7 +923,7 @@ export default (db, config, user) => {
783
923
  await axios.post(view.deleteCallback, {
784
924
  id: req.body.primaryValue,
785
925
  });
786
- } catch (e) {}
926
+ } catch (e) { }
787
927
  }
788
928
  if (view.deleteCallbackFunction) {
789
929
  await view.deleteCallbackFunction({
@@ -795,7 +935,7 @@ export default (db, config, user) => {
795
935
  });
796
936
 
797
937
  app.post("/deleteRecordFromPanel", async function (req, res) {
798
- const view = config.views[req.body.viewIndex];
938
+ const view = req.config.views[req.body.viewIndex];
799
939
  const panel = view.recordViewPanels.find(
800
940
  (x) => x.__id__ === req.body.panelId
801
941
  );
@@ -820,7 +960,7 @@ export default (db, config, user) => {
820
960
  await axios.post(panel.deleteCallback, {
821
961
  id: req.body.primaryValue,
822
962
  });
823
- } catch (e) {}
963
+ } catch (e) { }
824
964
  }
825
965
  if (panel.deleteCallbackFunction) {
826
966
  await panel.deleteCallbackFunction({
@@ -832,7 +972,7 @@ export default (db, config, user) => {
832
972
  });
833
973
 
834
974
  app.post("/deleteRecords", async function (req, res, next) {
835
- const view = config.views[req.body.viewIndex];
975
+ const view = req.config.views[req.body.viewIndex];
836
976
  const { err } = await db.query(
837
977
  `DELETE FROM ?? WHERE ?? IN(${req.body.primaryValues.map((x) => "'" + x + "'").join(",")})`,
838
978
  [view.tableName, view.primaryId]
@@ -866,7 +1006,7 @@ export default (db, config, user) => {
866
1006
  }
867
1007
  }
868
1008
 
869
- for (const view of config.views) {
1009
+ for (const view of req.config.views) {
870
1010
  await checkAndGetNotificationBadge(view);
871
1011
  if (view.subviews) {
872
1012
  for (const subview of view.subviews) {
@@ -878,6 +1018,15 @@ export default (db, config, user) => {
878
1018
  });
879
1019
 
880
1020
  app.post("/duplicateRecord", async function (req, res, next) {
1021
+ // SECURITY: bound the (client-supplied) target table to tables the role may write.
1022
+ const allowedTables = writableTables(req);
1023
+ const requestedTables = [
1024
+ req.body.table,
1025
+ ...(req.body.duplicateConfig?.foreignTables || []).map((t) => t.table),
1026
+ ];
1027
+ if (requestedTables.some((t) => !allowedTables.has(t))) {
1028
+ return res.sendStatus(403);
1029
+ }
881
1030
  const primaryId = req.body.duplicateConfig.primaryId;
882
1031
  const { err, rows } = await db.query("SELECT * FROM ?? WHERE ?? = ?", [
883
1032
  req.body.table,
@@ -923,7 +1072,7 @@ export default (db, config, user) => {
923
1072
  });
924
1073
 
925
1074
  app.post("/createRecord", async function (req, res, next) {
926
- const view = config.views[req.body.viewIndex];
1075
+ const view = req.config.views[req.body.viewIndex];
927
1076
  const panel = req.body.panelIndex
928
1077
  ? view.recordViewPanels[req.body.panelIndex]
929
1078
  : null;
@@ -955,6 +1104,9 @@ export default (db, config, user) => {
955
1104
  delete createPayload[key];
956
1105
  }
957
1106
  }
1107
+ if (!areSafeColumnNames(createPayload)) {
1108
+ return res.status(400).send({ error: "Invalid column name" });
1109
+ }
958
1110
  const queryExpression = Object.keys(createPayload).join(", ");
959
1111
  const queryPlaceholders = Object.keys(createPayload)
960
1112
  .map((x) => "?")
@@ -982,7 +1134,7 @@ export default (db, config, user) => {
982
1134
  id: r.insertId,
983
1135
  data: req.body.data,
984
1136
  });
985
- } catch (e) {}
1137
+ } catch (e) { }
986
1138
  }
987
1139
  if (panel ? panel.createCallbackFunction : view.createCallbackFunction) {
988
1140
  await (
@@ -1004,30 +1156,69 @@ export default (db, config, user) => {
1004
1156
  }
1005
1157
  });
1006
1158
 
1007
- app.get("/customQuery", async function (req, res, next) {
1008
- const query = (req.query.query as string).replace(
1009
- /\?/g,
1010
- req.query.primaryValue as string
1011
- );
1012
- const { err, rows } = await db.query(query);
1013
- if (err) {
1014
- return res.status(500).send(err);
1159
+ // Collect every SQL template declared anywhere in the (role-filtered) config:
1160
+ // panel `query`, `autocompleteListQuery`, etc. customQuery may only run a
1161
+ // template that already exists in the config the caller is allowed to see —
1162
+ // so it can never be turned into an arbitrary read/write primitive.
1163
+ function collectAllowedQueries(node, acc = new Set<string>()) {
1164
+ if (!node || typeof node !== "object") return acc;
1165
+ if (Array.isArray(node)) {
1166
+ for (const item of node) collectAllowedQueries(item, acc);
1167
+ return acc;
1015
1168
  }
1016
- if (req.query.viewIndex && req.query.panelIndex) {
1017
- const panel =
1018
- config.views[req.query.viewIndex as string].recordViewPanels[
1169
+ for (const key of Object.keys(node)) {
1170
+ const value = node[key];
1171
+ if (typeof value === "string" && (key === "query" || /Query$/.test(key))) {
1172
+ acc.add(value.trim());
1173
+ } else if (value && typeof value === "object") {
1174
+ collectAllowedQueries(value, acc);
1175
+ }
1176
+ }
1177
+ return acc;
1178
+ }
1179
+
1180
+ // Attack-surface reduction: only expose /customQuery at all when the config
1181
+ // actually declares SQL templates (panels, autocomplete lists, …). If nothing
1182
+ // uses it, the route is replaced by an explicit "disabled" stub so the raw-SQL
1183
+ // sink simply does not exist for that deployment.
1184
+ const CONFIG_USES_CUSTOM_QUERY = collectAllowedQueries(config.views).size > 0;
1185
+
1186
+ if (!CONFIG_USES_CUSTOM_QUERY) {
1187
+ app.get("/customQuery", (req, res) => res.sendStatus(404));
1188
+ } else {
1189
+ app.get("/customQuery", async function (req, res, next) {
1190
+ const template = typeof req.query.query === "string" ? req.query.query : "";
1191
+ // SECURITY: only run whitelisted, read-only templates, and bind the record id
1192
+ // as a parameter instead of substituting it into the SQL string.
1193
+ const allowed = collectAllowedQueries(req.config.views);
1194
+ if (!allowed.has(template.trim())) {
1195
+ return res.status(403).send({ error: "Query not allowed" });
1196
+ }
1197
+ if (!/^\s*\(?\s*(select|with)\b/i.test(template) || /;/.test(template)) {
1198
+ return res.status(400).send({ error: "Only read-only queries are allowed" });
1199
+ }
1200
+ const placeholderCount = (template.match(/\?/g) || []).length;
1201
+ const params = new Array(placeholderCount).fill(req.query.primaryValue);
1202
+ const { err, rows } = await db.query(template, params);
1203
+ if (err) {
1204
+ return res.status(500).send(err);
1205
+ }
1206
+ if (req.query.viewIndex && req.query.panelIndex) {
1207
+ const panel =
1208
+ req.config.views[req.query.viewIndex as string].recordViewPanels[
1019
1209
  req.query.panelIndex as string
1020
- ];
1021
- if (panel.formatValue) {
1022
- for (const row of rows) {
1023
- for (const key in panel.formatValue) {
1024
- row[key] = await panel.formatValue[key](row);
1210
+ ];
1211
+ if (panel.formatValue) {
1212
+ for (const row of rows) {
1213
+ for (const key in panel.formatValue) {
1214
+ row[key] = await panel.formatValue[key](row);
1215
+ }
1025
1216
  }
1026
1217
  }
1027
1218
  }
1028
- }
1029
- res.send(rows);
1030
- });
1219
+ res.send(rows);
1220
+ });
1221
+ }
1031
1222
 
1032
1223
  function uploadFile(column, fileParamName, req, res) {
1033
1224
  if (column.s3Config || column.gcpConfig) {
@@ -1171,9 +1362,9 @@ export default (db, config, user) => {
1171
1362
  await sftp.fastPut(
1172
1363
  files[fileParamName][0].path,
1173
1364
  column.publicPath +
1174
- (column.subdirectory ? "/" + column.subdirectory : "") +
1175
- "/" +
1176
- filename
1365
+ (column.subdirectory ? "/" + column.subdirectory : "") +
1366
+ "/" +
1367
+ filename
1177
1368
  );
1178
1369
  await sftp.end();
1179
1370
  res.send({
@@ -1211,7 +1402,7 @@ export default (db, config, user) => {
1211
1402
  );
1212
1403
 
1213
1404
  app.post("/uploadFile", async function (req, res, next) {
1214
- const view = config.views[req.query.viewIndex as string];
1405
+ const view = req.config.views[req.query.viewIndex as string];
1215
1406
  const column = [
1216
1407
  ...view.columns,
1217
1408
  ...(view.recordViewPanels
package/src/index.ts CHANGED
@@ -130,7 +130,11 @@ export default (config: Config): any => {
130
130
  // we check that the user still exists
131
131
  let stillExists = false;
132
132
  for (const authRole of config.authRoles) {
133
- const { rows } = await db.query("SELECT * FROM ?? WHERE ?? = ?", [authRole.table, authRole.idField, req.session.user.userId]);
133
+ // Re-apply the role's `filters` here too, so an account that becomes
134
+ // ineligible (e.g. deactivated) has its existing session invalidated
135
+ // on its next request — not just blocked at login time.
136
+ const filters = authRole.filters ? `AND ${authRole.filters}` : "";
137
+ const { rows } = await db.query(`SELECT * FROM ?? WHERE ?? = ? ${filters}`, [authRole.table, authRole.idField, req.session.user.userId]);
134
138
  if (rows.length > 0) {
135
139
  stillExists = true;
136
140
  break;
@@ -151,39 +155,7 @@ export default (config: Config): any => {
151
155
  }
152
156
  })
153
157
 
154
- app.use("/api", (req, res, next) => {
155
- return api(db, {
156
- ...config,
157
- views: config.views
158
- .filter(x => !x.roles
159
- || (req.session.user && ([...(x.roles || []), ...(x.readRoles || [])].includes(req.session.user.role)))
160
- )
161
- .map(view => {
162
- const readOnly = req.session.user && view.roles && !view.roles.includes(req.session.user.role)
163
- if (readOnly) {
164
- return {
165
- ...view,
166
- canEdit: false,
167
- canCreate: false,
168
- canDelete: false,
169
- clickToEdit: false
170
- }
171
- } else {
172
- return view;
173
- }
174
- })
175
- .map(view => {
176
- if (view.type === "table") {
177
- return {
178
- ...view,
179
- columns: view.columns.filter(column => !column.roles || (req.session.user && column.roles.includes(req.session.user.role)))
180
- }
181
- } else {
182
- return view;
183
- }
184
- })
185
- }, req.session.user)(req, res, next)
186
- })
158
+ app.use("/api", api(db, config))
187
159
 
188
160
  // Serve static files
189
161
  app.get("*", (req, res) => {
@@ -429,6 +429,9 @@ export type AuthRole = {
429
429
  };
430
430
 
431
431
  export type Config = {
432
+ /** Ne pas renseigner !!! */
433
+ enriched?: boolean;
434
+
432
435
  /** Nom du projet */
433
436
  name: string;
434
437
 
@@ -89,7 +89,10 @@ export default class dbManager {
89
89
  port: config.mysql.port || 3306,
90
90
  charset: 'UTF8MB4_GENERAL_CI',
91
91
  dateStrings: true,
92
- multipleStatements: true
92
+ // SECURITY: multi-statement queries let any SQL-injection escalate to stacked
93
+ // writes / DROP. Keep OFF by default; opt in explicitly only if a config truly
94
+ // relies on `;`-separated statements (none of galadmin's internal queries do).
95
+ multipleStatements: config.mysql.multipleStatements === true
93
96
  });
94
97
  this.dbType = "MYSQL";
95
98
  } else if (config.postgresql) {
package/tsconfig.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "module": "commonjs",
4
+ "skipLibCheck": true,
4
5
  "declaration": true,
5
6
  "removeComments": true,
6
7
  "emitDecoratorMetadata": true,