@sync-in/server 1.5.2 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (355) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +2 -1
  3. package/environment/environment.dist.min.yaml +1 -0
  4. package/environment/environment.dist.yaml +88 -30
  5. package/migrations/0002_sleepy_korath.sql +1 -0
  6. package/migrations/meta/0002_snapshot.json +2424 -0
  7. package/migrations/meta/_journal.json +7 -0
  8. package/package.json +14 -12
  9. package/server/app.bootstrap.js +1 -1
  10. package/server/app.bootstrap.js.map +1 -1
  11. package/server/applications/files/services/files-manager.service.js +1 -2
  12. package/server/applications/files/services/files-manager.service.js.map +1 -1
  13. package/server/applications/files/services/files-only-office-manager.service.js +5 -6
  14. package/server/applications/files/services/files-only-office-manager.service.js.map +1 -1
  15. package/server/applications/files/utils/files.js +6 -4
  16. package/server/applications/files/utils/files.js.map +1 -1
  17. package/server/applications/links/links.controller.js +2 -2
  18. package/server/applications/links/links.controller.js.map +1 -1
  19. package/server/applications/links/services/links-manager.service.js +2 -1
  20. package/server/applications/links/services/links-manager.service.js.map +1 -1
  21. package/server/applications/links/services/links-manager.service.spec.js +6 -3
  22. package/server/applications/links/services/links-manager.service.spec.js.map +1 -1
  23. package/server/applications/notifications/constants/notifications.js +9 -0
  24. package/server/applications/notifications/constants/notifications.js.map +1 -1
  25. package/server/applications/notifications/i18n/fr.js +10 -1
  26. package/server/applications/notifications/i18n/fr.js.map +1 -1
  27. package/server/applications/notifications/interfaces/notification-properties.interface.js.map +1 -1
  28. package/server/applications/notifications/mails/models.js +41 -3
  29. package/server/applications/notifications/mails/models.js.map +1 -1
  30. package/server/applications/notifications/mails/templates.js +1 -1
  31. package/server/applications/notifications/mails/templates.js.map +1 -1
  32. package/server/applications/notifications/schemas/notifications.schema.js +2 -1
  33. package/server/applications/notifications/schemas/notifications.schema.js.map +1 -1
  34. package/server/applications/notifications/services/notifications-manager.service.js +16 -13
  35. package/server/applications/notifications/services/notifications-manager.service.js.map +1 -1
  36. package/server/applications/notifications/services/notifications-manager.service.spec.js +9 -8
  37. package/server/applications/notifications/services/notifications-manager.service.spec.js.map +1 -1
  38. package/server/applications/notifications/services/notifications-queries.service.js +1 -1
  39. package/server/applications/notifications/services/notifications-queries.service.js.map +1 -1
  40. package/server/applications/shares/services/shares-manager.service.js +3 -2
  41. package/server/applications/shares/services/shares-manager.service.js.map +1 -1
  42. package/server/applications/sync/constants/auth.js +2 -2
  43. package/server/applications/sync/constants/auth.js.map +1 -1
  44. package/server/applications/sync/dtos/sync-client-registration.dto.js +5 -0
  45. package/server/applications/sync/dtos/sync-client-registration.dto.js.map +1 -1
  46. package/server/applications/sync/dtos/sync-operations.dto.js +1 -2
  47. package/server/applications/sync/dtos/sync-operations.dto.js.map +1 -1
  48. package/server/applications/sync/schemas/sync-clients.schema.js +2 -1
  49. package/server/applications/sync/schemas/sync-clients.schema.js.map +1 -1
  50. package/server/applications/sync/schemas/sync-paths.schema.js +2 -1
  51. package/server/applications/sync/schemas/sync-paths.schema.js.map +1 -1
  52. package/server/applications/sync/services/sync-clients-manager.service.js +28 -20
  53. package/server/applications/sync/services/sync-clients-manager.service.js.map +1 -1
  54. package/server/applications/sync/services/sync-clients-manager.service.spec.js +24 -18
  55. package/server/applications/sync/services/sync-clients-manager.service.spec.js.map +1 -1
  56. package/server/applications/sync/services/sync-queries.service.js +5 -5
  57. package/server/applications/sync/services/sync-queries.service.js.map +1 -1
  58. package/server/applications/users/admin-users.controller.js +48 -37
  59. package/server/applications/users/admin-users.controller.js.map +1 -1
  60. package/server/applications/users/admin-users.controller.spec.js +15 -0
  61. package/server/applications/users/admin-users.controller.spec.js.map +1 -1
  62. package/server/applications/users/constants/routes.js +5 -0
  63. package/server/applications/users/constants/routes.js.map +1 -1
  64. package/server/applications/users/constants/user.js +8 -0
  65. package/server/applications/users/constants/user.js.map +1 -1
  66. package/server/applications/users/dto/delete-user.dto.js +5 -23
  67. package/server/applications/users/dto/delete-user.dto.js.map +1 -1
  68. package/server/applications/users/dto/user-properties.dto.js +38 -3
  69. package/server/applications/users/dto/user-properties.dto.js.map +1 -1
  70. package/server/applications/users/interfaces/admin-user.interface.js.map +1 -1
  71. package/server/applications/users/interfaces/user-secrets.interface.js +10 -0
  72. package/server/applications/users/interfaces/user-secrets.interface.js.map +1 -0
  73. package/server/applications/users/models/user.model.js +84 -50
  74. package/server/applications/users/models/user.model.js.map +1 -1
  75. package/server/applications/users/schemas/user.interface.js.map +1 -1
  76. package/server/applications/users/schemas/users.schema.js +2 -0
  77. package/server/applications/users/schemas/users.schema.js.map +1 -1
  78. package/server/applications/users/services/admin-users-manager.service.js +7 -19
  79. package/server/applications/users/services/admin-users-manager.service.js.map +1 -1
  80. package/server/applications/users/services/admin-users-manager.service.spec.js +7 -26
  81. package/server/applications/users/services/admin-users-manager.service.spec.js.map +1 -1
  82. package/server/applications/users/services/admin-users-queries.service.js +1 -0
  83. package/server/applications/users/services/admin-users-queries.service.js.map +1 -1
  84. package/server/applications/users/services/users-manager.service.js +138 -28
  85. package/server/applications/users/services/users-manager.service.js.map +1 -1
  86. package/server/applications/users/services/users-manager.service.spec.js +11 -9
  87. package/server/applications/users/services/users-manager.service.spec.js.map +1 -1
  88. package/server/applications/users/services/users-queries.service.js +63 -57
  89. package/server/applications/users/services/users-queries.service.js.map +1 -1
  90. package/server/applications/users/users.controller.js +48 -1
  91. package/server/applications/users/users.controller.js.map +1 -1
  92. package/server/applications/users/users.controller.spec.js +8 -1
  93. package/server/applications/users/users.controller.spec.js.map +1 -1
  94. package/server/applications/users/users.e2e-spec.js +2 -1
  95. package/server/applications/users/users.e2e-spec.js.map +1 -1
  96. package/server/applications/users/utils/avatar.js +48 -0
  97. package/server/applications/users/utils/avatar.js.map +1 -0
  98. package/server/authentication/auth.config.js +89 -26
  99. package/server/authentication/auth.config.js.map +1 -1
  100. package/server/authentication/auth.controller.js +117 -9
  101. package/server/authentication/auth.controller.js.map +1 -1
  102. package/server/authentication/auth.controller.spec.js +16 -1
  103. package/server/authentication/auth.controller.spec.js.map +1 -1
  104. package/server/authentication/auth.e2e-spec.js +4 -3
  105. package/server/authentication/auth.e2e-spec.js.map +1 -1
  106. package/server/authentication/auth.module.js +4 -1
  107. package/server/authentication/auth.module.js.map +1 -1
  108. package/server/authentication/constants/auth-ldap.js +44 -0
  109. package/server/authentication/constants/auth-ldap.js.map +1 -0
  110. package/server/authentication/constants/auth.js +37 -4
  111. package/server/authentication/constants/auth.js.map +1 -1
  112. package/server/authentication/constants/routes.js +21 -0
  113. package/server/authentication/constants/routes.js.map +1 -1
  114. package/server/authentication/constants/scope.js +20 -0
  115. package/server/authentication/constants/scope.js.map +1 -0
  116. package/server/authentication/dto/login-response.dto.js +27 -4
  117. package/server/authentication/dto/login-response.dto.js.map +1 -1
  118. package/server/authentication/dto/token-response.dto.js +5 -0
  119. package/server/authentication/dto/token-response.dto.js.map +1 -1
  120. package/server/{applications/users/dto/user-password.dto.js → authentication/dto/two-fa-verify.dto.js} +27 -9
  121. package/server/authentication/dto/two-fa-verify.dto.js.map +1 -0
  122. package/server/authentication/guards/auth-basic.strategy.js +6 -5
  123. package/server/authentication/guards/auth-basic.strategy.js.map +1 -1
  124. package/server/authentication/guards/auth-token-access.strategy.js +3 -2
  125. package/server/authentication/guards/auth-token-access.strategy.js.map +1 -1
  126. package/server/authentication/guards/auth-token-refresh.strategy.js +3 -2
  127. package/server/authentication/guards/auth-token-refresh.strategy.js.map +1 -1
  128. package/server/authentication/guards/auth-two-fa-guard.js +81 -0
  129. package/server/authentication/guards/auth-two-fa-guard.js.map +1 -0
  130. package/server/authentication/interfaces/jwt-payload.interface.js +5 -0
  131. package/server/authentication/interfaces/jwt-payload.interface.js.map +1 -1
  132. package/server/authentication/interfaces/token.interface.js +2 -0
  133. package/server/authentication/interfaces/token.interface.js.map +1 -1
  134. package/server/authentication/interfaces/two-fa-setup.interface.js +10 -0
  135. package/server/authentication/interfaces/two-fa-setup.interface.js.map +1 -0
  136. package/server/authentication/models/auth-method.js.map +1 -1
  137. package/server/authentication/services/auth-manager.service.js +72 -49
  138. package/server/authentication/services/auth-manager.service.js.map +1 -1
  139. package/server/authentication/services/auth-methods/auth-method-database.service.js +3 -3
  140. package/server/authentication/services/auth-methods/auth-method-database.service.js.map +1 -1
  141. package/server/authentication/services/auth-methods/auth-method-database.service.spec.js +5 -0
  142. package/server/authentication/services/auth-methods/auth-method-database.service.spec.js.map +1 -1
  143. package/server/authentication/services/auth-methods/auth-method-ldap.service.js +151 -66
  144. package/server/authentication/services/auth-methods/auth-method-ldap.service.js.map +1 -1
  145. package/server/authentication/services/auth-methods/auth-method-ldap.service.spec.js +52 -50
  146. package/server/authentication/services/auth-methods/auth-method-ldap.service.spec.js.map +1 -1
  147. package/server/authentication/services/auth-methods/auth-method-two-fa.service.js +251 -0
  148. package/server/authentication/services/auth-methods/auth-method-two-fa.service.js.map +1 -0
  149. package/server/authentication/services/auth-methods/auth-method-two-fa.service.spec.js +41 -0
  150. package/server/authentication/services/auth-methods/auth-method-two-fa.service.spec.js.map +1 -0
  151. package/server/authentication/utils/crypt-secret.js +68 -0
  152. package/server/authentication/utils/crypt-secret.js.map +1 -0
  153. package/server/common/functions.js +18 -2
  154. package/server/common/functions.js.map +1 -1
  155. package/server/common/qrcode.js +34 -0
  156. package/server/common/qrcode.js.map +1 -0
  157. package/server/common/shared.js +18 -0
  158. package/server/common/shared.js.map +1 -1
  159. package/server/configuration/config.environment.js +23 -6
  160. package/server/configuration/config.environment.js.map +1 -1
  161. package/server/configuration/config.interfaces.js +10 -0
  162. package/server/configuration/config.interfaces.js.map +1 -0
  163. package/server/configuration/config.loader.js.map +1 -1
  164. package/server/configuration/config.validation.js +13 -13
  165. package/server/configuration/config.validation.js.map +1 -1
  166. package/server/infrastructure/cache/adapters/mysql-cache.adapter.js +6 -6
  167. package/server/infrastructure/cache/adapters/mysql-cache.adapter.js.map +1 -1
  168. package/server/infrastructure/cache/schemas/mysql-cache.schema.js +2 -1
  169. package/server/infrastructure/cache/schemas/mysql-cache.schema.js.map +1 -1
  170. package/server/infrastructure/cache/services/cache.service.js.map +1 -1
  171. package/server/infrastructure/database/columns.js +39 -0
  172. package/server/infrastructure/database/columns.js.map +1 -0
  173. package/server/infrastructure/database/database.config.js +0 -1
  174. package/server/infrastructure/database/database.config.js.map +1 -1
  175. package/server/infrastructure/mailer/interfaces/mail.interface.js.map +1 -1
  176. package/server/infrastructure/mailer/mailer.config.js +12 -0
  177. package/server/infrastructure/mailer/mailer.config.js.map +1 -1
  178. package/server/infrastructure/mailer/mailer.service.js +2 -1
  179. package/server/infrastructure/mailer/mailer.service.js.map +1 -1
  180. package/static/assets/mimes/text-x-c.svg +1 -0
  181. package/static/assets/pdfjs/build/pdf.mjs +2522 -914
  182. package/static/assets/pdfjs/build/pdf.mjs.map +1 -1
  183. package/static/assets/pdfjs/build/pdf.sandbox.mjs +2 -2
  184. package/static/assets/pdfjs/build/pdf.worker.mjs +1024 -566
  185. package/static/assets/pdfjs/build/pdf.worker.mjs.map +1 -1
  186. package/static/assets/pdfjs/version +1 -1
  187. package/static/assets/pdfjs/web/debugger.mjs +116 -37
  188. package/static/assets/pdfjs/web/images/comment-popup-editButton.svg +5 -0
  189. package/static/assets/pdfjs/web/locale/ach/viewer.ftl +0 -12
  190. package/static/assets/pdfjs/web/locale/af/viewer.ftl +0 -12
  191. package/static/assets/pdfjs/web/locale/an/viewer.ftl +0 -16
  192. package/static/assets/pdfjs/web/locale/ar/viewer.ftl +0 -32
  193. package/static/assets/pdfjs/web/locale/ast/viewer.ftl +0 -19
  194. package/static/assets/pdfjs/web/locale/az/viewer.ftl +0 -16
  195. package/static/assets/pdfjs/web/locale/be/viewer.ftl +0 -32
  196. package/static/assets/pdfjs/web/locale/bg/viewer.ftl +0 -32
  197. package/static/assets/pdfjs/web/locale/bn/viewer.ftl +0 -16
  198. package/static/assets/pdfjs/web/locale/bo/viewer.ftl +0 -12
  199. package/static/assets/pdfjs/web/locale/br/viewer.ftl +0 -22
  200. package/static/assets/pdfjs/web/locale/brx/viewer.ftl +0 -16
  201. package/static/assets/pdfjs/web/locale/bs/viewer.ftl +0 -32
  202. package/static/assets/pdfjs/web/locale/ca/viewer.ftl +12 -23
  203. package/static/assets/pdfjs/web/locale/cak/viewer.ftl +0 -23
  204. package/static/assets/pdfjs/web/locale/ckb/viewer.ftl +0 -16
  205. package/static/assets/pdfjs/web/locale/cs/viewer.ftl +0 -32
  206. package/static/assets/pdfjs/web/locale/cy/viewer.ftl +0 -32
  207. package/static/assets/pdfjs/web/locale/da/viewer.ftl +3 -35
  208. package/static/assets/pdfjs/web/locale/de/viewer.ftl +0 -32
  209. package/static/assets/pdfjs/web/locale/dsb/viewer.ftl +0 -32
  210. package/static/assets/pdfjs/web/locale/el/viewer.ftl +0 -32
  211. package/static/assets/pdfjs/web/locale/en-CA/viewer.ftl +0 -32
  212. package/static/assets/pdfjs/web/locale/en-GB/viewer.ftl +0 -32
  213. package/static/assets/pdfjs/web/locale/en-US/viewer.ftl +25 -13
  214. package/static/assets/pdfjs/web/locale/eo/viewer.ftl +0 -32
  215. package/static/assets/pdfjs/web/locale/es-AR/viewer.ftl +0 -32
  216. package/static/assets/pdfjs/web/locale/es-CL/viewer.ftl +0 -32
  217. package/static/assets/pdfjs/web/locale/es-ES/viewer.ftl +5 -32
  218. package/static/assets/pdfjs/web/locale/es-MX/viewer.ftl +0 -32
  219. package/static/assets/pdfjs/web/locale/et/viewer.ftl +0 -16
  220. package/static/assets/pdfjs/web/locale/eu/viewer.ftl +38 -32
  221. package/static/assets/pdfjs/web/locale/fa/viewer.ftl +0 -19
  222. package/static/assets/pdfjs/web/locale/ff/viewer.ftl +0 -12
  223. package/static/assets/pdfjs/web/locale/fi/viewer.ftl +0 -32
  224. package/static/assets/pdfjs/web/locale/fr/viewer.ftl +0 -32
  225. package/static/assets/pdfjs/web/locale/fur/viewer.ftl +0 -32
  226. package/static/assets/pdfjs/web/locale/fy-NL/viewer.ftl +0 -32
  227. package/static/assets/pdfjs/web/locale/ga-IE/viewer.ftl +0 -12
  228. package/static/assets/pdfjs/web/locale/gd/viewer.ftl +0 -23
  229. package/static/assets/pdfjs/web/locale/gl/viewer.ftl +0 -32
  230. package/static/assets/pdfjs/web/locale/gn/viewer.ftl +0 -32
  231. package/static/assets/pdfjs/web/locale/gu-IN/viewer.ftl +0 -12
  232. package/static/assets/pdfjs/web/locale/he/viewer.ftl +0 -32
  233. package/static/assets/pdfjs/web/locale/hi-IN/viewer.ftl +0 -16
  234. package/static/assets/pdfjs/web/locale/hr/viewer.ftl +0 -32
  235. package/static/assets/pdfjs/web/locale/hsb/viewer.ftl +0 -32
  236. package/static/assets/pdfjs/web/locale/hu/viewer.ftl +0 -32
  237. package/static/assets/pdfjs/web/locale/hy-AM/viewer.ftl +372 -16
  238. package/static/assets/pdfjs/web/locale/hye/viewer.ftl +0 -16
  239. package/static/assets/pdfjs/web/locale/ia/viewer.ftl +0 -32
  240. package/static/assets/pdfjs/web/locale/id/viewer.ftl +38 -32
  241. package/static/assets/pdfjs/web/locale/is/viewer.ftl +27 -32
  242. package/static/assets/pdfjs/web/locale/it/viewer.ftl +0 -33
  243. package/static/assets/pdfjs/web/locale/ja/viewer.ftl +31 -33
  244. package/static/assets/pdfjs/web/locale/ka/viewer.ftl +0 -32
  245. package/static/assets/pdfjs/web/locale/kab/viewer.ftl +0 -32
  246. package/static/assets/pdfjs/web/locale/kk/viewer.ftl +31 -32
  247. package/static/assets/pdfjs/web/locale/km/viewer.ftl +0 -12
  248. package/static/assets/pdfjs/web/locale/kn/viewer.ftl +0 -12
  249. package/static/assets/pdfjs/web/locale/ko/viewer.ftl +0 -32
  250. package/static/assets/pdfjs/web/locale/lij/viewer.ftl +0 -12
  251. package/static/assets/pdfjs/web/locale/lo/viewer.ftl +0 -23
  252. package/static/assets/pdfjs/web/locale/lt/viewer.ftl +0 -16
  253. package/static/assets/pdfjs/web/locale/ltg/viewer.ftl +0 -12
  254. package/static/assets/pdfjs/web/locale/lv/viewer.ftl +0 -12
  255. package/static/assets/pdfjs/web/locale/meh/viewer.ftl +0 -14
  256. package/static/assets/pdfjs/web/locale/mk/viewer.ftl +0 -19
  257. package/static/assets/pdfjs/web/locale/ml/viewer.ftl +0 -31
  258. package/static/assets/pdfjs/web/locale/mr/viewer.ftl +0 -16
  259. package/static/assets/pdfjs/web/locale/ms/viewer.ftl +0 -12
  260. package/static/assets/pdfjs/web/locale/my/viewer.ftl +0 -12
  261. package/static/assets/pdfjs/web/locale/nb-NO/viewer.ftl +0 -32
  262. package/static/assets/pdfjs/web/locale/ne-NP/viewer.ftl +0 -12
  263. package/static/assets/pdfjs/web/locale/nl/viewer.ftl +0 -32
  264. package/static/assets/pdfjs/web/locale/nn-NO/viewer.ftl +0 -32
  265. package/static/assets/pdfjs/web/locale/oc/viewer.ftl +0 -24
  266. package/static/assets/pdfjs/web/locale/pa-IN/viewer.ftl +0 -32
  267. package/static/assets/pdfjs/web/locale/pl/viewer.ftl +0 -32
  268. package/static/assets/pdfjs/web/locale/pt-BR/viewer.ftl +0 -32
  269. package/static/assets/pdfjs/web/locale/pt-PT/viewer.ftl +0 -32
  270. package/static/assets/pdfjs/web/locale/rm/viewer.ftl +0 -32
  271. package/static/assets/pdfjs/web/locale/ro/viewer.ftl +5 -37
  272. package/static/assets/pdfjs/web/locale/ru/viewer.ftl +0 -32
  273. package/static/assets/pdfjs/web/locale/sat/viewer.ftl +0 -23
  274. package/static/assets/pdfjs/web/locale/sc/viewer.ftl +8 -27
  275. package/static/assets/pdfjs/web/locale/sco/viewer.ftl +0 -16
  276. package/static/assets/pdfjs/web/locale/si/viewer.ftl +0 -22
  277. package/static/assets/pdfjs/web/locale/sk/viewer.ftl +0 -32
  278. package/static/assets/pdfjs/web/locale/skr/viewer.ftl +0 -32
  279. package/static/assets/pdfjs/web/locale/sl/viewer.ftl +30 -32
  280. package/static/assets/pdfjs/web/locale/son/viewer.ftl +0 -12
  281. package/static/assets/pdfjs/web/locale/sq/viewer.ftl +0 -32
  282. package/static/assets/pdfjs/web/locale/sr/viewer.ftl +0 -32
  283. package/static/assets/pdfjs/web/locale/sv-SE/viewer.ftl +0 -32
  284. package/static/assets/pdfjs/web/locale/szl/viewer.ftl +0 -16
  285. package/static/assets/pdfjs/web/locale/ta/viewer.ftl +0 -12
  286. package/static/assets/pdfjs/web/locale/te/viewer.ftl +0 -16
  287. package/static/assets/pdfjs/web/locale/tg/viewer.ftl +0 -32
  288. package/static/assets/pdfjs/web/locale/th/viewer.ftl +38 -32
  289. package/static/assets/pdfjs/web/locale/tl/viewer.ftl +0 -16
  290. package/static/assets/pdfjs/web/locale/tr/viewer.ftl +0 -32
  291. package/static/assets/pdfjs/web/locale/trs/viewer.ftl +0 -12
  292. package/static/assets/pdfjs/web/locale/uk/viewer.ftl +0 -32
  293. package/static/assets/pdfjs/web/locale/ur/viewer.ftl +0 -16
  294. package/static/assets/pdfjs/web/locale/uz/viewer.ftl +0 -12
  295. package/static/assets/pdfjs/web/locale/vi/viewer.ftl +0 -32
  296. package/static/assets/pdfjs/web/locale/xh/viewer.ftl +0 -12
  297. package/static/assets/pdfjs/web/locale/zh-CN/viewer.ftl +0 -32
  298. package/static/assets/pdfjs/web/locale/zh-TW/viewer.ftl +0 -32
  299. package/static/assets/pdfjs/web/viewer.css +586 -437
  300. package/static/assets/pdfjs/web/viewer.html +12 -23
  301. package/static/assets/pdfjs/web/viewer.mjs +955 -514
  302. package/static/assets/pdfjs/web/viewer.mjs.map +1 -1
  303. package/static/assets/pdfjs/web/wasm/openjpeg.wasm +0 -0
  304. package/static/assets/pdfjs/web/wasm/openjpeg_nowasm_fallback.js +10 -22
  305. package/static/{chunk-SPTF6FSM.js → chunk-27YQB3TE.js} +1 -1
  306. package/static/chunk-2I4CUFUA.js +1 -0
  307. package/static/chunk-2MTM6SWN.js +4 -0
  308. package/static/{chunk-7VRUZRJG.js → chunk-34MKICK5.js} +2 -2
  309. package/static/chunk-5O3DIUU3.js +1 -0
  310. package/static/{chunk-VJRTMDEJ.js → chunk-6NMVZIIT.js} +1 -1
  311. package/static/{chunk-L6MU6S2V.js → chunk-7DN7ZAPU.js} +1 -1
  312. package/static/{chunk-MVO4WZLK.js → chunk-7FUM3JGM.js} +1 -1
  313. package/static/{chunk-RSS6GYNE.js → chunk-7ITZXYYJ.js} +1 -1
  314. package/static/chunk-7P27WBGC.js +4 -0
  315. package/static/chunk-ATP3BFHV.js +562 -0
  316. package/static/chunk-AWQ2YTVC.js +1 -0
  317. package/static/chunk-DSOE3FEP.js +1 -0
  318. package/static/{chunk-2R6HHGUR.js → chunk-EFKMBLRE.js} +1 -1
  319. package/static/chunk-FUFKVHPU.js +1 -0
  320. package/static/{chunk-MRSWNAVB.js → chunk-HCDLWTMW.js} +1 -1
  321. package/static/chunk-IPAC4VAF.js +1 -0
  322. package/static/{chunk-ZC5NIT55.js → chunk-IQOALFYU.js} +1 -1
  323. package/static/chunk-JASU3CIH.js +1 -0
  324. package/static/{chunk-6OJZWYRZ.js → chunk-JQ5FTO2M.js} +1 -1
  325. package/static/chunk-JUNZFADM.js +1 -0
  326. package/static/{chunk-LLWSLOSX.js → chunk-LJUKI4SQ.js} +1 -1
  327. package/static/{chunk-WI7FOANP.js → chunk-LUWQFIWR.js} +1 -1
  328. package/static/{chunk-BIUNUYZ5.js → chunk-ORMRCEGT.js} +1 -1
  329. package/static/{chunk-IZL7JPTS.js → chunk-Q7D6RN4N.js} +1 -1
  330. package/static/{chunk-JYXLQRHG.js → chunk-QJX6ITLW.js} +1 -1
  331. package/static/{chunk-YJMN3B4N.js → chunk-QQ6UQQBR.js} +1 -1
  332. package/static/chunk-S2HDY3OL.js +1 -0
  333. package/static/{chunk-NE4NDO45.js → chunk-S75P2FFI.js} +1 -1
  334. package/static/{chunk-CRQNEHTX.js → chunk-T3EYFSVZ.js} +1 -1
  335. package/static/{chunk-MCLQFZ3S.js → chunk-U34OZUZ7.js} +1 -1
  336. package/static/chunk-Y7EH7G5K.js +1 -0
  337. package/static/{chunk-MGGT6MIJ.js → chunk-ZQQPUYLU.js} +1 -1
  338. package/static/index.html +2 -2
  339. package/static/main-7SQDDVMD.js +9 -0
  340. package/static/{styles-FYUSO6OJ.css → styles-A5VYX3CE.css} +1 -1
  341. package/server/applications/users/dto/user-password.dto.js.map +0 -1
  342. package/static/chunk-4U5A2DEP.js +0 -4
  343. package/static/chunk-54EAZ2UD.js +0 -1
  344. package/static/chunk-7ZRXJONB.js +0 -1
  345. package/static/chunk-F2J2IIJE.js +0 -1
  346. package/static/chunk-FNFGUIQH.js +0 -4
  347. package/static/chunk-GGLK52CG.js +0 -1
  348. package/static/chunk-HW2H3ISM.js +0 -559
  349. package/static/chunk-HX6BBYVD.js +0 -1
  350. package/static/chunk-JF7S3UYQ.js +0 -1
  351. package/static/chunk-KSHPKI4G.js +0 -1
  352. package/static/chunk-VPJ2V27B.js +0 -1
  353. package/static/chunk-VUI3KV7V.js +0 -1
  354. package/static/chunk-ZXS4V7J2.js +0 -1
  355. package/static/main-FFIWFD2F.js +0 -7
