@saltcorn/server 0.9.6-beta.17 → 0.9.6-beta.19

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 CHANGED
@@ -50,6 +50,7 @@ const locales = Object.keys(available_languages);
50
50
  const i18n = new I18n({
51
51
  locales,
52
52
  directory: path.join(__dirname, "locales"),
53
+ mustacheConfig: { disable: true },
53
54
  });
54
55
  // jwt config
55
56
  const jwt_secret = db.connectObj.jwt_secret;
@@ -171,9 +172,13 @@ const getApp = async (opts = {}) => {
171
172
  const tenants = await getAllTenants();
172
173
  await init_multi_tenant(loadAllPlugins, opts.disableMigrate, tenants);
173
174
  }
175
+ const pruneSessionInterval = +getState().getConfig(
176
+ "prune_session_interval",
177
+ 900
178
+ );
174
179
  //
175
180
  // todo ability to configure session_secret Age
176
- app.use(getSessionStore());
181
+ app.use(getSessionStore(pruneSessionInterval));
177
182
 
178
183
  app.use(passport.initialize());
179
184
  app.use(passport.authenticate(["jwt", "session"]));
package/auth/admin.js CHANGED
@@ -388,6 +388,7 @@ const http_settings_form = async (req) =>
388
388
  "cross_domain_iframe",
389
389
  "body_limit",
390
390
  "url_encoded_limit",
391
+ ...(!db.isSQLite ? ["prune_session_interval"] : []),
391
392
  ],
392
393
  action: "/useradmin/http",
393
394
  submitLabel: req.__("Save"),
