expressa 2.0.18 → 2.0.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.
Files changed (281) hide show
  1. package/.circleci/config.yml +21 -21
  2. package/.eslintrc.json +29 -29
  3. package/LICENSE +21 -21
  4. package/README.md +137 -137
  5. package/auth/index.js +76 -76
  6. package/auth/jwt.js +31 -31
  7. package/controllers/collections.js +260 -260
  8. package/controllers/install.js +105 -105
  9. package/controllers/status.js +43 -43
  10. package/controllers/users.js +52 -52
  11. package/cypress/fixtures/example.json +4 -4
  12. package/cypress/integration/0_install_spec.js +8 -8
  13. package/cypress/integration/custom_collections.js +28 -28
  14. package/cypress/integration/home.js +9 -9
  15. package/cypress/integration/settings.js +34 -34
  16. package/cypress/integration/users.js +61 -61
  17. package/cypress/plugins/index.js +17 -17
  18. package/cypress/support/commands.js +54 -54
  19. package/cypress/support/index.js +20 -20
  20. package/cypress/support/util.js +10 -10
  21. package/cypress.json +4 -4
  22. package/db/cached.js +40 -40
  23. package/db/file.js +112 -112
  24. package/db/memory.js +79 -79
  25. package/db/mongo.js +86 -86
  26. package/db/postgres.js +93 -93
  27. package/doc/authentication.md +38 -38
  28. package/doc/automatic-fields.md +12 -12
  29. package/doc/blogexample.md +16 -16
  30. package/doc/custom-endpoints.md +20 -20
  31. package/doc/database.md +23 -23
  32. package/doc/development.md +33 -33
  33. package/doc/listeners.md +103 -103
  34. package/doc/modules.md +8 -8
  35. package/doc/permissions.md +14 -14
  36. package/doc/relationships.md +65 -65
  37. package/doc/testing.md +47 -47
  38. package/doc/uploading-files.md +53 -53
  39. package/index.js +297 -297
  40. package/listeners.js +65 -65
  41. package/listeners_collection_permissions.js +29 -29
  42. package/listeners_users.js +96 -96
  43. package/listeners_validation.js +43 -43
  44. package/middleware/logging.js +30 -30
  45. package/middleware/permissions.js +41 -41
  46. package/modules/access_keys/access_keys.js +32 -32
  47. package/modules/admin/.babelrc +19 -19
  48. package/modules/admin/.editorconfig +14 -14
  49. package/modules/admin/.eslintignore +5 -5
  50. package/modules/admin/.eslintrc.js +197 -197
  51. package/modules/admin/.postcssrc.js +10 -10
  52. package/modules/admin/.travis.yml +5 -5
  53. package/modules/admin/LICENSE +21 -21
  54. package/modules/admin/README.md +88 -88
  55. package/modules/admin/build/build.js +45 -45
  56. package/modules/admin/build/check-versions.js +64 -64
  57. package/modules/admin/build/utils.js +108 -108
  58. package/modules/admin/build/vue-loader.conf.js +5 -5
  59. package/modules/admin/build/webpack.base.conf.js +112 -112
  60. package/modules/admin/build/webpack.dev.conf.js +95 -95
  61. package/modules/admin/build/webpack.prod.conf.js +112 -112
  62. package/modules/admin/config/dev.env.js +8 -8
  63. package/modules/admin/config/index.js +86 -86
  64. package/modules/admin/config/prod.env.js +5 -5
  65. package/modules/admin/dist/164c6167c1f3c497c07c.woff +1 -0
  66. package/modules/admin/dist/42ac07abc8473600c41f.ttf +1 -0
  67. package/modules/admin/dist/86dccb18a7eb4be8ef1a.woff +1 -0
  68. package/modules/admin/dist/c45297c558e0318fc9c5.ttf +1 -0
  69. package/modules/admin/dist/c717c9930715e24fa12c.woff +1 -0
  70. package/modules/admin/dist/ca6f07ef23f24cc2738a.ttf +1 -0
  71. package/modules/admin/dist/css/269.f7d5c7d5.css +37 -0
  72. package/modules/admin/dist/css/269.f7d5c7d5.css.gz +0 -0
  73. package/modules/admin/dist/css/451.c6646d20.css +1 -0
  74. package/modules/admin/dist/css/451.c6646d20.css.gz +0 -0
  75. package/modules/admin/dist/css/572.74d8e965.css +34 -0
  76. package/modules/admin/dist/css/572.74d8e965.css.gz +0 -0
  77. package/modules/admin/dist/css/651.e946bda7.css +6 -0
  78. package/modules/admin/dist/css/651.e946bda7.css.gz +0 -0
  79. package/modules/admin/dist/css/699.d1f90954.css +8 -0
  80. package/modules/admin/dist/css/699.d1f90954.css.gz +0 -0
  81. package/modules/admin/dist/css/750.4086915d.css +2 -0
  82. package/modules/admin/dist/css/750.4086915d.css.gz +0 -0
  83. package/modules/admin/dist/css/864.506917a4.css +7 -0
  84. package/modules/admin/dist/css/864.506917a4.css.gz +0 -0
  85. package/modules/admin/dist/css/899.506917a4.css +7 -0
  86. package/modules/admin/dist/css/899.506917a4.css.gz +0 -0
  87. package/modules/admin/dist/css/950.1fd9d57c.css +7 -0
  88. package/modules/admin/dist/css/950.1fd9d57c.css.gz +0 -0
  89. package/modules/admin/dist/css/app.11cc6245.css +39 -0
  90. package/modules/admin/dist/css/app.11cc6245.css.gz +0 -0
  91. package/modules/admin/dist/css/app.2e1b0d79.css +591 -0
  92. package/modules/admin/dist/css/app.2e1b0d79.css.gz +0 -0
  93. package/modules/admin/dist/css/app.4d43d0fd.css +592 -0
  94. package/modules/admin/dist/css/app.4d43d0fd.css.gz +0 -0
  95. package/modules/admin/dist/css/app.abace109.css +1 -0
  96. package/modules/admin/dist/css/app.abace109.css.gz +0 -0
  97. package/modules/admin/dist/css/chunk-elementUI.67ad86d7.css +1 -0
  98. package/modules/admin/dist/css/chunk-elementUI.67ad86d7.css.gz +0 -0
  99. package/modules/admin/dist/css/chunk-elementUI.9e660bfd.css +1 -0
  100. package/modules/admin/dist/css/chunk-elementUI.9e660bfd.css.gz +0 -0
  101. package/modules/admin/dist/css/chunk-elementUI.c77cf142.css +1 -0
  102. package/modules/admin/dist/css/chunk-elementUI.c77cf142.css.gz +0 -0
  103. package/modules/admin/dist/css/chunk-elementUI.f1625b35.css +1 -0
  104. package/modules/admin/dist/css/chunk-elementUI.f1625b35.css.gz +0 -0
  105. package/modules/admin/dist/css/chunk-elementUI.f25dfb9b.css +1 -0
  106. package/modules/admin/dist/css/chunk-elementUI.f25dfb9b.css.gz +0 -0
  107. package/modules/admin/dist/css/chunk-libs.de0d8484.css +551 -0
  108. package/modules/admin/dist/css/chunk-libs.de0d8484.css.gz +0 -0
  109. package/modules/admin/dist/index.html +1 -1
  110. package/modules/admin/dist/static/fonts/element-icons.313f7da.woff +0 -0
  111. package/modules/admin/dist/static/fonts/element-icons.4520188.ttf +0 -0
  112. package/modules/admin/dist/static/js/269.7245ceaf.js +1 -0
  113. package/modules/admin/dist/static/js/269.7245ceaf.js.gz +0 -0
  114. package/modules/admin/dist/static/js/451.645974a8.js +1 -0
  115. package/modules/admin/dist/static/js/451.645974a8.js.gz +0 -0
  116. package/modules/admin/dist/static/js/460.962d5bc0.js +1 -0
  117. package/modules/admin/dist/static/js/460.962d5bc0.js.gz +0 -0
  118. package/modules/admin/dist/static/js/556.1e08866a.js +1 -0
  119. package/modules/admin/dist/static/js/556.1e08866a.js.gz +0 -0
  120. package/modules/admin/dist/static/js/572.13392e1d.js +1 -0
  121. package/modules/admin/dist/static/js/572.13392e1d.js.gz +0 -0
  122. package/modules/admin/dist/static/js/603.7b34a650.js +1 -0
  123. package/modules/admin/dist/static/js/603.7b34a650.js.gz +0 -0
  124. package/modules/admin/dist/static/js/637.26413baa.js +1 -0
  125. package/modules/admin/dist/static/js/637.26413baa.js.gz +0 -0
  126. package/modules/admin/dist/static/js/651.564b24b6.js +1 -0
  127. package/modules/admin/dist/static/js/651.564b24b6.js.gz +0 -0
  128. package/modules/admin/dist/static/js/653.02ec1098.js +1 -0
  129. package/modules/admin/dist/static/js/653.02ec1098.js.gz +0 -0
  130. package/modules/admin/dist/static/js/699.c7564884.js +1 -0
  131. package/modules/admin/dist/static/js/699.c7564884.js.gz +0 -0
  132. package/modules/admin/dist/static/js/750.50c950da.js +1 -0
  133. package/modules/admin/dist/static/js/750.50c950da.js.gz +0 -0
  134. package/modules/admin/dist/static/js/864.4f43847d.js +1 -0
  135. package/modules/admin/dist/static/js/864.4f43847d.js.gz +0 -0
  136. package/modules/admin/dist/static/js/950.d4ff0a18.js +1 -0
  137. package/modules/admin/dist/static/js/950.d4ff0a18.js.gz +0 -0
  138. package/modules/admin/dist/static/js/app.981e71b3.js +2 -0
  139. package/modules/admin/dist/static/js/{chunk-libs.06c38828.js.LICENSE.txt → app.981e71b3.js.LICENSE.txt} +15 -18
  140. package/modules/admin/dist/static/js/app.981e71b3.js.LICENSE.txt.gz +0 -0
  141. package/modules/admin/dist/static/js/app.981e71b3.js.gz +0 -0
  142. package/modules/admin/index.html +16 -16
  143. package/modules/admin/package.json +101 -101
  144. package/modules/admin/src/App.vue +11 -11
  145. package/modules/admin/src/api/login.js +26 -26
  146. package/modules/admin/src/api/table.js +9 -9
  147. package/modules/admin/src/components/Breadcrumb/index.vue +79 -79
  148. package/modules/admin/src/components/Hamburger/index.vue +62 -62
  149. package/modules/admin/src/components/JSONEditor.vue +180 -180
  150. package/modules/admin/src/components/SvgIcon/index.vue +43 -43
  151. package/modules/admin/src/icons/index.js +9 -9
  152. package/modules/admin/src/icons/svgo.yml +22 -22
  153. package/modules/admin/src/main.js +42 -42
  154. package/modules/admin/src/permission.js +49 -49
  155. package/modules/admin/src/router/index.js +200 -200
  156. package/modules/admin/src/store/getters.js +11 -11
  157. package/modules/admin/src/store/index.js +17 -17
  158. package/modules/admin/src/store/modules/app.js +43 -43
  159. package/modules/admin/src/store/modules/user.js +104 -104
  160. package/modules/admin/src/styles/element-ui.scss +29 -29
  161. package/modules/admin/src/styles/index.scss +78 -78
  162. package/modules/admin/src/styles/mixin.scss +27 -27
  163. package/modules/admin/src/styles/sidebar.scss +133 -133
  164. package/modules/admin/src/styles/transition.scss +46 -46
  165. package/modules/admin/src/styles/variables.scss +4 -4
  166. package/modules/admin/src/utils/auth.js +15 -15
  167. package/modules/admin/src/utils/index.js +74 -74
  168. package/modules/admin/src/utils/request.js +48 -48
  169. package/modules/admin/src/utils/validate.js +31 -31
  170. package/modules/admin/src/views/404.vue +235 -235
  171. package/modules/admin/src/views/Dev.vue +33 -33
  172. package/modules/admin/src/views/EditDocument.vue +116 -116
  173. package/modules/admin/src/views/Endpoints.vue +279 -279
  174. package/modules/admin/src/views/Home.vue +84 -84
  175. package/modules/admin/src/views/Install.vue +79 -79
  176. package/modules/admin/src/views/ListDocuments.vue +72 -72
  177. package/modules/admin/src/views/ListDocuments2.vue +462 -462
  178. package/modules/admin/src/views/ManageListeners.vue +67 -67
  179. package/modules/admin/src/views/ManageMiddleware.vue +43 -43
  180. package/modules/admin/src/views/ManagePermissions.vue +75 -75
  181. package/modules/admin/src/views/ViewRequest.vue +77 -77
  182. package/modules/admin/src/views/form/index.vue +91 -91
  183. package/modules/admin/src/views/layout/Layout.vue +69 -69
  184. package/modules/admin/src/views/layout/components/AppMain.vue +41 -41
  185. package/modules/admin/src/views/layout/components/Navbar.vue +97 -97
  186. package/modules/admin/src/views/layout/components/Sidebar/Item.vue +29 -29
  187. package/modules/admin/src/views/layout/components/Sidebar/Link.vue +39 -39
  188. package/modules/admin/src/views/layout/components/Sidebar/SidebarItem.vue +103 -103
  189. package/modules/admin/src/views/layout/components/Sidebar/index.vue +62 -62
  190. package/modules/admin/src/views/layout/components/index.js +3 -3
  191. package/modules/admin/src/views/layout/mixin/ResizeHandler.js +41 -41
  192. package/modules/admin/src/views/login/index.vue +195 -195
  193. package/modules/admin/src/views/tree/index.vue +77 -77
  194. package/modules/collections/collections.js +39 -39
  195. package/modules/core/core.js +197 -197
  196. package/modules/logging/logging.js +88 -88
  197. package/modules/permissions/permissions.js +106 -106
  198. package/package.json +59 -59
  199. package/scripts/expressa +12 -12
  200. package/scripts/run_cypress_tests.sh +5 -5
  201. package/scripts/run_db_tests.sh +8 -8
  202. package/test/0-install.js +96 -96
  203. package/test/collections.js +431 -431
  204. package/test/collections.querying.js +288 -288
  205. package/test/db.js +258 -258
  206. package/test/db.strings.js +73 -73
  207. package/test/db.updating.js +144 -144
  208. package/test/logging.js +29 -29
  209. package/test/test.js +93 -93
  210. package/test/testserver.js +17 -17
  211. package/test/testutils.js +136 -136
  212. package/test/users.js +425 -425
  213. package/util.js +522 -522
  214. package/modules/admin/dist/index.html.gz +0 -0
  215. package/modules/admin/dist/static/css/app.9dd57eea.css +0 -1
  216. package/modules/admin/dist/static/css/app.9dd57eea.css.gz +0 -0
  217. package/modules/admin/dist/static/css/chunk-5061.ce0e4bdb.css +0 -1
  218. package/modules/admin/dist/static/css/chunk-5061.ce0e4bdb.css.gz +0 -0
  219. package/modules/admin/dist/static/css/chunk-5b97.7f9cb53e.css +0 -1
  220. package/modules/admin/dist/static/css/chunk-5b97.7f9cb53e.css.gz +0 -0
  221. package/modules/admin/dist/static/css/chunk-621e.b826be8d.css +0 -1
  222. package/modules/admin/dist/static/css/chunk-621e.b826be8d.css.gz +0 -0
  223. package/modules/admin/dist/static/css/chunk-656b.9c828b00.css +0 -1
  224. package/modules/admin/dist/static/css/chunk-656b.9c828b00.css.gz +0 -0
  225. package/modules/admin/dist/static/css/chunk-7291.13514e45.css +0 -0
  226. package/modules/admin/dist/static/css/chunk-7291.13514e45.css.gz +0 -0
  227. package/modules/admin/dist/static/css/chunk-7f29.9c828b00.css +0 -1
  228. package/modules/admin/dist/static/css/chunk-7f29.9c828b00.css.gz +0 -0
  229. package/modules/admin/dist/static/css/chunk-9a95.c647a2ef.css +0 -1
  230. package/modules/admin/dist/static/css/chunk-9a95.c647a2ef.css.gz +0 -0
  231. package/modules/admin/dist/static/css/chunk-b423.5f239333.css +0 -0
  232. package/modules/admin/dist/static/css/chunk-b423.5f239333.css.gz +0 -0
  233. package/modules/admin/dist/static/css/chunk-bd5f.aefa49d7.css +0 -1
  234. package/modules/admin/dist/static/css/chunk-bd5f.aefa49d7.css.gz +0 -0
  235. package/modules/admin/dist/static/css/chunk-e374.025d58bb.css +0 -1
  236. package/modules/admin/dist/static/css/chunk-e374.025d58bb.css.gz +0 -0
  237. package/modules/admin/dist/static/css/chunk-elementUI.3d004d9f.css +0 -1
  238. package/modules/admin/dist/static/css/chunk-elementUI.3d004d9f.css.gz +0 -0
  239. package/modules/admin/dist/static/css/chunk-f075.3a895a1e.css +0 -1
  240. package/modules/admin/dist/static/css/chunk-f075.3a895a1e.css.gz +0 -0
  241. package/modules/admin/dist/static/css/chunk-libs.327ad89e.css +0 -14
  242. package/modules/admin/dist/static/css/chunk-libs.327ad89e.css.gz +0 -0
  243. package/modules/admin/dist/static/fonts/element-icons.6f0a763.ttf +0 -0
  244. package/modules/admin/dist/static/js/app.e9461593.js +0 -1
  245. package/modules/admin/dist/static/js/app.e9461593.js.gz +0 -0
  246. package/modules/admin/dist/static/js/chunk-09d8.561880e0.js +0 -2
  247. package/modules/admin/dist/static/js/chunk-09d8.561880e0.js.LICENSE.txt +0 -13
  248. package/modules/admin/dist/static/js/chunk-09d8.561880e0.js.LICENSE.txt.gz +0 -0
  249. package/modules/admin/dist/static/js/chunk-09d8.561880e0.js.gz +0 -0
  250. package/modules/admin/dist/static/js/chunk-5061.83e6ccfa.js +0 -1
  251. package/modules/admin/dist/static/js/chunk-5061.83e6ccfa.js.gz +0 -0
  252. package/modules/admin/dist/static/js/chunk-5b97.ac360086.js +0 -1
  253. package/modules/admin/dist/static/js/chunk-5b97.ac360086.js.gz +0 -0
  254. package/modules/admin/dist/static/js/chunk-5e17.a2b69611.js +0 -1
  255. package/modules/admin/dist/static/js/chunk-5e17.a2b69611.js.gz +0 -0
  256. package/modules/admin/dist/static/js/chunk-621e.ccf42393.js +0 -1
  257. package/modules/admin/dist/static/js/chunk-621e.ccf42393.js.gz +0 -0
  258. package/modules/admin/dist/static/js/chunk-656b.9d5b03c0.js +0 -1
  259. package/modules/admin/dist/static/js/chunk-656b.9d5b03c0.js.gz +0 -0
  260. package/modules/admin/dist/static/js/chunk-7291.590524e9.js +0 -1
  261. package/modules/admin/dist/static/js/chunk-7291.590524e9.js.gz +0 -0
  262. package/modules/admin/dist/static/js/chunk-7f29.6ffc142e.js +0 -1
  263. package/modules/admin/dist/static/js/chunk-7f29.6ffc142e.js.gz +0 -0
  264. package/modules/admin/dist/static/js/chunk-9a95.bdd76583.js +0 -1
  265. package/modules/admin/dist/static/js/chunk-9a95.bdd76583.js.gz +0 -0
  266. package/modules/admin/dist/static/js/chunk-b423.387c504c.js +0 -1
  267. package/modules/admin/dist/static/js/chunk-b423.387c504c.js.gz +0 -0
  268. package/modules/admin/dist/static/js/chunk-bd5f.e9509f10.js +0 -1
  269. package/modules/admin/dist/static/js/chunk-bd5f.e9509f10.js.gz +0 -0
  270. package/modules/admin/dist/static/js/chunk-e374.7c87a0dd.js +0 -1
  271. package/modules/admin/dist/static/js/chunk-e374.7c87a0dd.js.gz +0 -0
  272. package/modules/admin/dist/static/js/chunk-e7c3.e5df8ceb.js +0 -1
  273. package/modules/admin/dist/static/js/chunk-e7c3.e5df8ceb.js.gz +0 -0
  274. package/modules/admin/dist/static/js/chunk-elementUI.b0c99c08.js +0 -1
  275. package/modules/admin/dist/static/js/chunk-elementUI.b0c99c08.js.gz +0 -0
  276. package/modules/admin/dist/static/js/chunk-f075.315ceec1.js +0 -1
  277. package/modules/admin/dist/static/js/chunk-f075.315ceec1.js.gz +0 -0
  278. package/modules/admin/dist/static/js/chunk-libs.06c38828.js +0 -2
  279. package/modules/admin/dist/static/js/chunk-libs.06c38828.js.LICENSE.txt.gz +0 -0
  280. package/modules/admin/dist/static/js/chunk-libs.06c38828.js.gz +0 -0
  281. /package/modules/admin/dist/static/img/{404.a57b6f3.png → 404.4cf6930.png} +0 -0
