@saltcorn/data 1.6.0-alpha.8 → 1.6.0-beta.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.
- package/dist/base-plugin/actions.d.ts +3 -3
- package/dist/base-plugin/actions.d.ts.map +1 -1
- package/dist/base-plugin/actions.js +59 -17
- package/dist/base-plugin/actions.js.map +1 -1
- package/dist/base-plugin/index.d.ts +10 -5
- package/dist/base-plugin/index.d.ts.map +1 -1
- package/dist/base-plugin/types.d.ts +7 -2
- package/dist/base-plugin/types.d.ts.map +1 -1
- package/dist/base-plugin/types.js +9 -4
- package/dist/base-plugin/types.js.map +1 -1
- package/dist/base-plugin/viewtemplates/edit.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/edit.js +3 -0
- package/dist/base-plugin/viewtemplates/edit.js.map +1 -1
- package/dist/base-plugin/viewtemplates/filter.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/filter.js +11 -3
- package/dist/base-plugin/viewtemplates/filter.js.map +1 -1
- package/dist/base-plugin/viewtemplates/list.d.ts +2 -1
- package/dist/base-plugin/viewtemplates/list.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/list.js +18 -8
- package/dist/base-plugin/viewtemplates/list.js.map +1 -1
- package/dist/base-plugin/viewtemplates/show.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/show.js +3 -0
- package/dist/base-plugin/viewtemplates/show.js.map +1 -1
- package/dist/db/state.d.ts +4 -0
- package/dist/db/state.d.ts.map +1 -1
- package/dist/db/state.js +27 -4
- package/dist/db/state.js.map +1 -1
- package/dist/migrate.d.ts.map +1 -1
- package/dist/migrate.js +9 -4
- package/dist/migrate.js.map +1 -1
- package/dist/migrations/202603101553.d.ts +4 -0
- package/dist/migrations/202603101553.d.ts.map +1 -0
- package/dist/migrations/202603101553.js +22 -0
- package/dist/migrations/202603101553.js.map +1 -0
- package/dist/mobile-mocks/node/assert.d.ts +1 -0
- package/dist/mobile-mocks/node/assert.d.ts.map +1 -0
- package/dist/mobile-mocks/node/assert.js +2 -0
- package/dist/mobile-mocks/node/assert.js.map +1 -0
- package/dist/models/expression.d.ts +1 -1
- package/dist/models/expression.d.ts.map +1 -1
- package/dist/models/expression.js +51 -16
- package/dist/models/expression.js.map +1 -1
- package/dist/models/field.d.ts.map +1 -1
- package/dist/models/field.js +67 -10
- package/dist/models/field.js.map +1 -1
- package/dist/models/index.d.ts +1 -1
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/internal/push_message_helper.d.ts.map +1 -1
- package/dist/models/internal/push_message_helper.js +5 -2
- package/dist/models/internal/push_message_helper.js.map +1 -1
- package/dist/models/metadata.d.ts +2 -1
- package/dist/models/metadata.d.ts.map +1 -1
- package/dist/models/metadata.js +5 -0
- package/dist/models/metadata.js.map +1 -1
- package/dist/models/page.d.ts.map +1 -1
- package/dist/models/page.js +3 -1
- package/dist/models/page.js.map +1 -1
- package/dist/models/plugin.d.ts +2 -2
- package/dist/models/plugin.d.ts.map +1 -1
- package/dist/models/plugin.js +2 -2
- package/dist/models/plugin.js.map +1 -1
- package/dist/models/table.d.ts +8 -0
- package/dist/models/table.d.ts.map +1 -1
- package/dist/models/table.js +196 -83
- package/dist/models/table.js.map +1 -1
- package/dist/models/trigger.d.ts.map +1 -1
- package/dist/models/trigger.js +20 -9
- package/dist/models/trigger.js.map +1 -1
- package/dist/models/user.d.ts.map +1 -1
- package/dist/models/user.js +4 -0
- package/dist/models/user.js.map +1 -1
- package/dist/models/workflow.d.ts.map +1 -1
- package/dist/models/workflow.js +6 -0
- package/dist/models/workflow.js.map +1 -1
- package/dist/plugin-helper.d.ts.map +1 -1
- package/dist/plugin-helper.js +8 -0
- package/dist/plugin-helper.js.map +1 -1
- package/dist/viewable_fields.d.ts +2 -2
- package/dist/viewable_fields.d.ts.map +1 -1
- package/dist/viewable_fields.js +13 -8
- package/dist/viewable_fields.js.map +1 -1
- package/package.json +8 -8
- package/webpack.config.js +1 -0
- package/dist/tests/actions.test.d.ts +0 -2
- package/dist/tests/actions.test.d.ts.map +0 -1
- package/dist/tests/actions.test.js +0 -936
- package/dist/tests/actions.test.js.map +0 -1
- package/dist/tests/auth.test.d.ts +0 -2
- package/dist/tests/auth.test.d.ts.map +0 -1
- package/dist/tests/auth.test.js +0 -824
- package/dist/tests/auth.test.js.map +0 -1
- package/dist/tests/auxtest.test.d.ts +0 -2
- package/dist/tests/auxtest.test.d.ts.map +0 -1
- package/dist/tests/auxtest.test.js +0 -562
- package/dist/tests/auxtest.test.js.map +0 -1
- package/dist/tests/base.test.d.ts +0 -2
- package/dist/tests/base.test.d.ts.map +0 -1
- package/dist/tests/base.test.js +0 -30
- package/dist/tests/base.test.js.map +0 -1
- package/dist/tests/calc.test.d.ts +0 -2
- package/dist/tests/calc.test.d.ts.map +0 -1
- package/dist/tests/calc.test.js +0 -1081
- package/dist/tests/calc.test.js.map +0 -1
- package/dist/tests/composite_pk.test.d.ts +0 -2
- package/dist/tests/composite_pk.test.d.ts.map +0 -1
- package/dist/tests/composite_pk.test.js +0 -98
- package/dist/tests/composite_pk.test.js.map +0 -1
- package/dist/tests/config.test.d.ts +0 -2
- package/dist/tests/config.test.d.ts.map +0 -1
- package/dist/tests/config.test.js +0 -86
- package/dist/tests/config.test.js.map +0 -1
- package/dist/tests/db.test.d.ts +0 -2
- package/dist/tests/db.test.d.ts.map +0 -1
- package/dist/tests/db.test.js +0 -178
- package/dist/tests/db.test.js.map +0 -1
- package/dist/tests/discover.test.d.ts +0 -2
- package/dist/tests/discover.test.d.ts.map +0 -1
- package/dist/tests/discover.test.js +0 -245
- package/dist/tests/discover.test.js.map +0 -1
- package/dist/tests/edit.test.d.ts +0 -2
- package/dist/tests/edit.test.d.ts.map +0 -1
- package/dist/tests/edit.test.js +0 -1161
- package/dist/tests/edit.test.js.map +0 -1
- package/dist/tests/email.test.d.ts +0 -2
- package/dist/tests/email.test.d.ts.map +0 -1
- package/dist/tests/email.test.js +0 -255
- package/dist/tests/email.test.js.map +0 -1
- package/dist/tests/exact_views.test.d.ts +0 -2
- package/dist/tests/exact_views.test.d.ts.map +0 -1
- package/dist/tests/exact_views.test.js +0 -1363
- package/dist/tests/exact_views.test.js.map +0 -1
- package/dist/tests/field.test.d.ts +0 -2
- package/dist/tests/field.test.d.ts.map +0 -1
- package/dist/tests/field.test.js +0 -588
- package/dist/tests/field.test.js.map +0 -1
- package/dist/tests/fieldviews.test.d.ts +0 -2
- package/dist/tests/fieldviews.test.d.ts.map +0 -1
- package/dist/tests/fieldviews.test.js +0 -74
- package/dist/tests/fieldviews.test.js.map +0 -1
- package/dist/tests/file.test.d.ts +0 -2
- package/dist/tests/file.test.d.ts.map +0 -1
- package/dist/tests/file.test.js +0 -148
- package/dist/tests/file.test.js.map +0 -1
- package/dist/tests/filter.test.d.ts +0 -2
- package/dist/tests/filter.test.d.ts.map +0 -1
- package/dist/tests/filter.test.js +0 -496
- package/dist/tests/filter.test.js.map +0 -1
- package/dist/tests/form.test.d.ts +0 -2
- package/dist/tests/form.test.d.ts.map +0 -1
- package/dist/tests/form.test.js +0 -264
- package/dist/tests/form.test.js.map +0 -1
- package/dist/tests/list.test.d.ts +0 -2
- package/dist/tests/list.test.d.ts.map +0 -1
- package/dist/tests/list.test.js +0 -1037
- package/dist/tests/list.test.js.map +0 -1
- package/dist/tests/models.test.d.ts +0 -2
- package/dist/tests/models.test.d.ts.map +0 -1
- package/dist/tests/models.test.js +0 -417
- package/dist/tests/models.test.js.map +0 -1
- package/dist/tests/page.test.d.ts +0 -2
- package/dist/tests/page.test.d.ts.map +0 -1
- package/dist/tests/page.test.js +0 -26
- package/dist/tests/page.test.js.map +0 -1
- package/dist/tests/page_group.test.d.ts +0 -2
- package/dist/tests/page_group.test.d.ts.map +0 -1
- package/dist/tests/page_group.test.js +0 -51
- package/dist/tests/page_group.test.js.map +0 -1
- package/dist/tests/plugin.test.d.ts +0 -2
- package/dist/tests/plugin.test.d.ts.map +0 -1
- package/dist/tests/plugin.test.js +0 -60
- package/dist/tests/plugin.test.js.map +0 -1
- package/dist/tests/show.test.d.ts +0 -2
- package/dist/tests/show.test.d.ts.map +0 -1
- package/dist/tests/show.test.js +0 -561
- package/dist/tests/show.test.js.map +0 -1
- package/dist/tests/state.test.d.ts +0 -2
- package/dist/tests/state.test.d.ts.map +0 -1
- package/dist/tests/state.test.js +0 -82
- package/dist/tests/state.test.js.map +0 -1
- package/dist/tests/table.test.d.ts +0 -2
- package/dist/tests/table.test.d.ts.map +0 -1
- package/dist/tests/table.test.js +0 -2717
- package/dist/tests/table.test.js.map +0 -1
- package/dist/tests/table_history.test.d.ts +0 -2
- package/dist/tests/table_history.test.d.ts.map +0 -1
- package/dist/tests/table_history.test.js +0 -413
- package/dist/tests/table_history.test.js.map +0 -1
- package/dist/tests/tag.test.d.ts +0 -2
- package/dist/tests/tag.test.d.ts.map +0 -1
- package/dist/tests/tag.test.js +0 -97
- package/dist/tests/tag.test.js.map +0 -1
- package/dist/tests/user.test.d.ts +0 -2
- package/dist/tests/user.test.d.ts.map +0 -1
- package/dist/tests/user.test.js +0 -441
- package/dist/tests/user.test.js.map +0 -1
- package/dist/tests/view.test.d.ts +0 -2
- package/dist/tests/view.test.d.ts.map +0 -1
- package/dist/tests/view.test.js +0 -699
- package/dist/tests/view.test.js.map +0 -1
- package/dist/tests/workflow.test.d.ts +0 -2
- package/dist/tests/workflow.test.d.ts.map +0 -1
- package/dist/tests/workflow.test.js +0 -303
- package/dist/tests/workflow.test.js.map +0 -1
- package/dist/tests/workflow_run.test.d.ts +0 -2
- package/dist/tests/workflow_run.test.d.ts.map +0 -1
- package/dist/tests/workflow_run.test.js +0 -922
- package/dist/tests/workflow_run.test.js.map +0 -1
package/dist/models/table.js
CHANGED
|
@@ -202,6 +202,14 @@ class Table {
|
|
|
202
202
|
this.provider_name = o.provider_name;
|
|
203
203
|
this.fields = o.fields.map((f) => new field_1.default(f));
|
|
204
204
|
}
|
|
205
|
+
static subClass({ user, read_only, } = {}) {
|
|
206
|
+
var _a;
|
|
207
|
+
return _a = class extends this {
|
|
208
|
+
},
|
|
209
|
+
_a.fixed_user = user || undefined,
|
|
210
|
+
_a.read_only = !!read_only,
|
|
211
|
+
_a;
|
|
212
|
+
}
|
|
205
213
|
get to_json() {
|
|
206
214
|
return {
|
|
207
215
|
name: this.name,
|
|
@@ -263,9 +271,9 @@ class Table {
|
|
|
263
271
|
return where;
|
|
264
272
|
// todo add string & number as possible types for where
|
|
265
273
|
if (typeof where === "string")
|
|
266
|
-
return
|
|
274
|
+
return this.findOne({ name: where });
|
|
267
275
|
if (typeof where === "number")
|
|
268
|
-
return
|
|
276
|
+
return this.findOne({ id: where });
|
|
269
277
|
if (typeof where === "undefined")
|
|
270
278
|
return null;
|
|
271
279
|
if (where === null)
|
|
@@ -283,10 +291,10 @@ class Table {
|
|
|
283
291
|
? (v) => v.name === where.name
|
|
284
292
|
: satisfies(where));
|
|
285
293
|
if (tbl?.provider_name) {
|
|
286
|
-
return new
|
|
294
|
+
return new this(structuredClone(tbl)).to_provided_table();
|
|
287
295
|
}
|
|
288
296
|
else
|
|
289
|
-
return tbl ? new
|
|
297
|
+
return tbl ? new this(structuredClone(tbl)) : null;
|
|
290
298
|
}
|
|
291
299
|
/**
|
|
292
300
|
* Find Tables
|
|
@@ -298,7 +306,7 @@ class Table {
|
|
|
298
306
|
if (selectopts.cached) {
|
|
299
307
|
const { getState } = require("../db/state");
|
|
300
308
|
return getState()
|
|
301
|
-
.tables.map((t) => new
|
|
309
|
+
.tables.map((t) => new this(structuredClone(t)))
|
|
302
310
|
.filter(satisfies(where || {}));
|
|
303
311
|
}
|
|
304
312
|
if (where?.name) {
|
|
@@ -327,7 +335,7 @@ class Table {
|
|
|
327
335
|
t.constraints = constraints
|
|
328
336
|
.filter((f) => f.table_id === t.id)
|
|
329
337
|
.map((f) => new _TableConstraint(f));
|
|
330
|
-
const tbl = new
|
|
338
|
+
const tbl = new this(t);
|
|
331
339
|
return tbl.to_provided_table();
|
|
332
340
|
});
|
|
333
341
|
}
|
|
@@ -355,7 +363,7 @@ class Table {
|
|
|
355
363
|
t.fields = flds
|
|
356
364
|
.filter((f) => f.table_id === t.id)
|
|
357
365
|
.map((f) => new field_1.default(f));
|
|
358
|
-
return new
|
|
366
|
+
return new this(t);
|
|
359
367
|
});
|
|
360
368
|
}
|
|
361
369
|
return [...dbs, ...externals];
|
|
@@ -401,18 +409,19 @@ class Table {
|
|
|
401
409
|
* @returns {boolean}
|
|
402
410
|
*/
|
|
403
411
|
is_owner(user, row) {
|
|
404
|
-
|
|
412
|
+
let use_user = this.constructor.fixed_user || user;
|
|
413
|
+
if (!use_user)
|
|
405
414
|
return false;
|
|
406
415
|
if (this.ownership_formula && this.fields) {
|
|
407
416
|
const f = get_expression_function(this.ownership_formula, this.fields);
|
|
408
|
-
return !!f(row,
|
|
417
|
+
return !!f(row, use_user);
|
|
409
418
|
}
|
|
410
419
|
const field_name = this.owner_fieldname();
|
|
411
420
|
// users are owners of their own row in users table
|
|
412
421
|
if (this.name === "users" && !field_name)
|
|
413
|
-
return !!
|
|
422
|
+
return !!use_user.id && `${row?.id}` === `${use_user.id}`;
|
|
414
423
|
return (typeof field_name === "string" &&
|
|
415
|
-
(row[field_name] ===
|
|
424
|
+
(row[field_name] === use_user.id || row[field_name]?.id === use_user.id));
|
|
416
425
|
}
|
|
417
426
|
/**
|
|
418
427
|
* get Ownership options
|
|
@@ -553,6 +562,8 @@ class Table {
|
|
|
553
562
|
if (pk_type !== "Integer") {
|
|
554
563
|
const { getState } = require("../db/state");
|
|
555
564
|
const type = getState().types[pk_type];
|
|
565
|
+
if (!type)
|
|
566
|
+
throw new Error(`Cannot find primary key type ${pk_type} in fields ${JSON.stringify(fields)}`);
|
|
556
567
|
pk_sql_type = type.sql_name;
|
|
557
568
|
if (type.primaryKey?.default_sql)
|
|
558
569
|
pk_sql_type = `${type.sql_name} default ${type.primaryKey?.default_sql}`;
|
|
@@ -568,7 +579,11 @@ class Table {
|
|
|
568
579
|
*/
|
|
569
580
|
static async create(name, options = {}, //TODO not selectoptions
|
|
570
581
|
id) {
|
|
571
|
-
|
|
582
|
+
if (this.constructor.read_only)
|
|
583
|
+
throw new Error("Read-only access");
|
|
584
|
+
const { pk_type, pk_sql_type } = options.provider_name
|
|
585
|
+
? {}
|
|
586
|
+
: Table.pkSqlType(options.fields);
|
|
572
587
|
const schema = db_1.default.getTenantSchemaPrefix();
|
|
573
588
|
// create table in database
|
|
574
589
|
if (!options.provider_name)
|
|
@@ -645,6 +660,8 @@ class Table {
|
|
|
645
660
|
* @param table
|
|
646
661
|
*/
|
|
647
662
|
static async createInDb(table) {
|
|
663
|
+
if (this.constructor.read_only)
|
|
664
|
+
throw new Error("Read-only access");
|
|
648
665
|
const is_sqlite = db_1.default.isSQLite;
|
|
649
666
|
const schema = db_1.default.getTenantSchemaPrefix();
|
|
650
667
|
const { pk_sql_type } = Table.pkSqlType(table.fields);
|
|
@@ -676,6 +693,8 @@ class Table {
|
|
|
676
693
|
*/
|
|
677
694
|
// tbd check all other tables related to table description
|
|
678
695
|
async delete(only_forget = false) {
|
|
696
|
+
if (this.constructor.read_only)
|
|
697
|
+
throw new Error("Read-only access");
|
|
679
698
|
const schema = db_1.default.getTenantSchemaPrefix();
|
|
680
699
|
const is_sqlite = db_1.default.isSQLite;
|
|
681
700
|
await this.update({ ownership_field_id: null });
|
|
@@ -708,6 +727,8 @@ class Table {
|
|
|
708
727
|
* Reset Sequence
|
|
709
728
|
*/
|
|
710
729
|
async resetSequence() {
|
|
730
|
+
if (this.constructor.read_only)
|
|
731
|
+
throw new Error("Read-only access");
|
|
711
732
|
const fields = this.fields;
|
|
712
733
|
const pk = fields.find((f) => f.primary_key);
|
|
713
734
|
if (!pk) {
|
|
@@ -726,13 +747,14 @@ class Table {
|
|
|
726
747
|
* @param forRead
|
|
727
748
|
*/
|
|
728
749
|
updateWhereWithOwnership(where, user, forRead) {
|
|
729
|
-
|
|
750
|
+
let use_user = this.constructor.fixed_user || user;
|
|
751
|
+
const role = use_user?.role_id;
|
|
730
752
|
const min_role = forRead ? this.min_role_read : this.min_role_write;
|
|
731
753
|
if (role &&
|
|
732
754
|
role > min_role &&
|
|
733
755
|
((!this.ownership_field_id && !this.ownership_formula) || role === 100))
|
|
734
756
|
return { notAuthorized: true };
|
|
735
|
-
if (
|
|
757
|
+
if (use_user &&
|
|
736
758
|
role &&
|
|
737
759
|
role < 100 &&
|
|
738
760
|
role > min_role &&
|
|
@@ -741,16 +763,16 @@ class Table {
|
|
|
741
763
|
if (!owner_field)
|
|
742
764
|
throw new Error(`Owner field in table ${this.name} not found`);
|
|
743
765
|
mergeIntoWhere(where, {
|
|
744
|
-
[owner_field.name]:
|
|
766
|
+
[owner_field.name]: use_user.id,
|
|
745
767
|
});
|
|
746
768
|
}
|
|
747
|
-
else if (
|
|
769
|
+
else if (use_user &&
|
|
748
770
|
role &&
|
|
749
771
|
role < 100 &&
|
|
750
772
|
role > min_role &&
|
|
751
773
|
this.ownership_formula) {
|
|
752
774
|
try {
|
|
753
|
-
mergeIntoWhere(where, this.ownership_formula_where(
|
|
775
|
+
mergeIntoWhere(where, this.ownership_formula_where(use_user));
|
|
754
776
|
}
|
|
755
777
|
catch (e) {
|
|
756
778
|
//ignore, ownership formula is too difficult to merge with where
|
|
@@ -759,6 +781,8 @@ class Table {
|
|
|
759
781
|
}
|
|
760
782
|
}
|
|
761
783
|
async addDeleteSyncInfo(ids, timestamp) {
|
|
784
|
+
if (this.constructor.read_only)
|
|
785
|
+
throw new Error("Read-only access");
|
|
762
786
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
763
787
|
if (ids.length > 0) {
|
|
764
788
|
const schema = db_1.default.getTenantSchemaPrefix();
|
|
@@ -797,9 +821,12 @@ class Table {
|
|
|
797
821
|
* @returns
|
|
798
822
|
*/
|
|
799
823
|
async deleteRows(where, user, noTrigger, resultCollector) {
|
|
824
|
+
let use_user = this.constructor.fixed_user || user;
|
|
825
|
+
if (this.constructor.read_only)
|
|
826
|
+
throw new Error("Read-only access");
|
|
800
827
|
//Fast truncate if user is admin and where is blank
|
|
801
828
|
const cfields = await field_1.default.find({ reftable_name: this.name }, { cached: true });
|
|
802
|
-
if ((!
|
|
829
|
+
if ((!use_user || use_user?.role_id === 1) &&
|
|
803
830
|
Object.keys(where).length == 0 &&
|
|
804
831
|
db_1.default.truncate &&
|
|
805
832
|
noTrigger &&
|
|
@@ -815,7 +842,7 @@ class Table {
|
|
|
815
842
|
// get triggers on delete
|
|
816
843
|
const triggers = await trigger_1.default.getTableTriggers("Delete", this);
|
|
817
844
|
const fields = this.fields;
|
|
818
|
-
if (this.updateWhereWithOwnership(where,
|
|
845
|
+
if (this.updateWhereWithOwnership(where, use_user)?.notAuthorized) {
|
|
819
846
|
const state = require("../db/state").getState();
|
|
820
847
|
state.log(4, `Not authorized to deleteRows in table ${this.name}.`);
|
|
821
848
|
return;
|
|
@@ -829,11 +856,13 @@ class Table {
|
|
|
829
856
|
}, { cached: true });
|
|
830
857
|
let rows;
|
|
831
858
|
if (calc_agg_fields.length ||
|
|
832
|
-
(
|
|
859
|
+
(use_user &&
|
|
860
|
+
use_user.role_id > this.min_role_write &&
|
|
861
|
+
this.ownership_formula)) {
|
|
833
862
|
rows = await this.getJoinedRows({
|
|
834
863
|
where,
|
|
835
|
-
forUser:
|
|
836
|
-
forPublic:
|
|
864
|
+
forUser: use_user,
|
|
865
|
+
forPublic: use_user?.role_id === 100,
|
|
837
866
|
});
|
|
838
867
|
}
|
|
839
868
|
const deleteFileFields = fields.filter((f) => f.type === "File" && f.attributes?.also_delete_file);
|
|
@@ -843,15 +872,15 @@ class Table {
|
|
|
843
872
|
if (!rows)
|
|
844
873
|
rows = await this.getJoinedRows({
|
|
845
874
|
where,
|
|
846
|
-
forUser:
|
|
847
|
-
forPublic:
|
|
875
|
+
forUser: use_user,
|
|
876
|
+
forPublic: use_user?.role_id === 100,
|
|
848
877
|
});
|
|
849
878
|
for (const trigger of triggers) {
|
|
850
879
|
for (const row of rows) {
|
|
851
880
|
// run triggers on delete
|
|
852
|
-
if (trigger.haltOnOnlyIf?.(row,
|
|
881
|
+
if (trigger.haltOnOnlyIf?.(row, use_user))
|
|
853
882
|
continue;
|
|
854
|
-
const runres = await trigger.run(row, { user });
|
|
883
|
+
const runres = await trigger.run(row, { user: use_user });
|
|
855
884
|
if (runres && resultCollector)
|
|
856
885
|
mergeActionResults(resultCollector, runres);
|
|
857
886
|
}
|
|
@@ -972,7 +1001,8 @@ class Table {
|
|
|
972
1001
|
async getRow(where = {}, selopts = {}) {
|
|
973
1002
|
const fields = this.fields;
|
|
974
1003
|
const { forUser, forPublic, ...selopts1 } = selopts;
|
|
975
|
-
const
|
|
1004
|
+
const use_forUser = this.constructor.fixed_user || forUser;
|
|
1005
|
+
const role = use_forUser ? use_forUser.role_id : forPublic ? 100 : null;
|
|
976
1006
|
this.normalise_fkey_values(where);
|
|
977
1007
|
const row = await db_1.default.selectMaybeOne(this.name, where, this.processSelectOptions(selopts1));
|
|
978
1008
|
if (!row || !this.fields)
|
|
@@ -985,11 +1015,11 @@ class Table {
|
|
|
985
1015
|
const owner_field = fields.find((f) => f.id === this.ownership_field_id);
|
|
986
1016
|
if (!owner_field)
|
|
987
1017
|
throw new Error(`Owner field in table ${this.name} not found`);
|
|
988
|
-
if (row[owner_field.name] !==
|
|
1018
|
+
if (row[owner_field.name] !== use_forUser.id)
|
|
989
1019
|
return null;
|
|
990
1020
|
}
|
|
991
1021
|
else if (this.ownership_formula || this.name === "users") {
|
|
992
|
-
if (!this.is_owner(
|
|
1022
|
+
if (!this.is_owner(use_forUser, row))
|
|
993
1023
|
return null;
|
|
994
1024
|
}
|
|
995
1025
|
else
|
|
@@ -1027,10 +1057,10 @@ class Table {
|
|
|
1027
1057
|
if (!this.fields)
|
|
1028
1058
|
return [];
|
|
1029
1059
|
const { forUser, forPublic, ...selopts1 } = selopts;
|
|
1030
|
-
const
|
|
1060
|
+
const use_forUser = this.constructor.fixed_user || forUser;
|
|
1061
|
+
const role = use_forUser ? use_forUser.role_id : forPublic ? 100 : null;
|
|
1031
1062
|
if (role &&
|
|
1032
|
-
this.updateWhereWithOwnership(where,
|
|
1033
|
-
?.notAuthorized) {
|
|
1063
|
+
this.updateWhereWithOwnership(where, use_forUser || { role_id: 100 }, true)?.notAuthorized) {
|
|
1034
1064
|
return [];
|
|
1035
1065
|
}
|
|
1036
1066
|
this.normalise_fkey_values(where);
|
|
@@ -1043,7 +1073,8 @@ class Table {
|
|
|
1043
1073
|
//already dealt with by changing where
|
|
1044
1074
|
}
|
|
1045
1075
|
else if (this.ownership_formula || this.name === "users") {
|
|
1046
|
-
|
|
1076
|
+
if (!selopts?.disable_ownership_postqfilter)
|
|
1077
|
+
rows = rows.filter((row) => this.is_owner(use_forUser, row));
|
|
1047
1078
|
}
|
|
1048
1079
|
else
|
|
1049
1080
|
return []; //no ownership
|
|
@@ -1155,6 +1186,9 @@ class Table {
|
|
|
1155
1186
|
* @returns
|
|
1156
1187
|
*/
|
|
1157
1188
|
async updateRow(v_in, id_in, user, noTrigger, resultCollector, restore_of_version, syncTimestamp, additionalTriggerValues, autoRecalcIterations, extraArgs) {
|
|
1189
|
+
let use_user = this.constructor.fixed_user || user;
|
|
1190
|
+
if (this.constructor.read_only)
|
|
1191
|
+
throw new Error("Read-only access");
|
|
1158
1192
|
// migrating to options arg
|
|
1159
1193
|
if (typeof noTrigger === "object") {
|
|
1160
1194
|
const extraOptions = noTrigger;
|
|
@@ -1188,7 +1222,7 @@ class Table {
|
|
|
1188
1222
|
]);
|
|
1189
1223
|
const fields = this.fields;
|
|
1190
1224
|
const pk_name = this.pk_name;
|
|
1191
|
-
const role =
|
|
1225
|
+
const role = use_user?.role_id;
|
|
1192
1226
|
const state = require("../db/state").getState();
|
|
1193
1227
|
let stringified = false;
|
|
1194
1228
|
const sqliteJsonCols = !isNode()
|
|
@@ -1206,7 +1240,7 @@ class Table {
|
|
|
1206
1240
|
}
|
|
1207
1241
|
if (this.ownership_formula)
|
|
1208
1242
|
add_free_variables_to_joinfields(freeVariables(this.ownership_formula), joinFields, fields);
|
|
1209
|
-
if (
|
|
1243
|
+
if (use_user &&
|
|
1210
1244
|
role &&
|
|
1211
1245
|
(role > this.min_role_write || role > this.min_role_read)) {
|
|
1212
1246
|
if (role === 100)
|
|
@@ -1215,19 +1249,19 @@ class Table {
|
|
|
1215
1249
|
const owner_field = fields.find((f) => f.id === this.ownership_field_id);
|
|
1216
1250
|
if (!owner_field)
|
|
1217
1251
|
throw new Error(`Owner field in table ${this.name} not found`);
|
|
1218
|
-
if (v[owner_field.name] && v[owner_field.name] !=
|
|
1219
|
-
state.log(4, `Not authorized to updateRow in table ${this.name}. ${
|
|
1252
|
+
if (v[owner_field.name] && v[owner_field.name] != use_user.id) {
|
|
1253
|
+
state.log(4, `Not authorized to updateRow in table ${this.name}. ${use_user.id} does not match owner field in updates`);
|
|
1220
1254
|
return "Not authorized";
|
|
1221
1255
|
}
|
|
1222
1256
|
//need to check existing
|
|
1223
1257
|
if (!existing)
|
|
1224
1258
|
existing = await this.getJoinedRow({
|
|
1225
1259
|
where: { [pk_name]: id },
|
|
1226
|
-
forUser:
|
|
1260
|
+
forUser: use_user,
|
|
1227
1261
|
joinFields,
|
|
1228
1262
|
});
|
|
1229
|
-
if (!existing || existing?.[owner_field.name] !==
|
|
1230
|
-
state.log(4, `Not authorized to updateRow in table ${this.name}. ${
|
|
1263
|
+
if (!existing || existing?.[owner_field.name] !== use_user.id) {
|
|
1264
|
+
state.log(4, `Not authorized to updateRow in table ${this.name}. ${use_user.id} does not match owner field in exisiting`);
|
|
1231
1265
|
return "Not authorized";
|
|
1232
1266
|
}
|
|
1233
1267
|
}
|
|
@@ -1235,11 +1269,11 @@ class Table {
|
|
|
1235
1269
|
if (!existing)
|
|
1236
1270
|
existing = await this.getJoinedRow({
|
|
1237
1271
|
where: { [pk_name]: id },
|
|
1238
|
-
forUser:
|
|
1272
|
+
forUser: use_user,
|
|
1239
1273
|
joinFields,
|
|
1240
1274
|
});
|
|
1241
|
-
if (!existing || !this.is_owner(
|
|
1242
|
-
state.log(4, `Not authorized to updateRow in table ${this.name}. User does not match formula: ${JSON.stringify(
|
|
1275
|
+
if (!existing || !this.is_owner(use_user, existing)) {
|
|
1276
|
+
state.log(4, `Not authorized to updateRow in table ${this.name}. User does not match formula: ${JSON.stringify(use_user)}`);
|
|
1243
1277
|
return "Not authorized";
|
|
1244
1278
|
}
|
|
1245
1279
|
}
|
|
@@ -1252,7 +1286,7 @@ class Table {
|
|
|
1252
1286
|
if (!existing)
|
|
1253
1287
|
existing = await this.getJoinedRow({
|
|
1254
1288
|
where: { [pk_name]: id },
|
|
1255
|
-
forUser:
|
|
1289
|
+
forUser: use_user,
|
|
1256
1290
|
joinFields,
|
|
1257
1291
|
});
|
|
1258
1292
|
const newRow = { ...existing, ...v };
|
|
@@ -1260,8 +1294,8 @@ class Table {
|
|
|
1260
1294
|
if (constraint_check)
|
|
1261
1295
|
return constraint_check;
|
|
1262
1296
|
}
|
|
1263
|
-
if (
|
|
1264
|
-
let field_write_check = this.check_field_write_role(v,
|
|
1297
|
+
if (use_user) {
|
|
1298
|
+
let field_write_check = this.check_field_write_role(v, use_user);
|
|
1265
1299
|
if (field_write_check)
|
|
1266
1300
|
return field_write_check;
|
|
1267
1301
|
}
|
|
@@ -1270,11 +1304,11 @@ class Table {
|
|
|
1270
1304
|
if (!existing)
|
|
1271
1305
|
existing = await this.getJoinedRow({
|
|
1272
1306
|
where: { [pk_name]: id },
|
|
1273
|
-
forUser:
|
|
1307
|
+
forUser: use_user,
|
|
1274
1308
|
joinFields,
|
|
1275
1309
|
});
|
|
1276
1310
|
const valResCollector = resultCollector || {};
|
|
1277
|
-
await trigger_1.default.runTableTriggers("Validate", this, { ...(additionalTriggerValues || {}), ...existing, ...v }, valResCollector,
|
|
1311
|
+
await trigger_1.default.runTableTriggers("Validate", this, { ...(additionalTriggerValues || {}), ...existing, ...v }, valResCollector, use_user, { old_row: existing, updated_fields: v_in, ...(extraArgs || {}) });
|
|
1278
1312
|
if ("error" in valResCollector)
|
|
1279
1313
|
return valResCollector.error;
|
|
1280
1314
|
if ("set_fields" in valResCollector)
|
|
@@ -1286,7 +1320,7 @@ class Table {
|
|
|
1286
1320
|
let need_to_update = Object.keys(v_in).some((k) => freeVarFKFields.has(k));
|
|
1287
1321
|
existing = await this.getJoinedRow({
|
|
1288
1322
|
where: { [pk_name]: id },
|
|
1289
|
-
forUser:
|
|
1323
|
+
forUser: use_user,
|
|
1290
1324
|
joinFields,
|
|
1291
1325
|
});
|
|
1292
1326
|
let updated;
|
|
@@ -1300,7 +1334,7 @@ class Table {
|
|
|
1300
1334
|
});
|
|
1301
1335
|
updated = await this.getJoinedRow({
|
|
1302
1336
|
where: { [pk_name]: id },
|
|
1303
|
-
forUser:
|
|
1337
|
+
forUser: use_user,
|
|
1304
1338
|
joinFields,
|
|
1305
1339
|
});
|
|
1306
1340
|
}
|
|
@@ -1334,7 +1368,7 @@ class Table {
|
|
|
1334
1368
|
pk_name,
|
|
1335
1369
|
},
|
|
1336
1370
|
_time: new Date(),
|
|
1337
|
-
_userid:
|
|
1371
|
+
_userid: use_user?.id,
|
|
1338
1372
|
_restore_of_version: restore_of_version || null,
|
|
1339
1373
|
});
|
|
1340
1374
|
}
|
|
@@ -1343,7 +1377,7 @@ class Table {
|
|
|
1343
1377
|
if (triggers.length > 0)
|
|
1344
1378
|
existing = await this.getJoinedRow({
|
|
1345
1379
|
where: { [pk_name]: id },
|
|
1346
|
-
forUser:
|
|
1380
|
+
forUser: use_user,
|
|
1347
1381
|
joinFields,
|
|
1348
1382
|
});
|
|
1349
1383
|
}
|
|
@@ -1364,7 +1398,7 @@ class Table {
|
|
|
1364
1398
|
if (!existing && really_changed_field_names.size && keyChanged)
|
|
1365
1399
|
existing = await this.getJoinedRow({
|
|
1366
1400
|
where: { [pk_name]: id },
|
|
1367
|
-
forUser:
|
|
1401
|
+
forUser: use_user,
|
|
1368
1402
|
joinFields,
|
|
1369
1403
|
});
|
|
1370
1404
|
await db_1.default.update(this.name, v, id, {
|
|
@@ -1387,10 +1421,12 @@ class Table {
|
|
|
1387
1421
|
await this.auto_update_calc_aggregations(existing, !existing, (autoRecalcIterations || 0) + 1, really_changed_field_names, keyChanged);
|
|
1388
1422
|
}
|
|
1389
1423
|
if (!noTrigger) {
|
|
1390
|
-
await trigger_1.default.runTableTriggers("Update", this, { ...(additionalTriggerValues || {}), ...newRow }, resultCollector, role === 100 ? undefined :
|
|
1424
|
+
await trigger_1.default.runTableTriggers("Update", this, { ...(additionalTriggerValues || {}), ...newRow }, resultCollector, role === 100 ? undefined : use_user, { old_row: existing, updated_fields: v_in, ...(extraArgs || {}) });
|
|
1391
1425
|
}
|
|
1392
1426
|
}
|
|
1393
1427
|
static async analyze_all_indexed_tables() {
|
|
1428
|
+
if (this.constructor.read_only)
|
|
1429
|
+
throw new Error("Read-only access");
|
|
1394
1430
|
const tables = await Table.find({}, { cached: true });
|
|
1395
1431
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
1396
1432
|
for (const table of tables)
|
|
@@ -1398,6 +1434,8 @@ class Table {
|
|
|
1398
1434
|
await db_1.default.query(`analyze ${schemaPrefix}"${(0, internal_1.sqlsanitize)(table.name)}";`);
|
|
1399
1435
|
}
|
|
1400
1436
|
async insert_history_row(v0, retry = 0) {
|
|
1437
|
+
if (this.constructor.read_only)
|
|
1438
|
+
throw new Error("Read-only access");
|
|
1401
1439
|
// sometimes there is a race condition in history inserts
|
|
1402
1440
|
// https://dba.stackexchange.com/questions/212580/concurrent-transactions-result-in-race-condition-with-unique-constraint-on-inser
|
|
1403
1441
|
// solution: retry 3 times, if fails run with on conflict do nothing
|
|
@@ -1452,6 +1490,8 @@ class Table {
|
|
|
1452
1490
|
});
|
|
1453
1491
|
}
|
|
1454
1492
|
async insertSyncInfo(id, updates, syncTimestamp) {
|
|
1493
|
+
if (this.constructor.read_only)
|
|
1494
|
+
throw new Error("Read-only access");
|
|
1455
1495
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
1456
1496
|
const schema = db_1.default.getTenantSchemaPrefix();
|
|
1457
1497
|
if (isNode()) {
|
|
@@ -1479,6 +1519,8 @@ class Table {
|
|
|
1479
1519
|
});
|
|
1480
1520
|
}
|
|
1481
1521
|
async updateSyncInfo(id, v, oldLastModified, syncTimestamp) {
|
|
1522
|
+
if (this.constructor.read_only)
|
|
1523
|
+
throw new Error("Read-only access");
|
|
1482
1524
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
1483
1525
|
const schema = db_1.default.getTenantSchemaPrefix();
|
|
1484
1526
|
if (!db_1.default.isSQLite) {
|
|
@@ -1521,8 +1563,11 @@ class Table {
|
|
|
1521
1563
|
* @returns {Promise<{error}|{success: boolean}>}
|
|
1522
1564
|
*/
|
|
1523
1565
|
async tryUpdateRow(v, id, user, resultCollector, extraArgs) {
|
|
1566
|
+
if (this.constructor.read_only)
|
|
1567
|
+
throw new Error("Read-only access");
|
|
1568
|
+
let use_user = this.constructor.fixed_user || user;
|
|
1524
1569
|
try {
|
|
1525
|
-
const maybe_err = await this.updateRow(v, id,
|
|
1570
|
+
const maybe_err = await this.updateRow(v, id, use_user, {
|
|
1526
1571
|
noTrigger: false,
|
|
1527
1572
|
resultCollector,
|
|
1528
1573
|
extraArgs,
|
|
@@ -1543,9 +1588,12 @@ class Table {
|
|
|
1543
1588
|
* @returns {Promise<void>}
|
|
1544
1589
|
*/
|
|
1545
1590
|
async toggleBool(id, field_name, user) {
|
|
1591
|
+
let use_user = this.constructor.fixed_user || user;
|
|
1592
|
+
if (this.constructor.read_only)
|
|
1593
|
+
throw new Error("Read-only access");
|
|
1546
1594
|
const row = await this.getRow({ [this.pk_name]: id });
|
|
1547
1595
|
if (row)
|
|
1548
|
-
await this.updateRow({ [field_name]: !row[field_name] }, id,
|
|
1596
|
+
await this.updateRow({ [field_name]: !row[field_name] }, id, use_user);
|
|
1549
1597
|
}
|
|
1550
1598
|
delete_url(row, moreQuery) {
|
|
1551
1599
|
const comppk = this.composite_pk_names;
|
|
@@ -1608,10 +1656,11 @@ class Table {
|
|
|
1608
1656
|
* @param user
|
|
1609
1657
|
*/
|
|
1610
1658
|
check_field_write_role(row, user) {
|
|
1659
|
+
let use_user = this.constructor.fixed_user || user;
|
|
1611
1660
|
for (const field of this.fields) {
|
|
1612
1661
|
if (typeof row[field.name] !== "undefined" &&
|
|
1613
1662
|
field.attributes?.min_role_write &&
|
|
1614
|
-
|
|
1663
|
+
use_user.role_id > +field.attributes?.min_role_write)
|
|
1615
1664
|
return "Not authorized";
|
|
1616
1665
|
}
|
|
1617
1666
|
return undefined;
|
|
@@ -1627,6 +1676,15 @@ class Table {
|
|
|
1627
1676
|
v_in[field.name] = v_in[field.name][pk];
|
|
1628
1677
|
}
|
|
1629
1678
|
}
|
|
1679
|
+
async run_trigger(trigger_name, row, user, extraArgs) {
|
|
1680
|
+
let use_user = this.constructor.fixed_user || user;
|
|
1681
|
+
const trigger = trigger_1.default.findOne({ name: trigger_name });
|
|
1682
|
+
return await trigger.runWithoutRow({
|
|
1683
|
+
row,
|
|
1684
|
+
user: use_user,
|
|
1685
|
+
...(extraArgs || {}),
|
|
1686
|
+
});
|
|
1687
|
+
}
|
|
1630
1688
|
/**
|
|
1631
1689
|
* Insert row into the table. By passing in the user as
|
|
1632
1690
|
* the second argument, it will check write rights. If a user object is not
|
|
@@ -1651,6 +1709,9 @@ class Table {
|
|
|
1651
1709
|
* @returns
|
|
1652
1710
|
*/
|
|
1653
1711
|
async insertRow(v_in0, user, resultCollector, noTrigger, syncTimestamp) {
|
|
1712
|
+
let use_user = this.constructor.fixed_user || user;
|
|
1713
|
+
if (this.constructor.read_only)
|
|
1714
|
+
throw new Error("Read-only access");
|
|
1654
1715
|
const v_in = { ...v_in0 };
|
|
1655
1716
|
const fields = this.fields;
|
|
1656
1717
|
const pk_name = this.pk_name;
|
|
@@ -1667,13 +1728,13 @@ class Table {
|
|
|
1667
1728
|
}
|
|
1668
1729
|
: {};
|
|
1669
1730
|
this.normalise_fkey_values(v_in);
|
|
1670
|
-
if (
|
|
1731
|
+
if (use_user && use_user.role_id > this.min_role_write) {
|
|
1671
1732
|
if (this.ownership_field_id) {
|
|
1672
1733
|
const owner_field = fields.find((f) => f.id === this.ownership_field_id);
|
|
1673
1734
|
if (!owner_field)
|
|
1674
1735
|
throw new Error(`Owner field in table ${this.name} not found`);
|
|
1675
|
-
if (v_in[owner_field.name] !=
|
|
1676
|
-
state.log(4, `Not authorized to insertRow in table ${this.name}. ${
|
|
1736
|
+
if (v_in[owner_field.name] != use_user.id) {
|
|
1737
|
+
state.log(4, `Not authorized to insertRow in table ${this.name}. ${use_user.id} does not match owner field`);
|
|
1677
1738
|
return;
|
|
1678
1739
|
}
|
|
1679
1740
|
}
|
|
@@ -1685,14 +1746,14 @@ class Table {
|
|
|
1685
1746
|
let constraint_check = this.check_table_constraints(v_in);
|
|
1686
1747
|
if (constraint_check)
|
|
1687
1748
|
throw new Error(constraint_check);
|
|
1688
|
-
if (
|
|
1689
|
-
let field_write_check = this.check_field_write_role(v_in,
|
|
1749
|
+
if (use_user) {
|
|
1750
|
+
let field_write_check = this.check_field_write_role(v_in, use_user);
|
|
1690
1751
|
if (field_write_check)
|
|
1691
1752
|
return field_write_check;
|
|
1692
1753
|
}
|
|
1693
1754
|
//check validate here based on v_in
|
|
1694
1755
|
const valResCollector = resultCollector || {};
|
|
1695
|
-
await trigger_1.default.runTableTriggers("Validate", this, { ...v_in }, valResCollector,
|
|
1756
|
+
await trigger_1.default.runTableTriggers("Validate", this, { ...v_in }, valResCollector, use_user);
|
|
1696
1757
|
if ("error" in valResCollector)
|
|
1697
1758
|
return valResCollector; //???
|
|
1698
1759
|
if ("set_fields" in valResCollector)
|
|
@@ -1705,7 +1766,7 @@ class Table {
|
|
|
1705
1766
|
let existing = await this.getJoinedRows({
|
|
1706
1767
|
where: { [pk_name]: id },
|
|
1707
1768
|
joinFields,
|
|
1708
|
-
forUser:
|
|
1769
|
+
forUser: use_user,
|
|
1709
1770
|
});
|
|
1710
1771
|
if (!existing?.[0]) {
|
|
1711
1772
|
//failed ownership test
|
|
@@ -1731,15 +1792,17 @@ class Table {
|
|
|
1731
1792
|
...sqliteJsonCols,
|
|
1732
1793
|
});
|
|
1733
1794
|
}
|
|
1734
|
-
if (
|
|
1795
|
+
if (use_user &&
|
|
1796
|
+
use_user.role_id > this.min_role_write &&
|
|
1797
|
+
this.ownership_formula) {
|
|
1735
1798
|
let existing = await this.getJoinedRow({
|
|
1736
1799
|
where: { [pk_name]: id },
|
|
1737
1800
|
joinFields,
|
|
1738
|
-
forUser:
|
|
1801
|
+
forUser: use_user,
|
|
1739
1802
|
});
|
|
1740
|
-
if (!existing || !this.is_owner(
|
|
1803
|
+
if (!existing || !this.is_owner(use_user, existing)) {
|
|
1741
1804
|
await this.deleteRows({ [pk_name]: id });
|
|
1742
|
-
state.log(4, `Not authorized to insertRow in table ${this.name}. User does not match formula: ${JSON.stringify(
|
|
1805
|
+
state.log(4, `Not authorized to insertRow in table ${this.name}. User does not match formula: ${JSON.stringify(use_user)}`);
|
|
1743
1806
|
return;
|
|
1744
1807
|
}
|
|
1745
1808
|
}
|
|
@@ -1751,7 +1814,7 @@ class Table {
|
|
|
1751
1814
|
next_version_by_id: id,
|
|
1752
1815
|
pk_name,
|
|
1753
1816
|
},
|
|
1754
|
-
_userid:
|
|
1817
|
+
_userid: use_user?.id,
|
|
1755
1818
|
_time: new Date(),
|
|
1756
1819
|
});
|
|
1757
1820
|
if (this.has_sync_info) {
|
|
@@ -1779,11 +1842,13 @@ class Table {
|
|
|
1779
1842
|
await this.auto_update_calc_aggregations(newRow);
|
|
1780
1843
|
if (!noTrigger) {
|
|
1781
1844
|
apply_calculated_fields([newRow], this.fields);
|
|
1782
|
-
await trigger_1.default.runTableTriggers("Insert", this, newRow, resultCollector,
|
|
1845
|
+
await trigger_1.default.runTableTriggers("Insert", this, newRow, resultCollector, use_user);
|
|
1783
1846
|
}
|
|
1784
1847
|
return id;
|
|
1785
1848
|
}
|
|
1786
1849
|
async auto_update_calc_aggregations(v0, refetch, iterations = 1, changedFields, keyChanged = false) {
|
|
1850
|
+
if (this.constructor.read_only)
|
|
1851
|
+
throw new Error("Read-only access");
|
|
1787
1852
|
const state = require("../db/state").getState();
|
|
1788
1853
|
const pk_name = this.pk_name;
|
|
1789
1854
|
state.log(6, `auto_update_calc_aggregations table=${this.name} id=${v0[pk_name]} iters=${iterations}${changedFields ? ` changedFields=${[...(changedFields || [])]}` : ""}`);
|
|
@@ -1915,8 +1980,11 @@ class Table {
|
|
|
1915
1980
|
* @returns {Promise<{error}|{success: *}>}
|
|
1916
1981
|
*/
|
|
1917
1982
|
async tryInsertRow(v, user, resultCollector) {
|
|
1983
|
+
let use_user = this.constructor.fixed_user || user;
|
|
1984
|
+
if (this.constructor.read_only)
|
|
1985
|
+
throw new Error("Read-only access");
|
|
1918
1986
|
try {
|
|
1919
|
-
const id = await this.insertRow(v,
|
|
1987
|
+
const id = await this.insertRow(v, use_user, resultCollector);
|
|
1920
1988
|
if (!id)
|
|
1921
1989
|
return { error: "Not authorized" };
|
|
1922
1990
|
if (id?.includes?.("Not authorized"))
|
|
@@ -2064,6 +2132,8 @@ class Table {
|
|
|
2064
2132
|
);`);
|
|
2065
2133
|
}
|
|
2066
2134
|
async create_sync_info_table() {
|
|
2135
|
+
if (this.constructor.read_only)
|
|
2136
|
+
throw new Error("Read-only access");
|
|
2067
2137
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
2068
2138
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
2069
2139
|
const fields = this.fields;
|
|
@@ -2086,6 +2156,8 @@ class Table {
|
|
|
2086
2156
|
});
|
|
2087
2157
|
}
|
|
2088
2158
|
async drop_sync_table() {
|
|
2159
|
+
if (this.constructor.read_only)
|
|
2160
|
+
throw new Error("Read-only access");
|
|
2089
2161
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
2090
2162
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
2091
2163
|
await db_1.default.query(`
|
|
@@ -2103,6 +2175,9 @@ class Table {
|
|
|
2103
2175
|
* @param user
|
|
2104
2176
|
*/
|
|
2105
2177
|
async restore_row_version(id, version, user) {
|
|
2178
|
+
let use_user = this.constructor.fixed_user || user;
|
|
2179
|
+
if (this.constructor.read_only)
|
|
2180
|
+
throw new Error("Read-only access");
|
|
2106
2181
|
const row = await db_1.default.selectOne(`${db_1.default.sqlsanitize(this.name)}__history`, {
|
|
2107
2182
|
[this.pk_name]: id,
|
|
2108
2183
|
_version: version,
|
|
@@ -2113,7 +2188,7 @@ class Table {
|
|
|
2113
2188
|
r[f.name] = row[f.name];
|
|
2114
2189
|
});
|
|
2115
2190
|
//console.log("restore_row_version", r);
|
|
2116
|
-
await this.updateRow(r, id,
|
|
2191
|
+
await this.updateRow(r, id, use_user, false, undefined, version);
|
|
2117
2192
|
}
|
|
2118
2193
|
/**
|
|
2119
2194
|
* Undo row chnages
|
|
@@ -2121,6 +2196,9 @@ class Table {
|
|
|
2121
2196
|
* @param user
|
|
2122
2197
|
*/
|
|
2123
2198
|
async undo_row_changes(id, user) {
|
|
2199
|
+
let use_user = this.constructor.fixed_user || user;
|
|
2200
|
+
if (this.constructor.read_only)
|
|
2201
|
+
throw new Error("Read-only access");
|
|
2124
2202
|
const current_version_row = await db_1.default.selectMaybeOne(`${(0, internal_1.sqlsanitize)(this.name)}__history`, { [this.pk_name]: id }, { orderBy: "_version", orderDesc: true, limit: 1 });
|
|
2125
2203
|
//get max that is not a restore
|
|
2126
2204
|
const last_non_restore = await db_1.default.selectMaybeOne(`${(0, internal_1.sqlsanitize)(this.name)}__history`, {
|
|
@@ -2132,7 +2210,7 @@ class Table {
|
|
|
2132
2210
|
},
|
|
2133
2211
|
}, { orderBy: "_version", orderDesc: true, limit: 1 });
|
|
2134
2212
|
if (last_non_restore) {
|
|
2135
|
-
await this.restore_row_version(id, last_non_restore._version,
|
|
2213
|
+
await this.restore_row_version(id, last_non_restore._version, use_user);
|
|
2136
2214
|
}
|
|
2137
2215
|
}
|
|
2138
2216
|
/**
|
|
@@ -2141,6 +2219,9 @@ class Table {
|
|
|
2141
2219
|
* @param user
|
|
2142
2220
|
*/
|
|
2143
2221
|
async redo_row_changes(id, user) {
|
|
2222
|
+
let use_user = this.constructor.fixed_user || user;
|
|
2223
|
+
if (this.constructor.read_only)
|
|
2224
|
+
throw new Error("Read-only access");
|
|
2144
2225
|
const current_version_row = await db_1.default.selectMaybeOne(`${(0, internal_1.sqlsanitize)(this.name)}__history`, { [this.pk_name]: id }, { orderBy: "_version", orderDesc: true, limit: 1 });
|
|
2145
2226
|
if (current_version_row._restore_of_version) {
|
|
2146
2227
|
const next_version = await db_1.default.selectMaybeOne(`${(0, internal_1.sqlsanitize)(this.name)}__history`, {
|
|
@@ -2150,7 +2231,7 @@ class Table {
|
|
|
2150
2231
|
},
|
|
2151
2232
|
}, { orderBy: "_version", limit: 1 });
|
|
2152
2233
|
if (next_version) {
|
|
2153
|
-
await this.restore_row_version(id, next_version._version,
|
|
2234
|
+
await this.restore_row_version(id, next_version._version, use_user);
|
|
2154
2235
|
}
|
|
2155
2236
|
}
|
|
2156
2237
|
}
|
|
@@ -2159,6 +2240,8 @@ class Table {
|
|
|
2159
2240
|
* with options object, or just minimal interval for legacy code
|
|
2160
2241
|
*/
|
|
2161
2242
|
async compress_history(options) {
|
|
2243
|
+
if (this.constructor.read_only)
|
|
2244
|
+
throw new Error("Read-only access");
|
|
2162
2245
|
const interval_secs = typeof options === "number" ? options : options?.interval_secs;
|
|
2163
2246
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
2164
2247
|
const pk = this.pk_name;
|
|
@@ -2205,6 +2288,8 @@ class Table {
|
|
|
2205
2288
|
*/
|
|
2206
2289
|
async drop_history_table() {
|
|
2207
2290
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
2291
|
+
if (this.constructor.read_only)
|
|
2292
|
+
throw new Error("Read-only access");
|
|
2208
2293
|
await db_1.default.query(`
|
|
2209
2294
|
drop table ${schemaPrefix}"${(0, internal_1.sqlsanitize)(this.name)}__history";`);
|
|
2210
2295
|
}
|
|
@@ -2214,6 +2299,8 @@ class Table {
|
|
|
2214
2299
|
* @returns {Promise<void>}
|
|
2215
2300
|
*/
|
|
2216
2301
|
async rename(new_name) {
|
|
2302
|
+
if (this.constructor.read_only)
|
|
2303
|
+
throw new Error("Read-only access");
|
|
2217
2304
|
//in transaction
|
|
2218
2305
|
if (db_1.default.isSQLite)
|
|
2219
2306
|
throw new InvalidAdminAction("Cannot rename table on SQLite");
|
|
@@ -2238,6 +2325,8 @@ class Table {
|
|
|
2238
2325
|
* @returns {Promise<void>}
|
|
2239
2326
|
*/
|
|
2240
2327
|
async update(new_table_rec) {
|
|
2328
|
+
if (this.constructor.read_only)
|
|
2329
|
+
throw new Error("Read-only access");
|
|
2241
2330
|
if (new_table_rec.ownership_field_id === "")
|
|
2242
2331
|
delete new_table_rec.ownership_field_id;
|
|
2243
2332
|
const existing = Table.findOne({ id: this.id });
|
|
@@ -2290,6 +2379,8 @@ class Table {
|
|
|
2290
2379
|
* @returns {Promise<void>}
|
|
2291
2380
|
*/
|
|
2292
2381
|
async enable_fkey_constraints() {
|
|
2382
|
+
if (this.constructor.read_only)
|
|
2383
|
+
throw new Error("Read-only access");
|
|
2293
2384
|
const fields = this.fields;
|
|
2294
2385
|
for (const f of fields)
|
|
2295
2386
|
await f.enable_fkey_constraint(this);
|
|
@@ -2301,6 +2392,8 @@ class Table {
|
|
|
2301
2392
|
* @returns {Promise<{error: string}|{error: string}|{error: string}|{error: string}|{error: string}|{success: string}|{error: (string|string|*)}>}
|
|
2302
2393
|
*/
|
|
2303
2394
|
static async create_from_csv(name, filePath) {
|
|
2395
|
+
if (this.constructor.read_only)
|
|
2396
|
+
throw new Error("Read-only access");
|
|
2304
2397
|
let rows;
|
|
2305
2398
|
const state = await require("../db/state").getState();
|
|
2306
2399
|
try {
|
|
@@ -2472,6 +2565,9 @@ class Table {
|
|
|
2472
2565
|
* @returns {Promise<{error: string}|{success: string}>}
|
|
2473
2566
|
*/
|
|
2474
2567
|
async import_csv_file(filePath, options) {
|
|
2568
|
+
//todo user check
|
|
2569
|
+
if (this.constructor.read_only)
|
|
2570
|
+
throw new Error("Read-only access");
|
|
2475
2571
|
if (typeof options === "boolean") {
|
|
2476
2572
|
options = { recalc_stored: options };
|
|
2477
2573
|
}
|
|
@@ -2556,6 +2652,10 @@ class Table {
|
|
|
2556
2652
|
// start sql transaction
|
|
2557
2653
|
if (!options?.no_transaction)
|
|
2558
2654
|
await client.query("BEGIN");
|
|
2655
|
+
if (db_1.default.isSQLite)
|
|
2656
|
+
await client.query("PRAGMA defer_foreign_keys = ON");
|
|
2657
|
+
else
|
|
2658
|
+
await client.query("SET CONSTRAINTS ALL DEFERRED");
|
|
2559
2659
|
const readStream = (0, fs_1.createReadStream)(filePath);
|
|
2560
2660
|
const returnedRows = [];
|
|
2561
2661
|
try {
|
|
@@ -2801,6 +2901,8 @@ ${rejectDetails}`,
|
|
|
2801
2901
|
return v1;
|
|
2802
2902
|
}
|
|
2803
2903
|
async import_json_history_file(filePath) {
|
|
2904
|
+
if (this.constructor.read_only)
|
|
2905
|
+
throw new Error("Read-only access");
|
|
2804
2906
|
return await (0, async_json_stream_1.default)(filePath, async (row) => {
|
|
2805
2907
|
await this.insert_history_row(row);
|
|
2806
2908
|
});
|
|
@@ -2812,6 +2914,8 @@ ${rejectDetails}`,
|
|
|
2812
2914
|
* @returns {Promise<{error: string}|{success: string}>}
|
|
2813
2915
|
*/
|
|
2814
2916
|
async import_json_file(filePath, skip_first_data_row) {
|
|
2917
|
+
if (this.constructor.read_only)
|
|
2918
|
+
throw new Error("Read-only access");
|
|
2815
2919
|
const fields = this.fields;
|
|
2816
2920
|
const pk_name = this.pk_name;
|
|
2817
2921
|
const { readState } = require("../plugin-helper");
|
|
@@ -3087,10 +3191,10 @@ ${rejectDetails}`,
|
|
|
3087
3191
|
async aggregationQuery(aggregations, options) {
|
|
3088
3192
|
const { forUser, forPublic } = options || {};
|
|
3089
3193
|
const role = forUser ? forUser.role_id : forPublic ? 100 : null;
|
|
3194
|
+
const use_forUser = this.constructor.fixed_user || forUser;
|
|
3090
3195
|
const where = { ...(options?.where || {}) };
|
|
3091
3196
|
if (role &&
|
|
3092
|
-
this.updateWhereWithOwnership(where,
|
|
3093
|
-
?.notAuthorized) {
|
|
3197
|
+
this.updateWhereWithOwnership(where, use_forUser || { role_id: 100 }, true)?.notAuthorized) {
|
|
3094
3198
|
const emptyRet = {};
|
|
3095
3199
|
Object.entries(aggregations).forEach(([nm, aggObj]) => {
|
|
3096
3200
|
const agg = aggObj?.aggregate?.toLowerCase?.() || "count";
|
|
@@ -3114,9 +3218,10 @@ ${rejectDetails}`,
|
|
|
3114
3218
|
return res.rows[0];
|
|
3115
3219
|
}
|
|
3116
3220
|
ownership_formula_where(user) {
|
|
3221
|
+
let use_user = this.constructor.fixed_user || user;
|
|
3117
3222
|
if (!this.ownership_formula)
|
|
3118
3223
|
return {};
|
|
3119
|
-
const wh = jsexprToWhere(this.ownership_formula, { user }, this.fields);
|
|
3224
|
+
const wh = jsexprToWhere(this.ownership_formula, { user: use_user }, this.fields);
|
|
3120
3225
|
if (wh.eq && Array.isArray(wh.eq)) {
|
|
3121
3226
|
let arr = wh.eq;
|
|
3122
3227
|
for (let index = 0; index < arr.length; index++) {
|
|
@@ -3152,7 +3257,8 @@ ${rejectDetails}`,
|
|
|
3152
3257
|
? `"${opts.schema}".`
|
|
3153
3258
|
: db_1.default.getTenantSchemaPrefix();
|
|
3154
3259
|
const { forUser, forPublic } = opts;
|
|
3155
|
-
const
|
|
3260
|
+
const use_forUser = this.constructor.fixed_user || forUser;
|
|
3261
|
+
const role = use_forUser ? use_forUser.role_id : forPublic ? 100 : null;
|
|
3156
3262
|
if (role && role > this.min_role_read && this.ownership_formula) {
|
|
3157
3263
|
const freeVars = freeVariables(this.ownership_formula);
|
|
3158
3264
|
add_free_variables_to_joinfields(freeVars, joinFields, fields);
|
|
@@ -3166,17 +3272,17 @@ ${rejectDetails}`,
|
|
|
3166
3272
|
if (!owner_field)
|
|
3167
3273
|
throw new Error(`Owner field in table ${this.name} not found`);
|
|
3168
3274
|
mergeIntoWhere(opts.where, {
|
|
3169
|
-
[owner_field.name]:
|
|
3275
|
+
[owner_field.name]: use_forUser.id,
|
|
3170
3276
|
});
|
|
3171
3277
|
}
|
|
3172
|
-
else if (
|
|
3278
|
+
else if (use_forUser &&
|
|
3173
3279
|
role &&
|
|
3174
3280
|
role > this.min_role_read &&
|
|
3175
3281
|
this.ownership_formula) {
|
|
3176
3282
|
if (forPublic || role === 100)
|
|
3177
3283
|
return { notAuthorized: true }; //TODO may not be true
|
|
3178
3284
|
try {
|
|
3179
|
-
mergeIntoWhere(opts.where, this.ownership_formula_where(
|
|
3285
|
+
mergeIntoWhere(opts.where, this.ownership_formula_where(use_forUser));
|
|
3180
3286
|
}
|
|
3181
3287
|
catch (e) {
|
|
3182
3288
|
//ignore, ownership formula is too difficult to merge with where
|
|
@@ -3353,7 +3459,8 @@ ${rejectDetails}`,
|
|
|
3353
3459
|
async getJoinedRows(opts = {}) {
|
|
3354
3460
|
const fields = this.fields;
|
|
3355
3461
|
const { forUser, forPublic, ...selopts1 } = opts;
|
|
3356
|
-
const
|
|
3462
|
+
const use_forUser = this.constructor.fixed_user || forUser;
|
|
3463
|
+
const role = use_forUser ? use_forUser.role_id : forPublic ? 100 : null;
|
|
3357
3464
|
const { sql, values, notAuthorized, joinFields, aggregations } = await this.getJoinedQuery(opts);
|
|
3358
3465
|
if (notAuthorized)
|
|
3359
3466
|
return [];
|
|
@@ -3394,7 +3501,7 @@ ${rejectDetails}`,
|
|
|
3394
3501
|
//already dealt with by changing where
|
|
3395
3502
|
}
|
|
3396
3503
|
else if (this.ownership_formula || this.name === "users") {
|
|
3397
|
-
calcRow = calcRow.filter((row) => this.is_owner(
|
|
3504
|
+
calcRow = calcRow.filter((row) => this.is_owner(use_forUser, row));
|
|
3398
3505
|
}
|
|
3399
3506
|
else
|
|
3400
3507
|
return []; //no ownership
|
|
@@ -3466,6 +3573,8 @@ ${rejectDetails}`,
|
|
|
3466
3573
|
return (0, table_helper_1.get_formula_examples)(typename, this.fields.filter((f) => !f.calculated));
|
|
3467
3574
|
}
|
|
3468
3575
|
async repairCompositePrimary() {
|
|
3576
|
+
if (this.constructor.read_only)
|
|
3577
|
+
throw new Error("Read-only access");
|
|
3469
3578
|
const primaryKeys = this.fields.filter((f) => f.primary_key);
|
|
3470
3579
|
const nonSerialPKS = primaryKeys.some((f) => f.attributes?.NonSerial);
|
|
3471
3580
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
@@ -3507,6 +3616,8 @@ where table_schema = '${db_1.default.getTenantSchema() || "public"}'
|
|
|
3507
3616
|
await Table.state_refresh(true);
|
|
3508
3617
|
}
|
|
3509
3618
|
async move_include_fts_to_search_context() {
|
|
3619
|
+
if (this.constructor.read_only)
|
|
3620
|
+
throw new Error("Read-only access");
|
|
3510
3621
|
const include_fts_fields = this.fields.filter((f) => f.attributes?.include_fts);
|
|
3511
3622
|
if (!include_fts_fields.length)
|
|
3512
3623
|
return;
|
|
@@ -3543,6 +3654,8 @@ where table_schema = '${db_1.default.getTenantSchema() || "public"}'
|
|
|
3543
3654
|
}
|
|
3544
3655
|
}
|
|
3545
3656
|
}
|
|
3657
|
+
Table.fixed_user = undefined;
|
|
3658
|
+
Table.read_only = false;
|
|
3546
3659
|
async function dump_table_to_json_file(filePath, tableName) {
|
|
3547
3660
|
const writeStream = (0, fs_1.createWriteStream)(filePath);
|
|
3548
3661
|
const client = db_1.default.isSQLite ? db_1.default : await db_1.default.getClient();
|