@@ -14,6 +14,7 @@ Object.defineProperty(exports, "syncPaths", {
14
14
  });
15
15
  const _drizzleorm = require("drizzle-orm");
16
16
  const _mysqlcore = require("drizzle-orm/mysql-core");
17
+ const _columns = require("../../../infrastructure/database/columns");
17
18
  const _filesschema = require("../../files/schemas/files.schema");
18
19
  const _sharesschema = require("../../shares/schemas/shares.schema");
19
20
  const _spacesrootsschema = require("../../spaces/schemas/spaces-roots.schema");
@@ -60,7 +61,7 @@ const syncPaths = (0, _mysqlcore.mysqlTable)('sync_paths', {
60
61
  }).references(()=>_filesschema.files.id, {
61
62
  onDelete: 'cascade'
62
63
  }),
63
- settings: (0, _mysqlcore.json)('settings').$type().notNull(),
64
+ settings: (0, _columns.jsonColumn)()('settings').notNull(),
64
65
  createdAt: (0, _mysqlcore.datetime)('createdAt', {
65
66
  mode: 'date'
66
67
  }).default((0, _drizzleorm.sql)`CURRENT_TIMESTAMP`).notNull()
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/applications/sync/schemas/sync-paths.schema.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { sql } from 'drizzle-orm'\nimport { bigint, char, datetime, index, json, mysqlTable } from 'drizzle-orm/mysql-core'\nimport { files } from '../../files/schemas/files.schema'\nimport { shares } from '../../shares/schemas/shares.schema'\nimport { spacesRoots } from '../../spaces/schemas/spaces-roots.schema'\nimport { spaces } from '../../spaces/schemas/spaces.schema'\nimport { users } from '../../users/schemas/users.schema'\nimport { SyncPathSettings } from '../interfaces/sync-path.interface'\nimport { syncClients } from './sync-clients.schema'\n\n/*\n ownerId: sync personal space partially (fileId is required)\n spaceId: sync space partially (fileId or spaceRootId are required)\n spaceRootId: sync all the space root or partially if fileId is specified (spaceId required)\n shareId: sync all the share or partially (fileId is required)\n fileId: sync a specific directory (ownerId, spaceId (or spaceId with spaceRootId), shareId are required)\n*/\n\nexport const syncPaths = mysqlTable(\n 'sync_paths',\n {\n id: bigint('id', { mode: 'number', unsigned: true }).autoincrement().primaryKey(),\n clientId: char('clientId', { length: 36 })\n .references(() => syncClients.id, { onDelete: 'cascade' })\n .notNull(),\n ownerId: bigint('ownerId', { mode: 'number', unsigned: true }).references(() => users.id, { onDelete: 'cascade' }),\n spaceId: bigint('spaceId', { mode: 'number', unsigned: true }).references(() => spaces.id, { onDelete: 'cascade' }),\n spaceRootId: bigint('spaceRootId', { mode: 'number', unsigned: true }).references(() => spacesRoots.id, { onDelete: 'cascade' }),\n shareId: bigint('shareId', { mode: 'number', unsigned: true }).references(() => shares.id, { onDelete: 'cascade' }),\n fileId: bigint('fileId', { mode: 'number', unsigned: true }).references(() => files.id, { onDelete: 'cascade' }),\n settings: json('settings').$type<SyncPathSettings>().notNull(),\n createdAt: datetime('createdAt', { mode: 'date' })\n .default(sql`CURRENT_TIMESTAMP`)\n .notNull()\n },\n (table) => [\n index('client_idx').on(table.clientId),\n index('owner_idx').on(table.ownerId),\n index('space_idx').on(table.spaceId),\n index('space_root_idx').on(table.spaceRootId),\n index('share_idx').on(table.shareId),\n index('file_idx').on(table.fileId)\n ]\n)\n"],"names":["syncPaths","mysqlTable","id","bigint","mode","unsigned","autoincrement","primaryKey","clientId","char","length","references","syncClients","onDelete","notNull","ownerId","users","spaceId","spaces","spaceRootId","spacesRoots","shareId","shares","fileId","files","settings","json","$type","createdAt","datetime","default","sql","table","index","on"],"mappings":"AAAA;;;;CAIC;;;;+BAoBYA;;;eAAAA;;;4BAlBO;2BAC4C;6BAC1C;8BACC;mCACK;8BACL;6BACD;mCAEM;AAUrB,MAAMA,YAAYC,IAAAA,qBAAU,EACjC,cACA;IACEC,IAAIC,IAAAA,iBAAM,EAAC,MAAM;QAAEC,MAAM;QAAUC,UAAU;IAAK,GAAGC,aAAa,GAAGC,UAAU;IAC/EC,UAAUC,IAAAA,eAAI,EAAC,YAAY;QAAEC,QAAQ;IAAG,GACrCC,UAAU,CAAC,IAAMC,8BAAW,CAACV,EAAE,EAAE;QAAEW,UAAU;IAAU,GACvDC,OAAO;IACVC,SAASZ,IAAAA,iBAAM,EAAC,WAAW;QAAEC,MAAM;QAAUC,UAAU;IAAK,GAAGM,UAAU,CAAC,IAAMK,kBAAK,CAACd,EAAE,EAAE;QAAEW,UAAU;IAAU;IAChHI,SAASd,IAAAA,iBAAM,EAAC,WAAW;QAAEC,MAAM;QAAUC,UAAU;IAAK,GAAGM,UAAU,CAAC,IAAMO,oBAAM,CAAChB,EAAE,EAAE;QAAEW,UAAU;IAAU;IACjHM,aAAahB,IAAAA,iBAAM,EAAC,eAAe;QAAEC,MAAM;QAAUC,UAAU;IAAK,GAAGM,UAAU,CAAC,IAAMS,8BAAW,CAAClB,EAAE,EAAE;QAAEW,UAAU;IAAU;IAC9HQ,SAASlB,IAAAA,iBAAM,EAAC,WAAW;QAAEC,MAAM;QAAUC,UAAU;IAAK,GAAGM,UAAU,CAAC,IAAMW,oBAAM,CAACpB,EAAE,EAAE;QAAEW,UAAU;IAAU;IACjHU,QAAQpB,IAAAA,iBAAM,EAAC,UAAU;QAAEC,MAAM;QAAUC,UAAU;IAAK,GAAGM,UAAU,CAAC,IAAMa,kBAAK,CAACtB,EAAE,EAAE;QAAEW,UAAU;IAAU;IAC9GY,UAAUC,IAAAA,eAAI,EAAC,YAAYC,KAAK,GAAqBb,OAAO;IAC5Dc,WAAWC,IAAAA,mBAAQ,EAAC,aAAa;QAAEzB,MAAM;IAAO,GAC7C0B,OAAO,CAACC,IAAAA,eAAG,CAAA,CAAC,iBAAiB,CAAC,EAC9BjB,OAAO;AACZ,GACA,CAACkB,QAAU;QACTC,IAAAA,gBAAK,EAAC,cAAcC,EAAE,CAACF,MAAMxB,QAAQ;QACrCyB,IAAAA,gBAAK,EAAC,aAAaC,EAAE,CAACF,MAAMjB,OAAO;QACnCkB,IAAAA,gBAAK,EAAC,aAAaC,EAAE,CAACF,MAAMf,OAAO;QACnCgB,IAAAA,gBAAK,EAAC,kBAAkBC,EAAE,CAACF,MAAMb,WAAW;QAC5Cc,IAAAA,gBAAK,EAAC,aAAaC,EAAE,CAACF,MAAMX,OAAO;QACnCY,IAAAA,gBAAK,EAAC,YAAYC,EAAE,CAACF,MAAMT,MAAM;KAClC"}
1
+ {"version":3,"sources":["../../../../../backend/src/applications/sync/schemas/sync-paths.schema.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { sql } from 'drizzle-orm'\nimport { bigint, char, datetime, index, mysqlTable } from 'drizzle-orm/mysql-core'\nimport { jsonColumn } from '../../../infrastructure/database/columns'\nimport { files } from '../../files/schemas/files.schema'\nimport { shares } from '../../shares/schemas/shares.schema'\nimport { spacesRoots } from '../../spaces/schemas/spaces-roots.schema'\nimport { spaces } from '../../spaces/schemas/spaces.schema'\nimport { users } from '../../users/schemas/users.schema'\nimport { SyncPathSettings } from '../interfaces/sync-path.interface'\nimport { syncClients } from './sync-clients.schema'\n\n/*\n ownerId: sync personal space partially (fileId is required)\n spaceId: sync space partially (fileId or spaceRootId are required)\n spaceRootId: sync all the space root or partially if fileId is specified (spaceId required)\n shareId: sync all the share or partially (fileId is required)\n fileId: sync a specific directory (ownerId, spaceId (or spaceId with spaceRootId), shareId are required)\n*/\n\nexport const syncPaths = mysqlTable(\n 'sync_paths',\n {\n id: bigint('id', { mode: 'number', unsigned: true }).autoincrement().primaryKey(),\n clientId: char('clientId', { length: 36 })\n .references(() => syncClients.id, { onDelete: 'cascade' })\n .notNull(),\n ownerId: bigint('ownerId', { mode: 'number', unsigned: true }).references(() => users.id, { onDelete: 'cascade' }),\n spaceId: bigint('spaceId', { mode: 'number', unsigned: true }).references(() => spaces.id, { onDelete: 'cascade' }),\n spaceRootId: bigint('spaceRootId', { mode: 'number', unsigned: true }).references(() => spacesRoots.id, { onDelete: 'cascade' }),\n shareId: bigint('shareId', { mode: 'number', unsigned: true }).references(() => shares.id, { onDelete: 'cascade' }),\n fileId: bigint('fileId', { mode: 'number', unsigned: true }).references(() => files.id, { onDelete: 'cascade' }),\n settings: jsonColumn<SyncPathSettings>()('settings').notNull(),\n createdAt: datetime('createdAt', { mode: 'date' })\n .default(sql`CURRENT_TIMESTAMP`)\n .notNull()\n },\n (table) => [\n index('client_idx').on(table.clientId),\n index('owner_idx').on(table.ownerId),\n index('space_idx').on(table.spaceId),\n index('space_root_idx').on(table.spaceRootId),\n index('share_idx').on(table.shareId),\n index('file_idx').on(table.fileId)\n ]\n)\n"],"names":["syncPaths","mysqlTable","id","bigint","mode","unsigned","autoincrement","primaryKey","clientId","char","length","references","syncClients","onDelete","notNull","ownerId","users","spaceId","spaces","spaceRootId","spacesRoots","shareId","shares","fileId","files","settings","jsonColumn","createdAt","datetime","default","sql","table","index","on"],"mappings":"AAAA;;;;CAIC;;;;+BAqBYA;;;eAAAA;;;4BAnBO;2BACsC;yBAC/B;6BACL;8BACC;mCACK;8BACL;6BACD;mCAEM;AAUrB,MAAMA,YAAYC,IAAAA,qBAAU,EACjC,cACA;IACEC,IAAIC,IAAAA,iBAAM,EAAC,MAAM;QAAEC,MAAM;QAAUC,UAAU;IAAK,GAAGC,aAAa,GAAGC,UAAU;IAC/EC,UAAUC,IAAAA,eAAI,EAAC,YAAY;QAAEC,QAAQ;IAAG,GACrCC,UAAU,CAAC,IAAMC,8BAAW,CAACV,EAAE,EAAE;QAAEW,UAAU;IAAU,GACvDC,OAAO;IACVC,SAASZ,IAAAA,iBAAM,EAAC,WAAW;QAAEC,MAAM;QAAUC,UAAU;IAAK,GAAGM,UAAU,CAAC,IAAMK,kBAAK,CAACd,EAAE,EAAE;QAAEW,UAAU;IAAU;IAChHI,SAASd,IAAAA,iBAAM,EAAC,WAAW;QAAEC,MAAM;QAAUC,UAAU;IAAK,GAAGM,UAAU,CAAC,IAAMO,oBAAM,CAAChB,EAAE,EAAE;QAAEW,UAAU;IAAU;IACjHM,aAAahB,IAAAA,iBAAM,EAAC,eAAe;QAAEC,MAAM;QAAUC,UAAU;IAAK,GAAGM,UAAU,CAAC,IAAMS,8BAAW,CAAClB,EAAE,EAAE;QAAEW,UAAU;IAAU;IAC9HQ,SAASlB,IAAAA,iBAAM,EAAC,WAAW;QAAEC,MAAM;QAAUC,UAAU;IAAK,GAAGM,UAAU,CAAC,IAAMW,oBAAM,CAACpB,EAAE,EAAE;QAAEW,UAAU;IAAU;IACjHU,QAAQpB,IAAAA,iBAAM,EAAC,UAAU;QAAEC,MAAM;QAAUC,UAAU;IAAK,GAAGM,UAAU,CAAC,IAAMa,kBAAK,CAACtB,EAAE,EAAE;QAAEW,UAAU;IAAU;IAC9GY,UAAUC,IAAAA,mBAAU,IAAqB,YAAYZ,OAAO;IAC5Da,WAAWC,IAAAA,mBAAQ,EAAC,aAAa;QAAExB,MAAM;IAAO,GAC7CyB,OAAO,CAACC,IAAAA,eAAG,CAAA,CAAC,iBAAiB,CAAC,EAC9BhB,OAAO;AACZ,GACA,CAACiB,QAAU;QACTC,IAAAA,gBAAK,EAAC,cAAcC,EAAE,CAACF,MAAMvB,QAAQ;QACrCwB,IAAAA,gBAAK,EAAC,aAAaC,EAAE,CAACF,MAAMhB,OAAO;QACnCiB,IAAAA,gBAAK,EAAC,aAAaC,EAAE,CAACF,MAAMd,OAAO;QACnCe,IAAAA,gBAAK,EAAC,kBAAkBC,EAAE,CAACF,MAAMZ,WAAW;QAC5Ca,IAAAA,gBAAK,EAAC,aAAaC,EAAE,CAACF,MAAMV,OAAO;QACnCW,IAAAA,gBAAK,EAAC,YAAYC,EAAE,CAACF,MAAMR,MAAM;KAClC"}
@@ -19,6 +19,7 @@ const _promises = /*#__PURE__*/ _interop_require_default(require("node:fs/promis
19
19
  const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
20
20
  const _authmethod = require("../../../authentication/models/auth-method");
21
21
  const _authmanagerservice = require("../../../authentication/services/auth-manager.service");
22
+ const _authmethodtwofaservice = require("../../../authentication/services/auth-methods/auth-method-two-fa.service");
22
23
  const _functions = require("../../../common/functions");
23
24
  const _shared = require("../../../common/shared");
24
25
  const _configconstants = require("../../../configuration/config.constants");
@@ -27,8 +28,7 @@ const _cachedecorator = require("../../../infrastructure/cache/cache.decorator")
27
28
  const _applicationsconstants = require("../../applications.constants");
28
29
  const _files = require("../../files/utils/files");
29
30
  const _user = require("../../users/constants/user");
30
- const _usermodel = require("../../users/models/user.model");
31
- const _usersqueriesservice = require("../../users/services/users-queries.service");
31
+ const _usersmanagerservice = require("../../users/services/users-manager.service");
32
32
  const _auth = require("../constants/auth");
33
33
  const _store = require("../constants/store");
34
34
  const _sync = require("../constants/sync");
@@ -52,12 +52,24 @@ let SyncClientsManager = class SyncClientsManager {
52
52
  const user = await this.authMethod.validateUser(clientRegistrationDto.login, clientRegistrationDto.password);
53
53
  if (!user) {
54
54
  this.logger.warn(`${this.register.name} - auth failed for user *${clientRegistrationDto.login}*`);
55
- throw new _common.HttpException('Not authorized', _common.HttpStatus.UNAUTHORIZED);
55
+ throw new _common.HttpException('Wrong login or password', _common.HttpStatus.UNAUTHORIZED);
56
56
  }
57
57
  if (!user.havePermission(_user.USER_PERMISSION.DESKTOP_APP)) {
58
- this.logger.warn(`${this.register.name} - does not have permission : ${_user.USER_PERMISSION.DESKTOP_APP}`);
58
+ this.logger.warn(`${this.register.name} - user *${user.login}* (${user.id}) does not have permission : ${_user.USER_PERMISSION.DESKTOP_APP}`);
59
59
  throw new _common.HttpException('Missing permission', _common.HttpStatus.FORBIDDEN);
60
60
  }
61
+ if (_configenvironment.configuration.auth.mfa.totp.enabled && user.twoFaEnabled) {
62
+ if (!clientRegistrationDto.code) {
63
+ this.logger.warn(`${this.register.name} - missing two-fa code for user *${user.login}* (${user.id})`);
64
+ throw new _common.HttpException('Missing TWO-FA code', _common.HttpStatus.UNAUTHORIZED);
65
+ }
66
+ const auth = this.authMethod2Fa.validateTwoFactorCode(clientRegistrationDto.code, user.secrets.twoFaSecret);
67
+ if (!auth.success) {
68
+ this.logger.warn(`${this.register.name} - wrong two-fa code for user *${user.login}* (${user.id})`);
69
+ this.usersManager.updateAccesses(user, ip, false).catch((e)=>this.logger.error(`${this.register.name} - ${e}`));
70
+ throw new _common.HttpException(auth.message, _common.HttpStatus.UNAUTHORIZED);
71
+ }
72
+ }
61
73
  try {
62
74
  const token = await this.syncQueries.getOrCreateClient(user.id, clientRegistrationDto.clientId, clientRegistrationDto.info, ip);
63
75
  this.logger.log(`${this.register.name} - client *${clientRegistrationDto.info.type}* was registered for user *${user.login}* (${user.id})`);
@@ -89,37 +101,31 @@ let SyncClientsManager = class SyncClientsManager {
89
101
  throw new _common.HttpException(_auth.CLIENT_TOKEN_EXPIRED_ERROR, _common.HttpStatus.FORBIDDEN);
90
102
  }
91
103
  this.syncQueries.updateClientInfo(client, client.info, ip).catch((e)=>this.logger.error(`${this.authenticate.name} - ${e}`));
92
- const user = await this.usersQueries.from(client.ownerId);
104
+ const user = await this.usersManager.fromUserId(client.ownerId);
93
105
  if (!user) {
94
106
  throw new _common.HttpException('User does not exist', _common.HttpStatus.FORBIDDEN);
95
107
  }
96
108
  if (!user.isActive) {
97
109
  throw new _common.HttpException('Account suspended or not authorized', _common.HttpStatus.FORBIDDEN);
98
110
  }
99
- const owner = new _usermodel.UserModel(user);
100
- if (!owner.havePermission(_user.USER_PERMISSION.DESKTOP_APP)) {
111
+ if (!user.havePermission(_user.USER_PERMISSION.DESKTOP_APP)) {
101
112
  this.logger.warn(`${this.register.name} - does not have permission : ${_user.USER_PERMISSION.DESKTOP_APP}`);
102
113
  throw new _common.HttpException('Missing permission', _common.HttpStatus.FORBIDDEN);
103
114
  }
104
115
  // set clientId
105
- owner.clientId = client.id;
116
+ user.clientId = client.id;
106
117
  // update accesses
107
- this.usersQueries.updateUserOrGuest(owner.id, {
108
- lastAccess: owner.currentAccess,
109
- currentAccess: new Date(),
110
- lastIp: owner.currentIp,
111
- currentIp: ip
112
- }).catch((e)=>this.logger.error(`${this.authenticate.name} - ${e}`));
118
+ this.usersManager.updateAccesses(user, ip, true).catch((e)=>this.logger.error(`${this.authenticate.name} - ${e}`));
113
119
  let r;
114
120
  if (authType === _auth.CLIENT_AUTH_TYPE.COOKIE) {
115
121
  // used by the desktop app to perform the login setup using cookies
116
- r = await this.authManager.setCookies(owner, res);
122
+ r = await this.authManager.setCookies(user, res);
117
123
  } else if (authType === _auth.CLIENT_AUTH_TYPE.TOKEN) {
118
124
  // used by the cli app and the sync core
119
- r = await this.authManager.getTokens(owner);
125
+ r = await this.authManager.getTokens(user);
120
126
  }
121
127
  // check if the client token must be updated
122
- r.client_token_update = await this.renewTokenAndExpiration(client, owner);
128
+ r.client_token_update = await this.renewTokenAndExpiration(client, user);
123
129
  return r;
124
130
  }
125
131
  getClients(user) {
@@ -188,11 +194,12 @@ let SyncClientsManager = class SyncClientsManager {
188
194
  }
189
195
  return manifest;
190
196
  }
191
- constructor(http, authManager, authMethod, usersQueries, syncQueries){
197
+ constructor(http, authManager, authMethod, authMethod2Fa, usersManager, syncQueries){
192
198
  this.http = http;
193
199
  this.authManager = authManager;
194
200
  this.authMethod = authMethod;
195
- this.usersQueries = usersQueries;
201
+ this.authMethod2Fa = authMethod2Fa;
202
+ this.usersManager = usersManager;
196
203
  this.syncQueries = syncQueries;
197
204
  this.logger = new _common.Logger(SyncClientsManager.name);
198
205
  }
@@ -210,7 +217,8 @@ SyncClientsManager = _ts_decorate([
210
217
  typeof _axios.HttpService === "undefined" ? Object : _axios.HttpService,
211
218
  typeof _authmanagerservice.AuthManager === "undefined" ? Object : _authmanagerservice.AuthManager,
212
219
  typeof _authmethod.AuthMethod === "undefined" ? Object : _authmethod.AuthMethod,
213
- typeof _usersqueriesservice.UsersQueries === "undefined" ? Object : _usersqueriesservice.UsersQueries,
220
+ typeof _authmethodtwofaservice.AuthMethod2FA === "undefined" ? Object : _authmethodtwofaservice.AuthMethod2FA,
221
+ typeof _usersmanagerservice.UsersManager === "undefined" ? Object : _usersmanagerservice.UsersManager,
214
222
  typeof _syncqueriesservice.SyncQueries === "undefined" ? Object : _syncqueriesservice.SyncQueries
215
223
  ])
216
224
  ], SyncClientsManager);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/applications/sync/services/sync-clients-manager.service.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { HttpService } from '@nestjs/axios'\nimport { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'\nimport { AxiosResponse } from 'axios'\nimport { FastifyReply } from 'fastify'\nimport crypto from 'node:crypto'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { User } from 'src/applications/users/schemas/user.interface'\nimport { AuthMethod } from '../../../authentication/models/auth-method'\nimport { AuthManager } from '../../../authentication/services/auth-manager.service'\nimport { convertHumanTimeToSeconds } from '../../../common/functions'\nimport { currentTimeStamp } from '../../../common/shared'\nimport { STATIC_PATH } from '../../../configuration/config.constants'\nimport { configuration } from '../../../configuration/config.environment'\nimport { CacheDecorator } from '../../../infrastructure/cache/cache.decorator'\nimport { HTTP_METHOD } from '../../applications.constants'\nimport { isPathExists } from '../../files/utils/files'\nimport { USER_PERMISSION } from '../../users/constants/user'\nimport { UserModel } from '../../users/models/user.model'\nimport { UsersQueries } from '../../users/services/users-queries.service'\nimport { CLIENT_AUTH_TYPE, CLIENT_TOKEN_EXPIRATION_TIME, CLIENT_TOKEN_EXPIRED_ERROR, CLIENT_TOKEN_RENEW_TIME } from '../constants/auth'\nimport { APP_STORE_DIRNAME, APP_STORE_MANIFEST_FILE, APP_STORE_REPOSITORY, APP_STORE_URL } from '../constants/store'\nimport { SYNC_CLIENT_TYPE } from '../constants/sync'\nimport type { SyncClientAuthDto } from '../dtos/sync-client-auth.dto'\nimport type { SyncClientRegistrationDto } from '../dtos/sync-client-registration.dto'\nimport { AppStoreManifest } from '../interfaces/store-manifest.interface'\nimport { ClientAuthCookieDto, ClientAuthTokenDto } from '../interfaces/sync-client-auth.interface'\nimport { SyncClientPaths } from '../interfaces/sync-client-paths.interface'\nimport { SyncClient } from '../schemas/sync-client.interface'\nimport { SyncQueries } from './sync-queries.service'\n\n@Injectable()\nexport class SyncClientsManager {\n private readonly logger = new Logger(SyncClientsManager.name)\n\n constructor(\n private readonly http: HttpService,\n private readonly authManager: AuthManager,\n private readonly authMethod: AuthMethod,\n private readonly usersQueries: UsersQueries,\n private readonly syncQueries: SyncQueries\n ) {}\n\n async register(clientRegistrationDto: SyncClientRegistrationDto, ip: string): Promise<{ clientToken: string }> {\n const user: UserModel = await this.authMethod.validateUser(clientRegistrationDto.login, clientRegistrationDto.password)\n if (!user) {\n this.logger.warn(`${this.register.name} - auth failed for user *${clientRegistrationDto.login}*`)\n throw new HttpException('Not authorized', HttpStatus.UNAUTHORIZED)\n }\n if (!user.havePermission(USER_PERMISSION.DESKTOP_APP)) {\n this.logger.warn(`${this.register.name} - does not have permission : ${USER_PERMISSION.DESKTOP_APP}`)\n throw new HttpException('Missing permission', HttpStatus.FORBIDDEN)\n }\n try {\n const token = await this.syncQueries.getOrCreateClient(user.id, clientRegistrationDto.clientId, clientRegistrationDto.info, ip)\n this.logger.log(`${this.register.name} - client *${clientRegistrationDto.info.type}* was registered for user *${user.login}* (${user.id})`)\n return { clientToken: token }\n } catch (e) {\n this.logger.error(`${this.register.name} - ${e}`)\n throw new HttpException('Error during the client registration', HttpStatus.INTERNAL_SERVER_ERROR)\n }\n }\n\n async unregister(user: UserModel): Promise<void> {\n try {\n await this.syncQueries.deleteClient(user.id, user.clientId)\n } catch (e) {\n this.logger.error(`${this.unregister.name} - ${e}`)\n throw new HttpException('Error during the removing of client registration', HttpStatus.INTERNAL_SERVER_ERROR)\n }\n }\n\n async authenticate(\n authType: CLIENT_AUTH_TYPE,\n syncClientAuthDto: SyncClientAuthDto,\n ip: string,\n res: FastifyReply\n ): Promise<ClientAuthTokenDto | ClientAuthCookieDto> {\n const client = await this.syncQueries.getClient(syncClientAuthDto.clientId, null, syncClientAuthDto.token)\n if (!client) {\n throw new HttpException('Client is unknown', HttpStatus.FORBIDDEN)\n }\n if (!client.enabled) {\n throw new HttpException('Client is disabled', HttpStatus.FORBIDDEN)\n }\n if (currentTimeStamp() >= client.tokenExpiration) {\n throw new HttpException(CLIENT_TOKEN_EXPIRED_ERROR, HttpStatus.FORBIDDEN)\n }\n this.syncQueries.updateClientInfo(client, client.info, ip).catch((e: Error) => this.logger.error(`${this.authenticate.name} - ${e}`))\n const user: User = await this.usersQueries.from(client.ownerId)\n if (!user) {\n throw new HttpException('User does not exist', HttpStatus.FORBIDDEN)\n }\n if (!user.isActive) {\n throw new HttpException('Account suspended or not authorized', HttpStatus.FORBIDDEN)\n }\n const owner = new UserModel(user)\n if (!owner.havePermission(USER_PERMISSION.DESKTOP_APP)) {\n this.logger.warn(`${this.register.name} - does not have permission : ${USER_PERMISSION.DESKTOP_APP}`)\n throw new HttpException('Missing permission', HttpStatus.FORBIDDEN)\n }\n // set clientId\n owner.clientId = client.id\n // update accesses\n this.usersQueries\n .updateUserOrGuest(owner.id, {\n lastAccess: owner.currentAccess,\n currentAccess: new Date(),\n lastIp: owner.currentIp,\n currentIp: ip\n })\n .catch((e: Error) => this.logger.error(`${this.authenticate.name} - ${e}`))\n let r: ClientAuthTokenDto | ClientAuthCookieDto\n if (authType === CLIENT_AUTH_TYPE.COOKIE) {\n // used by the desktop app to perform the login setup using cookies\n r = await this.authManager.setCookies(owner, res)\n } else if (authType === CLIENT_AUTH_TYPE.TOKEN) {\n // used by the cli app and the sync core\n r = await this.authManager.getTokens(owner)\n }\n // check if the client token must be updated\n r.client_token_update = await this.renewTokenAndExpiration(client, owner)\n return r\n }\n\n getClients(user: UserModel): Promise<SyncClientPaths[]> {\n return this.syncQueries.getClients(user)\n }\n\n async renewTokenAndExpiration(client: SyncClient, owner: UserModel): Promise<string | undefined> {\n if (currentTimeStamp() + convertHumanTimeToSeconds(CLIENT_TOKEN_RENEW_TIME) < client.tokenExpiration) {\n // client token expiration is not close enough\n return undefined\n }\n const token = crypto.randomUUID()\n const expiration = currentTimeStamp() + convertHumanTimeToSeconds(CLIENT_TOKEN_EXPIRATION_TIME)\n this.logger.log(`${this.renewTokenAndExpiration.name} - renew token for user *${owner.login}* and client *${client.id}*`)\n try {\n await this.syncQueries.renewClientTokenAndExpiration(client.id, token, expiration)\n } catch (e) {\n this.logger.error(`${this.renewTokenAndExpiration.name} - unable to renew token for user *${owner.login}* and client *${client.id}* : ${e}`)\n throw new HttpException('Unable to update client token', HttpStatus.BAD_REQUEST)\n }\n return token\n }\n\n async deleteClient(user: UserModel, clientId: string): Promise<void> {\n try {\n await this.syncQueries.deleteClient(user.id, clientId)\n } catch (e) {\n this.logger.error(`${this.deleteClient.name} - ${e}`)\n throw new HttpException('Unable to delete client', HttpStatus.INTERNAL_SERVER_ERROR)\n }\n }\n\n @CacheDecorator(3600)\n async checkAppStore(): Promise<AppStoreManifest> {\n let manifest: AppStoreManifest = null\n if (configuration.applications.appStore.repository === APP_STORE_REPOSITORY.PUBLIC) {\n const url = `${APP_STORE_URL}/${APP_STORE_MANIFEST_FILE}`\n try {\n const res: AxiosResponse = await this.http.axiosRef({\n method: HTTP_METHOD.GET,\n url: url\n })\n manifest = res.data\n manifest.repository = APP_STORE_REPOSITORY.PUBLIC\n } catch (e) {\n this.logger.warn(`${this.checkAppStore.name} - unable to retrieve ${url} : ${e}`)\n }\n } else {\n const latestFile = path.join(STATIC_PATH, APP_STORE_DIRNAME, APP_STORE_MANIFEST_FILE)\n if (!(await isPathExists(latestFile))) {\n this.logger.warn(`${this.checkAppStore.name} - ${latestFile} does not exist`)\n } else {\n try {\n manifest = JSON.parse(await fs.readFile(latestFile, 'utf8'))\n manifest.repository = APP_STORE_REPOSITORY.LOCAL\n // rewrite urls to local repository\n for (const [os, packages] of Object.entries(manifest.platform)) {\n for (const p of packages) {\n if (p.package.toLowerCase().startsWith(SYNC_CLIENT_TYPE.DESKTOP)) {\n p.url = `${APP_STORE_DIRNAME}/${SYNC_CLIENT_TYPE.DESKTOP}/${os}/${p.package}`\n } else {\n p.url = `${APP_STORE_DIRNAME}/${SYNC_CLIENT_TYPE.CLI}/${p.package}`\n }\n }\n }\n } catch (e) {\n this.logger.error(`${this.checkAppStore.name} - ${latestFile} : ${e}`)\n }\n }\n }\n return manifest\n }\n}\n"],"names":["SyncClientsManager","register","clientRegistrationDto","ip","user","authMethod","validateUser","login","password","logger","warn","name","HttpException","HttpStatus","UNAUTHORIZED","havePermission","USER_PERMISSION","DESKTOP_APP","FORBIDDEN","token","syncQueries","getOrCreateClient","id","clientId","info","log","type","clientToken","e","error","INTERNAL_SERVER_ERROR","unregister","deleteClient","authenticate","authType","syncClientAuthDto","res","client","getClient","enabled","currentTimeStamp","tokenExpiration","CLIENT_TOKEN_EXPIRED_ERROR","updateClientInfo","catch","usersQueries","from","ownerId","isActive","owner","UserModel","updateUserOrGuest","lastAccess","currentAccess","Date","lastIp","currentIp","r","CLIENT_AUTH_TYPE","COOKIE","authManager","setCookies","TOKEN","getTokens","client_token_update","renewTokenAndExpiration","getClients","convertHumanTimeToSeconds","CLIENT_TOKEN_RENEW_TIME","undefined","crypto","randomUUID","expiration","CLIENT_TOKEN_EXPIRATION_TIME","renewClientTokenAndExpiration","BAD_REQUEST","checkAppStore","manifest","configuration","applications","appStore","repository","APP_STORE_REPOSITORY","PUBLIC","url","APP_STORE_URL","APP_STORE_MANIFEST_FILE","http","axiosRef","method","HTTP_METHOD","GET","data","latestFile","path","join","STATIC_PATH","APP_STORE_DIRNAME","isPathExists","JSON","parse","fs","readFile","LOCAL","os","packages","Object","entries","platform","p","package","toLowerCase","startsWith","SYNC_CLIENT_TYPE","DESKTOP","CLI","Logger"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../backend/src/applications/sync/services/sync-clients-manager.service.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { HttpService } from '@nestjs/axios'\nimport { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'\nimport { AxiosResponse } from 'axios'\nimport { FastifyReply } from 'fastify'\nimport crypto from 'node:crypto'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { AuthMethod } from '../../../authentication/models/auth-method'\nimport { AuthManager } from '../../../authentication/services/auth-manager.service'\nimport { AuthMethod2FA } from '../../../authentication/services/auth-methods/auth-method-two-fa.service'\nimport { convertHumanTimeToSeconds } from '../../../common/functions'\nimport { currentTimeStamp } from '../../../common/shared'\nimport { STATIC_PATH } from '../../../configuration/config.constants'\nimport { configuration } from '../../../configuration/config.environment'\nimport { CacheDecorator } from '../../../infrastructure/cache/cache.decorator'\nimport { HTTP_METHOD } from '../../applications.constants'\nimport { isPathExists } from '../../files/utils/files'\nimport { USER_PERMISSION } from '../../users/constants/user'\nimport { UserModel } from '../../users/models/user.model'\nimport { UsersManager } from '../../users/services/users-manager.service'\nimport { CLIENT_AUTH_TYPE, CLIENT_TOKEN_EXPIRATION_TIME, CLIENT_TOKEN_EXPIRED_ERROR, CLIENT_TOKEN_RENEW_TIME } from '../constants/auth'\nimport { APP_STORE_DIRNAME, APP_STORE_MANIFEST_FILE, APP_STORE_REPOSITORY, APP_STORE_URL } from '../constants/store'\nimport { SYNC_CLIENT_TYPE } from '../constants/sync'\nimport type { SyncClientAuthDto } from '../dtos/sync-client-auth.dto'\nimport type { SyncClientRegistrationDto } from '../dtos/sync-client-registration.dto'\nimport { AppStoreManifest } from '../interfaces/store-manifest.interface'\nimport { ClientAuthCookieDto, ClientAuthTokenDto } from '../interfaces/sync-client-auth.interface'\nimport { SyncClientPaths } from '../interfaces/sync-client-paths.interface'\nimport { SyncClient } from '../schemas/sync-client.interface'\nimport { SyncQueries } from './sync-queries.service'\n\n@Injectable()\nexport class SyncClientsManager {\n private readonly logger = new Logger(SyncClientsManager.name)\n\n constructor(\n private readonly http: HttpService,\n private readonly authManager: AuthManager,\n private readonly authMethod: AuthMethod,\n private readonly authMethod2Fa: AuthMethod2FA,\n private readonly usersManager: UsersManager,\n private readonly syncQueries: SyncQueries\n ) {}\n\n async register(clientRegistrationDto: SyncClientRegistrationDto, ip: string): Promise<{ clientToken: string }> {\n const user: UserModel = await this.authMethod.validateUser(clientRegistrationDto.login, clientRegistrationDto.password)\n if (!user) {\n this.logger.warn(`${this.register.name} - auth failed for user *${clientRegistrationDto.login}*`)\n throw new HttpException('Wrong login or password', HttpStatus.UNAUTHORIZED)\n }\n if (!user.havePermission(USER_PERMISSION.DESKTOP_APP)) {\n this.logger.warn(`${this.register.name} - user *${user.login}* (${user.id}) does not have permission : ${USER_PERMISSION.DESKTOP_APP}`)\n throw new HttpException('Missing permission', HttpStatus.FORBIDDEN)\n }\n if (configuration.auth.mfa.totp.enabled && user.twoFaEnabled) {\n if (!clientRegistrationDto.code) {\n this.logger.warn(`${this.register.name} - missing two-fa code for user *${user.login}* (${user.id})`)\n throw new HttpException('Missing TWO-FA code', HttpStatus.UNAUTHORIZED)\n }\n const auth = this.authMethod2Fa.validateTwoFactorCode(clientRegistrationDto.code, user.secrets.twoFaSecret)\n if (!auth.success) {\n this.logger.warn(`${this.register.name} - wrong two-fa code for user *${user.login}* (${user.id})`)\n this.usersManager.updateAccesses(user, ip, false).catch((e: Error) => this.logger.error(`${this.register.name} - ${e}`))\n throw new HttpException(auth.message, HttpStatus.UNAUTHORIZED)\n }\n }\n try {\n const token = await this.syncQueries.getOrCreateClient(user.id, clientRegistrationDto.clientId, clientRegistrationDto.info, ip)\n this.logger.log(`${this.register.name} - client *${clientRegistrationDto.info.type}* was registered for user *${user.login}* (${user.id})`)\n return { clientToken: token }\n } catch (e) {\n this.logger.error(`${this.register.name} - ${e}`)\n throw new HttpException('Error during the client registration', HttpStatus.INTERNAL_SERVER_ERROR)\n }\n }\n\n async unregister(user: UserModel): Promise<void> {\n try {\n await this.syncQueries.deleteClient(user.id, user.clientId)\n } catch (e) {\n this.logger.error(`${this.unregister.name} - ${e}`)\n throw new HttpException('Error during the removing of client registration', HttpStatus.INTERNAL_SERVER_ERROR)\n }\n }\n\n async authenticate(\n authType: CLIENT_AUTH_TYPE,\n syncClientAuthDto: SyncClientAuthDto,\n ip: string,\n res: FastifyReply\n ): Promise<ClientAuthTokenDto | ClientAuthCookieDto> {\n const client = await this.syncQueries.getClient(syncClientAuthDto.clientId, null, syncClientAuthDto.token)\n if (!client) {\n throw new HttpException('Client is unknown', HttpStatus.FORBIDDEN)\n }\n if (!client.enabled) {\n throw new HttpException('Client is disabled', HttpStatus.FORBIDDEN)\n }\n if (currentTimeStamp() >= client.tokenExpiration) {\n throw new HttpException(CLIENT_TOKEN_EXPIRED_ERROR, HttpStatus.FORBIDDEN)\n }\n this.syncQueries.updateClientInfo(client, client.info, ip).catch((e: Error) => this.logger.error(`${this.authenticate.name} - ${e}`))\n const user: UserModel = await this.usersManager.fromUserId(client.ownerId)\n if (!user) {\n throw new HttpException('User does not exist', HttpStatus.FORBIDDEN)\n }\n if (!user.isActive) {\n throw new HttpException('Account suspended or not authorized', HttpStatus.FORBIDDEN)\n }\n if (!user.havePermission(USER_PERMISSION.DESKTOP_APP)) {\n this.logger.warn(`${this.register.name} - does not have permission : ${USER_PERMISSION.DESKTOP_APP}`)\n throw new HttpException('Missing permission', HttpStatus.FORBIDDEN)\n }\n // set clientId\n user.clientId = client.id\n // update accesses\n this.usersManager.updateAccesses(user, ip, true).catch((e: Error) => this.logger.error(`${this.authenticate.name} - ${e}`))\n let r: ClientAuthTokenDto | ClientAuthCookieDto\n if (authType === CLIENT_AUTH_TYPE.COOKIE) {\n // used by the desktop app to perform the login setup using cookies\n r = await this.authManager.setCookies(user, res)\n } else if (authType === CLIENT_AUTH_TYPE.TOKEN) {\n // used by the cli app and the sync core\n r = await this.authManager.getTokens(user)\n }\n // check if the client token must be updated\n r.client_token_update = await this.renewTokenAndExpiration(client, user)\n return r\n }\n\n getClients(user: UserModel): Promise<SyncClientPaths[]> {\n return this.syncQueries.getClients(user)\n }\n\n async renewTokenAndExpiration(client: SyncClient, owner: UserModel): Promise<string | undefined> {\n if (currentTimeStamp() + convertHumanTimeToSeconds(CLIENT_TOKEN_RENEW_TIME) < client.tokenExpiration) {\n // client token expiration is not close enough\n return undefined\n }\n const token = crypto.randomUUID()\n const expiration = currentTimeStamp() + convertHumanTimeToSeconds(CLIENT_TOKEN_EXPIRATION_TIME)\n this.logger.log(`${this.renewTokenAndExpiration.name} - renew token for user *${owner.login}* and client *${client.id}*`)\n try {\n await this.syncQueries.renewClientTokenAndExpiration(client.id, token, expiration)\n } catch (e) {\n this.logger.error(`${this.renewTokenAndExpiration.name} - unable to renew token for user *${owner.login}* and client *${client.id}* : ${e}`)\n throw new HttpException('Unable to update client token', HttpStatus.BAD_REQUEST)\n }\n return token\n }\n\n async deleteClient(user: UserModel, clientId: string): Promise<void> {\n try {\n await this.syncQueries.deleteClient(user.id, clientId)\n } catch (e) {\n this.logger.error(`${this.deleteClient.name} - ${e}`)\n throw new HttpException('Unable to delete client', HttpStatus.INTERNAL_SERVER_ERROR)\n }\n }\n\n @CacheDecorator(3600)\n async checkAppStore(): Promise<AppStoreManifest> {\n let manifest: AppStoreManifest = null\n if (configuration.applications.appStore.repository === APP_STORE_REPOSITORY.PUBLIC) {\n const url = `${APP_STORE_URL}/${APP_STORE_MANIFEST_FILE}`\n try {\n const res: AxiosResponse = await this.http.axiosRef({\n method: HTTP_METHOD.GET,\n url: url\n })\n manifest = res.data\n manifest.repository = APP_STORE_REPOSITORY.PUBLIC\n } catch (e) {\n this.logger.warn(`${this.checkAppStore.name} - unable to retrieve ${url} : ${e}`)\n }\n } else {\n const latestFile = path.join(STATIC_PATH, APP_STORE_DIRNAME, APP_STORE_MANIFEST_FILE)\n if (!(await isPathExists(latestFile))) {\n this.logger.warn(`${this.checkAppStore.name} - ${latestFile} does not exist`)\n } else {\n try {\n manifest = JSON.parse(await fs.readFile(latestFile, 'utf8'))\n manifest.repository = APP_STORE_REPOSITORY.LOCAL\n // rewrite urls to local repository\n for (const [os, packages] of Object.entries(manifest.platform)) {\n for (const p of packages) {\n if (p.package.toLowerCase().startsWith(SYNC_CLIENT_TYPE.DESKTOP)) {\n p.url = `${APP_STORE_DIRNAME}/${SYNC_CLIENT_TYPE.DESKTOP}/${os}/${p.package}`\n } else {\n p.url = `${APP_STORE_DIRNAME}/${SYNC_CLIENT_TYPE.CLI}/${p.package}`\n }\n }\n }\n } catch (e) {\n this.logger.error(`${this.checkAppStore.name} - ${latestFile} : ${e}`)\n }\n }\n }\n return manifest\n }\n}\n"],"names":["SyncClientsManager","register","clientRegistrationDto","ip","user","authMethod","validateUser","login","password","logger","warn","name","HttpException","HttpStatus","UNAUTHORIZED","havePermission","USER_PERMISSION","DESKTOP_APP","id","FORBIDDEN","configuration","auth","mfa","totp","enabled","twoFaEnabled","code","authMethod2Fa","validateTwoFactorCode","secrets","twoFaSecret","success","usersManager","updateAccesses","catch","e","error","message","token","syncQueries","getOrCreateClient","clientId","info","log","type","clientToken","INTERNAL_SERVER_ERROR","unregister","deleteClient","authenticate","authType","syncClientAuthDto","res","client","getClient","currentTimeStamp","tokenExpiration","CLIENT_TOKEN_EXPIRED_ERROR","updateClientInfo","fromUserId","ownerId","isActive","r","CLIENT_AUTH_TYPE","COOKIE","authManager","setCookies","TOKEN","getTokens","client_token_update","renewTokenAndExpiration","getClients","owner","convertHumanTimeToSeconds","CLIENT_TOKEN_RENEW_TIME","undefined","crypto","randomUUID","expiration","CLIENT_TOKEN_EXPIRATION_TIME","renewClientTokenAndExpiration","BAD_REQUEST","checkAppStore","manifest","applications","appStore","repository","APP_STORE_REPOSITORY","PUBLIC","url","APP_STORE_URL","APP_STORE_MANIFEST_FILE","http","axiosRef","method","HTTP_METHOD","GET","data","latestFile","path","join","STATIC_PATH","APP_STORE_DIRNAME","isPathExists","JSON","parse","fs","readFile","LOCAL","os","packages","Object","entries","platform","p","package","toLowerCase","startsWith","SYNC_CLIENT_TYPE","DESKTOP","CLI","Logger"],"mappings":"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"}
@@ -13,12 +13,14 @@ const _nodecrypto = /*#__PURE__*/ _interop_require_default(require("node:crypto"
13
13
  const _promises = /*#__PURE__*/ _interop_require_default(require("node:fs/promises"));
14
14
  const _authmethod = require("../../../authentication/models/auth-method");
15
15
  const _authmanagerservice = require("../../../authentication/services/auth-manager.service");
16
+ const _authmethodtwofaservice = require("../../../authentication/services/auth-methods/auth-method-two-fa.service");
16
17
  const _functions = /*#__PURE__*/ _interop_require_wildcard(require("../../../common/functions"));
17
18
  const _shared = /*#__PURE__*/ _interop_require_wildcard(require("../../../common/shared"));
18
19
  const _configenvironment = require("../../../configuration/config.environment");
19
20
  const _cacheservice = require("../../../infrastructure/cache/services/cache.service");
20
21
  const _files = require("../../files/utils/files");
21
- const _usersqueriesservice = require("../../users/services/users-queries.service");
22
+ const _usermodel = require("../../users/models/user.model");
23
+ const _usersmanagerservice = require("../../users/services/users-manager.service");
22
24
  const _auth = require("../constants/auth");
23
25
  const _store = require("../constants/store");
24
26
  const _sync = require("../constants/sync");
@@ -103,7 +105,7 @@ describe(_syncclientsmanagerservice.SyncClientsManager.name, ()=>{
103
105
  let http;
104
106
  let authManager;
105
107
  let authMethod;
106
- let usersQueries;
108
+ let usersManager;
107
109
  let syncQueries;
108
110
  let cacheMock;
109
111
  // Helpers
@@ -121,7 +123,7 @@ describe(_syncclientsmanagerservice.SyncClientsManager.name, ()=>{
121
123
  },
122
124
  ...overrides
123
125
  });
124
- const makeUser = (overrides = {})=>({
126
+ const makeUser = (overrides = {})=>new _usermodel.UserModel({
125
127
  id: 1,
126
128
  isActive: true,
127
129
  login: 'u',
@@ -143,9 +145,9 @@ describe(_syncclientsmanagerservice.SyncClientsManager.name, ()=>{
143
145
  authMethod = {
144
146
  validateUser: jest.fn()
145
147
  };
146
- usersQueries = {
147
- from: jest.fn(),
148
- updateUserOrGuest: jest.fn()
148
+ usersManager = {
149
+ fromUserId: jest.fn(),
150
+ updateAccesses: jest.fn()
149
151
  };
150
152
  syncQueries = {
151
153
  getOrCreateClient: jest.fn(),
@@ -177,8 +179,8 @@ describe(_syncclientsmanagerservice.SyncClientsManager.name, ()=>{
177
179
  useValue: syncQueries
178
180
  },
179
181
  {
180
- provide: _usersqueriesservice.UsersQueries,
181
- useValue: usersQueries
182
+ provide: _usersmanagerservice.UsersManager,
183
+ useValue: usersManager
182
184
  },
183
185
  {
184
186
  provide: _authmanagerservice.AuthManager,
@@ -187,6 +189,10 @@ describe(_syncclientsmanagerservice.SyncClientsManager.name, ()=>{
187
189
  {
188
190
  provide: _authmethod.AuthMethod,
189
191
  useValue: authMethod
192
+ },
193
+ {
194
+ provide: _authmethodtwofaservice.AuthMethod2FA,
195
+ useValue: {}
190
196
  }
191
197
  ]
192
198
  }).compile();
@@ -210,7 +216,7 @@ describe(_syncclientsmanagerservice.SyncClientsManager.name, ()=>{
210
216
  _files.isPathExists.mockReset();
211
217
  _promises.default.readFile.mockReset();
212
218
  syncQueries.updateClientInfo.mockResolvedValue(undefined);
213
- usersQueries.updateUserOrGuest.mockResolvedValue(undefined);
219
+ usersManager.updateAccesses.mockResolvedValue(undefined);
214
220
  service.cache = cacheMock;
215
221
  cacheMock.get.mockResolvedValue(undefined);
216
222
  cacheMock.get.mockClear();
@@ -332,7 +338,7 @@ describe(_syncclientsmanagerservice.SyncClientsManager.name, ()=>{
332
338
  it('should forbid when owner user does not exist', async ()=>{
333
339
  syncQueries.getClient.mockResolvedValue(makeClient());
334
340
  syncQueries.updateClientInfo.mockRejectedValueOnce(new Error('update-fails')); // silence expected
335
- usersQueries.from.mockResolvedValue(null);
341
+ usersManager.fromUserId.mockResolvedValue(null);
336
342
  await expect(service.authenticate(_auth.CLIENT_AUTH_TYPE.TOKEN, dto, ip, {})).rejects.toMatchObject({
337
343
  status: _common.HttpStatus.FORBIDDEN,
338
344
  response: 'User does not exist'
@@ -340,7 +346,7 @@ describe(_syncclientsmanagerservice.SyncClientsManager.name, ()=>{
340
346
  });
341
347
  it('should forbid when owner account is inactive', async ()=>{
342
348
  syncQueries.getClient.mockResolvedValue(makeClient());
343
- usersQueries.from.mockResolvedValue(makeUser({
349
+ usersManager.fromUserId.mockResolvedValue(makeUser({
344
350
  isActive: false
345
351
  }));
346
352
  await expect(service.authenticate(_auth.CLIENT_AUTH_TYPE.TOKEN, dto, ip, {})).rejects.toMatchObject({
@@ -351,7 +357,7 @@ describe(_syncclientsmanagerservice.SyncClientsManager.name, ()=>{
351
357
  it('should forbid when owner lacks DESKTOP_APP permission', async ()=>{
352
358
  mockHavePermission = false;
353
359
  syncQueries.getClient.mockResolvedValue(makeClient());
354
- usersQueries.from.mockResolvedValue(makeUser({
360
+ usersManager.fromUserId.mockResolvedValue(makeUser({
355
361
  permissions: '',
356
362
  role: 999
357
363
  }));
@@ -364,14 +370,14 @@ describe(_syncclientsmanagerservice.SyncClientsManager.name, ()=>{
364
370
  syncQueries.getClient.mockResolvedValue(makeClient({
365
371
  ownerId: 7
366
372
  }));
367
- usersQueries.from.mockResolvedValue(makeUser({
373
+ usersManager.fromUserId.mockResolvedValue(makeUser({
368
374
  id: 7,
369
375
  login: 'john',
370
376
  email: 'john@doe',
371
377
  firstName: 'John',
372
378
  lastName: 'Doe'
373
379
  }));
374
- usersQueries.updateUserOrGuest.mockRejectedValueOnce(new Error('update-access-fail')); // silence expected
380
+ usersManager.updateAccesses.mockRejectedValueOnce(new Error('update-access-fail')); // silence expected
375
381
  authManager.setCookies.mockResolvedValue({
376
382
  access_token: 'a',
377
383
  refresh_token: 'b'
@@ -387,7 +393,7 @@ describe(_syncclientsmanagerservice.SyncClientsManager.name, ()=>{
387
393
  syncQueries.getClient.mockResolvedValue(makeClient({
388
394
  ownerId: 8
389
395
  }));
390
- usersQueries.from.mockResolvedValue(makeUser({
396
+ usersManager.fromUserId.mockResolvedValue(makeUser({
391
397
  id: 8,
392
398
  login: 'alice',
393
399
  email: 'alice@doe',
@@ -406,7 +412,7 @@ describe(_syncclientsmanagerservice.SyncClientsManager.name, ()=>{
406
412
  syncQueries.getClient.mockResolvedValue(makeClient({
407
413
  ownerId: 9
408
414
  }));
409
- usersQueries.from.mockResolvedValue(makeUser({
415
+ usersManager.fromUserId.mockResolvedValue(makeUser({
410
416
  id: 9,
411
417
  login: 'bob',
412
418
  email: 'bob@doe',
@@ -457,10 +463,10 @@ describe(_syncclientsmanagerservice.SyncClientsManager.name, ()=>{
457
463
  it('should renew token and return new value when close to expiration', async ()=>{
458
464
  ;
459
465
  _shared.currentTimeStamp.mockReturnValue(1_000);
460
- _functions.convertHumanTimeToSeconds.mockImplementation((v)=>v === '90d' ? 90 * 24 * 3600 : v === '180d' ? 180 * 24 * 3600 : 0);
466
+ _functions.convertHumanTimeToSeconds.mockImplementation((v)=>v === '60d' ? 60 * 24 * 3600 : v === '120d' ? 120 * 24 * 3600 : 0);
461
467
  const client = {
462
468
  id: 'cid',
463
- tokenExpiration: 1_000 + 90 * 24 * 3600 - 1
469
+ tokenExpiration: 1_000 + 60 * 24 * 3600 - 1
464
470
  };
465
471
  syncQueries.renewClientTokenAndExpiration.mockResolvedValue(undefined);
466
472
  const r = await service.renewTokenAndExpiration(client, owner);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/applications/sync/services/sync-clients-manager.service.spec.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { HttpService } from '@nestjs/axios'\nimport { HttpStatus } from '@nestjs/common'\nimport { Test, TestingModule } from '@nestjs/testing'\nimport { FastifyReply } from 'fastify'\nimport crypto from 'node:crypto'\nimport fs from 'node:fs/promises'\nimport { AuthMethod } from '../../../authentication/models/auth-method'\nimport { AuthManager } from '../../../authentication/services/auth-manager.service'\nimport * as commonFunctions from '../../../common/functions'\nimport * as commonShared from '../../../common/shared'\nimport { configuration } from '../../../configuration/config.environment'\nimport { Cache } from '../../../infrastructure/cache/services/cache.service'\nimport { isPathExists } from '../../files/utils/files'\nimport { UsersQueries } from '../../users/services/users-queries.service'\nimport { CLIENT_AUTH_TYPE, CLIENT_TOKEN_EXPIRED_ERROR } from '../constants/auth'\nimport { APP_STORE_DIRNAME, APP_STORE_REPOSITORY } from '../constants/store'\nimport { SYNC_CLIENT_TYPE } from '../constants/sync'\nimport { SyncClientsManager } from './sync-clients-manager.service'\nimport { SyncQueries } from './sync-queries.service'\n\n// Pilotage permission via UserModel\nlet mockHavePermission = true\njest.mock('../../users/models/user.model', () => ({\n UserModel: jest.fn().mockImplementation((props: any) => ({\n ...props,\n havePermission: () => mockHavePermission\n }))\n}))\n\n// Mock ciblé de convertHumanTimeToSeconds\njest.mock('../../../common/functions', () => {\n const actual = jest.requireActual('../../../common/functions')\n return { ...actual, convertHumanTimeToSeconds: jest.fn() }\n})\n\n// Mock currentTimeStamp\njest.mock('../../../common/shared', () => ({ currentTimeStamp: jest.fn() }))\n\n// Mock FS et helper d'existence\njest.mock('node:fs/promises', () => ({ readFile: jest.fn() }))\njest.mock('../../files/utils/files', () => ({ isPathExists: jest.fn() }))\n\ndescribe(SyncClientsManager.name, () => {\n let service: SyncClientsManager\n\n // Mocks\n let http: { axiosRef: jest.Mock }\n let authManager: { setCookies: jest.Mock; getTokens: jest.Mock }\n let authMethod: { validateUser: jest.Mock }\n let usersQueries: { from: jest.Mock; updateUserOrGuest: jest.Mock }\n let syncQueries: {\n getOrCreateClient: jest.Mock\n deleteClient: jest.Mock\n getClient: jest.Mock\n updateClientInfo: jest.Mock\n renewClientTokenAndExpiration: jest.Mock\n getClients: jest.Mock\n }\n let cacheMock: { genSlugKey: jest.Mock; get: jest.Mock; set: jest.Mock; del: jest.Mock }\n\n // Helpers\n const setRepo = (repo: APP_STORE_REPOSITORY) => {\n ;(configuration as any).applications.appStore.repository = repo\n }\n const makeClient = (overrides: any = {}) => ({\n id: 'cid',\n ownerId: 1,\n tokenExpiration: 2000,\n enabled: true,\n info: { type: 'desktop' },\n ...overrides\n })\n const makeUser = (overrides: any = {}) => ({\n id: 1,\n isActive: true,\n login: 'u',\n email: 'u@x',\n firstName: 'U',\n lastName: 'X',\n role: 1,\n permissions: 'desktop',\n ...overrides\n })\n\n beforeAll(async () => {\n http = { axiosRef: jest.fn() }\n authManager = { setCookies: jest.fn(), getTokens: jest.fn() }\n authMethod = { validateUser: jest.fn() }\n usersQueries = { from: jest.fn(), updateUserOrGuest: jest.fn() }\n syncQueries = {\n getOrCreateClient: jest.fn(),\n deleteClient: jest.fn(),\n getClient: jest.fn(),\n updateClientInfo: jest.fn(),\n renewClientTokenAndExpiration: jest.fn(),\n getClients: jest.fn()\n }\n cacheMock = {\n genSlugKey: jest.fn().mockReturnValue('syncclientsmanager:checkappstore'),\n get: jest.fn().mockResolvedValue(undefined),\n set: jest.fn().mockResolvedValue(undefined),\n del: jest.fn().mockResolvedValue(undefined)\n }\n\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n SyncClientsManager,\n { provide: Cache, useValue: cacheMock },\n { provide: HttpService, useValue: http },\n { provide: SyncQueries, useValue: syncQueries },\n { provide: UsersQueries, useValue: usersQueries },\n { provide: AuthManager, useValue: authManager },\n { provide: AuthMethod, useValue: authMethod }\n ]\n }).compile()\n\n module.useLogger(['fatal'])\n service = module.get<SyncClientsManager>(SyncClientsManager)\n ;(service as any).cache = cacheMock\n })\n\n beforeEach(() => {\n jest.restoreAllMocks()\n jest.clearAllMocks()\n mockHavePermission = true\n ;(commonShared.currentTimeStamp as jest.Mock).mockReturnValue(1_000)\n ;(commonFunctions.convertHumanTimeToSeconds as jest.Mock).mockImplementation((v: string | number) => {\n if (v === '90d') return 90 * 24 * 3600\n if (v === '180d') return 180 * 24 * 3600\n if (typeof v === 'number') return v\n return 0\n })\n ;(isPathExists as jest.Mock).mockReset()\n ;(fs.readFile as jest.Mock).mockReset()\n ;(syncQueries.updateClientInfo as jest.Mock).mockResolvedValue(undefined)\n ;(usersQueries.updateUserOrGuest as jest.Mock).mockResolvedValue(undefined)\n ;(service as any).cache = cacheMock\n cacheMock.get.mockResolvedValue(undefined)\n cacheMock.get.mockClear()\n cacheMock.set.mockClear()\n cacheMock.del.mockClear()\n cacheMock.genSlugKey.mockClear()\n setRepo(APP_STORE_REPOSITORY.PUBLIC)\n })\n\n it('should be defined', () => expect(service).toBeDefined())\n\n describe('register', () => {\n const baseDto = { login: 'john', password: 'secret', clientId: 'client-1', info: { type: 'desktop', version: '1.0.0' } }\n\n test.each([\n ['Unauthorized when credentials are invalid', null, HttpStatus.UNAUTHORIZED],\n ['Forbidden when user lacks DESKTOP_APP permission', { id: 10, login: 'john', havePermission: () => false }, HttpStatus.FORBIDDEN]\n ])('should throw %s', async (_label, user, status) => {\n authMethod.validateUser.mockResolvedValue(user)\n await expect(service.register(baseDto as any, '1.2.3.4')).rejects.toMatchObject({ status })\n })\n\n it('should return client token when registration succeeds', async () => {\n authMethod.validateUser.mockResolvedValue({ id: 10, login: 'john', havePermission: () => true })\n syncQueries.getOrCreateClient.mockResolvedValue('token-abc')\n\n const r = await service.register(baseDto as any, '1.2.3.4')\n expect(r).toEqual({ clientToken: 'token-abc' })\n expect(syncQueries.getOrCreateClient).toHaveBeenCalledWith(10, 'client-1', baseDto.info, '1.2.3.4')\n })\n\n it('should throw Internal Server Error when persistence fails', async () => {\n authMethod.validateUser.mockResolvedValue({ id: 10, login: 'john', havePermission: () => true })\n syncQueries.getOrCreateClient.mockRejectedValue(new Error('db error'))\n await expect(service.register(baseDto as any, '1.2.3.4')).rejects.toMatchObject({ status: HttpStatus.INTERNAL_SERVER_ERROR })\n })\n })\n\n describe('unregister', () => {\n it('should delete client without error', async () => {\n syncQueries.deleteClient.mockResolvedValue(undefined)\n await expect(service.unregister({ id: 1, clientId: 'c1' } as any)).resolves.toBeUndefined()\n expect(syncQueries.deleteClient).toHaveBeenCalledWith(1, 'c1')\n })\n it('should throw Internal Server Error when deletion fails', async () => {\n syncQueries.deleteClient.mockRejectedValue(new Error('db error'))\n await expect(service.unregister({ id: 1, clientId: 'c1' } as any)).rejects.toMatchObject({ status: HttpStatus.INTERNAL_SERVER_ERROR })\n })\n })\n\n describe('authenticate', () => {\n const ip = '9.9.9.9'\n const dto = { clientId: 'cid', token: 'ctok' }\n\n it('should forbid when client is unknown', async () => {\n syncQueries.getClient.mockResolvedValue(undefined)\n await expect(service.authenticate(CLIENT_AUTH_TYPE.TOKEN, dto as any, ip, {} as FastifyReply)).rejects.toMatchObject({\n status: HttpStatus.FORBIDDEN,\n response: 'Client is unknown'\n })\n })\n\n it('should forbid when client is disabled', async () => {\n syncQueries.getClient.mockResolvedValue(makeClient({ enabled: false, tokenExpiration: 5000 }))\n await expect(service.authenticate(CLIENT_AUTH_TYPE.TOKEN, dto as any, ip, {} as FastifyReply)).rejects.toMatchObject({\n status: HttpStatus.FORBIDDEN,\n response: 'Client is disabled'\n })\n })\n\n it('should forbid when client token is expired', async () => {\n ;(commonShared.currentTimeStamp as jest.Mock).mockReturnValue(1000)\n syncQueries.getClient.mockResolvedValue(makeClient({ tokenExpiration: 1000 }))\n await expect(service.authenticate(CLIENT_AUTH_TYPE.TOKEN, dto as any, ip, {} as FastifyReply)).rejects.toMatchObject({\n status: HttpStatus.FORBIDDEN,\n response: CLIENT_TOKEN_EXPIRED_ERROR\n })\n })\n\n it('should forbid when owner user does not exist', async () => {\n syncQueries.getClient.mockResolvedValue(makeClient())\n syncQueries.updateClientInfo.mockRejectedValueOnce(new Error('update-fails')) // silence expected\n usersQueries.from.mockResolvedValue(null)\n await expect(service.authenticate(CLIENT_AUTH_TYPE.TOKEN, dto as any, ip, {} as FastifyReply)).rejects.toMatchObject({\n status: HttpStatus.FORBIDDEN,\n response: 'User does not exist'\n })\n })\n\n it('should forbid when owner account is inactive', async () => {\n syncQueries.getClient.mockResolvedValue(makeClient())\n usersQueries.from.mockResolvedValue(makeUser({ isActive: false }))\n await expect(service.authenticate(CLIENT_AUTH_TYPE.TOKEN, dto as any, ip, {} as FastifyReply)).rejects.toMatchObject({\n status: HttpStatus.FORBIDDEN,\n response: 'Account suspended or not authorized'\n })\n })\n\n it('should forbid when owner lacks DESKTOP_APP permission', async () => {\n mockHavePermission = false\n syncQueries.getClient.mockResolvedValue(makeClient())\n usersQueries.from.mockResolvedValue(makeUser({ permissions: '', role: 999 }))\n await expect(service.authenticate(CLIENT_AUTH_TYPE.TOKEN, dto as any, ip, {} as FastifyReply)).rejects.toMatchObject({\n status: HttpStatus.FORBIDDEN,\n response: 'Missing permission'\n })\n })\n\n it('should perform COOKIE authentication and renew client token when needed', async () => {\n syncQueries.getClient.mockResolvedValue(makeClient({ ownerId: 7 }))\n usersQueries.from.mockResolvedValue(makeUser({ id: 7, login: 'john', email: 'john@doe', firstName: 'John', lastName: 'Doe' }))\n usersQueries.updateUserOrGuest.mockRejectedValueOnce(new Error('update-access-fail')) // silence expected\n authManager.setCookies.mockResolvedValue({ access_token: 'a', refresh_token: 'b' })\n jest.spyOn(service, 'renewTokenAndExpiration').mockResolvedValue('new-client-token')\n\n const reply = {} as unknown as FastifyReply\n const r: any = await service.authenticate(CLIENT_AUTH_TYPE.COOKIE, dto as any, ip, reply)\n\n expect(authManager.setCookies).toHaveBeenCalledTimes(1)\n expect(service.renewTokenAndExpiration).toHaveBeenCalledTimes(1)\n expect(r.client_token_update).toBe('new-client-token')\n })\n\n it('should perform TOKEN authentication and not renew when not needed', async () => {\n syncQueries.getClient.mockResolvedValue(makeClient({ ownerId: 8 }))\n usersQueries.from.mockResolvedValue(makeUser({ id: 8, login: 'alice', email: 'alice@doe', firstName: 'Alice' }))\n authManager.getTokens.mockResolvedValue({ access_token: 'x', refresh_token: 'y' })\n jest.spyOn(service, 'renewTokenAndExpiration').mockResolvedValue(undefined)\n\n const r: any = await service.authenticate(CLIENT_AUTH_TYPE.TOKEN, dto as any, ip, {} as FastifyReply)\n expect(authManager.getTokens).toHaveBeenCalledTimes(1)\n expect(r.client_token_update).toBeUndefined()\n })\n\n it('should throw when auth type is unknown (else branch)', async () => {\n syncQueries.getClient.mockResolvedValue(makeClient({ ownerId: 9 }))\n usersQueries.from.mockResolvedValue(makeUser({ id: 9, login: 'bob', email: 'bob@doe', firstName: 'Bob' }))\n jest.spyOn(service, 'renewTokenAndExpiration').mockResolvedValue(undefined)\n await expect(service.authenticate('unknown' as any, { clientId: 'cid', token: 'ctok' } as any, ip, {} as FastifyReply)).rejects.toBeInstanceOf(\n TypeError\n )\n })\n })\n\n describe('getClients', () => {\n it('should proxy to SyncQueries.getClients', async () => {\n const fake = [{ id: 'c1', paths: [] }]\n syncQueries.getClients.mockResolvedValue(fake)\n const r = await service.getClients({ id: 1, clientId: 'c1' } as any)\n expect(r).toBe(fake)\n expect(syncQueries.getClients).toHaveBeenCalledWith({ id: 1, clientId: 'c1' })\n })\n })\n\n describe('renewTokenAndExpiration', () => {\n const owner = { id: 1, login: 'bob' } as any\n\n it('should return undefined when token expiration is far enough', async () => {\n ;(commonShared.currentTimeStamp as jest.Mock).mockReturnValue(1_000)\n ;(commonFunctions.convertHumanTimeToSeconds as jest.Mock).mockImplementation((v: string) => (v === '90d' ? 90 * 24 * 3600 : 0))\n const client = { id: 'cid', tokenExpiration: 1_000 + 90 * 24 * 3600 + 1 } as any\n expect(await service.renewTokenAndExpiration(client, owner)).toBeUndefined()\n })\n\n it('should renew token and return new value when close to expiration', async () => {\n ;(commonShared.currentTimeStamp as jest.Mock).mockReturnValue(1_000)\n ;(commonFunctions.convertHumanTimeToSeconds as jest.Mock).mockImplementation((v: string) =>\n v === '90d' ? 90 * 24 * 3600 : v === '180d' ? 180 * 24 * 3600 : 0\n )\n const client = { id: 'cid', tokenExpiration: 1_000 + 90 * 24 * 3600 - 1 } as any\n syncQueries.renewClientTokenAndExpiration.mockResolvedValue(undefined)\n\n const r = await service.renewTokenAndExpiration(client, owner)\n expect(typeof r).toBe('string')\n expect(r).toBeTruthy()\n expect(syncQueries.renewClientTokenAndExpiration).toHaveBeenCalledWith('cid', r, expect.any(Number))\n })\n\n it('should throw Bad Request when renewal persistence fails', async () => {\n ;(commonShared.currentTimeStamp as jest.Mock).mockReturnValue(1_000)\n const client = { id: 'cid', tokenExpiration: 1_000 } as any\n jest.spyOn(crypto, 'randomUUID').mockReturnValue('uuid-err' as any)\n syncQueries.renewClientTokenAndExpiration.mockRejectedValue(new Error('db fail'))\n await expect(service.renewTokenAndExpiration(client, owner)).rejects.toMatchObject({ status: HttpStatus.BAD_REQUEST })\n })\n })\n\n describe('deleteClient', () => {\n it('should delete client successfully', async () => {\n syncQueries.deleteClient.mockResolvedValue(undefined)\n await expect(service.deleteClient({ id: 5 } as any, 'cid')).resolves.toBeUndefined()\n expect(syncQueries.deleteClient).toHaveBeenCalledWith(5, 'cid')\n })\n it('should throw Internal Server Error when deletion fails', async () => {\n syncQueries.deleteClient.mockRejectedValue(new Error('db error'))\n await expect(service.deleteClient({ id: 5 } as any, 'cid')).rejects.toMatchObject({ status: HttpStatus.INTERNAL_SERVER_ERROR })\n })\n })\n\n describe('checkAppStore', () => {\n it('should return PUBLIC manifest when HTTP fetch succeeds', async () => {\n setRepo(APP_STORE_REPOSITORY.PUBLIC)\n http.axiosRef.mockResolvedValue({ data: { platform: { win: [] } } })\n\n const manifest: any = await service.checkAppStore()\n expect(manifest).toBeTruthy()\n expect(manifest.repository).toBe(APP_STORE_REPOSITORY.PUBLIC)\n expect(http.axiosRef).toHaveBeenCalled()\n })\n\n it('should return null when PUBLIC manifest fetch fails', async () => {\n setRepo(APP_STORE_REPOSITORY.PUBLIC)\n http.axiosRef.mockRejectedValue(new Error('network'))\n expect(await service.checkAppStore()).toBeNull()\n })\n\n it('should return null when LOCAL manifest file does not exist', async () => {\n setRepo(APP_STORE_REPOSITORY.LOCAL)\n ;(isPathExists as jest.Mock).mockResolvedValue(false)\n expect(await service.checkAppStore()).toBeNull()\n })\n\n it('should return LOCAL manifest with rewritten URLs when file is valid', async () => {\n setRepo(APP_STORE_REPOSITORY.LOCAL)\n ;(isPathExists as jest.Mock).mockResolvedValue(true)\n const raw = {\n platform: {\n win: [{ package: 'desktop-win.exe' }, { package: 'cli-win.zip' }],\n linux: [{ package: 'desktop-linux.AppImage' }]\n }\n }\n ;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify(raw))\n\n const manifest: any = await service.checkAppStore()\n expect(manifest.repository).toBe(APP_STORE_REPOSITORY.LOCAL)\n expect(manifest.platform.win[0].url.startsWith(APP_STORE_DIRNAME)).toBe(true)\n expect(manifest.platform.win[0].url.endsWith('desktop-win.exe')).toBe(true)\n expect(manifest.platform.win[1].url.startsWith(APP_STORE_DIRNAME)).toBe(true)\n expect(manifest.platform.win[1].url.endsWith('cli-win.zip')).toBe(true)\n expect(manifest.platform.linux[0].url.startsWith(APP_STORE_DIRNAME)).toBe(true)\n expect(manifest.platform.linux[0].url.endsWith('desktop-linux.AppImage')).toBe(true)\n })\n\n it('should return null when LOCAL manifest cannot be parsed', async () => {\n setRepo(APP_STORE_REPOSITORY.LOCAL)\n ;(isPathExists as jest.Mock).mockResolvedValue(true)\n ;(fs.readFile as jest.Mock).mockRejectedValue(new Error('fs error'))\n expect(await service.checkAppStore()).toBeNull()\n })\n\n it('should rewrite desktop packages under desktop/os when package starts with \"desktop\"', async () => {\n setRepo(APP_STORE_REPOSITORY.LOCAL)\n ;(isPathExists as jest.Mock).mockResolvedValue(true)\n const raw = {\n platform: {\n win: [{ package: `${SYNC_CLIENT_TYPE.DESKTOP}-win.exe` }],\n mac: [{ package: `${SYNC_CLIENT_TYPE.DESKTOP}-mac.dmg` }],\n linux: [{ package: `${SYNC_CLIENT_TYPE.DESKTOP}-linux.AppImage` }]\n }\n }\n ;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify(raw))\n\n const manifest: any = await service.checkAppStore()\n expect(manifest).toBeTruthy()\n expect(manifest.repository).toBe(APP_STORE_REPOSITORY.LOCAL)\n expect(manifest.platform.win[0].url).toBe(`${APP_STORE_DIRNAME}/${SYNC_CLIENT_TYPE.DESKTOP}/win/${SYNC_CLIENT_TYPE.DESKTOP}-win.exe`)\n expect(manifest.platform.mac[0].url).toBe(`${APP_STORE_DIRNAME}/${SYNC_CLIENT_TYPE.DESKTOP}/mac/${SYNC_CLIENT_TYPE.DESKTOP}-mac.dmg`)\n expect(manifest.platform.linux[0].url).toBe(`${APP_STORE_DIRNAME}/${SYNC_CLIENT_TYPE.DESKTOP}/linux/${SYNC_CLIENT_TYPE.DESKTOP}-linux.AppImage`)\n })\n })\n})\n"],"names":["mockHavePermission","jest","mock","UserModel","fn","mockImplementation","props","havePermission","actual","requireActual","convertHumanTimeToSeconds","currentTimeStamp","readFile","isPathExists","describe","SyncClientsManager","name","service","http","authManager","authMethod","usersQueries","syncQueries","cacheMock","setRepo","repo","configuration","applications","appStore","repository","makeClient","overrides","id","ownerId","tokenExpiration","enabled","info","type","makeUser","isActive","login","email","firstName","lastName","role","permissions","beforeAll","axiosRef","setCookies","getTokens","validateUser","from","updateUserOrGuest","getOrCreateClient","deleteClient","getClient","updateClientInfo","renewClientTokenAndExpiration","getClients","genSlugKey","mockReturnValue","get","mockResolvedValue","undefined","set","del","module","Test","createTestingModule","providers","provide","Cache","useValue","HttpService","SyncQueries","UsersQueries","AuthManager","AuthMethod","compile","useLogger","cache","beforeEach","restoreAllMocks","clearAllMocks","commonShared","commonFunctions","v","mockReset","fs","mockClear","APP_STORE_REPOSITORY","PUBLIC","it","expect","toBeDefined","baseDto","password","clientId","version","test","each","HttpStatus","UNAUTHORIZED","FORBIDDEN","_label","user","status","register","rejects","toMatchObject","r","toEqual","clientToken","toHaveBeenCalledWith","mockRejectedValue","Error","INTERNAL_SERVER_ERROR","unregister","resolves","toBeUndefined","ip","dto","token","authenticate","CLIENT_AUTH_TYPE","TOKEN","response","CLIENT_TOKEN_EXPIRED_ERROR","mockRejectedValueOnce","access_token","refresh_token","spyOn","reply","COOKIE","toHaveBeenCalledTimes","renewTokenAndExpiration","client_token_update","toBe","toBeInstanceOf","TypeError","fake","paths","owner","client","toBeTruthy","any","Number","crypto","BAD_REQUEST","data","platform","win","manifest","checkAppStore","toHaveBeenCalled","toBeNull","LOCAL","raw","package","linux","JSON","stringify","url","startsWith","APP_STORE_DIRNAME","endsWith","SYNC_CLIENT_TYPE","DESKTOP","mac"],"mappings":"AAAA;;;;CAIC;;;;uBAE2B;wBACD;yBACS;mEAEjB;iEACJ;4BACY;oCACC;mEACK;gEACH;mCACA;8BACR;uBACO;qCACA;sBACgC;uBACL;sBACvB;2CACE;oCACP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE5B,oCAAoC;AACpC,IAAIA,qBAAqB;AACzBC,KAAKC,IAAI,CAAC,iCAAiC,IAAO,CAAA;QAChDC,WAAWF,KAAKG,EAAE,GAAGC,kBAAkB,CAAC,CAACC,QAAgB,CAAA;gBACvD,GAAGA,KAAK;gBACRC,gBAAgB,IAAMP;YACxB,CAAA;IACF,CAAA;AAEA,0CAA0C;AAC1CC,KAAKC,IAAI,CAAC,6BAA6B;IACrC,MAAMM,SAASP,KAAKQ,aAAa,CAAC;IAClC,OAAO;QAAE,GAAGD,MAAM;QAAEE,2BAA2BT,KAAKG,EAAE;IAAG;AAC3D;AAEA,wBAAwB;AACxBH,KAAKC,IAAI,CAAC,0BAA0B,IAAO,CAAA;QAAES,kBAAkBV,KAAKG,EAAE;IAAG,CAAA;AAEzE,gCAAgC;AAChCH,KAAKC,IAAI,CAAC,oBAAoB,IAAO,CAAA;QAAEU,UAAUX,KAAKG,EAAE;IAAG,CAAA;AAC3DH,KAAKC,IAAI,CAAC,2BAA2B,IAAO,CAAA;QAAEW,cAAcZ,KAAKG,EAAE;IAAG,CAAA;AAEtEU,SAASC,6CAAkB,CAACC,IAAI,EAAE;IAChC,IAAIC;IAEJ,QAAQ;IACR,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IAQJ,IAAIC;IAEJ,UAAU;IACV,MAAMC,UAAU,CAACC;;QACbC,gCAAa,CAASC,YAAY,CAACC,QAAQ,CAACC,UAAU,GAAGJ;IAC7D;IACA,MAAMK,aAAa,CAACC,YAAiB,CAAC,CAAC,GAAM,CAAA;YAC3CC,IAAI;YACJC,SAAS;YACTC,iBAAiB;YACjBC,SAAS;YACTC,MAAM;gBAAEC,MAAM;YAAU;YACxB,GAAGN,SAAS;QACd,CAAA;IACA,MAAMO,WAAW,CAACP,YAAiB,CAAC,CAAC,GAAM,CAAA;YACzCC,IAAI;YACJO,UAAU;YACVC,OAAO;YACPC,OAAO;YACPC,WAAW;YACXC,UAAU;YACVC,MAAM;YACNC,aAAa;YACb,GAAGd,SAAS;QACd,CAAA;IAEAe,UAAU;QACR5B,OAAO;YAAE6B,UAAU9C,KAAKG,EAAE;QAAG;QAC7Be,cAAc;YAAE6B,YAAY/C,KAAKG,EAAE;YAAI6C,WAAWhD,KAAKG,EAAE;QAAG;QAC5DgB,aAAa;YAAE8B,cAAcjD,KAAKG,EAAE;QAAG;QACvCiB,eAAe;YAAE8B,MAAMlD,KAAKG,EAAE;YAAIgD,mBAAmBnD,KAAKG,EAAE;QAAG;QAC/DkB,cAAc;YACZ+B,mBAAmBpD,KAAKG,EAAE;YAC1BkD,cAAcrD,KAAKG,EAAE;YACrBmD,WAAWtD,KAAKG,EAAE;YAClBoD,kBAAkBvD,KAAKG,EAAE;YACzBqD,+BAA+BxD,KAAKG,EAAE;YACtCsD,YAAYzD,KAAKG,EAAE;QACrB;QACAmB,YAAY;YACVoC,YAAY1D,KAAKG,EAAE,GAAGwD,eAAe,CAAC;YACtCC,KAAK5D,KAAKG,EAAE,GAAG0D,iBAAiB,CAACC;YACjCC,KAAK/D,KAAKG,EAAE,GAAG0D,iBAAiB,CAACC;YACjCE,KAAKhE,KAAKG,EAAE,GAAG0D,iBAAiB,CAACC;QACnC;QAEA,MAAMG,SAAwB,MAAMC,aAAI,CAACC,mBAAmB,CAAC;YAC3DC,WAAW;gBACTtD,6CAAkB;gBAClB;oBAAEuD,SAASC,mBAAK;oBAAEC,UAAUjD;gBAAU;gBACtC;oBAAE+C,SAASG,kBAAW;oBAAED,UAAUtD;gBAAK;gBACvC;oBAAEoD,SAASI,+BAAW;oBAAEF,UAAUlD;gBAAY;gBAC9C;oBAAEgD,SAASK,iCAAY;oBAAEH,UAAUnD;gBAAa;gBAChD;oBAAEiD,SAASM,+BAAW;oBAAEJ,UAAUrD;gBAAY;gBAC9C;oBAAEmD,SAASO,sBAAU;oBAAEL,UAAUpD;gBAAW;aAC7C;QACH,GAAG0D,OAAO;QAEVZ,OAAOa,SAAS,CAAC;YAAC;SAAQ;QAC1B9D,UAAUiD,OAAOL,GAAG,CAAqB9C,6CAAkB;QACzDE,QAAgB+D,KAAK,GAAGzD;IAC5B;IAEA0D,WAAW;QACThF,KAAKiF,eAAe;QACpBjF,KAAKkF,aAAa;QAClBnF,qBAAqB;QACnBoF,QAAazE,gBAAgB,CAAeiD,eAAe,CAAC;QAC5DyB,WAAgB3E,yBAAyB,CAAeL,kBAAkB,CAAC,CAACiF;YAC5E,IAAIA,MAAM,OAAO,OAAO,KAAK,KAAK;YAClC,IAAIA,MAAM,QAAQ,OAAO,MAAM,KAAK;YACpC,IAAI,OAAOA,MAAM,UAAU,OAAOA;YAClC,OAAO;QACT;QACEzE,mBAAY,CAAe0E,SAAS;QACpCC,iBAAE,CAAC5E,QAAQ,CAAe2E,SAAS;QACnCjE,YAAYkC,gBAAgB,CAAeM,iBAAiB,CAACC;QAC7D1C,aAAa+B,iBAAiB,CAAeU,iBAAiB,CAACC;QAC/D9C,QAAgB+D,KAAK,GAAGzD;QAC1BA,UAAUsC,GAAG,CAACC,iBAAiB,CAACC;QAChCxC,UAAUsC,GAAG,CAAC4B,SAAS;QACvBlE,UAAUyC,GAAG,CAACyB,SAAS;QACvBlE,UAAU0C,GAAG,CAACwB,SAAS;QACvBlE,UAAUoC,UAAU,CAAC8B,SAAS;QAC9BjE,QAAQkE,2BAAoB,CAACC,MAAM;IACrC;IAEAC,GAAG,qBAAqB,IAAMC,OAAO5E,SAAS6E,WAAW;IAEzDhF,SAAS,YAAY;QACnB,MAAMiF,UAAU;YAAEvD,OAAO;YAAQwD,UAAU;YAAUC,UAAU;YAAY7D,MAAM;gBAAEC,MAAM;gBAAW6D,SAAS;YAAQ;QAAE;QAEvHC,KAAKC,IAAI,CAAC;YACR;gBAAC;gBAA6C;gBAAMC,kBAAU,CAACC,YAAY;aAAC;YAC5E;gBAAC;gBAAoD;oBAAEtE,IAAI;oBAAIQ,OAAO;oBAAQjC,gBAAgB,IAAM;gBAAM;gBAAG8F,kBAAU,CAACE,SAAS;aAAC;SACnI,EAAE,mBAAmB,OAAOC,QAAQC,MAAMC;YACzCtF,WAAW8B,YAAY,CAACY,iBAAiB,CAAC2C;YAC1C,MAAMZ,OAAO5E,QAAQ0F,QAAQ,CAACZ,SAAgB,YAAYa,OAAO,CAACC,aAAa,CAAC;gBAAEH;YAAO;QAC3F;QAEAd,GAAG,yDAAyD;YAC1DxE,WAAW8B,YAAY,CAACY,iBAAiB,CAAC;gBAAE9B,IAAI;gBAAIQ,OAAO;gBAAQjC,gBAAgB,IAAM;YAAK;YAC9Fe,YAAY+B,iBAAiB,CAACS,iBAAiB,CAAC;YAEhD,MAAMgD,IAAI,MAAM7F,QAAQ0F,QAAQ,CAACZ,SAAgB;YACjDF,OAAOiB,GAAGC,OAAO,CAAC;gBAAEC,aAAa;YAAY;YAC7CnB,OAAOvE,YAAY+B,iBAAiB,EAAE4D,oBAAoB,CAAC,IAAI,YAAYlB,QAAQ3D,IAAI,EAAE;QAC3F;QAEAwD,GAAG,6DAA6D;YAC9DxE,WAAW8B,YAAY,CAACY,iBAAiB,CAAC;gBAAE9B,IAAI;gBAAIQ,OAAO;gBAAQjC,gBAAgB,IAAM;YAAK;YAC9Fe,YAAY+B,iBAAiB,CAAC6D,iBAAiB,CAAC,IAAIC,MAAM;YAC1D,MAAMtB,OAAO5E,QAAQ0F,QAAQ,CAACZ,SAAgB,YAAYa,OAAO,CAACC,aAAa,CAAC;gBAAEH,QAAQL,kBAAU,CAACe,qBAAqB;YAAC;QAC7H;IACF;IAEAtG,SAAS,cAAc;QACrB8E,GAAG,sCAAsC;YACvCtE,YAAYgC,YAAY,CAACQ,iBAAiB,CAACC;YAC3C,MAAM8B,OAAO5E,QAAQoG,UAAU,CAAC;gBAAErF,IAAI;gBAAGiE,UAAU;YAAK,IAAWqB,QAAQ,CAACC,aAAa;YACzF1B,OAAOvE,YAAYgC,YAAY,EAAE2D,oBAAoB,CAAC,GAAG;QAC3D;QACArB,GAAG,0DAA0D;YAC3DtE,YAAYgC,YAAY,CAAC4D,iBAAiB,CAAC,IAAIC,MAAM;YACrD,MAAMtB,OAAO5E,QAAQoG,UAAU,CAAC;gBAAErF,IAAI;gBAAGiE,UAAU;YAAK,IAAWW,OAAO,CAACC,aAAa,CAAC;gBAAEH,QAAQL,kBAAU,CAACe,qBAAqB;YAAC;QACtI;IACF;IAEAtG,SAAS,gBAAgB;QACvB,MAAM0G,KAAK;QACX,MAAMC,MAAM;YAAExB,UAAU;YAAOyB,OAAO;QAAO;QAE7C9B,GAAG,wCAAwC;YACzCtE,YAAYiC,SAAS,CAACO,iBAAiB,CAACC;YACxC,MAAM8B,OAAO5E,QAAQ0G,YAAY,CAACC,sBAAgB,CAACC,KAAK,EAAEJ,KAAYD,IAAI,CAAC,IAAoBZ,OAAO,CAACC,aAAa,CAAC;gBACnHH,QAAQL,kBAAU,CAACE,SAAS;gBAC5BuB,UAAU;YACZ;QACF;QAEAlC,GAAG,yCAAyC;YAC1CtE,YAAYiC,SAAS,CAACO,iBAAiB,CAAChC,WAAW;gBAAEK,SAAS;gBAAOD,iBAAiB;YAAK;YAC3F,MAAM2D,OAAO5E,QAAQ0G,YAAY,CAACC,sBAAgB,CAACC,KAAK,EAAEJ,KAAYD,IAAI,CAAC,IAAoBZ,OAAO,CAACC,aAAa,CAAC;gBACnHH,QAAQL,kBAAU,CAACE,SAAS;gBAC5BuB,UAAU;YACZ;QACF;QAEAlC,GAAG,8CAA8C;;YAC7CR,QAAazE,gBAAgB,CAAeiD,eAAe,CAAC;YAC9DtC,YAAYiC,SAAS,CAACO,iBAAiB,CAAChC,WAAW;gBAAEI,iBAAiB;YAAK;YAC3E,MAAM2D,OAAO5E,QAAQ0G,YAAY,CAACC,sBAAgB,CAACC,KAAK,EAAEJ,KAAYD,IAAI,CAAC,IAAoBZ,OAAO,CAACC,aAAa,CAAC;gBACnHH,QAAQL,kBAAU,CAACE,SAAS;gBAC5BuB,UAAUC,gCAA0B;YACtC;QACF;QAEAnC,GAAG,gDAAgD;YACjDtE,YAAYiC,SAAS,CAACO,iBAAiB,CAAChC;YACxCR,YAAYkC,gBAAgB,CAACwE,qBAAqB,CAAC,IAAIb,MAAM,kBAAiB,mBAAmB;YACjG9F,aAAa8B,IAAI,CAACW,iBAAiB,CAAC;YACpC,MAAM+B,OAAO5E,QAAQ0G,YAAY,CAACC,sBAAgB,CAACC,KAAK,EAAEJ,KAAYD,IAAI,CAAC,IAAoBZ,OAAO,CAACC,aAAa,CAAC;gBACnHH,QAAQL,kBAAU,CAACE,SAAS;gBAC5BuB,UAAU;YACZ;QACF;QAEAlC,GAAG,gDAAgD;YACjDtE,YAAYiC,SAAS,CAACO,iBAAiB,CAAChC;YACxCT,aAAa8B,IAAI,CAACW,iBAAiB,CAACxB,SAAS;gBAAEC,UAAU;YAAM;YAC/D,MAAMsD,OAAO5E,QAAQ0G,YAAY,CAACC,sBAAgB,CAACC,KAAK,EAAEJ,KAAYD,IAAI,CAAC,IAAoBZ,OAAO,CAACC,aAAa,CAAC;gBACnHH,QAAQL,kBAAU,CAACE,SAAS;gBAC5BuB,UAAU;YACZ;QACF;QAEAlC,GAAG,yDAAyD;YAC1D5F,qBAAqB;YACrBsB,YAAYiC,SAAS,CAACO,iBAAiB,CAAChC;YACxCT,aAAa8B,IAAI,CAACW,iBAAiB,CAACxB,SAAS;gBAAEO,aAAa;gBAAID,MAAM;YAAI;YAC1E,MAAMiD,OAAO5E,QAAQ0G,YAAY,CAACC,sBAAgB,CAACC,KAAK,EAAEJ,KAAYD,IAAI,CAAC,IAAoBZ,OAAO,CAACC,aAAa,CAAC;gBACnHH,QAAQL,kBAAU,CAACE,SAAS;gBAC5BuB,UAAU;YACZ;QACF;QAEAlC,GAAG,2EAA2E;YAC5EtE,YAAYiC,SAAS,CAACO,iBAAiB,CAAChC,WAAW;gBAAEG,SAAS;YAAE;YAChEZ,aAAa8B,IAAI,CAACW,iBAAiB,CAACxB,SAAS;gBAAEN,IAAI;gBAAGQ,OAAO;gBAAQC,OAAO;gBAAYC,WAAW;gBAAQC,UAAU;YAAM;YAC3HtB,aAAa+B,iBAAiB,CAAC4E,qBAAqB,CAAC,IAAIb,MAAM,wBAAuB,mBAAmB;YACzGhG,YAAY6B,UAAU,CAACc,iBAAiB,CAAC;gBAAEmE,cAAc;gBAAKC,eAAe;YAAI;YACjFjI,KAAKkI,KAAK,CAAClH,SAAS,2BAA2B6C,iBAAiB,CAAC;YAEjE,MAAMsE,QAAQ,CAAC;YACf,MAAMtB,IAAS,MAAM7F,QAAQ0G,YAAY,CAACC,sBAAgB,CAACS,MAAM,EAAEZ,KAAYD,IAAIY;YAEnFvC,OAAO1E,YAAY6B,UAAU,EAAEsF,qBAAqB,CAAC;YACrDzC,OAAO5E,QAAQsH,uBAAuB,EAAED,qBAAqB,CAAC;YAC9DzC,OAAOiB,EAAE0B,mBAAmB,EAAEC,IAAI,CAAC;QACrC;QAEA7C,GAAG,qEAAqE;YACtEtE,YAAYiC,SAAS,CAACO,iBAAiB,CAAChC,WAAW;gBAAEG,SAAS;YAAE;YAChEZ,aAAa8B,IAAI,CAACW,iBAAiB,CAACxB,SAAS;gBAAEN,IAAI;gBAAGQ,OAAO;gBAASC,OAAO;gBAAaC,WAAW;YAAQ;YAC7GvB,YAAY8B,SAAS,CAACa,iBAAiB,CAAC;gBAAEmE,cAAc;gBAAKC,eAAe;YAAI;YAChFjI,KAAKkI,KAAK,CAAClH,SAAS,2BAA2B6C,iBAAiB,CAACC;YAEjE,MAAM+C,IAAS,MAAM7F,QAAQ0G,YAAY,CAACC,sBAAgB,CAACC,KAAK,EAAEJ,KAAYD,IAAI,CAAC;YACnF3B,OAAO1E,YAAY8B,SAAS,EAAEqF,qBAAqB,CAAC;YACpDzC,OAAOiB,EAAE0B,mBAAmB,EAAEjB,aAAa;QAC7C;QAEA3B,GAAG,wDAAwD;YACzDtE,YAAYiC,SAAS,CAACO,iBAAiB,CAAChC,WAAW;gBAAEG,SAAS;YAAE;YAChEZ,aAAa8B,IAAI,CAACW,iBAAiB,CAACxB,SAAS;gBAAEN,IAAI;gBAAGQ,OAAO;gBAAOC,OAAO;gBAAWC,WAAW;YAAM;YACvGzC,KAAKkI,KAAK,CAAClH,SAAS,2BAA2B6C,iBAAiB,CAACC;YACjE,MAAM8B,OAAO5E,QAAQ0G,YAAY,CAAC,WAAkB;gBAAE1B,UAAU;gBAAOyB,OAAO;YAAO,GAAUF,IAAI,CAAC,IAAoBZ,OAAO,CAAC8B,cAAc,CAC5IC;QAEJ;IACF;IAEA7H,SAAS,cAAc;QACrB8E,GAAG,0CAA0C;YAC3C,MAAMgD,OAAO;gBAAC;oBAAE5G,IAAI;oBAAM6G,OAAO,EAAE;gBAAC;aAAE;YACtCvH,YAAYoC,UAAU,CAACI,iBAAiB,CAAC8E;YACzC,MAAM9B,IAAI,MAAM7F,QAAQyC,UAAU,CAAC;gBAAE1B,IAAI;gBAAGiE,UAAU;YAAK;YAC3DJ,OAAOiB,GAAG2B,IAAI,CAACG;YACf/C,OAAOvE,YAAYoC,UAAU,EAAEuD,oBAAoB,CAAC;gBAAEjF,IAAI;gBAAGiE,UAAU;YAAK;QAC9E;IACF;IAEAnF,SAAS,2BAA2B;QAClC,MAAMgI,QAAQ;YAAE9G,IAAI;YAAGQ,OAAO;QAAM;QAEpCoD,GAAG,+DAA+D;;YAC9DR,QAAazE,gBAAgB,CAAeiD,eAAe,CAAC;YAC5DyB,WAAgB3E,yBAAyB,CAAeL,kBAAkB,CAAC,CAACiF,IAAeA,MAAM,QAAQ,KAAK,KAAK,OAAO;YAC5H,MAAMyD,SAAS;gBAAE/G,IAAI;gBAAOE,iBAAiB,QAAQ,KAAK,KAAK,OAAO;YAAE;YACxE2D,OAAO,MAAM5E,QAAQsH,uBAAuB,CAACQ,QAAQD,QAAQvB,aAAa;QAC5E;QAEA3B,GAAG,oEAAoE;;YACnER,QAAazE,gBAAgB,CAAeiD,eAAe,CAAC;YAC5DyB,WAAgB3E,yBAAyB,CAAeL,kBAAkB,CAAC,CAACiF,IAC5EA,MAAM,QAAQ,KAAK,KAAK,OAAOA,MAAM,SAAS,MAAM,KAAK,OAAO;YAElE,MAAMyD,SAAS;gBAAE/G,IAAI;gBAAOE,iBAAiB,QAAQ,KAAK,KAAK,OAAO;YAAE;YACxEZ,YAAYmC,6BAA6B,CAACK,iBAAiB,CAACC;YAE5D,MAAM+C,IAAI,MAAM7F,QAAQsH,uBAAuB,CAACQ,QAAQD;YACxDjD,OAAO,OAAOiB,GAAG2B,IAAI,CAAC;YACtB5C,OAAOiB,GAAGkC,UAAU;YACpBnD,OAAOvE,YAAYmC,6BAA6B,EAAEwD,oBAAoB,CAAC,OAAOH,GAAGjB,OAAOoD,GAAG,CAACC;QAC9F;QAEAtD,GAAG,2DAA2D;;YAC1DR,QAAazE,gBAAgB,CAAeiD,eAAe,CAAC;YAC9D,MAAMmF,SAAS;gBAAE/G,IAAI;gBAAOE,iBAAiB;YAAM;YACnDjC,KAAKkI,KAAK,CAACgB,mBAAM,EAAE,cAAcvF,eAAe,CAAC;YACjDtC,YAAYmC,6BAA6B,CAACyD,iBAAiB,CAAC,IAAIC,MAAM;YACtE,MAAMtB,OAAO5E,QAAQsH,uBAAuB,CAACQ,QAAQD,QAAQlC,OAAO,CAACC,aAAa,CAAC;gBAAEH,QAAQL,kBAAU,CAAC+C,WAAW;YAAC;QACtH;IACF;IAEAtI,SAAS,gBAAgB;QACvB8E,GAAG,qCAAqC;YACtCtE,YAAYgC,YAAY,CAACQ,iBAAiB,CAACC;YAC3C,MAAM8B,OAAO5E,QAAQqC,YAAY,CAAC;gBAAEtB,IAAI;YAAE,GAAU,QAAQsF,QAAQ,CAACC,aAAa;YAClF1B,OAAOvE,YAAYgC,YAAY,EAAE2D,oBAAoB,CAAC,GAAG;QAC3D;QACArB,GAAG,0DAA0D;YAC3DtE,YAAYgC,YAAY,CAAC4D,iBAAiB,CAAC,IAAIC,MAAM;YACrD,MAAMtB,OAAO5E,QAAQqC,YAAY,CAAC;gBAAEtB,IAAI;YAAE,GAAU,QAAQ4E,OAAO,CAACC,aAAa,CAAC;gBAAEH,QAAQL,kBAAU,CAACe,qBAAqB;YAAC;QAC/H;IACF;IAEAtG,SAAS,iBAAiB;QACxB8E,GAAG,0DAA0D;YAC3DpE,QAAQkE,2BAAoB,CAACC,MAAM;YACnCzE,KAAK6B,QAAQ,CAACe,iBAAiB,CAAC;gBAAEuF,MAAM;oBAAEC,UAAU;wBAAEC,KAAK,EAAE;oBAAC;gBAAE;YAAE;YAElE,MAAMC,WAAgB,MAAMvI,QAAQwI,aAAa;YACjD5D,OAAO2D,UAAUR,UAAU;YAC3BnD,OAAO2D,SAAS3H,UAAU,EAAE4G,IAAI,CAAC/C,2BAAoB,CAACC,MAAM;YAC5DE,OAAO3E,KAAK6B,QAAQ,EAAE2G,gBAAgB;QACxC;QAEA9D,GAAG,uDAAuD;YACxDpE,QAAQkE,2BAAoB,CAACC,MAAM;YACnCzE,KAAK6B,QAAQ,CAACmE,iBAAiB,CAAC,IAAIC,MAAM;YAC1CtB,OAAO,MAAM5E,QAAQwI,aAAa,IAAIE,QAAQ;QAChD;QAEA/D,GAAG,8DAA8D;YAC/DpE,QAAQkE,2BAAoB,CAACkE,KAAK;YAChC/I,mBAAY,CAAeiD,iBAAiB,CAAC;YAC/C+B,OAAO,MAAM5E,QAAQwI,aAAa,IAAIE,QAAQ;QAChD;QAEA/D,GAAG,uEAAuE;YACxEpE,QAAQkE,2BAAoB,CAACkE,KAAK;YAChC/I,mBAAY,CAAeiD,iBAAiB,CAAC;YAC/C,MAAM+F,MAAM;gBACVP,UAAU;oBACRC,KAAK;wBAAC;4BAAEO,SAAS;wBAAkB;wBAAG;4BAAEA,SAAS;wBAAc;qBAAE;oBACjEC,OAAO;wBAAC;4BAAED,SAAS;wBAAyB;qBAAE;gBAChD;YACF;YACEtE,iBAAE,CAAC5E,QAAQ,CAAekD,iBAAiB,CAACkG,KAAKC,SAAS,CAACJ;YAE7D,MAAML,WAAgB,MAAMvI,QAAQwI,aAAa;YACjD5D,OAAO2D,SAAS3H,UAAU,EAAE4G,IAAI,CAAC/C,2BAAoB,CAACkE,KAAK;YAC3D/D,OAAO2D,SAASF,QAAQ,CAACC,GAAG,CAAC,EAAE,CAACW,GAAG,CAACC,UAAU,CAACC,wBAAiB,GAAG3B,IAAI,CAAC;YACxE5C,OAAO2D,SAASF,QAAQ,CAACC,GAAG,CAAC,EAAE,CAACW,GAAG,CAACG,QAAQ,CAAC,oBAAoB5B,IAAI,CAAC;YACtE5C,OAAO2D,SAASF,QAAQ,CAACC,GAAG,CAAC,EAAE,CAACW,GAAG,CAACC,UAAU,CAACC,wBAAiB,GAAG3B,IAAI,CAAC;YACxE5C,OAAO2D,SAASF,QAAQ,CAACC,GAAG,CAAC,EAAE,CAACW,GAAG,CAACG,QAAQ,CAAC,gBAAgB5B,IAAI,CAAC;YAClE5C,OAAO2D,SAASF,QAAQ,CAACS,KAAK,CAAC,EAAE,CAACG,GAAG,CAACC,UAAU,CAACC,wBAAiB,GAAG3B,IAAI,CAAC;YAC1E5C,OAAO2D,SAASF,QAAQ,CAACS,KAAK,CAAC,EAAE,CAACG,GAAG,CAACG,QAAQ,CAAC,2BAA2B5B,IAAI,CAAC;QACjF;QAEA7C,GAAG,2DAA2D;YAC5DpE,QAAQkE,2BAAoB,CAACkE,KAAK;YAChC/I,mBAAY,CAAeiD,iBAAiB,CAAC;YAC7C0B,iBAAE,CAAC5E,QAAQ,CAAesG,iBAAiB,CAAC,IAAIC,MAAM;YACxDtB,OAAO,MAAM5E,QAAQwI,aAAa,IAAIE,QAAQ;QAChD;QAEA/D,GAAG,uFAAuF;YACxFpE,QAAQkE,2BAAoB,CAACkE,KAAK;YAChC/I,mBAAY,CAAeiD,iBAAiB,CAAC;YAC/C,MAAM+F,MAAM;gBACVP,UAAU;oBACRC,KAAK;wBAAC;4BAAEO,SAAS,GAAGQ,sBAAgB,CAACC,OAAO,CAAC,QAAQ,CAAC;wBAAC;qBAAE;oBACzDC,KAAK;wBAAC;4BAAEV,SAAS,GAAGQ,sBAAgB,CAACC,OAAO,CAAC,QAAQ,CAAC;wBAAC;qBAAE;oBACzDR,OAAO;wBAAC;4BAAED,SAAS,GAAGQ,sBAAgB,CAACC,OAAO,CAAC,eAAe,CAAC;wBAAC;qBAAE;gBACpE;YACF;YACE/E,iBAAE,CAAC5E,QAAQ,CAAekD,iBAAiB,CAACkG,KAAKC,SAAS,CAACJ;YAE7D,MAAML,WAAgB,MAAMvI,QAAQwI,aAAa;YACjD5D,OAAO2D,UAAUR,UAAU;YAC3BnD,OAAO2D,SAAS3H,UAAU,EAAE4G,IAAI,CAAC/C,2BAAoB,CAACkE,KAAK;YAC3D/D,OAAO2D,SAASF,QAAQ,CAACC,GAAG,CAAC,EAAE,CAACW,GAAG,EAAEzB,IAAI,CAAC,GAAG2B,wBAAiB,CAAC,CAAC,EAAEE,sBAAgB,CAACC,OAAO,CAAC,KAAK,EAAED,sBAAgB,CAACC,OAAO,CAAC,QAAQ,CAAC;YACpI1E,OAAO2D,SAASF,QAAQ,CAACkB,GAAG,CAAC,EAAE,CAACN,GAAG,EAAEzB,IAAI,CAAC,GAAG2B,wBAAiB,CAAC,CAAC,EAAEE,sBAAgB,CAACC,OAAO,CAAC,KAAK,EAAED,sBAAgB,CAACC,OAAO,CAAC,QAAQ,CAAC;YACpI1E,OAAO2D,SAASF,QAAQ,CAACS,KAAK,CAAC,EAAE,CAACG,GAAG,EAAEzB,IAAI,CAAC,GAAG2B,wBAAiB,CAAC,CAAC,EAAEE,sBAAgB,CAACC,OAAO,CAAC,OAAO,EAAED,sBAAgB,CAACC,OAAO,CAAC,eAAe,CAAC;QACjJ;IACF;AACF"}
1
+ {"version":3,"sources":["../../../../../backend/src/applications/sync/services/sync-clients-manager.service.spec.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { HttpService } from '@nestjs/axios'\nimport { HttpStatus } from '@nestjs/common'\nimport { Test, TestingModule } from '@nestjs/testing'\nimport { FastifyReply } from 'fastify'\nimport crypto from 'node:crypto'\nimport fs from 'node:fs/promises'\nimport { AuthMethod } from '../../../authentication/models/auth-method'\nimport { AuthManager } from '../../../authentication/services/auth-manager.service'\nimport { AuthMethod2FA } from '../../../authentication/services/auth-methods/auth-method-two-fa.service'\nimport * as commonFunctions from '../../../common/functions'\nimport * as commonShared from '../../../common/shared'\nimport { configuration } from '../../../configuration/config.environment'\nimport { Cache } from '../../../infrastructure/cache/services/cache.service'\nimport { isPathExists } from '../../files/utils/files'\nimport { UserModel } from '../../users/models/user.model'\nimport { UsersManager } from '../../users/services/users-manager.service'\nimport { CLIENT_AUTH_TYPE, CLIENT_TOKEN_EXPIRED_ERROR } from '../constants/auth'\nimport { APP_STORE_DIRNAME, APP_STORE_REPOSITORY } from '../constants/store'\nimport { SYNC_CLIENT_TYPE } from '../constants/sync'\nimport { SyncClientsManager } from './sync-clients-manager.service'\nimport { SyncQueries } from './sync-queries.service'\n\n// Pilotage permission via UserModel\nlet mockHavePermission = true\njest.mock('../../users/models/user.model', () => ({\n UserModel: jest.fn().mockImplementation((props: any) => ({\n ...props,\n havePermission: () => mockHavePermission\n }))\n}))\n\n// Mock ciblé de convertHumanTimeToSeconds\njest.mock('../../../common/functions', () => {\n const actual = jest.requireActual('../../../common/functions')\n return { ...actual, convertHumanTimeToSeconds: jest.fn() }\n})\n\n// Mock currentTimeStamp\njest.mock('../../../common/shared', () => ({ currentTimeStamp: jest.fn() }))\n\n// Mock FS et helper d'existence\njest.mock('node:fs/promises', () => ({ readFile: jest.fn() }))\njest.mock('../../files/utils/files', () => ({ isPathExists: jest.fn() }))\n\ndescribe(SyncClientsManager.name, () => {\n let service: SyncClientsManager\n\n // Mocks\n let http: { axiosRef: jest.Mock }\n let authManager: { setCookies: jest.Mock; getTokens: jest.Mock }\n let authMethod: { validateUser: jest.Mock }\n let usersManager: { fromUserId: jest.Mock; updateAccesses: jest.Mock }\n let syncQueries: {\n getOrCreateClient: jest.Mock\n deleteClient: jest.Mock\n getClient: jest.Mock\n updateClientInfo: jest.Mock\n renewClientTokenAndExpiration: jest.Mock\n getClients: jest.Mock\n }\n let cacheMock: { genSlugKey: jest.Mock; get: jest.Mock; set: jest.Mock; del: jest.Mock }\n\n // Helpers\n const setRepo = (repo: APP_STORE_REPOSITORY) => {\n ;(configuration as any).applications.appStore.repository = repo\n }\n const makeClient = (overrides: any = {}) => ({\n id: 'cid',\n ownerId: 1,\n tokenExpiration: 2000,\n enabled: true,\n info: { type: 'desktop' },\n ...overrides\n })\n const makeUser = (overrides: any = {}) =>\n new UserModel({\n id: 1,\n isActive: true,\n login: 'u',\n email: 'u@x',\n firstName: 'U',\n lastName: 'X',\n role: 1,\n permissions: 'desktop',\n ...overrides\n })\n\n beforeAll(async () => {\n http = { axiosRef: jest.fn() }\n authManager = { setCookies: jest.fn(), getTokens: jest.fn() }\n authMethod = { validateUser: jest.fn() }\n usersManager = { fromUserId: jest.fn(), updateAccesses: jest.fn() }\n syncQueries = {\n getOrCreateClient: jest.fn(),\n deleteClient: jest.fn(),\n getClient: jest.fn(),\n updateClientInfo: jest.fn(),\n renewClientTokenAndExpiration: jest.fn(),\n getClients: jest.fn()\n }\n cacheMock = {\n genSlugKey: jest.fn().mockReturnValue('syncclientsmanager:checkappstore'),\n get: jest.fn().mockResolvedValue(undefined),\n set: jest.fn().mockResolvedValue(undefined),\n del: jest.fn().mockResolvedValue(undefined)\n }\n\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n SyncClientsManager,\n { provide: Cache, useValue: cacheMock },\n { provide: HttpService, useValue: http },\n { provide: SyncQueries, useValue: syncQueries },\n { provide: UsersManager, useValue: usersManager },\n { provide: AuthManager, useValue: authManager },\n { provide: AuthMethod, useValue: authMethod },\n { provide: AuthMethod2FA, useValue: {} }\n ]\n }).compile()\n\n module.useLogger(['fatal'])\n service = module.get<SyncClientsManager>(SyncClientsManager)\n ;(service as any).cache = cacheMock\n })\n\n beforeEach(() => {\n jest.restoreAllMocks()\n jest.clearAllMocks()\n mockHavePermission = true\n ;(commonShared.currentTimeStamp as jest.Mock).mockReturnValue(1_000)\n ;(commonFunctions.convertHumanTimeToSeconds as jest.Mock).mockImplementation((v: string | number) => {\n if (v === '90d') return 90 * 24 * 3600\n if (v === '180d') return 180 * 24 * 3600\n if (typeof v === 'number') return v\n return 0\n })\n ;(isPathExists as jest.Mock).mockReset()\n ;(fs.readFile as jest.Mock).mockReset()\n ;(syncQueries.updateClientInfo as jest.Mock).mockResolvedValue(undefined)\n ;(usersManager.updateAccesses as jest.Mock).mockResolvedValue(undefined)\n ;(service as any).cache = cacheMock\n cacheMock.get.mockResolvedValue(undefined)\n cacheMock.get.mockClear()\n cacheMock.set.mockClear()\n cacheMock.del.mockClear()\n cacheMock.genSlugKey.mockClear()\n setRepo(APP_STORE_REPOSITORY.PUBLIC)\n })\n\n it('should be defined', () => expect(service).toBeDefined())\n\n describe('register', () => {\n const baseDto = { login: 'john', password: 'secret', clientId: 'client-1', info: { type: 'desktop', version: '1.0.0' } }\n\n test.each([\n ['Unauthorized when credentials are invalid', null, HttpStatus.UNAUTHORIZED],\n ['Forbidden when user lacks DESKTOP_APP permission', { id: 10, login: 'john', havePermission: () => false }, HttpStatus.FORBIDDEN]\n ])('should throw %s', async (_label, user, status) => {\n authMethod.validateUser.mockResolvedValue(user)\n await expect(service.register(baseDto as any, '1.2.3.4')).rejects.toMatchObject({ status })\n })\n\n it('should return client token when registration succeeds', async () => {\n authMethod.validateUser.mockResolvedValue({ id: 10, login: 'john', havePermission: () => true })\n syncQueries.getOrCreateClient.mockResolvedValue('token-abc')\n\n const r = await service.register(baseDto as any, '1.2.3.4')\n expect(r).toEqual({ clientToken: 'token-abc' })\n expect(syncQueries.getOrCreateClient).toHaveBeenCalledWith(10, 'client-1', baseDto.info, '1.2.3.4')\n })\n\n it('should throw Internal Server Error when persistence fails', async () => {\n authMethod.validateUser.mockResolvedValue({ id: 10, login: 'john', havePermission: () => true })\n syncQueries.getOrCreateClient.mockRejectedValue(new Error('db error'))\n await expect(service.register(baseDto as any, '1.2.3.4')).rejects.toMatchObject({ status: HttpStatus.INTERNAL_SERVER_ERROR })\n })\n })\n\n describe('unregister', () => {\n it('should delete client without error', async () => {\n syncQueries.deleteClient.mockResolvedValue(undefined)\n await expect(service.unregister({ id: 1, clientId: 'c1' } as any)).resolves.toBeUndefined()\n expect(syncQueries.deleteClient).toHaveBeenCalledWith(1, 'c1')\n })\n it('should throw Internal Server Error when deletion fails', async () => {\n syncQueries.deleteClient.mockRejectedValue(new Error('db error'))\n await expect(service.unregister({ id: 1, clientId: 'c1' } as any)).rejects.toMatchObject({ status: HttpStatus.INTERNAL_SERVER_ERROR })\n })\n })\n\n describe('authenticate', () => {\n const ip = '9.9.9.9'\n const dto = { clientId: 'cid', token: 'ctok' }\n\n it('should forbid when client is unknown', async () => {\n syncQueries.getClient.mockResolvedValue(undefined)\n await expect(service.authenticate(CLIENT_AUTH_TYPE.TOKEN, dto as any, ip, {} as FastifyReply)).rejects.toMatchObject({\n status: HttpStatus.FORBIDDEN,\n response: 'Client is unknown'\n })\n })\n\n it('should forbid when client is disabled', async () => {\n syncQueries.getClient.mockResolvedValue(makeClient({ enabled: false, tokenExpiration: 5000 }))\n await expect(service.authenticate(CLIENT_AUTH_TYPE.TOKEN, dto as any, ip, {} as FastifyReply)).rejects.toMatchObject({\n status: HttpStatus.FORBIDDEN,\n response: 'Client is disabled'\n })\n })\n\n it('should forbid when client token is expired', async () => {\n ;(commonShared.currentTimeStamp as jest.Mock).mockReturnValue(1000)\n syncQueries.getClient.mockResolvedValue(makeClient({ tokenExpiration: 1000 }))\n await expect(service.authenticate(CLIENT_AUTH_TYPE.TOKEN, dto as any, ip, {} as FastifyReply)).rejects.toMatchObject({\n status: HttpStatus.FORBIDDEN,\n response: CLIENT_TOKEN_EXPIRED_ERROR\n })\n })\n\n it('should forbid when owner user does not exist', async () => {\n syncQueries.getClient.mockResolvedValue(makeClient())\n syncQueries.updateClientInfo.mockRejectedValueOnce(new Error('update-fails')) // silence expected\n usersManager.fromUserId.mockResolvedValue(null)\n await expect(service.authenticate(CLIENT_AUTH_TYPE.TOKEN, dto as any, ip, {} as FastifyReply)).rejects.toMatchObject({\n status: HttpStatus.FORBIDDEN,\n response: 'User does not exist'\n })\n })\n\n it('should forbid when owner account is inactive', async () => {\n syncQueries.getClient.mockResolvedValue(makeClient())\n usersManager.fromUserId.mockResolvedValue(makeUser({ isActive: false }))\n await expect(service.authenticate(CLIENT_AUTH_TYPE.TOKEN, dto as any, ip, {} as FastifyReply)).rejects.toMatchObject({\n status: HttpStatus.FORBIDDEN,\n response: 'Account suspended or not authorized'\n })\n })\n\n it('should forbid when owner lacks DESKTOP_APP permission', async () => {\n mockHavePermission = false\n syncQueries.getClient.mockResolvedValue(makeClient())\n usersManager.fromUserId.mockResolvedValue(makeUser({ permissions: '', role: 999 }))\n await expect(service.authenticate(CLIENT_AUTH_TYPE.TOKEN, dto as any, ip, {} as FastifyReply)).rejects.toMatchObject({\n status: HttpStatus.FORBIDDEN,\n response: 'Missing permission'\n })\n })\n\n it('should perform COOKIE authentication and renew client token when needed', async () => {\n syncQueries.getClient.mockResolvedValue(makeClient({ ownerId: 7 }))\n usersManager.fromUserId.mockResolvedValue(makeUser({ id: 7, login: 'john', email: 'john@doe', firstName: 'John', lastName: 'Doe' }))\n usersManager.updateAccesses.mockRejectedValueOnce(new Error('update-access-fail')) // silence expected\n authManager.setCookies.mockResolvedValue({ access_token: 'a', refresh_token: 'b' })\n jest.spyOn(service, 'renewTokenAndExpiration').mockResolvedValue('new-client-token')\n\n const reply = {} as unknown as FastifyReply\n const r: any = await service.authenticate(CLIENT_AUTH_TYPE.COOKIE, dto as any, ip, reply)\n\n expect(authManager.setCookies).toHaveBeenCalledTimes(1)\n expect(service.renewTokenAndExpiration).toHaveBeenCalledTimes(1)\n expect(r.client_token_update).toBe('new-client-token')\n })\n\n it('should perform TOKEN authentication and not renew when not needed', async () => {\n syncQueries.getClient.mockResolvedValue(makeClient({ ownerId: 8 }))\n usersManager.fromUserId.mockResolvedValue(makeUser({ id: 8, login: 'alice', email: 'alice@doe', firstName: 'Alice' }))\n authManager.getTokens.mockResolvedValue({ access_token: 'x', refresh_token: 'y' })\n jest.spyOn(service, 'renewTokenAndExpiration').mockResolvedValue(undefined)\n\n const r: any = await service.authenticate(CLIENT_AUTH_TYPE.TOKEN, dto as any, ip, {} as FastifyReply)\n expect(authManager.getTokens).toHaveBeenCalledTimes(1)\n expect(r.client_token_update).toBeUndefined()\n })\n\n it('should throw when auth type is unknown (else branch)', async () => {\n syncQueries.getClient.mockResolvedValue(makeClient({ ownerId: 9 }))\n usersManager.fromUserId.mockResolvedValue(makeUser({ id: 9, login: 'bob', email: 'bob@doe', firstName: 'Bob' }))\n jest.spyOn(service, 'renewTokenAndExpiration').mockResolvedValue(undefined)\n await expect(service.authenticate('unknown' as any, { clientId: 'cid', token: 'ctok' } as any, ip, {} as FastifyReply)).rejects.toBeInstanceOf(\n TypeError\n )\n })\n })\n\n describe('getClients', () => {\n it('should proxy to SyncQueries.getClients', async () => {\n const fake = [{ id: 'c1', paths: [] }]\n syncQueries.getClients.mockResolvedValue(fake)\n const r = await service.getClients({ id: 1, clientId: 'c1' } as any)\n expect(r).toBe(fake)\n expect(syncQueries.getClients).toHaveBeenCalledWith({ id: 1, clientId: 'c1' })\n })\n })\n\n describe('renewTokenAndExpiration', () => {\n const owner = { id: 1, login: 'bob' } as any\n\n it('should return undefined when token expiration is far enough', async () => {\n ;(commonShared.currentTimeStamp as jest.Mock).mockReturnValue(1_000)\n ;(commonFunctions.convertHumanTimeToSeconds as jest.Mock).mockImplementation((v: string) => (v === '90d' ? 90 * 24 * 3600 : 0))\n const client = { id: 'cid', tokenExpiration: 1_000 + 90 * 24 * 3600 + 1 } as any\n expect(await service.renewTokenAndExpiration(client, owner)).toBeUndefined()\n })\n\n it('should renew token and return new value when close to expiration', async () => {\n ;(commonShared.currentTimeStamp as jest.Mock).mockReturnValue(1_000)\n ;(commonFunctions.convertHumanTimeToSeconds as jest.Mock).mockImplementation((v: string) =>\n v === '60d' ? 60 * 24 * 3600 : v === '120d' ? 120 * 24 * 3600 : 0\n )\n const client = { id: 'cid', tokenExpiration: 1_000 + 60 * 24 * 3600 - 1 } as any\n syncQueries.renewClientTokenAndExpiration.mockResolvedValue(undefined)\n\n const r = await service.renewTokenAndExpiration(client, owner)\n expect(typeof r).toBe('string')\n expect(r).toBeTruthy()\n expect(syncQueries.renewClientTokenAndExpiration).toHaveBeenCalledWith('cid', r, expect.any(Number))\n })\n\n it('should throw Bad Request when renewal persistence fails', async () => {\n ;(commonShared.currentTimeStamp as jest.Mock).mockReturnValue(1_000)\n const client = { id: 'cid', tokenExpiration: 1_000 } as any\n jest.spyOn(crypto, 'randomUUID').mockReturnValue('uuid-err' as any)\n syncQueries.renewClientTokenAndExpiration.mockRejectedValue(new Error('db fail'))\n await expect(service.renewTokenAndExpiration(client, owner)).rejects.toMatchObject({ status: HttpStatus.BAD_REQUEST })\n })\n })\n\n describe('deleteClient', () => {\n it('should delete client successfully', async () => {\n syncQueries.deleteClient.mockResolvedValue(undefined)\n await expect(service.deleteClient({ id: 5 } as any, 'cid')).resolves.toBeUndefined()\n expect(syncQueries.deleteClient).toHaveBeenCalledWith(5, 'cid')\n })\n it('should throw Internal Server Error when deletion fails', async () => {\n syncQueries.deleteClient.mockRejectedValue(new Error('db error'))\n await expect(service.deleteClient({ id: 5 } as any, 'cid')).rejects.toMatchObject({ status: HttpStatus.INTERNAL_SERVER_ERROR })\n })\n })\n\n describe('checkAppStore', () => {\n it('should return PUBLIC manifest when HTTP fetch succeeds', async () => {\n setRepo(APP_STORE_REPOSITORY.PUBLIC)\n http.axiosRef.mockResolvedValue({ data: { platform: { win: [] } } })\n\n const manifest: any = await service.checkAppStore()\n expect(manifest).toBeTruthy()\n expect(manifest.repository).toBe(APP_STORE_REPOSITORY.PUBLIC)\n expect(http.axiosRef).toHaveBeenCalled()\n })\n\n it('should return null when PUBLIC manifest fetch fails', async () => {\n setRepo(APP_STORE_REPOSITORY.PUBLIC)\n http.axiosRef.mockRejectedValue(new Error('network'))\n expect(await service.checkAppStore()).toBeNull()\n })\n\n it('should return null when LOCAL manifest file does not exist', async () => {\n setRepo(APP_STORE_REPOSITORY.LOCAL)\n ;(isPathExists as jest.Mock).mockResolvedValue(false)\n expect(await service.checkAppStore()).toBeNull()\n })\n\n it('should return LOCAL manifest with rewritten URLs when file is valid', async () => {\n setRepo(APP_STORE_REPOSITORY.LOCAL)\n ;(isPathExists as jest.Mock).mockResolvedValue(true)\n const raw = {\n platform: {\n win: [{ package: 'desktop-win.exe' }, { package: 'cli-win.zip' }],\n linux: [{ package: 'desktop-linux.AppImage' }]\n }\n }\n ;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify(raw))\n\n const manifest: any = await service.checkAppStore()\n expect(manifest.repository).toBe(APP_STORE_REPOSITORY.LOCAL)\n expect(manifest.platform.win[0].url.startsWith(APP_STORE_DIRNAME)).toBe(true)\n expect(manifest.platform.win[0].url.endsWith('desktop-win.exe')).toBe(true)\n expect(manifest.platform.win[1].url.startsWith(APP_STORE_DIRNAME)).toBe(true)\n expect(manifest.platform.win[1].url.endsWith('cli-win.zip')).toBe(true)\n expect(manifest.platform.linux[0].url.startsWith(APP_STORE_DIRNAME)).toBe(true)\n expect(manifest.platform.linux[0].url.endsWith('desktop-linux.AppImage')).toBe(true)\n })\n\n it('should return null when LOCAL manifest cannot be parsed', async () => {\n setRepo(APP_STORE_REPOSITORY.LOCAL)\n ;(isPathExists as jest.Mock).mockResolvedValue(true)\n ;(fs.readFile as jest.Mock).mockRejectedValue(new Error('fs error'))\n expect(await service.checkAppStore()).toBeNull()\n })\n\n it('should rewrite desktop packages under desktop/os when package starts with \"desktop\"', async () => {\n setRepo(APP_STORE_REPOSITORY.LOCAL)\n ;(isPathExists as jest.Mock).mockResolvedValue(true)\n const raw = {\n platform: {\n win: [{ package: `${SYNC_CLIENT_TYPE.DESKTOP}-win.exe` }],\n mac: [{ package: `${SYNC_CLIENT_TYPE.DESKTOP}-mac.dmg` }],\n linux: [{ package: `${SYNC_CLIENT_TYPE.DESKTOP}-linux.AppImage` }]\n }\n }\n ;(fs.readFile as jest.Mock).mockResolvedValue(JSON.stringify(raw))\n\n const manifest: any = await service.checkAppStore()\n expect(manifest).toBeTruthy()\n expect(manifest.repository).toBe(APP_STORE_REPOSITORY.LOCAL)\n expect(manifest.platform.win[0].url).toBe(`${APP_STORE_DIRNAME}/${SYNC_CLIENT_TYPE.DESKTOP}/win/${SYNC_CLIENT_TYPE.DESKTOP}-win.exe`)\n expect(manifest.platform.mac[0].url).toBe(`${APP_STORE_DIRNAME}/${SYNC_CLIENT_TYPE.DESKTOP}/mac/${SYNC_CLIENT_TYPE.DESKTOP}-mac.dmg`)\n expect(manifest.platform.linux[0].url).toBe(`${APP_STORE_DIRNAME}/${SYNC_CLIENT_TYPE.DESKTOP}/linux/${SYNC_CLIENT_TYPE.DESKTOP}-linux.AppImage`)\n })\n })\n})\n"],"names":["mockHavePermission","jest","mock","UserModel","fn","mockImplementation","props","havePermission","actual","requireActual","convertHumanTimeToSeconds","currentTimeStamp","readFile","isPathExists","describe","SyncClientsManager","name","service","http","authManager","authMethod","usersManager","syncQueries","cacheMock","setRepo","repo","configuration","applications","appStore","repository","makeClient","overrides","id","ownerId","tokenExpiration","enabled","info","type","makeUser","isActive","login","email","firstName","lastName","role","permissions","beforeAll","axiosRef","setCookies","getTokens","validateUser","fromUserId","updateAccesses","getOrCreateClient","deleteClient","getClient","updateClientInfo","renewClientTokenAndExpiration","getClients","genSlugKey","mockReturnValue","get","mockResolvedValue","undefined","set","del","module","Test","createTestingModule","providers","provide","Cache","useValue","HttpService","SyncQueries","UsersManager","AuthManager","AuthMethod","AuthMethod2FA","compile","useLogger","cache","beforeEach","restoreAllMocks","clearAllMocks","commonShared","commonFunctions","v","mockReset","fs","mockClear","APP_STORE_REPOSITORY","PUBLIC","it","expect","toBeDefined","baseDto","password","clientId","version","test","each","HttpStatus","UNAUTHORIZED","FORBIDDEN","_label","user","status","register","rejects","toMatchObject","r","toEqual","clientToken","toHaveBeenCalledWith","mockRejectedValue","Error","INTERNAL_SERVER_ERROR","unregister","resolves","toBeUndefined","ip","dto","token","authenticate","CLIENT_AUTH_TYPE","TOKEN","response","CLIENT_TOKEN_EXPIRED_ERROR","mockRejectedValueOnce","access_token","refresh_token","spyOn","reply","COOKIE","toHaveBeenCalledTimes","renewTokenAndExpiration","client_token_update","toBe","toBeInstanceOf","TypeError","fake","paths","owner","client","toBeTruthy","any","Number","crypto","BAD_REQUEST","data","platform","win","manifest","checkAppStore","toHaveBeenCalled","toBeNull","LOCAL","raw","package","linux","JSON","stringify","url","startsWith","APP_STORE_DIRNAME","endsWith","SYNC_CLIENT_TYPE","DESKTOP","mac"],"mappings":"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"}