@@ -0,0 +1,11 @@
1
+ A table holds data organised by rows and columns, which you can visualise in a
2
+ two-dimensional grid. The rows hold the different cases, about which you have similar data.
3
+ Each column is called a Field, and every field must have a label (and a type).
4
+
5
+ The field label allows you to recognize the meaning of the values in the column in a
6
+ human-readable name. In a spreadsheet with a lot of data, it is often used as the header for
7
+ each column.
8
+
9
+ In Saltcorn, you can use spaces in field labels. Because we also need a valid identifier to use
10
+ in formulae, a variable name is generated from the label by replacing spaces with underscores and
11
+ converting all uppercase characters to lowercase.
@@ -0,0 +1,39 @@
1
+ Every field in a table stores values of a specific type. The type limits which
2
+ values this field can take in each row. For instance, if a field has a type `Integer`
3
+ then it cannot take the value `"Simon"` but the value `17` is admissible.
4
+
5
+ When building your table, you must therefore think carefully about not only which fields
6
+ to include but also what the types are. Sometimes this is easy - you know that names
7
+ should be stored as as `String` type. But at other times you need to think ahead a bit
8
+ and think carefully about what data your table will hold
9
+
10
+ Some types are more general than others. The `String` type and the `JSON`
11
+ type (from the json module) can hold values that can be represented by more specific types.
12
+ For instance the number 17 can be stored in a string field where it will be represented by the
13
+ string `"17"`. You should always try to use the most specific type available. This will give
14
+ you access to user interface elements that are richer and more appropriate for the data in the
15
+ given field. For example, you could use `String` to store dates, but then you will not
16
+ be able to use an interactive date picker or to display dates in flexible formats.
17
+
18
+ Most field types have some additional parameters that will be configured on the next screen.
19
+ `Integer` types have minimum and maximum values and strings can be restricted to a set of
20
+ options or by a regular expression. This allows you to further narrow what data it is admissible
21
+ in your table.
22
+
23
+ These types are available in your current installation:
24
+
25
+ {{# const tys = Object.values(scState.types) }}
26
+ {{# for (const ty of tys) { }}
27
+ * {{ ty.name }}{{ty.description ? `: ${ty.description}` : ""}}
28
+ {{# } }}
29
+
30
+ Further types can be installed from moduels in the [Module store](/plugins)
31
+
32
+ In addition, a field can have types File and Key to a table.
33
+
34
+ A File field holds a reference to a file in the Saltcorn file system. This is stored by the,
35
+ path to the file and if the file moves, the reference may become invalid.
36
+
37
+ A field that is a Key to another table is a reference to a row in that table. This is also known
38
+ as a foreign key and is used to link data between tables and to create the relational structure
39
+ of your application.
@@ -0,0 +1,76 @@
1
+ The ability to read or write to tables is normally limited by the settings
2
+ for "Minimum role to read" and "Minimum role to write", which is compared
3
+ to the user's role.
4
+
5
+ In some cases you want some users wo be able to read and write to some but
6
+ not to all rows. Some examples are:
7
+
8
+ * On a blog, Users should be able to create comments and to edit comments they have made.
9
+ But you do not want users to be able to edit another user's comments.
10
+
11
+ * In a todo list, Users should be able to create and edit new items for themselves but
12
+ they should not be able to read or edit items for other users.
13
+
14
+ * In a project management app, you may only want you supposed to be able to see and
15
+ contribute to projects they have been assigned to.
16
+
17
+ Saltcorn contains an authorization system that can be very simple (limit everything by role),
18
+ more flexible (rows have a user field and if you are that user, you can edit a row)
19
+ to very complex (featuring many-to-many relationships, where the user field can be on a
20
+ different table; and inheritance, where authorization schemes propagate through relationship).
21
+
22
+ ### Role-based authorization
23
+
24
+ In Saltcorn, every user has a role and the roles have a strictly hierachical ordering,
25
+ which you [can edit](/roleadmin). By
26
+ using the "Minimum role to read" and "Minimum role to write" settings for the table, you
27
+ can create a role cutoff limit for access. See the help topics for those settings for details.
28
+
29
+ ### Simple user field ownership
30
+
31
+ In the simplest deviation from role-based authorization, you can grant access to edit
32
+ a row to users that match a Key to users field on the row. To use this, you should:
33
+
34
+ 1. Set the "Minimum role to read" and "Minimum role to write" to a role that would stop the
35
+ user from accessing the row.
36
+
37
+ 2. Create a field with type Key to users. This field should ideally not be labelled `User` or `user`,
38
+ as this variable name will clash with access to logged-in user object in formulae.
39
+
40
+ 3. Make sure this is filled in when the user creates the row.
41
+ For instance in an Edit view under the "Fixed and blocked fields" settings, in the
42
+ Preset for this field pick the LoggedIn preset.
43
+
44
+ 4. Pick this field as the Ownership field from the dropdown in the table settings.
45
+
46
+ This is an additional access grant
47
+ in addition to that given by the minimum roles to read and write. If your user does *not*
48
+ match the designated field, The decision to grant access reverts to the role-based settings.
49
+ You therefore cannot use ownership to limit access, only to grant additional access.
50
+
51
+ ### Authorization by inheritance
52
+
53
+ If the table has a relationship (that is, has a field with Key type) with another table which
54
+ has an ownership field (or ownership formula), it can instead inherit the onwership from that
55
+ table - essentially, take the ownership of the row the Key is pointing to.
56
+
57
+ For instance, you have a project management app with a Projects table that has an `owner` Key to users
58
+ field and this is set as the ownership field; and a Tasks table with a Key to Projects field.
59
+ In this case "Inherit Projects" is available as an option in the Ownership field dropdown.
60
+ If you pick it and reload the page, you will See that it is in fact implemented as an ownership
61
+ formula which is created for you.
62
+
63
+ ### Authorization by user groups
64
+
65
+ If you need to grant access to tables based not on user fields on this table (ownership field)
66
+ or on tables it has keys to (inheritance), you can declare a table to be a user group.
67
+ The user group should be a table that has a key to users, and may also have a key to this table.
68
+ Use this to grant ownership rights to a row to more than one user. For instance, if more than one
69
+ user is working on a project, you can declare that all users assigned to this project are owners. See
70
+ the help topic for the User group option.
71
+
72
+ ### Authorisation by formula
73
+
74
+ You can also grant access to edit the row if an arbitrary formula is true. This can be used
75
+ For very flexible authorisation schemes. Choose formula in the ownership field drop down and then see
76
+ help for the Ownership formula.
@@ -0,0 +1,75 @@
1
+ An ownership formula on a table allows you to develop an extremely flexible
2
+ Authorisation scheme. It is also the mechanism by which ownership inheritance
3
+ and ownership by user groups is implemented. If you pick one of these options
4
+ in the Ownership field drop-down it will essentially generate the corresponding
5
+ formula for you.
6
+
7
+ The ownership formula is a JavaScript expression which should evaluate to a boolean
8
+ (true/false) value. If it evaluate to true (or a [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) value), then the logged-in user is an owner and
9
+ can read and write the row. If it evaluate to false (or a [falsey](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) value),
10
+ the user is not the owner. However, If they have a role equal or higher than that
11
+ required to read or write, they can still perform this operation. You cannot use the
12
+ ownership formula to deny access to a user who satisfies the role condition.
13
+
14
+ When evaluating the formula, the values in scope are as follows:
15
+
16
+ #### Row fields
17
+
18
+ All field values for the row being evaluated are in scope and can be addressed by
19
+ their variable name.
20
+
21
+ #### Join fields on row keys
22
+
23
+ For any fields on the current table that are Keys to another table, you can access the values
24
+ in the linked table by using the dot notation. For instance, if you have a field labelled
25
+ "Project" (variable name `project`) which is of type Key to Projects, and the Projects
26
+ table has a `name` field, you can refer to this as `project.name`. However, if the Project field
27
+ is not required, this can trigger an error as accessing a subfield on a `null` variable is a
28
+ JavaScript error. In that case, you can use [optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining)
29
+ (`project?.name`) which will evaluate to `null` if `project` is `null`.
30
+
31
+ #### User object
32
+
33
+ You can refer to the logged in user using the variable name user.
34
+ This is an object and you must use the dot to access user fields,
35
+ e.g. user.id for the logged in users id.
36
+
37
+ The user object has other fields than the primary key identifier.
38
+ For your login session the user object is:
39
+
40
+ ```
41
+ {{JSON.stringify(user, null, 2)}}
42
+ ```
43
+
44
+ #### User groups
45
+
46
+ If any tables have been designated as user groups (that is, if they User group option has been
47
+ checked in the table settings) then a value will appear in the user object, which is the list
48
+ of an array of user group rows to which the user belongs. The name of that field is
49
+ `{table name}_by_{user field in user group}`.
50
+
51
+ Normally you don't have to write theownership formula, you select it from the Ownership field
52
+ drop-down and the formula is generated for you.
53
+
54
+ When changes are made to user group membership, the user needs to login and log out again before these
55
+ changes are reflected in the user object. If you are removing user group membership you may need to force
56
+ log out those users.
57
+
58
+ ## Examples
59
+
60
+ User table has a `String` field named `department` which can take options "Finance", "HR",
61
+ or "Warp drive engineering". To give ownership to the Expenses table to users in the Finance
62
+ department:
63
+
64
+ `user.department === "Finance"`
65
+
66
+ Same as above, but the Expenses table also has a `filled_by` field which is Key to user, and
67
+ you want to give ownership to that user or any user in the Finance department:
68
+
69
+ `user.department === "Finance" || user.id === filled_by`
70
+
71
+ Similar to above, however department membership is not stored as User field but as in a table
72
+ named "User In Department" with a Key to user field labelled "Employee" and a string field "Name" for
73
+ the department name.
74
+
75
+ `user.UserinDepartment_by_employee.map(d=>d.name).includes("Finance") || user.id === filled_by`
@@ -0,0 +1,20 @@
1
+ You can restrict which users can read or write to this table by their role.
2
+
3
+ Each user in Saltcorn has a role and the roles have a strictly hierachical ordering,
4
+ which you [can edit](/roleadmin). The ordering means that users in a role can access
5
+ everything the users in the role "below" then can acceess, but the users in the role
6
+ "above" have further access.
7
+
8
+ Assigning access by role is a quick way to give users more or less access based on how
9
+ much you trust them.
10
+
11
+ Using the settings for "Minimum role to read" and "Minimum role to write" you set the roles
12
+ required to read and write to the table, respectively. Users also need to have the roles
13
+ required for running views and pages.
14
+
15
+ Restricting table access by role is the simplest form of authorisation in Saltcorn,
16
+ but it is often too limited. Row ownership is much more flexible; see the help topic for
17
+ Ownership field.
18
+
19
+ Note that if the user has ownership of the row, they can read and write that row even if
20
+ they have a role below the minimum role to read and write, respectively.
@@ -0,0 +1,35 @@
1
+ User groups are used to grant access to tables based on membership of groups
2
+ where each user can be a member of many groups and each group can have many
3
+ users.
4
+
5
+ Any table that has a field of type Key to User can be designated a user group, by
6
+ checking the User group option. This means that when a User group table has a row with
7
+ a key to a user, that user is a member of a group.
8
+
9
+ The consequence of designating a table as a User group is that if there is also a Key from
10
+ the user group table to another table, then the option of group membership appears in the
11
+ drop-down for the Ownership field option.
12
+
13
+ In addition, If a table is designated as a user group. a value indicating group membership
14
+ appears in the user object. For this to appear, the variable has to be referenced in an
15
+ ownership formula. The name of this variable is
16
+ `{user group table name}_by_{key to user field name}`.
17
+
18
+ When changes are made to user group membership, the user needs to login and log out again before these
19
+ changes are reflected in the user object. If you are removing user group membership you may need to force
20
+ log out those users.
21
+
22
+ ### Example
23
+
24
+ In a project management application you have a "Projects" table and a "Tasks" table (with a
25
+ Key to project). Several people can work on the same project.
26
+
27
+ You would like to restrict access to the project table such that only uses to work on the project have access.
28
+
29
+ 1. Create a "User Works On Project" table with a Key to projects field and a Key to user field called Participant.
30
+
31
+ 2. Designate the "User Works On Project" table as a user group by checking the user group box.
32
+
33
+ 3. In the project table settings, In the ownership field, there is now an option called "In User Works On Project by Participant", pick this option.
34
+
35
+ 4. Now add rows to the "User Works On Project" table. When the users logout and login again they will have the required access.
package/locales/en.json CHANGED
@@ -1448,5 +1448,10 @@
1448
1448
  "Pulling the cordova-builder docker image...": "Pulling the cordova-builder docker image...",
1449
1449
  "Check updates": "Check updates",
1450
1450
  "Choose version": "Choose version",
1451
- "Unknown authentication method %s": "Unknown authentication method %s"
1452
- }
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"
1457
+ }
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@saltcorn/server",
3
- "version": "0.9.6-beta.17",
3
+ "version": "0.9.6-beta.19",
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.6-beta.17",
11
- "@saltcorn/builder": "0.9.6-beta.17",
12
- "@saltcorn/data": "0.9.6-beta.17",
13
- "@saltcorn/admin-models": "0.9.6-beta.17",
14
- "@saltcorn/filemanager": "0.9.6-beta.17",
15
- "@saltcorn/markup": "0.9.6-beta.17",
16
- "@saltcorn/plugins-loader": "0.9.6-beta.17",
17
- "@saltcorn/sbadmin2": "0.9.6-beta.17",
10
+ "@saltcorn/base-plugin": "0.9.6-beta.19",
11
+ "@saltcorn/builder": "0.9.6-beta.19",
12
+ "@saltcorn/data": "0.9.6-beta.19",
13
+ "@saltcorn/admin-models": "0.9.6-beta.19",
14
+ "@saltcorn/filemanager": "0.9.6-beta.19",
15
+ "@saltcorn/markup": "0.9.6-beta.19",
16
+ "@saltcorn/plugins-loader": "0.9.6-beta.19",
17
+ "@saltcorn/sbadmin2": "0.9.6-beta.19",
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;