package/listeners.js CHANGED
@@ -1,65 +1,65 @@
1
- const debug = require('debug')('expressa')
2
- const util = require('./util')
3
-
4
- module.exports = function (api) {
5
- /* Listeners to avoid the need for a server restart */
6
- api.addCollectionListener('changed', 'collection', async function setupCollectionStorage (req, collection, data) {
7
- if (!(data.storage === 'memory' && util.getPath(api.db[data._id], 'type') === 'memory')) {
8
- await api.setupCollectionDb(data)
9
- }
10
- debug('updated ' + data._id + ' collection storage')
11
- })
12
- api.addCollectionListener('delete', 'collection', function cleanupCollectionStorage (req, collection, data) {
13
- delete api.db[data._id]
14
- debug('removed ' + data._id + ' collection storage')
15
- })
16
- api.addCollectionListener('changed', 'settings', function updateInMemorySettings (req, collection, data) {
17
- // TODO: only reload if current environment is updated
18
- Object.assign(req.settings, data)
19
- })
20
-
21
- api.addListener(['put', 'post'], async function updateMetadata (req, collection, data, { event }) {
22
- data.meta ??= {}
23
- data.meta.updated = new Date().toISOString()
24
- if (event === 'post') {
25
- data.meta.created = new Date().toISOString()
26
- }
27
- })
28
-
29
- api.addCollectionListener('get', 'schemas', function addMetaToSchema (req, collection, data) {
30
- const schema = data.schema
31
- if (!schema.properties.meta) {
32
- schema.properties.meta = {
33
- type: 'object',
34
- properties: {
35
- created: {
36
- type: 'string'
37
- },
38
- updated: {
39
- type: 'string'
40
- }
41
- }
42
- }
43
- }
44
- if (schema.properties.meta.propertyOrder === undefined) {
45
- schema.properties.meta.propertyOrder = 2000
46
- }
47
- if (data.documentsHaveOwners) {
48
- if (!schema.properties.meta.properties.owner) {
49
- schema.properties.meta.properties.owner = {
50
- type: 'string',
51
- links: [
52
- {
53
- rel: '» view owner user',
54
- href: '/admin/#/edit/users/{{self}}',
55
- class: 'comment-link'
56
- }
57
- ]
58
- },
59
- schema.properties.meta.properties.owner_collection = {
60
- type: 'string'
61
- }
62
- }
63
- }
64
- })
65
- }
1
+ const debug = require('debug')('expressa')
2
+ const util = require('./util')
3
+
4
+ module.exports = function (api) {
5
+ /* Listeners to avoid the need for a server restart */
6
+ api.addCollectionListener('changed', 'collection', async function setupCollectionStorage (req, collection, data) {
7
+ if (!(data.storage === 'memory' && util.getPath(api.db[data._id], 'type') === 'memory')) {
8
+ await api.setupCollectionDb(data)
9
+ }
10
+ debug('updated ' + data._id + ' collection storage')
11
+ })
12
+ api.addCollectionListener('delete', 'collection', function cleanupCollectionStorage (req, collection, data) {
13
+ delete api.db[data._id]
14
+ debug('removed ' + data._id + ' collection storage')
15
+ })
16
+ api.addCollectionListener('changed', 'settings', function updateInMemorySettings (req, collection, data) {
17
+ // TODO: only reload if current environment is updated
18
+ Object.assign(req.settings, data)
19
+ })
20
+
21
+ api.addListener(['put', 'post'], async function updateMetadata (req, collection, data, { event }) {
22
+ data.meta ??= {}
23
+ data.meta.updated = new Date().toISOString()
24
+ if (event === 'post') {
25
+ data.meta.created = new Date().toISOString()
26
+ }
27
+ })
28
+
29
+ api.addCollectionListener('get', 'schemas', function addMetaToSchema (req, collection, data) {
30
+ const schema = data.schema
31
+ if (!schema.properties.meta) {
32
+ schema.properties.meta = {
33
+ type: 'object',
34
+ properties: {
35
+ created: {
36
+ type: 'string'
37
+ },
38
+ updated: {
39
+ type: 'string'
40
+ }
41
+ }
42
+ }
43
+ }
44
+ if (schema.properties.meta.propertyOrder === undefined) {
45
+ schema.properties.meta.propertyOrder = 2000
46
+ }
47
+ if (data.documentsHaveOwners) {
48
+ if (!schema.properties.meta.properties.owner) {
49
+ schema.properties.meta.properties.owner = {
50
+ type: 'string',
51
+ links: [
52
+ {
53
+ rel: '» view owner user',
54
+ href: '/admin/#/edit/users/{{self}}',
55
+ class: 'comment-link'
56
+ }
57
+ ]
58
+ },
59
+ schema.properties.meta.properties.owner_collection = {
60
+ type: 'string'
61
+ }
62
+ }
63
+ }
64
+ })
65
+ }
@@ -1,29 +1,29 @@
1
- const util = require('./util')
2
- const debug = require('debug')('expressa')
3
-
4
- const eventToPermissionMapping = {
5
- get: 'view',
6
- put: 'edit',
7
- post: 'create',
8
- delete: 'delete'
9
- }
10
-
11
- module.exports = function (api) {
12
- api.addCollectionListener('get', 'collection', function viewRelevantCollections (req, collection, data) {
13
- if (req.hasPermission('collection: view relevant')) {
14
- if (req.hasPermission(data._id + ': view') || req.hasPermission(data._id + ': view own')) {
15
- return true // force allow
16
- }
17
- }
18
- })
19
-
20
- api.addListener(['get', 'put', 'post', 'delete'], function collectionPermissionCheck (req, collection, data, info) {
21
- const permission = eventToPermissionMapping[info.event]
22
- const editingOwnDoc = data.meta && data.meta.owner && data.meta.owner === req.uid
23
- const editingOwn = editingOwnDoc && req.hasPermission(collection + ': ' + permission + ' own')
24
- if (!editingOwn && !req.hasPermission(collection + ': ' + permission)) {
25
- debug(`cancelling, missing permission "${collection}: ${permission}"`)
26
- throw new util.ApiError(401, 'You do not have permission to perform this action.')
27
- }
28
- })
29
- }
1
+ const util = require('./util')
2
+ const debug = require('debug')('expressa')
3
+
4
+ const eventToPermissionMapping = {
5
+ get: 'view',
6
+ put: 'edit',
7
+ post: 'create',
8
+ delete: 'delete'
9
+ }
10
+
11
+ module.exports = function (api) {
12
+ api.addCollectionListener('get', 'collection', function viewRelevantCollections (req, collection, data) {
13
+ if (req.hasPermission('collection: view relevant')) {
14
+ if (req.hasPermission(data._id + ': view') || req.hasPermission(data._id + ': view own')) {
15
+ return true // force allow
16
+ }
17
+ }
18
+ })
19
+
20
+ api.addListener(['get', 'put', 'post', 'delete'], function collectionPermissionCheck (req, collection, data, info) {
21
+ const permission = eventToPermissionMapping[info.event]
22
+ const editingOwnDoc = data.meta && data.meta.owner && data.meta.owner === req.uid
23
+ const editingOwn = editingOwnDoc && req.hasPermission(collection + ': ' + permission + ' own')
24
+ if (!editingOwn && !req.hasPermission(collection + ': ' + permission)) {
25
+ debug(`cancelling, missing permission "${collection}: ${permission}"`)
26
+ throw new util.ApiError(401, 'You do not have permission to perform this action.')
27
+ }
28
+ })
29
+ }
@@ -1,96 +1,96 @@
1
- const auth = require('./auth')
2
- const util = require('./util.js')
3
- const debug = require('debug')('expressa')
4
-
5
- module.exports = async function(api) {
6
-
7
- const loginCollections = (await util.getLoginCollections(api)).map((coll) => coll._id)
8
-
9
- api.addCollectionListener(['post', 'put'], loginCollections, async function updatePassword(req, collection, data) {
10
- if (req.method === 'PUT') {
11
- if (!data.password) {
12
- const oldData = await api.db[collection].get(data._id)
13
- data.password = oldData.password // preserve password if not explicitly set
14
- }
15
- }
16
- if (!auth.isHashed(data.password)) {
17
- debug('hashing and replacing password in the user document.')
18
- data.password = auth.createHash(data.password)
19
- data.meta.password_last_updated_at = new Date().toISOString()
20
- }
21
- })
22
-
23
- api.addCollectionListener('post', loginCollections, async function roleCreateCheck(req, collection, data) {
24
- if (!data.roles) {
25
- data.roles = []
26
- }
27
- // user special case to allow first user to be admin
28
- if (collection === 'users') {
29
- const hasUsers = (await api.db.users.find({}, 0, 1)).length === 1
30
- if (!hasUsers && !data.roles.includes('Admin')) {
31
- throw new util.ApiError(400, 'first user must have role Admin')
32
- }
33
- if (hasUsers && data.roles.length > 0 && !req.hasPermission('users: modify roles')) {
34
- throw new util.ApiError(400, 'insufficient permissions to create user with roles')
35
- }
36
- }
37
- else {
38
- if (data.roles.length > 0 && !req.hasPermission(`${collection}: modify roles`)) {
39
- throw new util.ApiError(400, `insufficient permissions to create ${collection} with roles`)
40
- }
41
- }
42
- })
43
-
44
- api.addLateCollectionListener('put', loginCollections, async function roleChangeCheck(req, collection, data) {
45
- if (!req.hasPermission(`${collection}: modify roles`)) {
46
- const oldData = await api.db[collection].get(data._id)
47
- data.roles = oldData.roles
48
- }
49
- })
50
-
51
- api.addCollectionListenerWithPriority(['get', 'changed', 'deleted'], loginCollections, 100, function hidePasswordHashes(req, collection, data) {
52
- if (!req.hasPermission(`${collection}: view hashed passwords`)) {
53
- debug(`deleting password because "${collection}: view hashed passwords"-permission is not set`)
54
- delete data.password
55
- }
56
- })
57
-
58
- api.addCollectionListener(['post', 'put'], loginCollections, async function userEmailLowerCase(req, collection, data) {
59
- if (data.email) {
60
- data.email = data.email.toLowerCase()
61
- }
62
- })
63
-
64
- api.addCollectionListener('post', loginCollections, async function userUniquenessCheck(req, collection, data) {
65
- if (data.email) {
66
- const result = await api.db[collection].find({email: data.email})
67
- if (result.length > 0) {
68
- throw new util.ApiError(409, 'This email is already registered.')
69
- }
70
- }
71
- })
72
-
73
- /* Mantain users-roles reference */
74
- api.addCollectionListener('changed', 'role', async function addRoleToUserSchema(req, collection, data) {
75
- for (const coll of loginCollections) {
76
- const doc = await api.db.collection.get(coll)
77
- if (!doc.schema.properties.roles.items.enum.includes(data._id)) {
78
- doc.schema.properties.roles.items.enum.push(data._id)
79
- await api.db.collection.update(coll, doc)
80
- await api.notify('changed', req, 'collection', doc)
81
- }
82
- }
83
- })
84
-
85
- api.addCollectionListener('deleted', 'role', async function removeRoleToUserSchema(req, collection, data) {
86
- for (const coll of loginCollections) {
87
- const doc = await api.db.collection.get(coll)
88
- const roles = doc.schema.properties.roles.items.enum
89
- if (roles.includes(data._id)) {
90
- roles.splice(roles.indexOf(data._id), 1)
91
- await api.db.collection.update(coll, doc)
92
- await api.notify('changed', req, 'collection', doc)
93
- }
94
- }
95
- })
96
- }
1
+ const auth = require('./auth')
2
+ const util = require('./util.js')
3
+ const debug = require('debug')('expressa')
4
+
5
+ module.exports = async function(api) {
6
+
7
+ const loginCollections = (await util.getLoginCollections(api)).map((coll) => coll._id)
8
+
9
+ api.addCollectionListener(['post', 'put'], loginCollections, async function updatePassword(req, collection, data) {
10
+ if (req.method === 'PUT') {
11
+ if (!data.password) {
12
+ const oldData = await api.db[collection].get(data._id)
13
+ data.password = oldData.password // preserve password if not explicitly set
14
+ }
15
+ }
16
+ if (!auth.isHashed(data.password)) {
17
+ debug('hashing and replacing password in the user document.')
18
+ data.password = auth.createHash(data.password)
19
+ data.meta.password_last_updated_at = new Date().toISOString()
20
+ }
21
+ })
22
+
23
+ api.addCollectionListener('post', loginCollections, async function roleCreateCheck(req, collection, data) {
24
+ if (!data.roles) {
25
+ data.roles = []
26
+ }
27
+ // user special case to allow first user to be admin
28
+ if (collection === 'users') {
29
+ const hasUsers = (await api.db.users.find({}, 0, 1)).length === 1
30
+ if (!hasUsers && !data.roles.includes('Admin')) {
31
+ throw new util.ApiError(400, 'first user must have role Admin')
32
+ }
33
+ if (hasUsers && data.roles.length > 0 && !req.hasPermission('users: modify roles')) {
34
+ throw new util.ApiError(400, 'insufficient permissions to create user with roles')
35
+ }
36
+ }
37
+ else {
38
+ if (data.roles.length > 0 && !req.hasPermission(`${collection}: modify roles`)) {
39
+ throw new util.ApiError(400, `insufficient permissions to create ${collection} with roles`)
40
+ }
41
+ }
42
+ })
43
+
44
+ api.addLateCollectionListener('put', loginCollections, async function roleChangeCheck(req, collection, data) {
45
+ if (!req.hasPermission(`${collection}: modify roles`)) {
46
+ const oldData = await api.db[collection].get(data._id)
47
+ data.roles = oldData.roles
48
+ }
49
+ })
50
+
51
+ api.addCollectionListenerWithPriority(['get', 'changed', 'deleted'], loginCollections, 100, function hidePasswordHashes(req, collection, data) {
52
+ if (!req.hasPermission(`${collection}: view hashed passwords`)) {
53
+ debug(`deleting password because "${collection}: view hashed passwords"-permission is not set`)
54
+ delete data.password
55
+ }
56
+ })
57
+
58
+ api.addCollectionListener(['post', 'put'], loginCollections, async function userEmailLowerCase(req, collection, data) {
59
+ if (data.email) {
60
+ data.email = data.email.toLowerCase()
61
+ }
62
+ })
63
+
64
+ api.addCollectionListener('post', loginCollections, async function userUniquenessCheck(req, collection, data) {
65
+ if (data.email) {
66
+ const result = await api.db[collection].find({email: data.email})
67
+ if (result.length > 0) {
68
+ throw new util.ApiError(409, 'This email is already registered.')
69
+ }
70
+ }
71
+ })
72
+
73
+ /* Mantain users-roles reference */
74
+ api.addCollectionListener('changed', 'role', async function addRoleToUserSchema(req, collection, data) {
75
+ for (const coll of loginCollections) {
76
+ const doc = await api.db.collection.get(coll)
77
+ if (!doc.schema.properties.roles.items.enum.includes(data._id)) {
78
+ doc.schema.properties.roles.items.enum.push(data._id)
79
+ await api.db.collection.update(coll, doc)
80
+ await api.notify('changed', req, 'collection', doc)
81
+ }
82
+ }
83
+ })
84
+
85
+ api.addCollectionListener('deleted', 'role', async function removeRoleToUserSchema(req, collection, data) {
86
+ for (const coll of loginCollections) {
87
+ const doc = await api.db.collection.get(coll)
88
+ const roles = doc.schema.properties.roles.items.enum
89
+ if (roles.includes(data._id)) {
90
+ roles.splice(roles.indexOf(data._id), 1)
91
+ await api.db.collection.update(coll, doc)
92
+ await api.notify('changed', req, 'collection', doc)
93
+ }
94
+ }
95
+ })
96
+ }
@@ -1,43 +1,43 @@
1
- const util = require('./util')
2
-
3
- function addImplicitFields (schema) {
4
- schema['properties'] ??= {}
5
- schema['properties']['meta'] ??= { type: 'object' }
6
- schema['properties']['meta']['properties'] ??= {}
7
- schema['properties']['meta']['properties']['created'] ??= { type: 'string' }
8
- schema['properties']['meta']['properties']['updated'] ??= { type: 'string' }
9
- schema['properties']['meta']['properties']['owner'] ??= { type: 'string' }
10
- schema['properties']['meta']['properties']['owner_collection'] ??= { type: 'string' }
11
- schema['properties']['_id'] ??= { type: 'string' }
12
- return schema
13
- }
14
-
15
- module.exports = async function (api) {
16
-
17
- // Load all validators
18
- const collections = await api.db.collection.all()
19
- collections.forEach((collection) => {
20
- const schema = addImplicitFields(collection.schema)
21
- util.addSchema(collection._id, schema)
22
- })
23
-
24
- // Load new and update validators as necessary
25
- api.addCollectionListener('changed', 'collection', function updateSchemaValidators (req, collection, data) {
26
- const schema = addImplicitFields(data.schema)
27
- util.addSchema(data._id, schema)
28
- })
29
-
30
- api.addCollectionListener('get', ['collection', 'schemas'], function ensureIdAdded (req, collection, data) {
31
- if (data.schema) {
32
- data.schema = addImplicitFields(data.schema)
33
- }
34
- })
35
-
36
- api.addListener(['put', 'post'], function matchesSchema (req, collection, data) {
37
- // TODO (switch to check if "installed" after install tests are done.
38
- if (!req.settings.enforce_permissions) {
39
- return
40
- }
41
- util.validateSchema(collection, data)
42
- })
43
- }
1
+ const util = require('./util')
2
+
3
+ function addImplicitFields (schema) {
4
+ schema['properties'] ??= {}
5
+ schema['properties']['meta'] ??= { type: 'object' }
6
+ schema['properties']['meta']['properties'] ??= {}
7
+ schema['properties']['meta']['properties']['created'] ??= { type: 'string' }
8
+ schema['properties']['meta']['properties']['updated'] ??= { type: 'string' }
9
+ schema['properties']['meta']['properties']['owner'] ??= { type: 'string' }
10
+ schema['properties']['meta']['properties']['owner_collection'] ??= { type: 'string' }
11
+ schema['properties']['_id'] ??= { type: 'string' }
12
+ return schema
13
+ }
14
+
15
+ module.exports = async function (api) {
16
+
17
+ // Load all validators
18
+ const collections = await api.db.collection.all()
19
+ collections.forEach((collection) => {
20
+ const schema = addImplicitFields(collection.schema)
21
+ util.addSchema(collection._id, schema)
22
+ })
23
+
24
+ // Load new and update validators as necessary
25
+ api.addCollectionListener('changed', 'collection', function updateSchemaValidators (req, collection, data) {
26
+ const schema = addImplicitFields(data.schema)
27
+ util.addSchema(data._id, schema)
28
+ })
29
+
30
+ api.addCollectionListener('get', ['collection', 'schemas'], function ensureIdAdded (req, collection, data) {
31
+ if (data.schema) {
32
+ data.schema = addImplicitFields(data.schema)
33
+ }
34
+ })
35
+
36
+ api.addListener(['put', 'post'], function matchesSchema (req, collection, data) {
37
+ // TODO (switch to check if "installed" after install tests are done.
38
+ if (!req.settings.enforce_permissions) {
39
+ return
40
+ }
41
+ util.validateSchema(collection, data)
42
+ })
43
+ }
@@ -1,30 +1,30 @@
1
- const onFinished = require('on-finished')
2
-
3
- const util = require('../util')
4
-
5
- module.exports = function logRequests(req, res, next) {
6
- if (req.db.requestlog) {
7
- const oldSend = res.send
8
- res.send = function(...params) {
9
- const data = params[0]
10
- // ensure requests using custom middleware log error messages
11
- if (typeof data?.error === 'string') {
12
- req.returnedError ??= new util.ApiError(res.statusCode, data.error)
13
- }
14
- res.send = oldSend // set function back to avoid the 'double-send'
15
- return res.send(...params) // just call as normal with data
16
- }
17
- onFinished(res, async function (err) {
18
- if (err) {
19
- console.error('error found while logging.')
20
- console.error(err)
21
- return
22
- }
23
- if (util.shouldLogRequest(req, res)) {
24
- const entry = util.createLogEntry(req, res)
25
- await req.db.requestlog.create(entry)
26
- }
27
- })
28
- }
29
- next()
30
- }
1
+ const onFinished = require('on-finished')
2
+
3
+ const util = require('../util')
4
+
5
+ module.exports = function logRequests(req, res, next) {
6
+ if (req.db.requestlog) {
7
+ const oldSend = res.send
8
+ res.send = function(...params) {
9
+ const data = params[0]
10
+ // ensure requests using custom middleware log error messages
11
+ if (typeof data?.error === 'string') {
12
+ req.returnedError ??= new util.ApiError(res.statusCode, data.error)
13
+ }
14
+ res.send = oldSend // set function back to avoid the 'double-send'
15
+ return res.send(...params) // just call as normal with data
16
+ }
17
+ onFinished(res, async function (err) {
18
+ if (err) {
19
+ console.error('error found while logging.')
20
+ console.error(err)
21
+ return
22
+ }
23
+ if (util.shouldLogRequest(req, res)) {
24
+ const entry = util.createLogEntry(req, res)
25
+ await req.db.requestlog.create(entry)
26
+ }
27
+ })
28
+ }
29
+ next()
30
+ }
@@ -1,41 +1,41 @@
1
- const util = require('../util')
2
- let authenticatedRoleExists
3
-
4
- async function addRolePermissions (req, roles) {
5
- const rolePermissions = await util.getEffectivePermissionsForRoles(roles, req.db)
6
- req.permissions ??= {}
7
- req.permissions = { ...req.permissions, ...rolePermissions }
8
- }
9
-
10
- async function doesAuthenticatedRoleExist(req) {
11
- if (typeof authenticatedRoleExists === 'undefined') {
12
- try {
13
- await req.db.role.get('Authenticated')
14
- authenticatedRoleExists = true
15
- } catch (e) {
16
- authenticatedRoleExists = false
17
- }
18
- }
19
- return authenticatedRoleExists
20
- }
21
-
22
- module.exports.addRolePermissionsAsync = async function addRolePermissionsMiddlewareAsync(req) {
23
- if (!req.settings || !req.settings.enforce_permissions) {
24
- // Use a dummy permission getter
25
- req.hasPermission = () => true
26
- return
27
- }
28
- req.hasPermission = (permission) => util.hasPermission(req.permissions, permission)
29
- let roles = ['Anonymous']
30
- const isAuthenticatedRole = await doesAuthenticatedRoleExist(req)
31
- if (req.uid) {
32
- roles = (req.user.roles || []).concat(isAuthenticatedRole ? ['Authenticated'] : [])
33
- } else {
34
- req.user = {}
35
- }
36
- await addRolePermissions(req, roles)
37
- }
38
-
39
- module.exports.middleware = function addRolePermissionsMiddleware (req, res, next) {
40
- module.exports.addRolePermissionsAsync(req).then(next).catch(next)
41
- }
1
+ const util = require('../util')
2
+ let authenticatedRoleExists
3
+
4
+ async function addRolePermissions (req, roles) {
5
+ const rolePermissions = await util.getEffectivePermissionsForRoles(roles, req.db)
6
+ req.permissions ??= {}
7
+ req.permissions = { ...req.permissions, ...rolePermissions }
8
+ }
9
+
10
+ async function doesAuthenticatedRoleExist(req) {
11
+ if (typeof authenticatedRoleExists === 'undefined') {
12
+ try {
13
+ await req.db.role.get('Authenticated')
14
+ authenticatedRoleExists = true
15
+ } catch (e) {
16
+ authenticatedRoleExists = false
17
+ }
18
+ }
19
+ return authenticatedRoleExists
20
+ }
21
+
22
+ module.exports.addRolePermissionsAsync = async function addRolePermissionsMiddlewareAsync(req) {
23
+ if (!req.settings || !req.settings.enforce_permissions) {
24
+ // Use a dummy permission getter
25
+ req.hasPermission = () => true
26
+ return
27
+ }
28
+ req.hasPermission = (permission) => util.hasPermission(req.permissions, permission)
29
+ let roles = ['Anonymous']
30
+ const isAuthenticatedRole = await doesAuthenticatedRoleExist(req)
31
+ if (req.uid) {
32
+ roles = (req.user.roles || []).concat(isAuthenticatedRole ? ['Authenticated'] : [])
33
+ } else {
34
+ req.user = {}
35
+ }
36
+ await addRolePermissions(req, roles)
37
+ }
38
+
39
+ module.exports.middleware = function addRolePermissionsMiddleware (req, res, next) {
40
+ module.exports.addRolePermissionsAsync(req).then(next).catch(next)
41
+ }