@kalisio/kdk 2.6.4 → 2.7.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.
Files changed (398) hide show
  1. package/core/api/application.js +2 -4
  2. package/core/api/authentication.js +2 -3
  3. package/core/api/db.js +10 -2
  4. package/core/api/hooks/hooks.authorisations.js +4 -2
  5. package/core/api/hooks/hooks.push.js +6 -2
  6. package/core/api/hooks/hooks.query.js +29 -12
  7. package/core/api/hooks/hooks.users.js +30 -17
  8. package/core/api/models/configurations.model.mongodb.js +4 -0
  9. package/core/api/services/authorisations/authorisations.service.js +1 -1
  10. package/core/api/services/configurations/configurations.hooks.js +33 -0
  11. package/core/api/services/index.js +41 -7
  12. package/core/api/services/messages/messages.hooks.js +9 -3
  13. package/core/client/api.js +14 -1
  14. package/core/client/capabilities.js +1 -6
  15. package/core/client/components/KAvatar.vue +24 -20
  16. package/core/client/components/account/KProfile.vue +10 -71
  17. package/core/client/components/account/index.js +0 -2
  18. package/core/client/components/app/KSettings.vue +1 -0
  19. package/core/client/components/collection/KBoard.vue +4 -3
  20. package/core/client/components/collection/KCardSection.vue +1 -0
  21. package/core/client/components/collection/KGrid.vue +2 -0
  22. package/core/client/components/collection/KTable.vue +5 -1
  23. package/core/client/components/collection/KTimeLine.vue +9 -1
  24. package/core/client/components/collection/index.js +0 -2
  25. package/core/client/components/form/KChipsField.vue +2 -1
  26. package/core/client/components/form/KEmailField.vue +1 -0
  27. package/core/client/components/form/KFileField.vue +22 -1
  28. package/core/client/components/form/KForm.vue +2 -0
  29. package/core/client/components/form/KItemField.vue +1 -0
  30. package/core/client/components/form/KNumberField.vue +1 -0
  31. package/core/client/components/form/KPasswordField.vue +1 -0
  32. package/core/client/components/form/KPhoneField.vue +1 -0
  33. package/core/client/components/form/KPropertyItemField.vue +1 -0
  34. package/core/client/components/form/KResolutionField.vue +1 -0
  35. package/core/client/components/form/KSelectField.vue +31 -0
  36. package/core/client/components/form/KTagField.vue +1 -0
  37. package/core/client/components/form/KTextField.vue +1 -0
  38. package/core/client/components/form/KTokenField.vue +1 -0
  39. package/core/client/components/form/KUnitField.vue +1 -0
  40. package/core/client/components/form/KUrlField.vue +1 -0
  41. package/core/client/components/graphics/KIcon.vue +3 -4
  42. package/core/client/components/layout/KPage.vue +1 -0
  43. package/core/client/components/layout/KWindow.vue +6 -3
  44. package/core/client/components/messages/KMessageComposer.vue +2 -1
  45. package/core/client/components/messages/KMessagesTimeLine.vue +1 -1
  46. package/core/client/components/time/KDate.vue +1 -2
  47. package/core/client/components/time/KDateTime.vue +11 -11
  48. package/core/client/components/time/KTime.vue +1 -1
  49. package/core/client/composables/collection.js +33 -8
  50. package/core/client/composables/errors.js +1 -1
  51. package/core/client/composables/layout.js +9 -9
  52. package/core/client/configurations.js +50 -0
  53. package/core/client/exporter.js +1 -1
  54. package/core/client/i18n/core_en.json +6 -39
  55. package/core/client/i18n/core_fr.json +6 -39
  56. package/core/client/index.js +2 -0
  57. package/core/client/layout.js +8 -8
  58. package/core/client/mixins/mixin.base-activity.js +5 -2
  59. package/core/client/mixins/mixin.base-field.js +3 -3
  60. package/core/client/search.js +2 -1
  61. package/core/client/utils/utils.collection.js +8 -8
  62. package/core/client/utils/utils.items.js +4 -0
  63. package/core/client/utils/utils.push.js +3 -3
  64. package/core/client/utils/utils.session.js +7 -5
  65. package/core/client/utils/utils.shapes.js +38 -7
  66. package/core/client/utils/utils.time.js +21 -22
  67. package/core/common/schemas/users.update-profile.json +3 -2
  68. package/coverage/core/api/application.js.html +392 -398
  69. package/coverage/core/api/authentication.js.html +352 -187
  70. package/coverage/core/api/db.js.html +165 -126
  71. package/coverage/core/api/hooks/hooks.authentication.js.html +22 -196
  72. package/coverage/core/api/hooks/hooks.authorisations.js.html +383 -662
  73. package/coverage/core/api/hooks/hooks.logger.js.html +41 -41
  74. package/coverage/core/api/hooks/hooks.model.js.html +113 -101
  75. package/coverage/core/api/hooks/hooks.push.js.html +124 -97
  76. package/coverage/core/api/hooks/hooks.query.js.html +292 -217
  77. package/coverage/core/api/hooks/hooks.schemas.js.html +123 -123
  78. package/coverage/core/api/hooks/hooks.service.js.html +1 -1
  79. package/coverage/core/api/hooks/hooks.storage.js.html +1 -1
  80. package/coverage/core/api/hooks/{hooks.groups.js.html → hooks.tags.js.html} +100 -76
  81. package/coverage/core/api/hooks/hooks.users.js.html +255 -447
  82. package/coverage/core/api/hooks/index.html +107 -122
  83. package/coverage/core/api/hooks/index.js.html +4 -10
  84. package/coverage/core/api/index.html +46 -61
  85. package/coverage/core/api/index.js.html +9 -9
  86. package/coverage/core/api/marshall.js.html +9 -9
  87. package/coverage/core/api/models/{organisations.model.mongodb.js.html → configurations.model.mongodb.js.html} +10 -7
  88. package/coverage/core/api/models/index.html +35 -50
  89. package/coverage/core/api/models/messages.model.mongodb.js.html +39 -27
  90. package/coverage/core/api/models/tags.model.mongodb.js.html +26 -32
  91. package/coverage/core/api/models/users.model.mongodb.js.html +10 -10
  92. package/coverage/core/api/services/account/account.hooks.js.html +5 -5
  93. package/coverage/core/api/services/account/account.service.js.html +127 -127
  94. package/coverage/core/api/services/account/index.html +22 -22
  95. package/coverage/core/api/services/authorisations/authorisations.hooks.js.html +1 -1
  96. package/coverage/core/api/services/authorisations/authorisations.service.js.html +213 -222
  97. package/coverage/core/api/services/authorisations/index.html +21 -21
  98. package/coverage/core/api/services/{organisations/organisations.hooks.js.html → configurations/configurations.hooks.js.html} +16 -10
  99. package/coverage/core/api/services/{groups → configurations}/index.html +8 -8
  100. package/coverage/core/api/services/databases/databases.hooks.js.html +1 -1
  101. package/coverage/core/api/services/databases/databases.service.js.html +1 -1
  102. package/coverage/core/api/services/databases/index.html +1 -1
  103. package/coverage/core/api/services/import-export/import-export.hooks.js.html +76 -76
  104. package/coverage/core/api/services/import-export/import-export.service.js.html +32 -32
  105. package/coverage/core/api/services/import-export/index.html +32 -32
  106. package/coverage/core/api/services/index.html +21 -21
  107. package/coverage/core/api/services/index.js.html +313 -142
  108. package/coverage/core/api/services/mailer/index.html +32 -32
  109. package/coverage/core/api/services/mailer/mailer.hooks.js.html +80 -80
  110. package/coverage/core/api/services/mailer/mailer.service.js.html +32 -32
  111. package/coverage/core/api/services/messages/index.html +21 -21
  112. package/coverage/core/api/services/messages/messages.hooks.js.html +112 -76
  113. package/coverage/core/api/services/push/index.html +32 -32
  114. package/coverage/core/api/services/push/push.hooks.js.html +80 -80
  115. package/coverage/core/api/services/push/push.service.js.html +34 -34
  116. package/coverage/core/api/services/storage/index.html +29 -29
  117. package/coverage/core/api/services/storage/storage.hooks.js.html +80 -80
  118. package/coverage/core/api/services/storage/storage.service.js.html +29 -29
  119. package/coverage/core/api/services/tags/index.html +21 -21
  120. package/coverage/core/api/services/tags/tags.hooks.js.html +119 -71
  121. package/coverage/core/api/services/users/index.html +27 -12
  122. package/coverage/core/api/services/users/users.hooks.js.html +14 -11
  123. package/coverage/core/api/services/users/users.service.js.html +100 -0
  124. package/coverage/core/common/errors.js.html +1 -1
  125. package/coverage/core/common/index.html +42 -27
  126. package/coverage/core/common/index.js.html +1 -1
  127. package/coverage/core/common/permissions.js.html +166 -472
  128. package/coverage/core/common/schema.js.html +4 -4
  129. package/coverage/core/common/utils.js.html +31 -25
  130. package/coverage/core/common/utils.offline.js.html +199 -0
  131. package/coverage/index.html +192 -192
  132. package/coverage/lcov-report/core/api/application.js.html +392 -398
  133. package/coverage/lcov-report/core/api/authentication.js.html +352 -187
  134. package/coverage/lcov-report/core/api/db.js.html +165 -126
  135. package/coverage/lcov-report/core/api/hooks/hooks.authentication.js.html +22 -196
  136. package/coverage/lcov-report/core/api/hooks/hooks.authorisations.js.html +383 -662
  137. package/coverage/lcov-report/core/api/hooks/hooks.logger.js.html +41 -41
  138. package/coverage/lcov-report/core/api/hooks/hooks.model.js.html +113 -101
  139. package/coverage/lcov-report/core/api/hooks/hooks.push.js.html +124 -97
  140. package/coverage/lcov-report/core/api/hooks/hooks.query.js.html +292 -217
  141. package/coverage/lcov-report/core/api/hooks/hooks.schemas.js.html +123 -123
  142. package/coverage/lcov-report/core/api/hooks/hooks.service.js.html +1 -1
  143. package/coverage/lcov-report/core/api/hooks/hooks.storage.js.html +1 -1
  144. package/coverage/lcov-report/core/api/hooks/{hooks.groups.js.html → hooks.tags.js.html} +100 -76
  145. package/coverage/lcov-report/core/api/hooks/hooks.users.js.html +255 -447
  146. package/coverage/lcov-report/core/api/hooks/index.html +107 -122
  147. package/coverage/lcov-report/core/api/hooks/index.js.html +4 -10
  148. package/coverage/lcov-report/core/api/index.html +46 -61
  149. package/coverage/lcov-report/core/api/index.js.html +9 -9
  150. package/coverage/lcov-report/core/api/marshall.js.html +9 -9
  151. package/coverage/lcov-report/core/api/models/{organisations.model.mongodb.js.html → configurations.model.mongodb.js.html} +10 -7
  152. package/coverage/lcov-report/core/api/models/index.html +35 -50
  153. package/coverage/lcov-report/core/api/models/messages.model.mongodb.js.html +39 -27
  154. package/coverage/lcov-report/core/api/models/tags.model.mongodb.js.html +26 -32
  155. package/coverage/lcov-report/core/api/models/users.model.mongodb.js.html +10 -10
  156. package/coverage/lcov-report/core/api/services/account/account.hooks.js.html +5 -5
  157. package/coverage/lcov-report/core/api/services/account/account.service.js.html +127 -127
  158. package/coverage/lcov-report/core/api/services/account/index.html +22 -22
  159. package/coverage/lcov-report/core/api/services/authorisations/authorisations.hooks.js.html +1 -1
  160. package/coverage/lcov-report/core/api/services/authorisations/authorisations.service.js.html +213 -222
  161. package/coverage/lcov-report/core/api/services/authorisations/index.html +21 -21
  162. package/coverage/lcov-report/core/api/services/{groups/groups.hooks.js.html → configurations/configurations.hooks.js.html} +16 -10
  163. package/coverage/lcov-report/core/api/services/{groups → configurations}/index.html +8 -8
  164. package/coverage/lcov-report/core/api/services/databases/databases.hooks.js.html +1 -1
  165. package/coverage/lcov-report/core/api/services/databases/databases.service.js.html +1 -1
  166. package/coverage/lcov-report/core/api/services/databases/index.html +1 -1
  167. package/coverage/lcov-report/core/api/services/import-export/import-export.hooks.js.html +76 -76
  168. package/coverage/lcov-report/core/api/services/import-export/import-export.service.js.html +32 -32
  169. package/coverage/lcov-report/core/api/services/import-export/index.html +32 -32
  170. package/coverage/lcov-report/core/api/services/index.html +21 -21
  171. package/coverage/lcov-report/core/api/services/index.js.html +313 -142
  172. package/coverage/lcov-report/core/api/services/mailer/index.html +32 -32
  173. package/coverage/lcov-report/core/api/services/mailer/mailer.hooks.js.html +80 -80
  174. package/coverage/lcov-report/core/api/services/mailer/mailer.service.js.html +32 -32
  175. package/coverage/lcov-report/core/api/services/messages/index.html +21 -21
  176. package/coverage/lcov-report/core/api/services/messages/messages.hooks.js.html +112 -76
  177. package/coverage/lcov-report/core/api/services/push/index.html +32 -32
  178. package/coverage/lcov-report/core/api/services/push/push.hooks.js.html +80 -80
  179. package/coverage/lcov-report/core/api/services/push/push.service.js.html +34 -34
  180. package/coverage/lcov-report/core/api/services/storage/index.html +29 -29
  181. package/coverage/lcov-report/core/api/services/storage/storage.hooks.js.html +80 -80
  182. package/coverage/lcov-report/core/api/services/storage/storage.service.js.html +29 -29
  183. package/coverage/lcov-report/core/api/services/tags/index.html +21 -21
  184. package/coverage/lcov-report/core/api/services/tags/tags.hooks.js.html +119 -71
  185. package/coverage/lcov-report/core/api/services/users/index.html +27 -12
  186. package/coverage/lcov-report/core/api/services/users/users.hooks.js.html +14 -11
  187. package/coverage/lcov-report/core/api/services/users/users.service.js.html +100 -0
  188. package/coverage/lcov-report/core/common/errors.js.html +1 -1
  189. package/coverage/lcov-report/core/common/index.html +42 -27
  190. package/coverage/lcov-report/core/common/index.js.html +1 -1
  191. package/coverage/lcov-report/core/common/permissions.js.html +166 -472
  192. package/coverage/lcov-report/core/common/schema.js.html +4 -4
  193. package/coverage/lcov-report/core/common/utils.js.html +31 -25
  194. package/coverage/lcov-report/core/common/utils.offline.js.html +199 -0
  195. package/coverage/lcov-report/index.html +192 -192
  196. package/coverage/lcov-report/map/api/hooks/hooks.catalog.js.html +169 -31
  197. package/coverage/lcov-report/map/api/hooks/hooks.features.js.html +1 -1
  198. package/coverage/lcov-report/map/api/hooks/hooks.query.js.html +215 -35
  199. package/coverage/lcov-report/map/api/hooks/index.html +7 -7
  200. package/coverage/lcov-report/map/api/hooks/index.js.html +1 -1
  201. package/coverage/lcov-report/map/api/index.html +1 -1
  202. package/coverage/lcov-report/map/api/index.js.html +1 -1
  203. package/coverage/lcov-report/map/api/marshall.js.html +1 -1
  204. package/coverage/lcov-report/map/api/models/alerts.model.mongodb.js.html +1 -1
  205. package/coverage/lcov-report/map/api/models/catalog.model.mongodb.js.html +82 -7
  206. package/coverage/lcov-report/map/api/models/features.model.mongodb.js.html +1 -1
  207. package/coverage/lcov-report/map/api/models/index.html +22 -7
  208. package/coverage/lcov-report/map/api/models/projects.model.mongodb.js.html +1 -1
  209. package/coverage/lcov-report/{core/api/models/groups.model.mongodb.js.html → map/api/models/styles.model.mongodb.js.html} +10 -7
  210. package/coverage/lcov-report/map/api/services/alerts/alerts.hooks.js.html +1 -1
  211. package/coverage/lcov-report/map/api/services/alerts/alerts.service.js.html +1 -1
  212. package/coverage/lcov-report/map/api/services/alerts/index.html +1 -1
  213. package/coverage/lcov-report/map/api/services/catalog/catalog.hooks.js.html +39 -12
  214. package/coverage/lcov-report/map/api/services/catalog/index.html +5 -5
  215. package/coverage/lcov-report/map/api/services/daptiles/daptiles.service.js.html +1 -1
  216. package/coverage/lcov-report/map/api/services/daptiles/index.html +1 -1
  217. package/coverage/lcov-report/map/api/services/features/features.hooks.js.html +86 -11
  218. package/coverage/lcov-report/map/api/services/features/features.service.js.html +307 -4
  219. package/coverage/lcov-report/map/api/services/features/index.html +7 -7
  220. package/coverage/lcov-report/map/api/services/index.html +5 -5
  221. package/coverage/lcov-report/map/api/services/index.js.html +326 -50
  222. package/coverage/lcov-report/map/api/services/projects/index.html +1 -1
  223. package/coverage/lcov-report/map/api/services/projects/projects.hooks.js.html +1 -1
  224. package/coverage/{core/api/services/organisations → lcov-report/map/api/services/styles}/index.html +10 -25
  225. package/coverage/lcov-report/{core/api/services/organisations/organisations.hooks.js.html → map/api/services/styles/styles.hooks.js.html} +45 -12
  226. package/coverage/lcov-report/map/common/dynamic-grid-source.js.html +1 -1
  227. package/coverage/lcov-report/map/common/errors.js.html +1 -1
  228. package/coverage/lcov-report/map/common/geotiff-grid-source.js.html +7 -10
  229. package/coverage/lcov-report/map/common/grid.js.html +1 -1
  230. package/coverage/lcov-report/map/common/index.html +19 -19
  231. package/coverage/lcov-report/map/common/index.js.html +1 -1
  232. package/coverage/lcov-report/map/common/meteo-model-grid-source.js.html +1 -1
  233. package/coverage/lcov-report/map/common/moment-utils.js.html +1 -1
  234. package/coverage/lcov-report/map/common/opendap-grid-source.js.html +1 -1
  235. package/coverage/lcov-report/map/common/opendap-utils.js.html +4 -7
  236. package/coverage/lcov-report/map/common/permissions.js.html +10 -4
  237. package/coverage/lcov-report/map/common/time-based-grid-source.js.html +1 -1
  238. package/coverage/lcov-report/map/common/tms-utils.js.html +9 -12
  239. package/coverage/lcov-report/map/common/wcs-grid-source.js.html +3 -3
  240. package/coverage/lcov-report/map/common/wcs-utils.js.html +12 -15
  241. package/coverage/lcov-report/map/common/weacast-grid-source.js.html +2 -2
  242. package/coverage/lcov-report/map/common/wfs-utils.js.html +14 -17
  243. package/coverage/lcov-report/map/common/wms-utils.js.html +30 -12
  244. package/coverage/lcov-report/map/common/wmts-utils.js.html +10 -13
  245. package/coverage/lcov.info +4157 -3816
  246. package/coverage/map/api/hooks/hooks.catalog.js.html +169 -31
  247. package/coverage/map/api/hooks/hooks.features.js.html +1 -1
  248. package/coverage/map/api/hooks/hooks.query.js.html +215 -35
  249. package/coverage/map/api/hooks/index.html +7 -7
  250. package/coverage/map/api/hooks/index.js.html +1 -1
  251. package/coverage/map/api/index.html +1 -1
  252. package/coverage/map/api/index.js.html +1 -1
  253. package/coverage/map/api/marshall.js.html +1 -1
  254. package/coverage/map/api/models/alerts.model.mongodb.js.html +1 -1
  255. package/coverage/map/api/models/catalog.model.mongodb.js.html +82 -7
  256. package/coverage/map/api/models/features.model.mongodb.js.html +1 -1
  257. package/coverage/map/api/models/index.html +22 -7
  258. package/coverage/map/api/models/projects.model.mongodb.js.html +1 -1
  259. package/coverage/{core/api/models/groups.model.mongodb.js.html → map/api/models/styles.model.mongodb.js.html} +10 -7
  260. package/coverage/map/api/services/alerts/alerts.hooks.js.html +1 -1
  261. package/coverage/map/api/services/alerts/alerts.service.js.html +1 -1
  262. package/coverage/map/api/services/alerts/index.html +1 -1
  263. package/coverage/map/api/services/catalog/catalog.hooks.js.html +39 -12
  264. package/coverage/map/api/services/catalog/index.html +5 -5
  265. package/coverage/map/api/services/daptiles/daptiles.service.js.html +1 -1
  266. package/coverage/map/api/services/daptiles/index.html +1 -1
  267. package/coverage/map/api/services/features/features.hooks.js.html +86 -11
  268. package/coverage/map/api/services/features/features.service.js.html +307 -4
  269. package/coverage/map/api/services/features/index.html +7 -7
  270. package/coverage/map/api/services/index.html +5 -5
  271. package/coverage/map/api/services/index.js.html +326 -50
  272. package/coverage/map/api/services/projects/index.html +1 -1
  273. package/coverage/map/api/services/projects/projects.hooks.js.html +1 -1
  274. package/coverage/{lcov-report/core/api/services/organisations → map/api/services/styles}/index.html +10 -25
  275. package/coverage/{core/api/services/groups/groups.hooks.js.html → map/api/services/styles/styles.hooks.js.html} +45 -12
  276. package/coverage/map/common/dynamic-grid-source.js.html +1 -1
  277. package/coverage/map/common/errors.js.html +1 -1
  278. package/coverage/map/common/geotiff-grid-source.js.html +7 -10
  279. package/coverage/map/common/grid.js.html +1 -1
  280. package/coverage/map/common/index.html +19 -19
  281. package/coverage/map/common/index.js.html +1 -1
  282. package/coverage/map/common/meteo-model-grid-source.js.html +1 -1
  283. package/coverage/map/common/moment-utils.js.html +1 -1
  284. package/coverage/map/common/opendap-grid-source.js.html +1 -1
  285. package/coverage/map/common/opendap-utils.js.html +4 -7
  286. package/coverage/map/common/permissions.js.html +10 -4
  287. package/coverage/map/common/time-based-grid-source.js.html +1 -1
  288. package/coverage/map/common/tms-utils.js.html +9 -12
  289. package/coverage/map/common/wcs-grid-source.js.html +3 -3
  290. package/coverage/map/common/wcs-utils.js.html +12 -15
  291. package/coverage/map/common/weacast-grid-source.js.html +2 -2
  292. package/coverage/map/common/wfs-utils.js.html +14 -17
  293. package/coverage/map/common/wms-utils.js.html +30 -12
  294. package/coverage/map/common/wmts-utils.js.html +10 -13
  295. package/coverage/tmp/coverage-1028514-1773134124472-0.json +1 -0
  296. package/coverage/tmp/coverage-1028526-1773134124448-0.json +1 -0
  297. package/coverage/tmp/coverage-1028537-1773134124431-0.json +1 -0
  298. package/coverage/tmp/coverage-1028549-1773134124401-0.json +1 -0
  299. package/coverage/tmp/coverage-1028556-1773134124353-0.json +1 -0
  300. package/extras/configs/widgets.top.js +3 -3
  301. package/extras/tests/core/collection.mjs +2 -9
  302. package/extras/tours/pane.top.js +0 -9
  303. package/map/api/hooks/hooks.catalog.js +18 -4
  304. package/map/api/services/catalog/catalog.hooks.js +3 -0
  305. package/map/api/services/features/features.hooks.js +3 -1
  306. package/map/api/services/index.js +2 -6
  307. package/map/api/services/styles/styles.hooks.js +6 -1
  308. package/map/client/components/KFeatureActionButton.vue +9 -3
  309. package/map/client/components/KFeaturesFilterManager.vue +5 -5
  310. package/map/client/components/KFilterCondition.vue +17 -10
  311. package/map/client/components/KLayerEditor.vue +49 -39
  312. package/map/client/components/KMeasureTool.vue +7 -1
  313. package/map/client/components/KTimezoneMap.vue +29 -9
  314. package/map/client/components/catalog/KLayersPanel.vue +26 -16
  315. package/map/client/components/catalog/KLayersSelector.vue +13 -2
  316. package/map/client/components/catalog/KViewsPanel.vue +5 -4
  317. package/map/client/components/form/KSelectLayersField.vue +28 -17
  318. package/map/client/components/form/KSelectViewsField.vue +18 -9
  319. package/map/client/components/form/KTimezoneField.vue +1 -2
  320. package/map/client/components/legend/KVariablesLegend.vue +10 -1
  321. package/map/client/components/location/KLocationCardSection.vue +7 -2
  322. package/map/client/components/location/KLocationMap.vue +31 -7
  323. package/map/client/components/selection/KSelectedLayerFeatures.vue +2 -2
  324. package/map/client/components/stickies/KZoomControl.vue +1 -1
  325. package/map/client/components/styles/KStyleManager.vue +4 -1
  326. package/map/client/components/widget/KTimeSeries.vue +174 -497
  327. package/map/client/components/widget/KTimeSeriesSelector.vue +72 -0
  328. package/map/client/components/widget/KTimeSeriesToolbar.vue +83 -0
  329. package/map/client/composables/catalog.js +6 -10
  330. package/map/client/composables/highlight.js +12 -9
  331. package/map/client/composables/project.js +1 -1
  332. package/map/client/composables/selection.js +8 -7
  333. package/map/client/composables/weather.js +9 -2
  334. package/map/client/geolocation.js +8 -5
  335. package/map/client/i18n/map_en.json +11 -10
  336. package/map/client/i18n/map_fr.json +10 -9
  337. package/map/client/leaflet/TiledFeatureLayer.js +85 -82
  338. package/map/client/leaflet/utils/utils.geojson.js +3 -3
  339. package/map/client/mixins/globe/mixin.base-globe.js +15 -6
  340. package/map/client/mixins/globe/mixin.geojson-layers.js +27 -18
  341. package/map/client/mixins/map/mixin.edit-layers.js +9 -1
  342. package/map/client/mixins/map/mixin.pmtiles-layers.js +118 -29
  343. package/map/client/mixins/map/mixin.tiled-mesh-layers.js +12 -5
  344. package/map/client/mixins/map/mixin.tiled-wind-layers.js +19 -10
  345. package/map/client/mixins/mixin.activity.js +23 -30
  346. package/map/client/mixins/mixin.feature-selection.js +41 -5
  347. package/map/client/planets.js +1 -1
  348. package/map/client/readers/reader.kml.js +2 -3
  349. package/map/client/utils/utils.catalog.js +36 -10
  350. package/map/client/utils/utils.layers.js +39 -8
  351. package/map/client/utils/utils.project.js +4 -0
  352. package/map/client/utils/utils.style.js +37 -7
  353. package/map/client/utils/utils.time-series.js +215 -6
  354. package/map/common/schemas/catalog.update.json +1 -1
  355. package/map/common/weacast-grid-source.js +1 -1
  356. package/package.json +3 -3
  357. package/scripts/kash/CHANGELOG.md +0 -4
  358. package/scripts/kash/README.md +0 -9
  359. package/scripts/kash/kash.sh +45 -40
  360. package/scripts/kash/scripts/run_tests.sh +1 -4
  361. package/test/api/core/authentication.test.js +9 -4
  362. package/test/api/core/config/default.cjs +1 -0
  363. package/test/api/core/hooks.test.js +6 -0
  364. package/test/api/core/index.test.js +43 -18
  365. package/test/api/core/push.test.js +8 -8
  366. package/test/api/core/test-log-2026-03-10.log +60 -0
  367. package/test/api/core/users.test.js +384 -0
  368. package/test/api/map/grid-sources.test.js +1 -1
  369. package/test/api/map/test-log-2026-03-10.log +56 -0
  370. package/vite/package.json +11 -2
  371. package/vite/test/core/composables.test.js +77 -0
  372. package/vite/vitest.config.js +13 -0
  373. package/vite/yarn.lock +1096 -18
  374. package/.vscode/settings.json +0 -5
  375. package/core/client/components/account/KAccount.vue +0 -68
  376. package/core/client/components/account/KDeleteAccountManager.vue +0 -62
  377. package/core/client/components/account/KEmailManager.vue +0 -128
  378. package/core/client/components/account/KPasswordManager.vue +0 -90
  379. package/core/client/components/account/KVerifyEmailManager.vue +0 -105
  380. package/core/client/components/collection/KColumn.vue +0 -227
  381. package/core/client/components/collection/KHistory.vue +0 -113
  382. package/core/client/components/collection/KHistoryEntry.vue +0 -109
  383. package/coverage/core/api/hooks/hooks.organisations.js.html +0 -541
  384. package/coverage/core/api/services/organisations/organisations.service.js.html +0 -343
  385. package/coverage/core/api/utils.js.html +0 -118
  386. package/coverage/lcov-report/core/api/hooks/hooks.organisations.js.html +0 -541
  387. package/coverage/lcov-report/core/api/services/organisations/organisations.service.js.html +0 -343
  388. package/coverage/lcov-report/core/api/utils.js.html +0 -118
  389. package/coverage/tmp/coverage-151166-1723543324307-0.json +0 -1
  390. package/coverage/tmp/coverage-151178-1723543324283-0.json +0 -1
  391. package/coverage/tmp/coverage-151189-1723543324271-0.json +0 -1
  392. package/coverage/tmp/coverage-151201-1723543324248-0.json +0 -1
  393. package/coverage/tmp/coverage-151208-1723543324227-0.json +0 -1
  394. package/scripts/kash/LICENSE +0 -21
  395. package/test/api/core/test-log-2024-04-22.log +0 -84
  396. package/test/api/core/test-log-2024-04-23.log +0 -23
  397. package/test/api/core/test-log-2024-08-13.log +0 -3
  398. package/test/api/map/test-log-2025-03-08.log +0 -0
