@saltcorn/server 0.9.6-beta.9 → 0.9.7-rc.0
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 +9 -2
- package/auth/admin.js +51 -52
- package/auth/roleadmin.js +6 -2
- package/auth/routes.js +28 -10
- package/auth/testhelp.js +86 -0
- package/help/Field label.tmd +11 -0
- package/help/Field types.tmd +39 -0
- package/help/Inclusion Formula.tmd +38 -0
- package/help/Ownership field.tmd +76 -0
- package/help/Ownership formula.tmd +75 -0
- package/help/Table roles.tmd +20 -0
- package/help/User groups.tmd +35 -0
- package/help/User roles.tmd +30 -0
- package/load_plugins.js +28 -4
- package/locales/en.json +28 -1
- package/locales/it.json +3 -2
- package/markup/forms.js +5 -1
- package/package.json +9 -9
- package/public/log_viewer_utils.js +32 -0
- package/public/mermaid.min.js +705 -306
- package/public/saltcorn-builder.css +23 -0
- package/public/saltcorn-common.js +195 -71
- package/public/saltcorn.css +72 -0
- package/public/saltcorn.js +78 -0
- package/restart_watcher.js +1 -0
- package/routes/actions.js +27 -0
- package/routes/admin.js +180 -66
- package/routes/api.js +6 -0
- package/routes/common_lists.js +42 -32
- package/routes/fields.js +9 -1
- package/routes/homepage.js +2 -0
- package/routes/menu.js +69 -4
- package/routes/notifications.js +90 -10
- package/routes/pageedit.js +18 -13
- package/routes/plugins.js +5 -1
- package/routes/search.js +10 -4
- package/routes/tables.js +47 -27
- package/routes/tenant.js +4 -15
- package/routes/utils.js +20 -6
- package/routes/viewedit.js +11 -7
- package/serve.js +27 -5
- package/tests/edit.test.js +426 -0
- package/tests/fields.test.js +21 -0
- package/tests/filter.test.js +68 -0
- package/tests/page.test.js +2 -2
- package/tests/sync.test.js +59 -0
- package/wrapper.js +4 -1
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Each user in Saltcorn has a role and the roles have a strictly hierachical ordering,
|
|
2
|
+
which you [can edit](/roleadmin). The ordering means that users in a role can access
|
|
3
|
+
everything the users in the role "below" then can acceess, but the users in the role
|
|
4
|
+
"above" have further access.
|
|
5
|
+
|
|
6
|
+
Assigning access by role is a quick way to give users more or less access based on how
|
|
7
|
+
much you trust them. Use the role to Determine access to views, pages (by setting the
|
|
8
|
+
minimum role to run on each entity) and tables (by setting the minimum role to read or write.)
|
|
9
|
+
In addition many components of views, such as actionsm containers or columns in lists have a
|
|
10
|
+
setting for the minimum role to show or run.
|
|
11
|
+
|
|
12
|
+
Sometimes you need more refined access control than that afforded by a simple hierarchical
|
|
13
|
+
role system. For these purposes you can use table ownership (see the help topics in the table
|
|
14
|
+
properties).
|
|
15
|
+
|
|
16
|
+
When there is no logged in user, The permissions will treat the session as if it is a user
|
|
17
|
+
with the role public (role id = 100). You can use this to give access to resources to non-logged-in
|
|
18
|
+
visitors.
|
|
19
|
+
|
|
20
|
+
For each role, you can set the theme (out of the themes installed in the module store) and the
|
|
21
|
+
two-factor authentication (2FA) policy, where you can allow users to set up 2FA (Optional),
|
|
22
|
+
disallow 2FA (Disabled), or force users to set up 2FA (Mandatory)
|
|
23
|
+
|
|
24
|
+
In the role editor you can also create new roles. When you do so, you set the role name and the
|
|
25
|
+
role ID. The role ID determines the how high the new role is in the role hierarchy, with a lower
|
|
26
|
+
role ID indicating a more powerful role. The lowest role ID is the admin user role, with ID = 1,
|
|
27
|
+
who have access to all resource and in addition can edit views, pages etc. The least powerful role is
|
|
28
|
+
the public (not logged in) users, with role ID = 100. Normally newly created users have role 80
|
|
29
|
+
(role ID = 80), but you can set the role of signed up users in the
|
|
30
|
+
[Login and signup settings](useradmin/settings).
|
package/load_plugins.js
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
const db = require("@saltcorn/data/db");
|
|
9
9
|
const { getState, getRootState } = require("@saltcorn/data/db/state");
|
|
10
10
|
const Plugin = require("@saltcorn/data/models/plugin");
|
|
11
|
+
const { isRoot } = require("@saltcorn/data/utils");
|
|
12
|
+
const { eachTenant } = require("@saltcorn/admin-models/models/tenant");
|
|
11
13
|
|
|
12
14
|
const PluginInstaller = require("@saltcorn/plugins-loader/plugin_installer");
|
|
13
15
|
|
|
@@ -60,9 +62,23 @@ const loadPlugin = async (plugin, force) => {
|
|
|
60
62
|
console.error(error); // todo i think that situation is not resolved
|
|
61
63
|
}
|
|
62
64
|
}
|
|
65
|
+
|
|
66
|
+
if (isRoot() && res.plugin_module.authentication)
|
|
67
|
+
await eachTenant(reloadAuthFromRoot);
|
|
63
68
|
return res;
|
|
64
69
|
};
|
|
65
70
|
|
|
71
|
+
const reloadAuthFromRoot = () => {
|
|
72
|
+
if (isRoot()) return;
|
|
73
|
+
const rootState = getRootState();
|
|
74
|
+
const tenantState = getState();
|
|
75
|
+
if (!rootState || !tenantState || rootState === tenantState) return;
|
|
76
|
+
tenantState.auth_methods = {};
|
|
77
|
+
for (const [k, v] of Object.entries(rootState.auth_methods)) {
|
|
78
|
+
if (v.shareWithTenants) tenantState.auth_methods[k] = v;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
66
82
|
/**
|
|
67
83
|
* Install plugin
|
|
68
84
|
* @param plugin - plugin name
|
|
@@ -90,6 +106,7 @@ const loadAllPlugins = async (force) => {
|
|
|
90
106
|
}
|
|
91
107
|
await getState().refreshUserLayouts();
|
|
92
108
|
await getState().refresh(true);
|
|
109
|
+
if (!isRoot()) reloadAuthFromRoot();
|
|
93
110
|
};
|
|
94
111
|
|
|
95
112
|
/**
|
|
@@ -104,14 +121,14 @@ const loadAndSaveNewPlugin = async (
|
|
|
104
121
|
plugin,
|
|
105
122
|
force,
|
|
106
123
|
noSignalOrDB,
|
|
107
|
-
__ = (str) => str
|
|
124
|
+
__ = (str) => str,
|
|
125
|
+
allowUnsafeOnTenantsWithoutConfigSetting
|
|
108
126
|
) => {
|
|
109
127
|
const tenants_unsafe_plugins = getRootState().getConfig(
|
|
110
128
|
"tenants_unsafe_plugins",
|
|
111
129
|
false
|
|
112
130
|
);
|
|
113
|
-
|
|
114
|
-
if (!isRoot && !tenants_unsafe_plugins) {
|
|
131
|
+
if (!isRoot() && !tenants_unsafe_plugins) {
|
|
115
132
|
if (plugin.source !== "npm") {
|
|
116
133
|
console.error("\nWARNING: Skipping unsafe plugin ", plugin.name);
|
|
117
134
|
return;
|
|
@@ -126,7 +143,10 @@ const loadAndSaveNewPlugin = async (
|
|
|
126
143
|
|
|
127
144
|
const instore = getRootState().getConfig("available_plugins", []);
|
|
128
145
|
const safes = instore.filter((p) => !p.unsafe).map((p) => p.location);
|
|
129
|
-
if (
|
|
146
|
+
if (
|
|
147
|
+
!safes.includes(plugin.location) &&
|
|
148
|
+
!allowUnsafeOnTenantsWithoutConfigSetting
|
|
149
|
+
) {
|
|
130
150
|
console.error("\nWARNING: Skipping unsafe plugin ", plugin.name);
|
|
131
151
|
return;
|
|
132
152
|
}
|
|
@@ -196,6 +216,10 @@ const loadAndSaveNewPlugin = async (
|
|
|
196
216
|
}
|
|
197
217
|
}
|
|
198
218
|
if (version) plugin.version = version;
|
|
219
|
+
|
|
220
|
+
if (isRoot() && plugin_module.authentication)
|
|
221
|
+
await eachTenant(reloadAuthFromRoot);
|
|
222
|
+
|
|
199
223
|
if (!noSignalOrDB) {
|
|
200
224
|
await plugin.upsert();
|
|
201
225
|
getState().processSend({
|
package/locales/en.json
CHANGED
|
@@ -1426,5 +1426,32 @@
|
|
|
1426
1426
|
"Keystore Password": "Keystore Password",
|
|
1427
1427
|
"xcodebuild": "xcodebuild",
|
|
1428
1428
|
"Provisioning Profile": "Provisioning Profile",
|
|
1429
|
-
"Registry editor": "Registry editor"
|
|
1429
|
+
"Registry editor": "Registry editor",
|
|
1430
|
+
"Mobile HTML": "Mobile HTML",
|
|
1431
|
+
"HTML for the item in the bottom navigation bar. Currently, only supported by the metronic theme.": "HTML for the item in the bottom navigation bar. Currently, only supported by the metronic theme.",
|
|
1432
|
+
"A short name that will be in the page URL": "A short name that will be in the page URL",
|
|
1433
|
+
"A longer description that is not visible but appears in the page header and is indexed by search engines": "A longer description that is not visible but appears in the page header and is indexed by search engines",
|
|
1434
|
+
"User role required to access page": "User role required to access page",
|
|
1435
|
+
"Example: <code>`/view/TheOtherView?id=${id}`</code>": "Example: <code>`/view/TheOtherView?id=${id}`</code>",
|
|
1436
|
+
"Older": "Older",
|
|
1437
|
+
"Newest": "Newest",
|
|
1438
|
+
"Delete all read": "Delete all read",
|
|
1439
|
+
"Trigger %s duplicated as %s": "Trigger %s duplicated as %s",
|
|
1440
|
+
"Tooltip": "Tooltip",
|
|
1441
|
+
"Tooltip formula": "Tooltip formula",
|
|
1442
|
+
"Install a different version": "Install a different version",
|
|
1443
|
+
"Page group": "Page group",
|
|
1444
|
+
"Starting upgrade, please wait...\n": "Starting upgrade, please wait...\n",
|
|
1445
|
+
"Upgrade done (if it was available) with code 0.\n\nPress the BACK button in your browser, then RELOAD the page.": "Upgrade done (if it was available) with code 0.\n\nPress the BACK button in your browser, then RELOAD the page.",
|
|
1446
|
+
"Installing %s, please wait...\n": "Installing %s, please wait...\n",
|
|
1447
|
+
"Install done with code 0.\n\nPress the BACK button in your browser, then RELOAD the page.": "Install done with code 0.\n\nPress the BACK button in your browser, then RELOAD the page.",
|
|
1448
|
+
"Pulling the cordova-builder docker image...": "Pulling the cordova-builder docker image...",
|
|
1449
|
+
"Check updates": "Check updates",
|
|
1450
|
+
"Choose version": "Choose version",
|
|
1451
|
+
"Unknown authentication method %s": "Unknown authentication method %s",
|
|
1452
|
+
"Card rows": "Card rows",
|
|
1453
|
+
"Each row in a card. Not supported by all themes": "Each row in a card. Not supported by all themes",
|
|
1454
|
+
"Prune session interval (seconds)": "Prune session interval (seconds)",
|
|
1455
|
+
"Interval in seconds to check for expred sessions in the postgres db. 0, empty or a negative number to disable": "Interval in seconds to check for expred sessions in the postgres db. 0, empty or a negative number to disable",
|
|
1456
|
+
"Progressive Web Application is not enabled": "Progressive Web Application is not enabled"
|
|
1430
1457
|
}
|
package/locales/it.json
CHANGED
|
@@ -518,5 +518,6 @@
|
|
|
518
518
|
"Save before going back": "Save before going back",
|
|
519
519
|
"Reload after going back": "Reload after going back",
|
|
520
520
|
"Steps to go back": "Steps to go back",
|
|
521
|
-
"%s configuration": "%s configuration"
|
|
522
|
-
|
|
521
|
+
"%s configuration": "%s configuration",
|
|
522
|
+
"The current theme has no user specific settings": "The current theme has no user specific settings"
|
|
523
|
+
}
|
package/markup/forms.js
CHANGED
|
@@ -28,10 +28,14 @@ const editRoleForm = ({ url, current_role, roles, req }) =>
|
|
|
28
28
|
{
|
|
29
29
|
action: url,
|
|
30
30
|
method: "post",
|
|
31
|
+
onchange: "saveAndContinue(this)",
|
|
31
32
|
},
|
|
32
33
|
csrfField(req),
|
|
33
34
|
select(
|
|
34
|
-
{
|
|
35
|
+
{
|
|
36
|
+
name: "role",
|
|
37
|
+
class: "w-unset form-select form-select-sm",
|
|
38
|
+
},
|
|
35
39
|
roles.map((role) =>
|
|
36
40
|
option(
|
|
37
41
|
{
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.7-rc.0",
|
|
4
4
|
"description": "Server app for Saltcorn, open-source no-code platform",
|
|
5
5
|
"homepage": "https://saltcorn.com",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@aws-sdk/client-s3": "^3.451.0",
|
|
10
|
-
"@saltcorn/base-plugin": "0.9.
|
|
11
|
-
"@saltcorn/builder": "0.9.
|
|
12
|
-
"@saltcorn/data": "0.9.
|
|
13
|
-
"@saltcorn/admin-models": "0.9.
|
|
14
|
-
"@saltcorn/filemanager": "0.9.
|
|
15
|
-
"@saltcorn/markup": "0.9.
|
|
16
|
-
"@saltcorn/plugins-loader": "0.9.
|
|
17
|
-
"@saltcorn/sbadmin2": "0.9.
|
|
10
|
+
"@saltcorn/base-plugin": "0.9.7-rc.0",
|
|
11
|
+
"@saltcorn/builder": "0.9.7-rc.0",
|
|
12
|
+
"@saltcorn/data": "0.9.7-rc.0",
|
|
13
|
+
"@saltcorn/admin-models": "0.9.7-rc.0",
|
|
14
|
+
"@saltcorn/filemanager": "0.9.7-rc.0",
|
|
15
|
+
"@saltcorn/markup": "0.9.7-rc.0",
|
|
16
|
+
"@saltcorn/plugins-loader": "0.9.7-rc.0",
|
|
17
|
+
"@saltcorn/sbadmin2": "0.9.7-rc.0",
|
|
18
18
|
"@socket.io/cluster-adapter": "^0.2.1",
|
|
19
19
|
"@socket.io/sticky": "^1.0.1",
|
|
20
20
|
"adm-zip": "0.5.10",
|
|
@@ -10,6 +10,9 @@ var logViewerHelpers = (() => {
|
|
|
10
10
|
second: "2-digit",
|
|
11
11
|
};
|
|
12
12
|
let lostConnection = false;
|
|
13
|
+
let waitingForTestMessage = false;
|
|
14
|
+
let startedWaitingAt = null;
|
|
15
|
+
let waitTimeout = false;
|
|
13
16
|
|
|
14
17
|
const logLevelColor = (level) => {
|
|
15
18
|
switch (parseInt(level)) {
|
|
@@ -94,6 +97,17 @@ var logViewerHelpers = (() => {
|
|
|
94
97
|
});
|
|
95
98
|
};
|
|
96
99
|
|
|
100
|
+
const testMsgWaiter = (waiterStartedAt) => () => {
|
|
101
|
+
if (waitingForTestMessage && waiterStartedAt === startedWaitingAt) {
|
|
102
|
+
emptyAlerts();
|
|
103
|
+
waitTimeout = true;
|
|
104
|
+
notifyAlert({
|
|
105
|
+
type: "danger",
|
|
106
|
+
text: "You are connected but not receiving any messages",
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
97
111
|
const handleConnect = (socket) => {
|
|
98
112
|
socket.emit("join_log_room", (ack) => {
|
|
99
113
|
if (ack) {
|
|
@@ -109,6 +123,10 @@ var logViewerHelpers = (() => {
|
|
|
109
123
|
text: "You are connected again",
|
|
110
124
|
});
|
|
111
125
|
}
|
|
126
|
+
waitingForTestMessage = true;
|
|
127
|
+
waitTimeout = false;
|
|
128
|
+
startedWaitingAt = new Date().valueOf();
|
|
129
|
+
setTimeout(testMsgWaiter(startedWaitingAt), 5000);
|
|
112
130
|
} else if (ack.status === "error" && ack.msg) {
|
|
113
131
|
notifyAlert({
|
|
114
132
|
type: "danger",
|
|
@@ -129,6 +147,19 @@ var logViewerHelpers = (() => {
|
|
|
129
147
|
});
|
|
130
148
|
};
|
|
131
149
|
|
|
150
|
+
const handleTestConnMsg = () => {
|
|
151
|
+
waitingForTestMessage = false;
|
|
152
|
+
startedWaitingAt = null;
|
|
153
|
+
if (waitTimeout) {
|
|
154
|
+
emptyAlerts();
|
|
155
|
+
notifyAlert({
|
|
156
|
+
type: "success",
|
|
157
|
+
text: "You are connected and receiving messages",
|
|
158
|
+
});
|
|
159
|
+
waitTimeout = false;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
132
163
|
return {
|
|
133
164
|
init_log_socket: () => {
|
|
134
165
|
let socket = null;
|
|
@@ -152,6 +183,7 @@ var logViewerHelpers = (() => {
|
|
|
152
183
|
socket.on("connect", () => handleConnect(socket));
|
|
153
184
|
socket.on("disconnect", handleDisconnect);
|
|
154
185
|
socket.on("log_msg", handleLogMsg);
|
|
186
|
+
socket.on("test_conn_msg", handleTestConnMsg);
|
|
155
187
|
},
|
|
156
188
|
goToLogsPage: (n) => {
|
|
157
189
|
currentPage = n;
|