@saltcorn/server 0.7.4 → 0.8.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/app.js +18 -11
- package/auth/admin.js +370 -120
- package/auth/roleadmin.js +5 -23
- package/auth/routes.js +40 -15
- package/locales/de.json +1049 -273
- package/locales/en.json +58 -3
- package/locales/es.json +134 -134
- package/locales/it.json +6 -1
- package/locales/ru.json +44 -7
- package/markup/admin.js +46 -42
- package/markup/forms.js +4 -3
- package/package.json +8 -7
- package/public/blockly.js +19 -31
- package/public/diagram_utils.js +530 -0
- package/public/gridedit.js +4 -1
- package/public/jquery-menu-editor.min.js +112 -112
- package/public/saltcorn-common.js +31 -8
- package/public/saltcorn.css +11 -0
- package/public/saltcorn.js +211 -70
- package/restart_watcher.js +1 -0
- package/routes/actions.js +6 -14
- package/routes/admin.js +229 -79
- package/routes/api.js +19 -2
- package/routes/common_lists.js +137 -134
- package/routes/delete.js +6 -5
- package/routes/diagram.js +43 -117
- package/routes/edit.js +5 -10
- package/routes/fields.js +63 -29
- package/routes/files.js +137 -101
- package/routes/homepage.js +2 -2
- package/routes/infoarch.js +2 -2
- package/routes/list.js +12 -13
- package/routes/page.js +16 -3
- package/routes/pageedit.js +13 -8
- package/routes/scapi.js +1 -1
- package/routes/search.js +1 -1
- package/routes/tables.js +9 -14
- package/routes/tag_entries.js +31 -10
- package/routes/tags.js +10 -10
- package/routes/tenant.js +114 -50
- package/routes/utils.js +12 -0
- package/routes/view.js +3 -4
- package/routes/viewedit.js +57 -55
- package/serve.js +5 -0
- package/tests/admin.test.js +6 -2
- package/tests/auth.test.js +20 -0
- package/tests/fields.test.js +1 -0
- package/tests/files.test.js +11 -20
- package/tests/tenant.test.js +12 -2
- package/tests/viewedit.test.js +15 -1
package/auth/admin.js
CHANGED
|
@@ -5,9 +5,6 @@
|
|
|
5
5
|
* @subcategory auth
|
|
6
6
|
*/
|
|
7
7
|
// todo refactor to few modules + rename to be in sync with router url
|
|
8
|
-
/**
|
|
9
|
-
* @type {module:express-promise-router}
|
|
10
|
-
*/
|
|
11
8
|
const Router = require("express-promise-router");
|
|
12
9
|
const { contract, is } = require("contractis");
|
|
13
10
|
|
|
@@ -24,10 +21,10 @@ const {
|
|
|
24
21
|
settingsDropdown,
|
|
25
22
|
post_dropdown_item,
|
|
26
23
|
} = require("@saltcorn/markup");
|
|
27
|
-
const { isAdmin,
|
|
24
|
+
const { isAdmin, error_catcher } = require("../routes/utils");
|
|
28
25
|
const { send_reset_email } = require("./resetpw");
|
|
29
26
|
const { getState } = require("@saltcorn/data/db/state");
|
|
30
|
-
const { a, div,
|
|
27
|
+
const { a, div, span, code, h5, i, p } = require("@saltcorn/markup/tags");
|
|
31
28
|
const Table = require("@saltcorn/data/models/table");
|
|
32
29
|
const {
|
|
33
30
|
send_users_page,
|
|
@@ -38,7 +35,9 @@ const {
|
|
|
38
35
|
is_hsts_tld,
|
|
39
36
|
} = require("../markup/admin");
|
|
40
37
|
const { send_verification_email } = require("@saltcorn/data/models/email");
|
|
41
|
-
|
|
38
|
+
const {
|
|
39
|
+
expressionValidator,
|
|
40
|
+
} = require("@saltcorn/data/models/expression");
|
|
42
41
|
/**
|
|
43
42
|
* @type {object}
|
|
44
43
|
* @const
|
|
@@ -177,33 +176,33 @@ const user_dropdown = (user, req, can_reset) =>
|
|
|
177
176
|
req
|
|
178
177
|
),
|
|
179
178
|
can_reset &&
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
179
|
+
post_dropdown_item(
|
|
180
|
+
`/useradmin/reset-password/${user.id}`,
|
|
181
|
+
'<i class="fas fa-envelope"></i> ' +
|
|
182
|
+
req.__("Send password reset email"),
|
|
183
|
+
req
|
|
184
|
+
),
|
|
186
185
|
can_reset &&
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
186
|
+
!user.verified_on &&
|
|
187
|
+
getState().getConfig("verification_view", "") &&
|
|
188
|
+
post_dropdown_item(
|
|
189
|
+
`/useradmin/send-verification/${user.id}`,
|
|
190
|
+
'<i class="fas fa-envelope"></i> ' +
|
|
191
|
+
req.__("Send verification email"),
|
|
192
|
+
req
|
|
193
|
+
),
|
|
195
194
|
user.disabled &&
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
195
|
+
post_dropdown_item(
|
|
196
|
+
`/useradmin/enable/${user.id}`,
|
|
197
|
+
'<i class="fas fa-play"></i> ' + req.__("Enable"),
|
|
198
|
+
req
|
|
199
|
+
),
|
|
201
200
|
!user.disabled &&
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
201
|
+
post_dropdown_item(
|
|
202
|
+
`/useradmin/disable/${user.id}`,
|
|
203
|
+
'<i class="fas fa-pause"></i> ' + req.__("Disable"),
|
|
204
|
+
req
|
|
205
|
+
),
|
|
207
206
|
div({ class: "dropdown-divider" }),
|
|
208
207
|
post_dropdown_item(
|
|
209
208
|
`/useradmin/delete/${user.id}`,
|
|
@@ -215,6 +214,7 @@ const user_dropdown = (user, req, can_reset) =>
|
|
|
215
214
|
]);
|
|
216
215
|
|
|
217
216
|
/**
|
|
217
|
+
* Users List (HTTP Get)
|
|
218
218
|
* @name get
|
|
219
219
|
* @function
|
|
220
220
|
* @memberof module:auth/admin~auth/adminRouter
|
|
@@ -225,8 +225,8 @@ router.get(
|
|
|
225
225
|
error_catcher(async (req, res) => {
|
|
226
226
|
const users = await User.find({}, { orderBy: "id" });
|
|
227
227
|
const roles = await User.get_roles();
|
|
228
|
-
|
|
229
|
-
roles.forEach(
|
|
228
|
+
let roleMap = {};
|
|
229
|
+
roles.forEach(r => {
|
|
230
230
|
roleMap[r.id] = r.role;
|
|
231
231
|
});
|
|
232
232
|
const can_reset = getState().getConfig("smtp_host", "") !== "";
|
|
@@ -257,8 +257,8 @@ router.get(
|
|
|
257
257
|
key: (r) =>
|
|
258
258
|
!!r.verified_on
|
|
259
259
|
? i({
|
|
260
|
-
|
|
261
|
-
|
|
260
|
+
class: "fas fa-check-circle text-success",
|
|
261
|
+
})
|
|
262
262
|
: "",
|
|
263
263
|
},
|
|
264
264
|
{ label: req.__("Role"), key: (r) => roleMap[r.role_id] },
|
|
@@ -303,37 +303,67 @@ router.get(
|
|
|
303
303
|
);
|
|
304
304
|
|
|
305
305
|
/**
|
|
306
|
-
*
|
|
306
|
+
* Authentication Setting Form
|
|
307
|
+
* @param {object} req
|
|
308
|
+
* @returns {Form}
|
|
309
|
+
*/
|
|
310
|
+
const auth_settings_form = async (req) =>
|
|
311
|
+
await config_fields_form({
|
|
312
|
+
req,
|
|
313
|
+
field_names: [
|
|
314
|
+
"allow_signup",
|
|
315
|
+
"login_menu",
|
|
316
|
+
"allow_forgot",
|
|
317
|
+
"new_user_form",
|
|
318
|
+
"login_form",
|
|
319
|
+
"signup_form",
|
|
320
|
+
"user_settings_form",
|
|
321
|
+
"verification_view",
|
|
322
|
+
"elevate_verified",
|
|
323
|
+
"email_mask",
|
|
324
|
+
],
|
|
325
|
+
action: "/useradmin/settings",
|
|
326
|
+
submitLabel: req.__("Save"),
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* HTTP Settings Form
|
|
331
|
+
* @param {object} req
|
|
332
|
+
* @returns {Form}
|
|
333
|
+
*/
|
|
334
|
+
const http_settings_form = async (req) =>
|
|
335
|
+
await config_fields_form({
|
|
336
|
+
req,
|
|
337
|
+
field_names: [
|
|
338
|
+
"timeout",
|
|
339
|
+
"cookie_duration",
|
|
340
|
+
"cookie_duration_remember",
|
|
341
|
+
"cookie_sessions",
|
|
342
|
+
"custom_http_headers",
|
|
343
|
+
],
|
|
344
|
+
action: "/useradmin/http",
|
|
345
|
+
submitLabel: req.__("Save"),
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Permissions Setting Form
|
|
307
351
|
* @param {object} req
|
|
308
352
|
* @returns {Form}
|
|
309
353
|
*/
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
"verification_view",
|
|
321
|
-
"elevate_verified",
|
|
322
|
-
"min_role_upload",
|
|
323
|
-
"min_role_apikeygen",
|
|
324
|
-
"timeout",
|
|
325
|
-
"email_mask",
|
|
326
|
-
"allow_forgot",
|
|
327
|
-
"cookie_duration",
|
|
328
|
-
"cookie_duration_remember",
|
|
329
|
-
"cookie_sessions",
|
|
330
|
-
"custom_http_headers",
|
|
331
|
-
],
|
|
332
|
-
action: "/useradmin/settings",
|
|
333
|
-
submitLabel: req.__("Save"),
|
|
334
|
-
});
|
|
354
|
+
const permissions_settings_form = async (req) =>
|
|
355
|
+
await config_fields_form({
|
|
356
|
+
req,
|
|
357
|
+
field_names: [
|
|
358
|
+
"min_role_upload",
|
|
359
|
+
"min_role_apikeygen",
|
|
360
|
+
],
|
|
361
|
+
action: "/useradmin/permissions",
|
|
362
|
+
submitLabel: req.__("Save"),
|
|
363
|
+
});
|
|
335
364
|
|
|
336
365
|
/**
|
|
366
|
+
* HTTP GET for /useradmin/settings
|
|
337
367
|
* @name get/settings
|
|
338
368
|
* @function
|
|
339
369
|
* @memberof module:auth/admin~auth/adminRouter
|
|
@@ -342,7 +372,7 @@ router.get(
|
|
|
342
372
|
"/settings",
|
|
343
373
|
isAdmin,
|
|
344
374
|
error_catcher(async (req, res) => {
|
|
345
|
-
const form = await
|
|
375
|
+
const form = await auth_settings_form(req);
|
|
346
376
|
send_users_page({
|
|
347
377
|
res,
|
|
348
378
|
req,
|
|
@@ -357,6 +387,7 @@ router.get(
|
|
|
357
387
|
);
|
|
358
388
|
|
|
359
389
|
/**
|
|
390
|
+
* HTTP POST for /useradmin/settings
|
|
360
391
|
* @name post/settings
|
|
361
392
|
* @function
|
|
362
393
|
* @memberof module:auth/admin~auth/adminRouter
|
|
@@ -365,7 +396,7 @@ router.post(
|
|
|
365
396
|
"/settings",
|
|
366
397
|
isAdmin,
|
|
367
398
|
error_catcher(async (req, res) => {
|
|
368
|
-
const form = await
|
|
399
|
+
const form = await auth_settings_form(req);
|
|
369
400
|
form.validate(req.body);
|
|
370
401
|
if (form.hasErrors) {
|
|
371
402
|
send_users_page({
|
|
@@ -380,7 +411,7 @@ router.post(
|
|
|
380
411
|
});
|
|
381
412
|
} else {
|
|
382
413
|
await save_config_from_form(form);
|
|
383
|
-
req.flash("success", req.__("
|
|
414
|
+
req.flash("success", req.__("Authentication settings updated"));
|
|
384
415
|
if (!req.xhr) res.redirect("/useradmin/settings");
|
|
385
416
|
else res.json({ success: "ok" });
|
|
386
417
|
}
|
|
@@ -388,6 +419,119 @@ router.post(
|
|
|
388
419
|
);
|
|
389
420
|
|
|
390
421
|
/**
|
|
422
|
+
* HTTP GET for /useradmin/http
|
|
423
|
+
* @name get/settings
|
|
424
|
+
* @function
|
|
425
|
+
* @memberof module:auth/admin~auth/adminRouter
|
|
426
|
+
*/
|
|
427
|
+
router.get(
|
|
428
|
+
"/http",
|
|
429
|
+
isAdmin,
|
|
430
|
+
error_catcher(async (req, res) => {
|
|
431
|
+
const form = await http_settings_form(req);
|
|
432
|
+
send_users_page({
|
|
433
|
+
res,
|
|
434
|
+
req,
|
|
435
|
+
active_sub: "HTTP",
|
|
436
|
+
contents: {
|
|
437
|
+
type: "card",
|
|
438
|
+
title: req.__("HTTP settings"),
|
|
439
|
+
contents: [renderForm(form, req.csrfToken())],
|
|
440
|
+
},
|
|
441
|
+
});
|
|
442
|
+
})
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* HTTP POST for /useradmin/http
|
|
447
|
+
* @name post/settings
|
|
448
|
+
* @function
|
|
449
|
+
* @memberof module:auth/admin~auth/adminRouter
|
|
450
|
+
*/
|
|
451
|
+
router.post(
|
|
452
|
+
"/http",
|
|
453
|
+
isAdmin,
|
|
454
|
+
error_catcher(async (req, res) => {
|
|
455
|
+
const form = await http_settings_form(req);
|
|
456
|
+
form.validate(req.body);
|
|
457
|
+
if (form.hasErrors) {
|
|
458
|
+
send_users_page({
|
|
459
|
+
res,
|
|
460
|
+
req,
|
|
461
|
+
active_sub: "HTTP",
|
|
462
|
+
contents: {
|
|
463
|
+
type: "card",
|
|
464
|
+
title: req.__("HTTP settings"),
|
|
465
|
+
contents: [renderForm(form, req.csrfToken())],
|
|
466
|
+
},
|
|
467
|
+
});
|
|
468
|
+
} else {
|
|
469
|
+
await save_config_from_form(form);
|
|
470
|
+
req.flash("success", req.__("HTTP settings updated"));
|
|
471
|
+
if (!req.xhr) res.redirect("/useradmin/http");
|
|
472
|
+
else res.json({ success: "ok" });
|
|
473
|
+
}
|
|
474
|
+
})
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* HTTP GET for /useradmin/permissions
|
|
479
|
+
* @name get/settings
|
|
480
|
+
* @function
|
|
481
|
+
* @memberof module:auth/admin~auth/adminRouter
|
|
482
|
+
*/
|
|
483
|
+
router.get(
|
|
484
|
+
"/permissions",
|
|
485
|
+
isAdmin,
|
|
486
|
+
error_catcher(async (req, res) => {
|
|
487
|
+
const form = await permissions_settings_form(req);
|
|
488
|
+
send_users_page({
|
|
489
|
+
res,
|
|
490
|
+
req,
|
|
491
|
+
active_sub: "Permissions",
|
|
492
|
+
contents: {
|
|
493
|
+
type: "card",
|
|
494
|
+
title: req.__("Permissions settings"),
|
|
495
|
+
contents: [renderForm(form, req.csrfToken())],
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
})
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* HTTP POST for /useradmin/permissions
|
|
503
|
+
* @name post/settings
|
|
504
|
+
* @function
|
|
505
|
+
* @memberof module:auth/admin~auth/adminRouter
|
|
506
|
+
*/
|
|
507
|
+
router.post(
|
|
508
|
+
"/permissions",
|
|
509
|
+
isAdmin,
|
|
510
|
+
error_catcher(async (req, res) => {
|
|
511
|
+
const form = await permissions_settings_form(req);
|
|
512
|
+
form.validate(req.body);
|
|
513
|
+
if (form.hasErrors) {
|
|
514
|
+
send_users_page({
|
|
515
|
+
res,
|
|
516
|
+
req,
|
|
517
|
+
active_sub: "Permissions",
|
|
518
|
+
contents: {
|
|
519
|
+
type: "card",
|
|
520
|
+
title: req.__("Permissions settings"),
|
|
521
|
+
contents: [renderForm(form, req.csrfToken())],
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
} else {
|
|
525
|
+
await save_config_from_form(form);
|
|
526
|
+
req.flash("success", req.__("Permissions settings updated"));
|
|
527
|
+
if (!req.xhr) res.redirect("/useradmin/permissions");
|
|
528
|
+
else res.json({ success: "ok" });
|
|
529
|
+
}
|
|
530
|
+
})
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* HTTP GET for /useradmin/ssl
|
|
391
535
|
* @name get/ssl
|
|
392
536
|
* @function
|
|
393
537
|
* @memberof module:auth/admin~auth/adminRouter
|
|
@@ -421,15 +565,15 @@ router.get(
|
|
|
421
565
|
above: [
|
|
422
566
|
...(letsencrypt && has_custom
|
|
423
567
|
? [
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
568
|
+
{
|
|
569
|
+
type: "card",
|
|
570
|
+
contents: p(
|
|
571
|
+
req.__(
|
|
572
|
+
"You have enabled both Let's Encrypt certificates and custom SSL certificates. Let's Encrypt takes priority and the custom certificates will be ignored."
|
|
573
|
+
)
|
|
574
|
+
),
|
|
575
|
+
},
|
|
576
|
+
]
|
|
433
577
|
: []),
|
|
434
578
|
{
|
|
435
579
|
type: "card",
|
|
@@ -450,33 +594,33 @@ router.get(
|
|
|
450
594
|
),
|
|
451
595
|
letsencrypt
|
|
452
596
|
? post_btn(
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
597
|
+
"/config/delete/letsencrypt",
|
|
598
|
+
req.__("Disable LetsEncrypt HTTPS"),
|
|
599
|
+
req.csrfToken(),
|
|
600
|
+
{ btnClass: "btn-danger", req }
|
|
601
|
+
)
|
|
458
602
|
: post_btn(
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
603
|
+
"/admin/enable-letsencrypt",
|
|
604
|
+
req.__("Enable LetsEncrypt HTTPS"),
|
|
605
|
+
req.csrfToken(),
|
|
606
|
+
{ confirm: true, req }
|
|
607
|
+
),
|
|
464
608
|
!letsencrypt &&
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
)
|
|
473
|
-
),
|
|
474
|
-
p(
|
|
475
|
-
req.__(
|
|
476
|
-
"The DNS A records (for * and @, or a subdomain) should point to this server's IP address before enabling LetsEncrypt"
|
|
477
|
-
)
|
|
609
|
+
show_warning &&
|
|
610
|
+
!has_custom &&
|
|
611
|
+
div(
|
|
612
|
+
{ class: "mt-3 alert alert-danger" },
|
|
613
|
+
p(
|
|
614
|
+
req.__(
|
|
615
|
+
"The address you are using to reach Saltcorn does not match the Base URL."
|
|
478
616
|
)
|
|
479
617
|
),
|
|
618
|
+
p(
|
|
619
|
+
req.__(
|
|
620
|
+
"The DNS A records (for * and @, or a subdomain) should point to this server's IP address before enabling LetsEncrypt"
|
|
621
|
+
)
|
|
622
|
+
)
|
|
623
|
+
),
|
|
480
624
|
],
|
|
481
625
|
},
|
|
482
626
|
{
|
|
@@ -508,17 +652,19 @@ router.get(
|
|
|
508
652
|
);
|
|
509
653
|
|
|
510
654
|
/**
|
|
655
|
+
* SSL Setting form
|
|
511
656
|
* @param {object} req
|
|
512
657
|
* @returns {Form}
|
|
513
658
|
*/
|
|
514
|
-
const ssl_form = (req) =>
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
659
|
+
const ssl_form = async (req) =>
|
|
660
|
+
await config_fields_form({
|
|
661
|
+
req,
|
|
662
|
+
field_names: ["custom_ssl_certificate", "custom_ssl_private_key"],
|
|
663
|
+
action: "/useradmin/ssl/custom",
|
|
664
|
+
});
|
|
520
665
|
|
|
521
666
|
/**
|
|
667
|
+
* HTTP GET for /useradmin/ssl/custom
|
|
522
668
|
* @name get/ssl/custom
|
|
523
669
|
* @function
|
|
524
670
|
* @memberof module:auth/admin~auth/adminRouter
|
|
@@ -543,6 +689,7 @@ router.get(
|
|
|
543
689
|
);
|
|
544
690
|
|
|
545
691
|
/**
|
|
692
|
+
* HTTP POST for /useradmin/ssl/custom
|
|
546
693
|
* @name post/ssl/custom
|
|
547
694
|
* @function
|
|
548
695
|
* @memberof module:auth/admin~auth/adminRouter
|
|
@@ -570,8 +717,8 @@ router.post(
|
|
|
570
717
|
req.flash(
|
|
571
718
|
"success",
|
|
572
719
|
req.__("Custom SSL enabled. Restart for changes to take effect.") +
|
|
573
|
-
|
|
574
|
-
|
|
720
|
+
" " +
|
|
721
|
+
a({ href: "/admin/system" }, req.__("Restart here"))
|
|
575
722
|
);
|
|
576
723
|
if (!req.xhr) {
|
|
577
724
|
res.redirect("/useradmin/ssl");
|
|
@@ -580,6 +727,104 @@ router.post(
|
|
|
580
727
|
})
|
|
581
728
|
);
|
|
582
729
|
|
|
730
|
+
/**
|
|
731
|
+
* HTTP GET for /useradmin/table-access
|
|
732
|
+
* @name get/ssl/custom
|
|
733
|
+
* @function
|
|
734
|
+
* @memberof module:auth/admin~auth/adminRouter
|
|
735
|
+
*/
|
|
736
|
+
router.get(
|
|
737
|
+
"/table-access",
|
|
738
|
+
isAdmin,
|
|
739
|
+
error_catcher(async (req, res) => {
|
|
740
|
+
const tables = await Table.find()
|
|
741
|
+
const roleOptions = (await User.get_roles()).map((r) => ({
|
|
742
|
+
value: r.id,
|
|
743
|
+
label: r.role,
|
|
744
|
+
}));
|
|
745
|
+
|
|
746
|
+
const contents = []
|
|
747
|
+
for (const table of tables) {
|
|
748
|
+
if (table.external) continue
|
|
749
|
+
const fields = await table.getFields();
|
|
750
|
+
const userFields = fields
|
|
751
|
+
.filter((f) => f.reftable_name === "users")
|
|
752
|
+
.map((f) => ({ value: f.id, label: f.name }));
|
|
753
|
+
const form = new Form({
|
|
754
|
+
action: "/table",
|
|
755
|
+
noSubmitButton: true,
|
|
756
|
+
onChange: "saveAndContinue(this)",
|
|
757
|
+
fields: [
|
|
758
|
+
{
|
|
759
|
+
label: req.__("Ownership field"),
|
|
760
|
+
name: "ownership_field_id",
|
|
761
|
+
sublabel: req.__(
|
|
762
|
+
"The user referred to in this field will be the owner of the row"
|
|
763
|
+
),
|
|
764
|
+
input_type: "select",
|
|
765
|
+
options: [
|
|
766
|
+
{ value: "", label: req.__("None") },
|
|
767
|
+
...userFields,
|
|
768
|
+
{ value: "_formula", label: req.__("Formula") },
|
|
769
|
+
],
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
name: "ownership_formula",
|
|
773
|
+
label: req.__("Ownership formula"),
|
|
774
|
+
validator: expressionValidator,
|
|
775
|
+
type: "String",
|
|
776
|
+
class: "validate-expression",
|
|
777
|
+
sublabel:
|
|
778
|
+
req.__("User is treated as owner if true. In scope: ") +
|
|
779
|
+
["user", ...fields.map((f) => f.name)]
|
|
780
|
+
.map((fn) => code(fn))
|
|
781
|
+
.join(", "),
|
|
782
|
+
showIf: { ownership_field_id: "_formula" },
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
label: req.__("Minimum role to read"),
|
|
786
|
+
sublabel: req.__(
|
|
787
|
+
"User must have this role or higher to read rows from the table, unless they are the owner"
|
|
788
|
+
),
|
|
789
|
+
name: "min_role_read",
|
|
790
|
+
input_type: "select",
|
|
791
|
+
options: roleOptions,
|
|
792
|
+
attributes: { asideNext: true }
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
label: req.__("Minimum role to write"),
|
|
796
|
+
name: "min_role_write",
|
|
797
|
+
input_type: "select",
|
|
798
|
+
sublabel: req.__(
|
|
799
|
+
"User must have this role or higher to edit or create new rows in the table, unless they are the owner"
|
|
800
|
+
),
|
|
801
|
+
options: roleOptions,
|
|
802
|
+
},
|
|
803
|
+
]
|
|
804
|
+
})
|
|
805
|
+
form.hidden("id", "name");
|
|
806
|
+
form.values = table
|
|
807
|
+
if (table.ownership_formula && !table.ownership_field_id)
|
|
808
|
+
form.values.ownership_field_id = "_formula";
|
|
809
|
+
contents.push(div(
|
|
810
|
+
h5(a({ href: `/table/${table.id}` }, table.name)),
|
|
811
|
+
renderForm(form, req.csrfToken())
|
|
812
|
+
))
|
|
813
|
+
}
|
|
814
|
+
send_users_page({
|
|
815
|
+
res,
|
|
816
|
+
req,
|
|
817
|
+
active_sub: "Table access",
|
|
818
|
+
contents: {
|
|
819
|
+
type: "card",
|
|
820
|
+
title: req.__("Table access"),
|
|
821
|
+
contents
|
|
822
|
+
},
|
|
823
|
+
});
|
|
824
|
+
})
|
|
825
|
+
);
|
|
826
|
+
|
|
827
|
+
|
|
583
828
|
/**
|
|
584
829
|
* @name get/:id
|
|
585
830
|
* @function
|
|
@@ -613,9 +858,9 @@ router.get(
|
|
|
613
858
|
div(
|
|
614
859
|
user.api_token
|
|
615
860
|
? span(
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
861
|
+
{ class: "me-1" },
|
|
862
|
+
req.__("API token for this user: ")
|
|
863
|
+
) + code(user.api_token)
|
|
619
864
|
: req.__("No API token issued")
|
|
620
865
|
),
|
|
621
866
|
// button for reset or generate api token
|
|
@@ -629,16 +874,16 @@ router.get(
|
|
|
629
874
|
),
|
|
630
875
|
// button for remove api token
|
|
631
876
|
user.api_token &&
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
877
|
+
div(
|
|
878
|
+
{ class: "mt-4 ms-2 d-inline-block" },
|
|
879
|
+
post_btn(
|
|
880
|
+
`/useradmin/remove-api-token/${user.id}`,
|
|
881
|
+
// TBD localization
|
|
882
|
+
user.api_token ? req.__("Remove") : req.__("Generate"),
|
|
883
|
+
req.csrfToken(),
|
|
884
|
+
{ req: req, confirm: true }
|
|
885
|
+
)
|
|
886
|
+
),
|
|
642
887
|
],
|
|
643
888
|
},
|
|
644
889
|
],
|
|
@@ -707,7 +952,7 @@ router.post(
|
|
|
707
952
|
role_id: +role_id,
|
|
708
953
|
...rest,
|
|
709
954
|
});
|
|
710
|
-
// refactored to catch user errors
|
|
955
|
+
// refactored to catch user errors and stop processing if any errors
|
|
711
956
|
if (u.error) {
|
|
712
957
|
req.flash("error", u.error); // todo change to prompt near field like done for views
|
|
713
958
|
// todo return to create user form
|
|
@@ -727,7 +972,7 @@ router.post(
|
|
|
727
972
|
);
|
|
728
973
|
|
|
729
974
|
/**
|
|
730
|
-
* Reset password for
|
|
975
|
+
* Reset password for user
|
|
731
976
|
* @name post/reset-password/:id
|
|
732
977
|
* @function
|
|
733
978
|
* @memberof module:auth/admin~auth/adminRouter
|
|
@@ -757,8 +1002,13 @@ router.post(
|
|
|
757
1002
|
error_catcher(async (req, res) => {
|
|
758
1003
|
const { id } = req.params;
|
|
759
1004
|
const u = await User.findOne({ id });
|
|
760
|
-
|
|
761
|
-
|
|
1005
|
+
// todo add test case
|
|
1006
|
+
const result = await send_verification_email(u, req);
|
|
1007
|
+
if (result.error)
|
|
1008
|
+
req.flash(
|
|
1009
|
+
"danger",
|
|
1010
|
+
req.__(`Verification email sender error:`, result.error)
|
|
1011
|
+
);
|
|
762
1012
|
else
|
|
763
1013
|
req.flash(
|
|
764
1014
|
"success",
|