@saltcorn/data 1.6.0-alpha.15 → 1.6.0-alpha.17
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 +2 -2
- package/dist/base-plugin/actions.d.ts.map +1 -1
- package/dist/base-plugin/actions.js +40 -10
- package/dist/base-plugin/actions.js.map +1 -1
- package/dist/base-plugin/index.d.ts +9 -4
- 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 +2 -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 +9 -2
- package/dist/base-plugin/viewtemplates/filter.js.map +1 -1
- package/dist/base-plugin/viewtemplates/list.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/list.js +2 -0
- 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 +2 -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 +26 -3
- package/dist/db/state.js.map +1 -1
- package/dist/migrate.d.ts.map +1 -1
- package/dist/migrate.js +2 -0
- package/dist/migrate.js.map +1 -1
- package/dist/models/expression.d.ts +1 -1
- package/dist/models/expression.d.ts.map +1 -1
- package/dist/models/expression.js +42 -13
- package/dist/models/expression.js.map +1 -1
- package/dist/models/field.d.ts.map +1 -1
- package/dist/models/field.js +63 -6
- 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/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/table.d.ts +6 -0
- package/dist/models/table.d.ts.map +1 -1
- package/dist/models/table.js +188 -84
- package/dist/models/table.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/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/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);
|
|
@@ -1044,7 +1074,7 @@ class Table {
|
|
|
1044
1074
|
}
|
|
1045
1075
|
else if (this.ownership_formula || this.name === "users") {
|
|
1046
1076
|
if (!selopts?.disable_ownership_postqfilter)
|
|
1047
|
-
rows = rows.filter((row) => this.is_owner(
|
|
1077
|
+
rows = rows.filter((row) => this.is_owner(use_forUser, row));
|
|
1048
1078
|
}
|
|
1049
1079
|
else
|
|
1050
1080
|
return []; //no ownership
|
|
@@ -1156,6 +1186,9 @@ class Table {
|
|
|
1156
1186
|
* @returns
|
|
1157
1187
|
*/
|
|
1158
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");
|
|
1159
1192
|
// migrating to options arg
|
|
1160
1193
|
if (typeof noTrigger === "object") {
|
|
1161
1194
|
const extraOptions = noTrigger;
|
|
@@ -1189,7 +1222,7 @@ class Table {
|
|
|
1189
1222
|
]);
|
|
1190
1223
|
const fields = this.fields;
|
|
1191
1224
|
const pk_name = this.pk_name;
|
|
1192
|
-
const role =
|
|
1225
|
+
const role = use_user?.role_id;
|
|
1193
1226
|
const state = require("../db/state").getState();
|
|
1194
1227
|
let stringified = false;
|
|
1195
1228
|
const sqliteJsonCols = !isNode()
|
|
@@ -1207,7 +1240,7 @@ class Table {
|
|
|
1207
1240
|
}
|
|
1208
1241
|
if (this.ownership_formula)
|
|
1209
1242
|
add_free_variables_to_joinfields(freeVariables(this.ownership_formula), joinFields, fields);
|
|
1210
|
-
if (
|
|
1243
|
+
if (use_user &&
|
|
1211
1244
|
role &&
|
|
1212
1245
|
(role > this.min_role_write || role > this.min_role_read)) {
|
|
1213
1246
|
if (role === 100)
|
|
@@ -1216,19 +1249,19 @@ class Table {
|
|
|
1216
1249
|
const owner_field = fields.find((f) => f.id === this.ownership_field_id);
|
|
1217
1250
|
if (!owner_field)
|
|
1218
1251
|
throw new Error(`Owner field in table ${this.name} not found`);
|
|
1219
|
-
if (v[owner_field.name] && v[owner_field.name] !=
|
|
1220
|
-
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`);
|
|
1221
1254
|
return "Not authorized";
|
|
1222
1255
|
}
|
|
1223
1256
|
//need to check existing
|
|
1224
1257
|
if (!existing)
|
|
1225
1258
|
existing = await this.getJoinedRow({
|
|
1226
1259
|
where: { [pk_name]: id },
|
|
1227
|
-
forUser:
|
|
1260
|
+
forUser: use_user,
|
|
1228
1261
|
joinFields,
|
|
1229
1262
|
});
|
|
1230
|
-
if (!existing || existing?.[owner_field.name] !==
|
|
1231
|
-
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`);
|
|
1232
1265
|
return "Not authorized";
|
|
1233
1266
|
}
|
|
1234
1267
|
}
|
|
@@ -1236,11 +1269,11 @@ class Table {
|
|
|
1236
1269
|
if (!existing)
|
|
1237
1270
|
existing = await this.getJoinedRow({
|
|
1238
1271
|
where: { [pk_name]: id },
|
|
1239
|
-
forUser:
|
|
1272
|
+
forUser: use_user,
|
|
1240
1273
|
joinFields,
|
|
1241
1274
|
});
|
|
1242
|
-
if (!existing || !this.is_owner(
|
|
1243
|
-
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)}`);
|
|
1244
1277
|
return "Not authorized";
|
|
1245
1278
|
}
|
|
1246
1279
|
}
|
|
@@ -1253,7 +1286,7 @@ class Table {
|
|
|
1253
1286
|
if (!existing)
|
|
1254
1287
|
existing = await this.getJoinedRow({
|
|
1255
1288
|
where: { [pk_name]: id },
|
|
1256
|
-
forUser:
|
|
1289
|
+
forUser: use_user,
|
|
1257
1290
|
joinFields,
|
|
1258
1291
|
});
|
|
1259
1292
|
const newRow = { ...existing, ...v };
|
|
@@ -1261,8 +1294,8 @@ class Table {
|
|
|
1261
1294
|
if (constraint_check)
|
|
1262
1295
|
return constraint_check;
|
|
1263
1296
|
}
|
|
1264
|
-
if (
|
|
1265
|
-
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);
|
|
1266
1299
|
if (field_write_check)
|
|
1267
1300
|
return field_write_check;
|
|
1268
1301
|
}
|
|
@@ -1271,11 +1304,11 @@ class Table {
|
|
|
1271
1304
|
if (!existing)
|
|
1272
1305
|
existing = await this.getJoinedRow({
|
|
1273
1306
|
where: { [pk_name]: id },
|
|
1274
|
-
forUser:
|
|
1307
|
+
forUser: use_user,
|
|
1275
1308
|
joinFields,
|
|
1276
1309
|
});
|
|
1277
1310
|
const valResCollector = resultCollector || {};
|
|
1278
|
-
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 || {}) });
|
|
1279
1312
|
if ("error" in valResCollector)
|
|
1280
1313
|
return valResCollector.error;
|
|
1281
1314
|
if ("set_fields" in valResCollector)
|
|
@@ -1287,7 +1320,7 @@ class Table {
|
|
|
1287
1320
|
let need_to_update = Object.keys(v_in).some((k) => freeVarFKFields.has(k));
|
|
1288
1321
|
existing = await this.getJoinedRow({
|
|
1289
1322
|
where: { [pk_name]: id },
|
|
1290
|
-
forUser:
|
|
1323
|
+
forUser: use_user,
|
|
1291
1324
|
joinFields,
|
|
1292
1325
|
});
|
|
1293
1326
|
let updated;
|
|
@@ -1301,7 +1334,7 @@ class Table {
|
|
|
1301
1334
|
});
|
|
1302
1335
|
updated = await this.getJoinedRow({
|
|
1303
1336
|
where: { [pk_name]: id },
|
|
1304
|
-
forUser:
|
|
1337
|
+
forUser: use_user,
|
|
1305
1338
|
joinFields,
|
|
1306
1339
|
});
|
|
1307
1340
|
}
|
|
@@ -1335,7 +1368,7 @@ class Table {
|
|
|
1335
1368
|
pk_name,
|
|
1336
1369
|
},
|
|
1337
1370
|
_time: new Date(),
|
|
1338
|
-
_userid:
|
|
1371
|
+
_userid: use_user?.id,
|
|
1339
1372
|
_restore_of_version: restore_of_version || null,
|
|
1340
1373
|
});
|
|
1341
1374
|
}
|
|
@@ -1344,7 +1377,7 @@ class Table {
|
|
|
1344
1377
|
if (triggers.length > 0)
|
|
1345
1378
|
existing = await this.getJoinedRow({
|
|
1346
1379
|
where: { [pk_name]: id },
|
|
1347
|
-
forUser:
|
|
1380
|
+
forUser: use_user,
|
|
1348
1381
|
joinFields,
|
|
1349
1382
|
});
|
|
1350
1383
|
}
|
|
@@ -1365,7 +1398,7 @@ class Table {
|
|
|
1365
1398
|
if (!existing && really_changed_field_names.size && keyChanged)
|
|
1366
1399
|
existing = await this.getJoinedRow({
|
|
1367
1400
|
where: { [pk_name]: id },
|
|
1368
|
-
forUser:
|
|
1401
|
+
forUser: use_user,
|
|
1369
1402
|
joinFields,
|
|
1370
1403
|
});
|
|
1371
1404
|
await db_1.default.update(this.name, v, id, {
|
|
@@ -1388,10 +1421,12 @@ class Table {
|
|
|
1388
1421
|
await this.auto_update_calc_aggregations(existing, !existing, (autoRecalcIterations || 0) + 1, really_changed_field_names, keyChanged);
|
|
1389
1422
|
}
|
|
1390
1423
|
if (!noTrigger) {
|
|
1391
|
-
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 || {}) });
|
|
1392
1425
|
}
|
|
1393
1426
|
}
|
|
1394
1427
|
static async analyze_all_indexed_tables() {
|
|
1428
|
+
if (this.constructor.read_only)
|
|
1429
|
+
throw new Error("Read-only access");
|
|
1395
1430
|
const tables = await Table.find({}, { cached: true });
|
|
1396
1431
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
1397
1432
|
for (const table of tables)
|
|
@@ -1399,6 +1434,8 @@ class Table {
|
|
|
1399
1434
|
await db_1.default.query(`analyze ${schemaPrefix}"${(0, internal_1.sqlsanitize)(table.name)}";`);
|
|
1400
1435
|
}
|
|
1401
1436
|
async insert_history_row(v0, retry = 0) {
|
|
1437
|
+
if (this.constructor.read_only)
|
|
1438
|
+
throw new Error("Read-only access");
|
|
1402
1439
|
// sometimes there is a race condition in history inserts
|
|
1403
1440
|
// https://dba.stackexchange.com/questions/212580/concurrent-transactions-result-in-race-condition-with-unique-constraint-on-inser
|
|
1404
1441
|
// solution: retry 3 times, if fails run with on conflict do nothing
|
|
@@ -1453,6 +1490,8 @@ class Table {
|
|
|
1453
1490
|
});
|
|
1454
1491
|
}
|
|
1455
1492
|
async insertSyncInfo(id, updates, syncTimestamp) {
|
|
1493
|
+
if (this.constructor.read_only)
|
|
1494
|
+
throw new Error("Read-only access");
|
|
1456
1495
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
1457
1496
|
const schema = db_1.default.getTenantSchemaPrefix();
|
|
1458
1497
|
if (isNode()) {
|
|
@@ -1480,6 +1519,8 @@ class Table {
|
|
|
1480
1519
|
});
|
|
1481
1520
|
}
|
|
1482
1521
|
async updateSyncInfo(id, v, oldLastModified, syncTimestamp) {
|
|
1522
|
+
if (this.constructor.read_only)
|
|
1523
|
+
throw new Error("Read-only access");
|
|
1483
1524
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
1484
1525
|
const schema = db_1.default.getTenantSchemaPrefix();
|
|
1485
1526
|
if (!db_1.default.isSQLite) {
|
|
@@ -1522,8 +1563,11 @@ class Table {
|
|
|
1522
1563
|
* @returns {Promise<{error}|{success: boolean}>}
|
|
1523
1564
|
*/
|
|
1524
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;
|
|
1525
1569
|
try {
|
|
1526
|
-
const maybe_err = await this.updateRow(v, id,
|
|
1570
|
+
const maybe_err = await this.updateRow(v, id, use_user, {
|
|
1527
1571
|
noTrigger: false,
|
|
1528
1572
|
resultCollector,
|
|
1529
1573
|
extraArgs,
|
|
@@ -1544,9 +1588,12 @@ class Table {
|
|
|
1544
1588
|
* @returns {Promise<void>}
|
|
1545
1589
|
*/
|
|
1546
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");
|
|
1547
1594
|
const row = await this.getRow({ [this.pk_name]: id });
|
|
1548
1595
|
if (row)
|
|
1549
|
-
await this.updateRow({ [field_name]: !row[field_name] }, id,
|
|
1596
|
+
await this.updateRow({ [field_name]: !row[field_name] }, id, use_user);
|
|
1550
1597
|
}
|
|
1551
1598
|
delete_url(row, moreQuery) {
|
|
1552
1599
|
const comppk = this.composite_pk_names;
|
|
@@ -1609,10 +1656,11 @@ class Table {
|
|
|
1609
1656
|
* @param user
|
|
1610
1657
|
*/
|
|
1611
1658
|
check_field_write_role(row, user) {
|
|
1659
|
+
let use_user = this.constructor.fixed_user || user;
|
|
1612
1660
|
for (const field of this.fields) {
|
|
1613
1661
|
if (typeof row[field.name] !== "undefined" &&
|
|
1614
1662
|
field.attributes?.min_role_write &&
|
|
1615
|
-
|
|
1663
|
+
use_user.role_id > +field.attributes?.min_role_write)
|
|
1616
1664
|
return "Not authorized";
|
|
1617
1665
|
}
|
|
1618
1666
|
return undefined;
|
|
@@ -1629,8 +1677,13 @@ class Table {
|
|
|
1629
1677
|
}
|
|
1630
1678
|
}
|
|
1631
1679
|
async run_trigger(trigger_name, row, user, extraArgs) {
|
|
1680
|
+
let use_user = this.constructor.fixed_user || user;
|
|
1632
1681
|
const trigger = trigger_1.default.findOne({ name: trigger_name });
|
|
1633
|
-
return await trigger.runWithoutRow({
|
|
1682
|
+
return await trigger.runWithoutRow({
|
|
1683
|
+
row,
|
|
1684
|
+
user: use_user,
|
|
1685
|
+
...(extraArgs || {}),
|
|
1686
|
+
});
|
|
1634
1687
|
}
|
|
1635
1688
|
/**
|
|
1636
1689
|
* Insert row into the table. By passing in the user as
|
|
@@ -1656,6 +1709,9 @@ class Table {
|
|
|
1656
1709
|
* @returns
|
|
1657
1710
|
*/
|
|
1658
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");
|
|
1659
1715
|
const v_in = { ...v_in0 };
|
|
1660
1716
|
const fields = this.fields;
|
|
1661
1717
|
const pk_name = this.pk_name;
|
|
@@ -1672,13 +1728,13 @@ class Table {
|
|
|
1672
1728
|
}
|
|
1673
1729
|
: {};
|
|
1674
1730
|
this.normalise_fkey_values(v_in);
|
|
1675
|
-
if (
|
|
1731
|
+
if (use_user && use_user.role_id > this.min_role_write) {
|
|
1676
1732
|
if (this.ownership_field_id) {
|
|
1677
1733
|
const owner_field = fields.find((f) => f.id === this.ownership_field_id);
|
|
1678
1734
|
if (!owner_field)
|
|
1679
1735
|
throw new Error(`Owner field in table ${this.name} not found`);
|
|
1680
|
-
if (v_in[owner_field.name] !=
|
|
1681
|
-
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`);
|
|
1682
1738
|
return;
|
|
1683
1739
|
}
|
|
1684
1740
|
}
|
|
@@ -1690,14 +1746,14 @@ class Table {
|
|
|
1690
1746
|
let constraint_check = this.check_table_constraints(v_in);
|
|
1691
1747
|
if (constraint_check)
|
|
1692
1748
|
throw new Error(constraint_check);
|
|
1693
|
-
if (
|
|
1694
|
-
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);
|
|
1695
1751
|
if (field_write_check)
|
|
1696
1752
|
return field_write_check;
|
|
1697
1753
|
}
|
|
1698
1754
|
//check validate here based on v_in
|
|
1699
1755
|
const valResCollector = resultCollector || {};
|
|
1700
|
-
await trigger_1.default.runTableTriggers("Validate", this, { ...v_in }, valResCollector,
|
|
1756
|
+
await trigger_1.default.runTableTriggers("Validate", this, { ...v_in }, valResCollector, use_user);
|
|
1701
1757
|
if ("error" in valResCollector)
|
|
1702
1758
|
return valResCollector; //???
|
|
1703
1759
|
if ("set_fields" in valResCollector)
|
|
@@ -1710,7 +1766,7 @@ class Table {
|
|
|
1710
1766
|
let existing = await this.getJoinedRows({
|
|
1711
1767
|
where: { [pk_name]: id },
|
|
1712
1768
|
joinFields,
|
|
1713
|
-
forUser:
|
|
1769
|
+
forUser: use_user,
|
|
1714
1770
|
});
|
|
1715
1771
|
if (!existing?.[0]) {
|
|
1716
1772
|
//failed ownership test
|
|
@@ -1736,15 +1792,17 @@ class Table {
|
|
|
1736
1792
|
...sqliteJsonCols,
|
|
1737
1793
|
});
|
|
1738
1794
|
}
|
|
1739
|
-
if (
|
|
1795
|
+
if (use_user &&
|
|
1796
|
+
use_user.role_id > this.min_role_write &&
|
|
1797
|
+
this.ownership_formula) {
|
|
1740
1798
|
let existing = await this.getJoinedRow({
|
|
1741
1799
|
where: { [pk_name]: id },
|
|
1742
1800
|
joinFields,
|
|
1743
|
-
forUser:
|
|
1801
|
+
forUser: use_user,
|
|
1744
1802
|
});
|
|
1745
|
-
if (!existing || !this.is_owner(
|
|
1803
|
+
if (!existing || !this.is_owner(use_user, existing)) {
|
|
1746
1804
|
await this.deleteRows({ [pk_name]: id });
|
|
1747
|
-
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)}`);
|
|
1748
1806
|
return;
|
|
1749
1807
|
}
|
|
1750
1808
|
}
|
|
@@ -1756,7 +1814,7 @@ class Table {
|
|
|
1756
1814
|
next_version_by_id: id,
|
|
1757
1815
|
pk_name,
|
|
1758
1816
|
},
|
|
1759
|
-
_userid:
|
|
1817
|
+
_userid: use_user?.id,
|
|
1760
1818
|
_time: new Date(),
|
|
1761
1819
|
});
|
|
1762
1820
|
if (this.has_sync_info) {
|
|
@@ -1784,11 +1842,13 @@ class Table {
|
|
|
1784
1842
|
await this.auto_update_calc_aggregations(newRow);
|
|
1785
1843
|
if (!noTrigger) {
|
|
1786
1844
|
apply_calculated_fields([newRow], this.fields);
|
|
1787
|
-
await trigger_1.default.runTableTriggers("Insert", this, newRow, resultCollector,
|
|
1845
|
+
await trigger_1.default.runTableTriggers("Insert", this, newRow, resultCollector, use_user);
|
|
1788
1846
|
}
|
|
1789
1847
|
return id;
|
|
1790
1848
|
}
|
|
1791
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");
|
|
1792
1852
|
const state = require("../db/state").getState();
|
|
1793
1853
|
const pk_name = this.pk_name;
|
|
1794
1854
|
state.log(6, `auto_update_calc_aggregations table=${this.name} id=${v0[pk_name]} iters=${iterations}${changedFields ? ` changedFields=${[...(changedFields || [])]}` : ""}`);
|
|
@@ -1920,8 +1980,11 @@ class Table {
|
|
|
1920
1980
|
* @returns {Promise<{error}|{success: *}>}
|
|
1921
1981
|
*/
|
|
1922
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");
|
|
1923
1986
|
try {
|
|
1924
|
-
const id = await this.insertRow(v,
|
|
1987
|
+
const id = await this.insertRow(v, use_user, resultCollector);
|
|
1925
1988
|
if (!id)
|
|
1926
1989
|
return { error: "Not authorized" };
|
|
1927
1990
|
if (id?.includes?.("Not authorized"))
|
|
@@ -2069,6 +2132,8 @@ class Table {
|
|
|
2069
2132
|
);`);
|
|
2070
2133
|
}
|
|
2071
2134
|
async create_sync_info_table() {
|
|
2135
|
+
if (this.constructor.read_only)
|
|
2136
|
+
throw new Error("Read-only access");
|
|
2072
2137
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
2073
2138
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
2074
2139
|
const fields = this.fields;
|
|
@@ -2091,6 +2156,8 @@ class Table {
|
|
|
2091
2156
|
});
|
|
2092
2157
|
}
|
|
2093
2158
|
async drop_sync_table() {
|
|
2159
|
+
if (this.constructor.read_only)
|
|
2160
|
+
throw new Error("Read-only access");
|
|
2094
2161
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
2095
2162
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
2096
2163
|
await db_1.default.query(`
|
|
@@ -2108,6 +2175,9 @@ class Table {
|
|
|
2108
2175
|
* @param user
|
|
2109
2176
|
*/
|
|
2110
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");
|
|
2111
2181
|
const row = await db_1.default.selectOne(`${db_1.default.sqlsanitize(this.name)}__history`, {
|
|
2112
2182
|
[this.pk_name]: id,
|
|
2113
2183
|
_version: version,
|
|
@@ -2118,7 +2188,7 @@ class Table {
|
|
|
2118
2188
|
r[f.name] = row[f.name];
|
|
2119
2189
|
});
|
|
2120
2190
|
//console.log("restore_row_version", r);
|
|
2121
|
-
await this.updateRow(r, id,
|
|
2191
|
+
await this.updateRow(r, id, use_user, false, undefined, version);
|
|
2122
2192
|
}
|
|
2123
2193
|
/**
|
|
2124
2194
|
* Undo row chnages
|
|
@@ -2126,6 +2196,9 @@ class Table {
|
|
|
2126
2196
|
* @param user
|
|
2127
2197
|
*/
|
|
2128
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");
|
|
2129
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 });
|
|
2130
2203
|
//get max that is not a restore
|
|
2131
2204
|
const last_non_restore = await db_1.default.selectMaybeOne(`${(0, internal_1.sqlsanitize)(this.name)}__history`, {
|
|
@@ -2137,7 +2210,7 @@ class Table {
|
|
|
2137
2210
|
},
|
|
2138
2211
|
}, { orderBy: "_version", orderDesc: true, limit: 1 });
|
|
2139
2212
|
if (last_non_restore) {
|
|
2140
|
-
await this.restore_row_version(id, last_non_restore._version,
|
|
2213
|
+
await this.restore_row_version(id, last_non_restore._version, use_user);
|
|
2141
2214
|
}
|
|
2142
2215
|
}
|
|
2143
2216
|
/**
|
|
@@ -2146,6 +2219,9 @@ class Table {
|
|
|
2146
2219
|
* @param user
|
|
2147
2220
|
*/
|
|
2148
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");
|
|
2149
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 });
|
|
2150
2226
|
if (current_version_row._restore_of_version) {
|
|
2151
2227
|
const next_version = await db_1.default.selectMaybeOne(`${(0, internal_1.sqlsanitize)(this.name)}__history`, {
|
|
@@ -2155,7 +2231,7 @@ class Table {
|
|
|
2155
2231
|
},
|
|
2156
2232
|
}, { orderBy: "_version", limit: 1 });
|
|
2157
2233
|
if (next_version) {
|
|
2158
|
-
await this.restore_row_version(id, next_version._version,
|
|
2234
|
+
await this.restore_row_version(id, next_version._version, use_user);
|
|
2159
2235
|
}
|
|
2160
2236
|
}
|
|
2161
2237
|
}
|
|
@@ -2164,6 +2240,8 @@ class Table {
|
|
|
2164
2240
|
* with options object, or just minimal interval for legacy code
|
|
2165
2241
|
*/
|
|
2166
2242
|
async compress_history(options) {
|
|
2243
|
+
if (this.constructor.read_only)
|
|
2244
|
+
throw new Error("Read-only access");
|
|
2167
2245
|
const interval_secs = typeof options === "number" ? options : options?.interval_secs;
|
|
2168
2246
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
2169
2247
|
const pk = this.pk_name;
|
|
@@ -2210,6 +2288,8 @@ class Table {
|
|
|
2210
2288
|
*/
|
|
2211
2289
|
async drop_history_table() {
|
|
2212
2290
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
2291
|
+
if (this.constructor.read_only)
|
|
2292
|
+
throw new Error("Read-only access");
|
|
2213
2293
|
await db_1.default.query(`
|
|
2214
2294
|
drop table ${schemaPrefix}"${(0, internal_1.sqlsanitize)(this.name)}__history";`);
|
|
2215
2295
|
}
|
|
@@ -2219,6 +2299,8 @@ class Table {
|
|
|
2219
2299
|
* @returns {Promise<void>}
|
|
2220
2300
|
*/
|
|
2221
2301
|
async rename(new_name) {
|
|
2302
|
+
if (this.constructor.read_only)
|
|
2303
|
+
throw new Error("Read-only access");
|
|
2222
2304
|
//in transaction
|
|
2223
2305
|
if (db_1.default.isSQLite)
|
|
2224
2306
|
throw new InvalidAdminAction("Cannot rename table on SQLite");
|
|
@@ -2243,6 +2325,8 @@ class Table {
|
|
|
2243
2325
|
* @returns {Promise<void>}
|
|
2244
2326
|
*/
|
|
2245
2327
|
async update(new_table_rec) {
|
|
2328
|
+
if (this.constructor.read_only)
|
|
2329
|
+
throw new Error("Read-only access");
|
|
2246
2330
|
if (new_table_rec.ownership_field_id === "")
|
|
2247
2331
|
delete new_table_rec.ownership_field_id;
|
|
2248
2332
|
const existing = Table.findOne({ id: this.id });
|
|
@@ -2295,6 +2379,8 @@ class Table {
|
|
|
2295
2379
|
* @returns {Promise<void>}
|
|
2296
2380
|
*/
|
|
2297
2381
|
async enable_fkey_constraints() {
|
|
2382
|
+
if (this.constructor.read_only)
|
|
2383
|
+
throw new Error("Read-only access");
|
|
2298
2384
|
const fields = this.fields;
|
|
2299
2385
|
for (const f of fields)
|
|
2300
2386
|
await f.enable_fkey_constraint(this);
|
|
@@ -2306,6 +2392,8 @@ class Table {
|
|
|
2306
2392
|
* @returns {Promise<{error: string}|{error: string}|{error: string}|{error: string}|{error: string}|{success: string}|{error: (string|string|*)}>}
|
|
2307
2393
|
*/
|
|
2308
2394
|
static async create_from_csv(name, filePath) {
|
|
2395
|
+
if (this.constructor.read_only)
|
|
2396
|
+
throw new Error("Read-only access");
|
|
2309
2397
|
let rows;
|
|
2310
2398
|
const state = await require("../db/state").getState();
|
|
2311
2399
|
try {
|
|
@@ -2477,6 +2565,9 @@ class Table {
|
|
|
2477
2565
|
* @returns {Promise<{error: string}|{success: string}>}
|
|
2478
2566
|
*/
|
|
2479
2567
|
async import_csv_file(filePath, options) {
|
|
2568
|
+
//todo user check
|
|
2569
|
+
if (this.constructor.read_only)
|
|
2570
|
+
throw new Error("Read-only access");
|
|
2480
2571
|
if (typeof options === "boolean") {
|
|
2481
2572
|
options = { recalc_stored: options };
|
|
2482
2573
|
}
|
|
@@ -2810,6 +2901,8 @@ ${rejectDetails}`,
|
|
|
2810
2901
|
return v1;
|
|
2811
2902
|
}
|
|
2812
2903
|
async import_json_history_file(filePath) {
|
|
2904
|
+
if (this.constructor.read_only)
|
|
2905
|
+
throw new Error("Read-only access");
|
|
2813
2906
|
return await (0, async_json_stream_1.default)(filePath, async (row) => {
|
|
2814
2907
|
await this.insert_history_row(row);
|
|
2815
2908
|
});
|
|
@@ -2821,6 +2914,8 @@ ${rejectDetails}`,
|
|
|
2821
2914
|
* @returns {Promise<{error: string}|{success: string}>}
|
|
2822
2915
|
*/
|
|
2823
2916
|
async import_json_file(filePath, skip_first_data_row) {
|
|
2917
|
+
if (this.constructor.read_only)
|
|
2918
|
+
throw new Error("Read-only access");
|
|
2824
2919
|
const fields = this.fields;
|
|
2825
2920
|
const pk_name = this.pk_name;
|
|
2826
2921
|
const { readState } = require("../plugin-helper");
|
|
@@ -3096,10 +3191,10 @@ ${rejectDetails}`,
|
|
|
3096
3191
|
async aggregationQuery(aggregations, options) {
|
|
3097
3192
|
const { forUser, forPublic } = options || {};
|
|
3098
3193
|
const role = forUser ? forUser.role_id : forPublic ? 100 : null;
|
|
3194
|
+
const use_forUser = this.constructor.fixed_user || forUser;
|
|
3099
3195
|
const where = { ...(options?.where || {}) };
|
|
3100
3196
|
if (role &&
|
|
3101
|
-
this.updateWhereWithOwnership(where,
|
|
3102
|
-
?.notAuthorized) {
|
|
3197
|
+
this.updateWhereWithOwnership(where, use_forUser || { role_id: 100 }, true)?.notAuthorized) {
|
|
3103
3198
|
const emptyRet = {};
|
|
3104
3199
|
Object.entries(aggregations).forEach(([nm, aggObj]) => {
|
|
3105
3200
|
const agg = aggObj?.aggregate?.toLowerCase?.() || "count";
|
|
@@ -3123,9 +3218,10 @@ ${rejectDetails}`,
|
|
|
3123
3218
|
return res.rows[0];
|
|
3124
3219
|
}
|
|
3125
3220
|
ownership_formula_where(user) {
|
|
3221
|
+
let use_user = this.constructor.fixed_user || user;
|
|
3126
3222
|
if (!this.ownership_formula)
|
|
3127
3223
|
return {};
|
|
3128
|
-
const wh = jsexprToWhere(this.ownership_formula, { user }, this.fields);
|
|
3224
|
+
const wh = jsexprToWhere(this.ownership_formula, { user: use_user }, this.fields);
|
|
3129
3225
|
if (wh.eq && Array.isArray(wh.eq)) {
|
|
3130
3226
|
let arr = wh.eq;
|
|
3131
3227
|
for (let index = 0; index < arr.length; index++) {
|
|
@@ -3161,7 +3257,8 @@ ${rejectDetails}`,
|
|
|
3161
3257
|
? `"${opts.schema}".`
|
|
3162
3258
|
: db_1.default.getTenantSchemaPrefix();
|
|
3163
3259
|
const { forUser, forPublic } = opts;
|
|
3164
|
-
const
|
|
3260
|
+
const use_forUser = this.constructor.fixed_user || forUser;
|
|
3261
|
+
const role = use_forUser ? use_forUser.role_id : forPublic ? 100 : null;
|
|
3165
3262
|
if (role && role > this.min_role_read && this.ownership_formula) {
|
|
3166
3263
|
const freeVars = freeVariables(this.ownership_formula);
|
|
3167
3264
|
add_free_variables_to_joinfields(freeVars, joinFields, fields);
|
|
@@ -3175,17 +3272,17 @@ ${rejectDetails}`,
|
|
|
3175
3272
|
if (!owner_field)
|
|
3176
3273
|
throw new Error(`Owner field in table ${this.name} not found`);
|
|
3177
3274
|
mergeIntoWhere(opts.where, {
|
|
3178
|
-
[owner_field.name]:
|
|
3275
|
+
[owner_field.name]: use_forUser.id,
|
|
3179
3276
|
});
|
|
3180
3277
|
}
|
|
3181
|
-
else if (
|
|
3278
|
+
else if (use_forUser &&
|
|
3182
3279
|
role &&
|
|
3183
3280
|
role > this.min_role_read &&
|
|
3184
3281
|
this.ownership_formula) {
|
|
3185
3282
|
if (forPublic || role === 100)
|
|
3186
3283
|
return { notAuthorized: true }; //TODO may not be true
|
|
3187
3284
|
try {
|
|
3188
|
-
mergeIntoWhere(opts.where, this.ownership_formula_where(
|
|
3285
|
+
mergeIntoWhere(opts.where, this.ownership_formula_where(use_forUser));
|
|
3189
3286
|
}
|
|
3190
3287
|
catch (e) {
|
|
3191
3288
|
//ignore, ownership formula is too difficult to merge with where
|
|
@@ -3362,7 +3459,8 @@ ${rejectDetails}`,
|
|
|
3362
3459
|
async getJoinedRows(opts = {}) {
|
|
3363
3460
|
const fields = this.fields;
|
|
3364
3461
|
const { forUser, forPublic, ...selopts1 } = opts;
|
|
3365
|
-
const
|
|
3462
|
+
const use_forUser = this.constructor.fixed_user || forUser;
|
|
3463
|
+
const role = use_forUser ? use_forUser.role_id : forPublic ? 100 : null;
|
|
3366
3464
|
const { sql, values, notAuthorized, joinFields, aggregations } = await this.getJoinedQuery(opts);
|
|
3367
3465
|
if (notAuthorized)
|
|
3368
3466
|
return [];
|
|
@@ -3403,7 +3501,7 @@ ${rejectDetails}`,
|
|
|
3403
3501
|
//already dealt with by changing where
|
|
3404
3502
|
}
|
|
3405
3503
|
else if (this.ownership_formula || this.name === "users") {
|
|
3406
|
-
calcRow = calcRow.filter((row) => this.is_owner(
|
|
3504
|
+
calcRow = calcRow.filter((row) => this.is_owner(use_forUser, row));
|
|
3407
3505
|
}
|
|
3408
3506
|
else
|
|
3409
3507
|
return []; //no ownership
|
|
@@ -3475,6 +3573,8 @@ ${rejectDetails}`,
|
|
|
3475
3573
|
return (0, table_helper_1.get_formula_examples)(typename, this.fields.filter((f) => !f.calculated));
|
|
3476
3574
|
}
|
|
3477
3575
|
async repairCompositePrimary() {
|
|
3576
|
+
if (this.constructor.read_only)
|
|
3577
|
+
throw new Error("Read-only access");
|
|
3478
3578
|
const primaryKeys = this.fields.filter((f) => f.primary_key);
|
|
3479
3579
|
const nonSerialPKS = primaryKeys.some((f) => f.attributes?.NonSerial);
|
|
3480
3580
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
@@ -3516,6 +3616,8 @@ where table_schema = '${db_1.default.getTenantSchema() || "public"}'
|
|
|
3516
3616
|
await Table.state_refresh(true);
|
|
3517
3617
|
}
|
|
3518
3618
|
async move_include_fts_to_search_context() {
|
|
3619
|
+
if (this.constructor.read_only)
|
|
3620
|
+
throw new Error("Read-only access");
|
|
3519
3621
|
const include_fts_fields = this.fields.filter((f) => f.attributes?.include_fts);
|
|
3520
3622
|
if (!include_fts_fields.length)
|
|
3521
3623
|
return;
|
|
@@ -3552,6 +3654,8 @@ where table_schema = '${db_1.default.getTenantSchema() || "public"}'
|
|
|
3552
3654
|
}
|
|
3553
3655
|
}
|
|
3554
3656
|
}
|
|
3657
|
+
Table.fixed_user = undefined;
|
|
3658
|
+
Table.read_only = false;
|
|
3555
3659
|
async function dump_table_to_json_file(filePath, tableName) {
|
|
3556
3660
|
const writeStream = (0, fs_1.createWriteStream)(filePath);
|
|
3557
3661
|
const client = db_1.default.isSQLite ? db_1.default : await db_1.default.getClient();
|