@@ -6,6 +6,7 @@ import request from 'superagent'
6
6
  import chai from 'chai'
7
7
  import chailint from 'chai-lint'
8
8
  import spies from 'chai-spies'
9
+ import fuzzySearch from 'feathers-mongodb-fuzzy-search'
9
10
  import core, { kdk, hooks, permissions, createMessagesService } from '../../../core/api/index.js'
10
11
  import { fileURLToPath } from 'url'
11
12
 
@@ -15,7 +16,7 @@ const { util, expect } = chai
15
16
 
16
17
  describe('core:services', () => {
17
18
  let app, server, port, baseUrl, accessToken,
18
- userService, userObject, authorisationService, messagesService, messageObject,
19
+ usersService, userObject, authorisationService, messagesService, messageObject,
19
20
  spyUpdateAbilities
20
21
 
21
22
  before(async () => {
@@ -44,8 +45,12 @@ describe('core:services', () => {
44
45
  it('registers the services', async () => {
45
46
  await app.configure(core)
46
47
 
47
- userService = app.getService('users')
48
- expect(userService).toExist()
48
+ usersService = app.getService('users')
49
+ expect(usersService).toExist()
50
+ // Register search hooks
51
+ usersService.hooks({
52
+ before: { find: [fuzzySearch({ fields: ['profile.name'] }), hooks.diacriticSearch()] }
53
+ })
49
54
  // Create a global messages service for tests
50
55
  await createMessagesService.call(app)
51
56
  messagesService = app.getService('messages')
@@ -108,11 +113,11 @@ describe('core:services', () => {
108
113
  const [localStrategy] = app.service('api/authentication').getStrategies('local')
109
114
  const previousPassword = await localStrategy.hashPassword('weak;')
110
115
 
111
- await assert.rejects(() => userService.create({
116
+ await assert.rejects(() => usersService.create({
112
117
  email: 'test@test.org',
113
118
  password: 'weak;',
114
119
  previousPasswords: [previousPassword],
115
- name: 'test-user'
120
+ name: 'maëlis'
116
121
  }), error => {
117
122
  expect(error).toExist()
118
123
  expect(error.name).to.equal('BadRequest')
@@ -120,10 +125,10 @@ describe('core:services', () => {
120
125
  return true
121
126
  })
122
127
 
123
- await assert.rejects(() => userService.create({
128
+ await assert.rejects(() => usersService.create({
124
129
  email: 'test@test.org',
125
130
  password: '12345678',
126
- name: 'test-user'
131
+ name: 'maëlis'
127
132
  }), error => {
128
133
  expect(error).toExist()
129
134
  expect(error.name).to.equal('BadRequest')
@@ -137,17 +142,17 @@ describe('core:services', () => {
137
142
  it('creates a user', async () => {
138
143
  // Test password generation
139
144
  const hook = hooks.generatePassword()({ type: 'before', data: {}, params: {}, app })
140
- userObject = await userService.create({
145
+ userObject = await usersService.create({
141
146
  email: 'test@test.org',
142
147
  password: hook.data.password,
143
- name: 'test-user',
148
+ name: 'maëlis',
144
149
  profile: { phone: '0623256968' }
145
150
  }, { checkAuthorisation: true })
146
151
  expect(spyUpdateAbilities).to.have.been.called.once
147
152
  spyUpdateAbilities.reset()
148
153
  // Keep track of clear password
149
154
  userObject.clearPassword = hook.data.password
150
- const users = await userService.find({ query: { 'profile.name': 'test-user' } })
155
+ const users = await usersService.find({ query: { 'profile.name': 'maëlis' } })
151
156
  expect(users.data.length > 0).beTrue()
152
157
  expect(users.data[0].email).toExist()
153
158
  expect(users.data[0].clearPassword).beUndefined()
@@ -159,10 +164,10 @@ describe('core:services', () => {
159
164
  .timeout(10000)
160
165
 
161
166
  it('changing user password keeps password history', async () => {
162
- await userService.patch(userObject._id.toString(), { password: userObject.password })
167
+ await usersService.patch(userObject._id.toString(), { password: userObject.password })
163
168
  expect(spyUpdateAbilities).to.have.been.called.once
164
169
  spyUpdateAbilities.reset()
165
- const user = await userService.get(userObject._id.toString())
170
+ const user = await usersService.get(userObject._id.toString())
166
171
  expect(user.previousPasswords).toExist()
167
172
  expect(user.previousPasswords).to.deep.equal([userObject.password])
168
173
  })
@@ -226,15 +231,16 @@ describe('core:services', () => {
226
231
  .timeout(5000)
227
232
 
228
233
  it('authenticated user can access services', () => {
229
- return userService.find({ query: {}, params: { user: userObject, checkAuthorisation: true } })
234
+ return usersService.find({ query: {}, params: { user: userObject, checkAuthorisation: true } })
230
235
  .then(users => {
231
236
  expect(users.data.length === 1).beTrue()
232
237
  })
233
238
  })
234
239
 
235
240
  it('get user profile', () => {
236
- return userService.find({ query: { $select: ['profile'] } })
241
+ return usersService.find({ query: { $select: ['profile'] } })
237
242
  .then(users => {
243
+ expect(users.data.length > 0).beTrue()
238
244
  expect(users.data[0].name).beUndefined()
239
245
  expect(users.data[0].profile.name).toExist()
240
246
  expect(users.data[0].profile.description).toExist()
@@ -242,6 +248,25 @@ describe('core:services', () => {
242
248
  })
243
249
  })
244
250
 
251
+ it('search user profile', async () => {
252
+ const hook = hooks.generatePassword()({ type: 'before', data: {}, params: {}, app })
253
+ const user = await usersService.create({
254
+ email: 'anothertest@test.org',
255
+ password: hook.data.password,
256
+ name: 'maelis',
257
+ profile: { phone: '0623256968' }
258
+ })
259
+ spyUpdateAbilities.reset()
260
+ const allUsers = await usersService.find({ query: { 'profile.name': { $search: 'Mae' } } })
261
+ // Diacritic should be more specific
262
+ const singleUsers = await usersService.find({ query: { 'profile.name': { $search: 'Maë' } } })
263
+ await usersService.remove(user._id)
264
+ expect(allUsers.data.length === 2).beTrue()
265
+ expect(singleUsers.data.length === 1).beTrue()
266
+ })
267
+ // Let enough time to process
268
+ .timeout(10000)
269
+
245
270
  it('creates a user message', async () => {
246
271
  const message = await messagesService.create({
247
272
  title: 'Title',
@@ -271,7 +296,7 @@ describe('core:services', () => {
271
296
  expect(authorisation).toExist()
272
297
  expect(spyUpdateAbilities).to.have.been.called.once
273
298
  spyUpdateAbilities.reset()
274
- userObject = await userService.get(userObject._id.toString())
299
+ userObject = await usersService.get(userObject._id.toString())
275
300
  expect(userObject.authorisations).toExist()
276
301
  expect(userObject.authorisations.length > 0).beTrue()
277
302
  expect(userObject.authorisations[0].permissions).to.deep.equal('manager')
@@ -334,7 +359,7 @@ describe('core:services', () => {
334
359
  expect(authorisation).toExist()
335
360
  expect(spyUpdateAbilities).to.have.been.called.once
336
361
  spyUpdateAbilities.reset()
337
- const user = await userService.get(userObject._id.toString())
362
+ const user = await usersService.get(userObject._id.toString())
338
363
  expect(user.authorisations).toExist()
339
364
  expect(user.authorisations.length === 0).beTrue()
340
365
  })
@@ -361,11 +386,11 @@ describe('core:services', () => {
361
386
  })
362
387
 
363
388
  it('removes a user', async () => {
364
- await userService.remove(userObject._id, {
389
+ await usersService.remove(userObject._id, {
365
390
  user: userObject,
366
391
  checkAuthorisation: true
367
392
  })
368
- const users = await userService.find({ query: { name: 'test-user' } })
393
+ const users = await usersService.find({ query: { name: 'maëlis' } })
369
394
  expect(users.data.length === 0).beTrue()
370
395
  const messages = await messagesService.find({ query: { title: 'Title' } })
371
396
  expect(messages.data.length === 0).beTrue()
@@ -14,7 +14,7 @@ const { util, expect } = chai
14
14
 
15
15
  describe('core:push', () => {
16
16
  let app, server, port, mailerStub, gmailUser, user,
17
- mailerService, userService, pushService
17
+ mailerService, usersService, pushService
18
18
 
19
19
  const subscription = {
20
20
  endpoint: process.env.SUBSCRIPTION_ENDPOINT,
@@ -71,9 +71,9 @@ describe('core:push', () => {
71
71
 
72
72
  it('registers the services', async () => {
73
73
  await app.configure(core)
74
- userService = app.getService('users')
75
- expect(userService).toExist()
76
- userService.hooks({
74
+ usersService = app.getService('users')
75
+ expect(usersService).toExist()
76
+ usersService.hooks({
77
77
  before: {
78
78
  remove: [hooks.unregisterDevices]
79
79
  },
@@ -101,7 +101,7 @@ describe('core:push', () => {
101
101
  .timeout(5000)
102
102
 
103
103
  it('create a user', () => {
104
- return userService.create({
104
+ return usersService.create({
105
105
  email: gmailUser,
106
106
  password: 'Pass;word1',
107
107
  name: 'user'
@@ -117,7 +117,7 @@ describe('core:push', () => {
117
117
  const previousUser = _.cloneDeep(user)
118
118
  await addSubscription(user, subscription, 'subscriptions')
119
119
  // Subscriptions change detection requires the previous user to be set
120
- await userService.patch(user._id, { subscriptions: user.subscriptions }, { user: previousUser })
120
+ await usersService.patch(user._id, { subscriptions: user.subscriptions }, { user: previousUser })
121
121
  expect(user.subscriptions).toExist()
122
122
  expect(user.subscriptions.length === 1).beTrue()
123
123
  expect(user.subscriptions[0].endpoint).to.equal(subscription.endpoint)
@@ -156,7 +156,7 @@ describe('core:push', () => {
156
156
  it('delete expired subscriptions', async () => {
157
157
  // Add expired subscription
158
158
  await addSubscription(user, expiredSubscription, 'subscriptions')
159
- await userService.patch(user._id, { subscriptions: user.subscriptions })
159
+ await usersService.patch(user._id, { subscriptions: user.subscriptions })
160
160
  expect(user.subscriptions).toExist()
161
161
  expect(user.subscriptions.length === 2).beTrue()
162
162
  // Send push notification
@@ -166,7 +166,7 @@ describe('core:push', () => {
166
166
  subscriptionProperty: 'subscriptions',
167
167
  subscriptionFilter: { _id: user._id }
168
168
  })
169
- const users = await userService.find({ query: { email: gmailUser } })
169
+ const users = await usersService.find({ query: { email: gmailUser } })
170
170
  expect(users.data.length > 0).beTrue()
171
171
  user = users.data[0]
172
172
  // Check that expired subscriptions have been deleted
@@ -0,0 +1,60 @@
1
+ {"level":"error","message":"error: api/account - Method: create: The provided password does not comply to the password policy"}
2
+ {"level":"error","message":"error: api/account - Method: create: The provided password does not comply to the password policy"}
3
+ {"level":"error","message":"error: api/account - Method: create: The provided password does not comply to the password policy"}
4
+ {"level":"error","message":"error: api/messages - Method: create: You are not allowed to access service messages"}
5
+ {"level":"error","message":"error: api/users - Method: create: The provided password does not comply to the password policy"}
6
+ {"level":"error","message":"error: api/users - Method: create: The provided password does not comply to the password policy"}
7
+ {"level":"error","message":"error: api/authorisations - Method: create: You are not allowed to change authorisation on resource"}
8
+ {"level":"error","message":"error: api/authorisations - Method: remove: You are not allowed to change authorisation on subject(s)"}
9
+ {"level":"info","message":"This is a log test"}
10
+ {"level":"error","message":"error: api/service - Method: create: validation failed"}
11
+ {"level":"error","message":"error: api/service - Method: create: validation failed"}
12
+ {"level":"error","message":"error: api/service - Method: create: validation failed"}
13
+ {"level":"error","message":"error: api/service - Method: create: validation failed"}
14
+ {"level":"error","message":"error: api/storage - Method: get: NoSuchKey"}
15
+ {"level":"error","message":"error: api/storage - Method: get: NoSuchKey"}
16
+ {"level":"error","message":"error: api/account - Method: create: The provided password does not comply to the password policy"}
17
+ {"level":"error","message":"error: api/account - Method: create: The provided password does not comply to the password policy"}
18
+ {"level":"error","message":"error: api/account - Method: create: The provided password does not comply to the password policy"}
19
+ {"level":"error","message":"error: api/messages - Method: create: You are not allowed to access service messages"}
20
+ {"level":"error","message":"error: api/users - Method: create: The provided password does not comply to the password policy"}
21
+ {"level":"error","message":"error: api/users - Method: create: The provided password does not comply to the password policy"}
22
+ {"level":"error","message":"error: api/authorisations - Method: create: You are not allowed to change authorisation on resource"}
23
+ {"level":"error","message":"error: api/authorisations - Method: remove: You are not allowed to change authorisation on subject(s)"}
24
+ {"level":"info","message":"This is a log test"}
25
+ {"level":"error","message":"error: api/service - Method: create: validation failed"}
26
+ {"level":"error","message":"error: api/service - Method: create: validation failed"}
27
+ {"level":"error","message":"error: api/service - Method: create: validation failed"}
28
+ {"level":"error","message":"error: api/service - Method: create: validation failed"}
29
+ {"level":"error","message":"error: api/storage - Method: get: NoSuchKey"}
30
+ {"level":"error","message":"error: api/storage - Method: get: NoSuchKey"}
31
+ {"level":"error","message":"error: api/account - Method: create: The provided password does not comply to the password policy"}
32
+ {"level":"error","message":"error: api/account - Method: create: The provided password does not comply to the password policy"}
33
+ {"level":"error","message":"error: api/account - Method: create: The provided password does not comply to the password policy"}
34
+ {"level":"error","message":"error: api/messages - Method: create: You are not allowed to access service messages"}
35
+ {"level":"error","message":"error: api/users - Method: create: The provided password does not comply to the password policy"}
36
+ {"level":"error","message":"error: api/users - Method: create: The provided password does not comply to the password policy"}
37
+ {"level":"error","message":"error: api/authorisations - Method: create: You are not allowed to change authorisation on resource"}
38
+ {"level":"error","message":"error: api/authorisations - Method: remove: You are not allowed to change authorisation on subject(s)"}
39
+ {"level":"info","message":"This is a log test"}
40
+ {"level":"error","message":"error: api/service - Method: create: validation failed"}
41
+ {"level":"error","message":"error: api/service - Method: create: validation failed"}
42
+ {"level":"error","message":"error: api/service - Method: create: validation failed"}
43
+ {"level":"error","message":"error: api/service - Method: create: validation failed"}
44
+ {"level":"error","message":"error: api/storage - Method: get: NoSuchKey"}
45
+ {"level":"error","message":"error: api/storage - Method: get: NoSuchKey"}
46
+ {"level":"error","message":"error: api/account - Method: create: The provided password does not comply to the password policy"}
47
+ {"level":"error","message":"error: api/account - Method: create: The provided password does not comply to the password policy"}
48
+ {"level":"error","message":"error: api/account - Method: create: The provided password does not comply to the password policy"}
49
+ {"level":"error","message":"error: api/messages - Method: create: You are not allowed to access service messages"}
50
+ {"level":"error","message":"error: api/users - Method: create: The provided password does not comply to the password policy"}
51
+ {"level":"error","message":"error: api/users - Method: create: The provided password does not comply to the password policy"}
52
+ {"level":"error","message":"error: api/authorisations - Method: create: You are not allowed to change authorisation on resource"}
53
+ {"level":"error","message":"error: api/authorisations - Method: remove: You are not allowed to change authorisation on subject(s)"}
54
+ {"level":"info","message":"This is a log test"}
55
+ {"level":"error","message":"error: api/service - Method: create: validation failed"}
56
+ {"level":"error","message":"error: api/service - Method: create: validation failed"}
57
+ {"level":"error","message":"error: api/service - Method: create: validation failed"}
58
+ {"level":"error","message":"error: api/service - Method: create: validation failed"}
59
+ {"level":"error","message":"error: api/storage - Method: get: NoSuchKey"}
60
+ {"level":"error","message":"error: api/storage - Method: get: NoSuchKey"}
@@ -0,0 +1,384 @@
1
+ import _ from 'lodash'
2
+ import authentication from '@feathersjs/authentication'
3
+ import commonHooks from 'feathers-hooks-common'
4
+ import request from 'superagent'
5
+ import chai from 'chai'
6
+ import chailint from 'chai-lint'
7
+ import core, { kdk, hooks } from '../../../core/api/index.js'
8
+
9
+ const { authenticate } = authentication.hooks
10
+ const { util, expect } = chai
11
+ const { iff, disallow, isProvider, keep, discard } = commonHooks
12
+ const { isNotMe, onlyMe, preventChanges } = hooks
13
+
14
+ describe('core:users', () => {
15
+ let app, server, port, baseUrl, userIdAccessToken, emailAccessToken, phoneAccessToken, statelessAccessToken, adminAccessToken,
16
+ userService, userObject, anotherUserObject, authenticationService
17
+
18
+ before(async () => {
19
+ chailint(chai, util)
20
+
21
+ app = kdk()
22
+ port = app.get('port')
23
+ baseUrl = `http://localhost:${port}${app.get('apiPath')}`
24
+ await app.db.connect()
25
+ await app.db.instance.dropDatabase()
26
+ })
27
+
28
+ it('registers the services', async () => {
29
+ await app.configure(core)
30
+ authenticationService = app.getService('authentication')
31
+ expect(authenticationService).toExist()
32
+ userService = app.getService('users')
33
+ expect(userService).toExist()
34
+ // Register hooks, what we'd like is a configuration so that:
35
+ // - information disclosure about internal user secrets like password is not permitted
36
+ // - information disclosure about others users is not permitted for a given user unless it has 'administrator' permissions
37
+ // - privilege escalation is not permitted for a given user
38
+ // - user with 'administrator' permissions can change others user permissions
39
+ // - changing others users information is not permitted for a given user unless it has 'administrator' permissions
40
+ // - external calls can only target myself except if I have administrator permissions
41
+ const isNotAdministrator = (context) => {
42
+ const userPermissions = _.get(context.params, 'user.permissions')
43
+ return userPermissions !== 'administrator'
44
+ }
45
+ userService.hooks({
46
+ before: {
47
+ all: authenticate('jwt'),
48
+ get: [iff(isNotMe(), disallow('external'))],
49
+ find: [iff(isProvider('external'), iff(isNotAdministrator, onlyMe()))],
50
+ create: [iff(isProvider('external'), keep('name', 'email', 'profile', 'password'))],
51
+ update: [disallow('external')],
52
+ patch: [iff(isProvider('external'), iff(isNotAdministrator, onlyMe(), preventChanges(true, ['permissions'])))],
53
+ remove: [iff(isProvider('external'), iff(isNotAdministrator, onlyMe()))]
54
+ },
55
+ after: {
56
+ all: [iff(isProvider('external'), iff(isNotMe(), iff(isNotAdministrator, discard('permissions'))))]
57
+ }
58
+ })
59
+ // Now app is configured launch the server
60
+ server = await app.listen(port)
61
+ await new Promise(resolve => server.once('listening', resolve))
62
+ })
63
+ // Let enough time to process
64
+ .timeout(10000)
65
+
66
+ it('creates users and tokens with different subject identifiers', async () => {
67
+ userObject = await userService.create({
68
+ email: 'test@test.org',
69
+ name: 'test user',
70
+ permissions: 'user',
71
+ profile: { phone: '0623256968' }
72
+ })
73
+ userIdAccessToken = await authenticationService.createAccessToken({
74
+ sub: userObject._id
75
+ })
76
+ emailAccessToken = await authenticationService.createAccessToken({
77
+ sub: userObject.email
78
+ })
79
+ phoneAccessToken = await authenticationService.createAccessToken({
80
+ sub: userObject.profile.phone
81
+ })
82
+ statelessAccessToken = await authenticationService.createAccessToken({
83
+ property: 'mycustomproperty'
84
+ }, {
85
+ subject: 'mycustomapp'
86
+ })
87
+ anotherUserObject = await userService.create({
88
+ email: 'another_test@test.org',
89
+ name: 'another test user',
90
+ permissions: 'administrator',
91
+ profile: { phone: '0623256969' }
92
+ })
93
+ adminAccessToken = await authenticationService.createAccessToken({
94
+ sub: anotherUserObject._id
95
+ })
96
+ })
97
+
98
+ it('checks all user tokens are recognized', async () => {
99
+ let response = await request
100
+ .post(`${baseUrl}/authentication`)
101
+ .send({ accessToken: userIdAccessToken, strategy: 'jwt' })
102
+ let accessToken = response.body.accessToken
103
+ let user = response.body.user
104
+ expect(accessToken).toExist()
105
+ expect(accessToken).not.to.equal(userIdAccessToken)
106
+ expect(user).toExist()
107
+ response = await request
108
+ .post(`${baseUrl}/authentication`)
109
+ .send({ accessToken: emailAccessToken, strategy: 'jwt' })
110
+ accessToken = response.body.accessToken
111
+ user = response.body.user
112
+ expect(accessToken).toExist()
113
+ expect(accessToken).not.to.equal(emailAccessToken)
114
+ expect(user).toExist()
115
+ response = await request
116
+ .post(`${baseUrl}/authentication`)
117
+ .send({ accessToken: phoneAccessToken, strategy: 'jwt' })
118
+ accessToken = response.body.accessToken
119
+ user = response.body.user
120
+ expect(accessToken).toExist()
121
+ expect(accessToken).not.to.equal(phoneAccessToken)
122
+ expect(user).toExist()
123
+ response = await request
124
+ .post(`${baseUrl}/authentication`)
125
+ .send({ accessToken: statelessAccessToken, strategy: 'jwt' })
126
+ accessToken = response.body.accessToken
127
+ user = response.body.user
128
+ expect(accessToken).toExist()
129
+ expect(accessToken).not.to.equal(statelessAccessToken)
130
+ expect(user).beUndefined()
131
+ })
132
+
133
+ it('checks for user information disclosure', async () => {
134
+ // Should not retrieve internal user secret information like password in any case
135
+ // Should not list others users in case of requests with identified user
136
+ let response = await request
137
+ .get(`${baseUrl}/users`)
138
+ .set('Authorization', 'Bearer ' + userIdAccessToken)
139
+ let users = response.body.data
140
+ expect(users.length).to.equal(1)
141
+ let user = users[0]
142
+ expect(user._id).to.equal(userObject._id.toString())
143
+ expect(user.password).beUndefined()
144
+ expect(user.previousPasswords).beUndefined()
145
+ expect(user.permissions).toExist()
146
+ response = await request
147
+ .get(`${baseUrl}/users`)
148
+ .set('Authorization', 'Bearer ' + emailAccessToken)
149
+ users = response.body.data
150
+ expect(users.length).to.equal(1)
151
+ user = users[0]
152
+ expect(user._id).to.equal(userObject._id.toString())
153
+ expect(user.password).beUndefined()
154
+ expect(user.previousPasswords).beUndefined()
155
+ expect(user.permissions).toExist()
156
+ response = await request
157
+ .get(`${baseUrl}/users`)
158
+ .set('Authorization', 'Bearer ' + phoneAccessToken)
159
+ users = response.body.data
160
+ expect(users.length).to.equal(1)
161
+ user = users[0]
162
+ expect(user._id).to.equal(userObject._id.toString())
163
+ expect(user.password).beUndefined()
164
+ expect(user.previousPasswords).beUndefined()
165
+ expect(user.permissions).toExist()
166
+ // Should not list users in case of request without identified user
167
+ try {
168
+ response = await request
169
+ .get(`${baseUrl}/users/${anotherUserObject._id}`)
170
+ .set('Authorization', 'Bearer ' + statelessAccessToken)
171
+ } catch (error) {
172
+ // Not sure why but in this case the raised error is in text/html format
173
+ expect(error.status).to.equal(500)
174
+ expect(error.response.text.includes('MethodNotAllowed')).beTrue()
175
+ }
176
+ /*
177
+ response = await request
178
+ .get(`${baseUrl}/users`)
179
+ .set('Authorization', 'Bearer ' + statelessAccessToken)
180
+ users = response.body.data
181
+ expect(users.length).to.equal(2)
182
+ user = users[0]
183
+ expect(user._id).to.equal(userObject._id.toString())
184
+ expect(user.password).beUndefined()
185
+ expect(user.previousPasswords).beUndefined()
186
+ expect(user.permissions).beUndefined()
187
+ user = users[1]
188
+ expect(user._id).to.equal(anotherUserObject._id.toString())
189
+ expect(user.password).beUndefined()
190
+ expect(user.previousPasswords).beUndefined()
191
+ expect(user.permissions).beUndefined()
192
+ */
193
+ // Should not get others users in case of requests with identified user
194
+ try {
195
+ response = await request
196
+ .get(`${baseUrl}/users/${anotherUserObject._id}`)
197
+ .set('Authorization', 'Bearer ' + userIdAccessToken)
198
+ } catch (error) {
199
+ // Not sure why but in this case the raised error is in text/html format
200
+ expect(error.status).to.equal(500)
201
+ expect(error.response.text.includes('MethodNotAllowed')).beTrue()
202
+ }
203
+ try {
204
+ response = await request
205
+ .get(`${baseUrl}/users/${anotherUserObject._id}`)
206
+ .set('Authorization', 'Bearer ' + emailAccessToken)
207
+ } catch (error) {
208
+ // Not sure why but in this case the raised error is in text/html format
209
+ expect(error.status).to.equal(500)
210
+ expect(error.response.text.includes('MethodNotAllowed')).beTrue()
211
+ }
212
+ try {
213
+ response = await request
214
+ .get(`${baseUrl}/users/${anotherUserObject._id}`)
215
+ .set('Authorization', 'Bearer ' + phoneAccessToken)
216
+ } catch (error) {
217
+ // Not sure why but in this case the raised error is in text/html format
218
+ expect(error.status).to.equal(500)
219
+ expect(error.response.text.includes('MethodNotAllowed')).beTrue()
220
+ }
221
+ try {
222
+ response = await request
223
+ .get(`${baseUrl}/users/${anotherUserObject._id}`)
224
+ .set('Authorization', 'Bearer ' + statelessAccessToken)
225
+ } catch (error) {
226
+ // Not sure why but in this case the raised error is in text/html format
227
+ expect(error.status).to.equal(500)
228
+ expect(error.response.text.includes('MethodNotAllowed')).beTrue()
229
+ }
230
+ })
231
+
232
+ it('checks for user information integrity', async () => {
233
+ // Should not be able to update information of others users if not administrator
234
+ try {
235
+ await request
236
+ .patch(`${baseUrl}/users/${anotherUserObject._id}`)
237
+ .set('Authorization', 'Bearer ' + userIdAccessToken)
238
+ .send({ name: 'new name' })
239
+ } catch (error) {
240
+ // Not sure why but in this case the raised error is in text/html format
241
+ expect(error.status).to.equal(500)
242
+ expect(error.response.text.includes('NotFound')).beTrue()
243
+ }
244
+ try {
245
+ await request
246
+ .patch(`${baseUrl}/users/${anotherUserObject._id}`)
247
+ .set('Authorization', 'Bearer ' + emailAccessToken)
248
+ .send({ name: 'new name' })
249
+ } catch (error) {
250
+ // Not sure why but in this case the raised error is in text/html format
251
+ expect(error.status).to.equal(500)
252
+ expect(error.response.text.includes('NotFound')).beTrue()
253
+ }
254
+ try {
255
+ await request
256
+ .patch(`${baseUrl}/users/${anotherUserObject._id}`)
257
+ .set('Authorization', 'Bearer ' + phoneAccessToken)
258
+ .send({ name: 'new name' })
259
+ } catch (error) {
260
+ // Not sure why but in this case the raised error is in text/html format
261
+ expect(error.status).to.equal(500)
262
+ expect(error.response.text.includes('NotFound')).beTrue()
263
+ }
264
+ try {
265
+ await request
266
+ .patch(`${baseUrl}/users/${anotherUserObject._id}`)
267
+ .set('Authorization', 'Bearer ' + statelessAccessToken)
268
+ .send({ name: 'new name' })
269
+ } catch (error) {
270
+ // Not sure why but in this case the raised error is in text/html format
271
+ expect(error.status).to.equal(500)
272
+ expect(error.response.text.includes('Forbidden')).beTrue()
273
+ }
274
+ // Should be possible otherwise
275
+ const response = await request
276
+ .patch(`${baseUrl}/users/${userObject._id}`)
277
+ .set('Authorization', 'Bearer ' + adminAccessToken)
278
+ .send({ name: 'new name' })
279
+ const user = response.body
280
+ expect(user.name).to.equal('new name')
281
+ })
282
+
283
+ it('checks for user privilege escalation', async () => {
284
+ // Should not be able to upgrade user permissions when not administrator
285
+ try {
286
+ await request
287
+ .patch(`${baseUrl}/users/${userObject._id}`)
288
+ .set('Authorization', 'Bearer ' + userIdAccessToken)
289
+ .send({ permissions: 'administrator' })
290
+ } catch (error) {
291
+ // Not sure why but in this case the raised error is in text/html format
292
+ expect(error.status).to.equal(500)
293
+ expect(error.response.text.includes('BadRequest')).beTrue()
294
+ }
295
+ try {
296
+ await request
297
+ .patch(`${baseUrl}/users/${userObject._id}`)
298
+ .set('Authorization', 'Bearer ' + emailAccessToken)
299
+ .send({ permissions: 'administrator' })
300
+ } catch (error) {
301
+ // Not sure why but in this case the raised error is in text/html format
302
+ expect(error.status).to.equal(500)
303
+ expect(error.response.text.includes('BadRequest')).beTrue()
304
+ }
305
+ try {
306
+ await request
307
+ .patch(`${baseUrl}/users/${userObject._id}`)
308
+ .set('Authorization', 'Bearer ' + phoneAccessToken)
309
+ .send({ permissions: 'administrator' })
310
+ } catch (error) {
311
+ // Not sure why but in this case the raised error is in text/html format
312
+ expect(error.status).to.equal(500)
313
+ expect(error.response.text.includes('BadRequest')).beTrue()
314
+ }
315
+ try {
316
+ await request
317
+ .patch(`${baseUrl}/users/${userObject._id}`)
318
+ .set('Authorization', 'Bearer ' + statelessAccessToken)
319
+ .send({ permissions: 'administrator' })
320
+ } catch (error) {
321
+ // Not sure why but in this case the raised error is in text/html format
322
+ expect(error.status).to.equal(500)
323
+ expect(error.response.text.includes('Forbidden')).beTrue()
324
+ }
325
+ // Should be possible otherwise
326
+ const response = await request
327
+ .patch(`${baseUrl}/users/${userObject._id}`)
328
+ .set('Authorization', 'Bearer ' + adminAccessToken)
329
+ .send({ permissions: 'manager' })
330
+ const user = response.body
331
+ expect(user.permissions).to.equal('manager')
332
+ })
333
+
334
+ it('checks users removal', async () => {
335
+ // Should not be able to remove others users if not administrator
336
+ try {
337
+ await request
338
+ .delete(`${baseUrl}/users/${anotherUserObject._id}`)
339
+ .set('Authorization', 'Bearer ' + userIdAccessToken)
340
+ } catch (error) {
341
+ // Not sure why but in this case the raised error is in text/html format
342
+ expect(error.status).to.equal(500)
343
+ expect(error.response.text.includes('NotFound')).beTrue()
344
+ }
345
+ try {
346
+ await request
347
+ .delete(`${baseUrl}/users/${anotherUserObject._id}`)
348
+ .set('Authorization', 'Bearer ' + emailAccessToken)
349
+ } catch (error) {
350
+ // Not sure why but in this case the raised error is in text/html format
351
+ expect(error.status).to.equal(500)
352
+ expect(error.response.text.includes('NotFound')).beTrue()
353
+ }
354
+ try {
355
+ await request
356
+ .delete(`${baseUrl}/users/${anotherUserObject._id}`)
357
+ .set('Authorization', 'Bearer ' + phoneAccessToken)
358
+ } catch (error) {
359
+ // Not sure why but in this case the raised error is in text/html format
360
+ expect(error.status).to.equal(500)
361
+ expect(error.response.text.includes('NotFound')).beTrue()
362
+ }
363
+ try {
364
+ await request
365
+ .delete(`${baseUrl}/users/${anotherUserObject._id}`)
366
+ .set('Authorization', 'Bearer ' + statelessAccessToken)
367
+ } catch (error) {
368
+ // Not sure why but in this case the raised error is in text/html format
369
+ expect(error.status).to.equal(500)
370
+ expect(error.response.text.includes('Forbidden')).beTrue()
371
+ }
372
+ await userService.remove(userObject._id)
373
+ await userService.remove(anotherUserObject._id)
374
+ })
375
+ // Let enough time to process
376
+ .timeout(5000)
377
+
378
+ // Cleanup
379
+ after(async () => {
380
+ if (server) await server.close()
381
+ await app.db.instance.dropDatabase()
382
+ await app.db.disconnect()
383
+ })
384
+ })