@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.
- package/CHANGELOG.md +28 -0
- package/README.md +2 -1
- package/environment/environment.dist.min.yaml +1 -0
- package/environment/environment.dist.yaml +88 -30
- package/migrations/0002_sleepy_korath.sql +1 -0
- package/migrations/meta/0002_snapshot.json +2424 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +14 -12
- package/server/app.bootstrap.js +1 -1
- package/server/app.bootstrap.js.map +1 -1
- package/server/applications/files/services/files-manager.service.js +1 -2
- package/server/applications/files/services/files-manager.service.js.map +1 -1
- package/server/applications/files/services/files-only-office-manager.service.js +5 -6
- package/server/applications/files/services/files-only-office-manager.service.js.map +1 -1
- package/server/applications/files/utils/files.js +6 -4
- package/server/applications/files/utils/files.js.map +1 -1
- package/server/applications/links/links.controller.js +2 -2
- package/server/applications/links/links.controller.js.map +1 -1
- package/server/applications/links/services/links-manager.service.js +2 -1
- package/server/applications/links/services/links-manager.service.js.map +1 -1
- package/server/applications/links/services/links-manager.service.spec.js +6 -3
- package/server/applications/links/services/links-manager.service.spec.js.map +1 -1
- package/server/applications/notifications/constants/notifications.js +9 -0
- package/server/applications/notifications/constants/notifications.js.map +1 -1
- package/server/applications/notifications/i18n/fr.js +10 -1
- package/server/applications/notifications/i18n/fr.js.map +1 -1
- package/server/applications/notifications/interfaces/notification-properties.interface.js.map +1 -1
- package/server/applications/notifications/mails/models.js +41 -3
- package/server/applications/notifications/mails/models.js.map +1 -1
- package/server/applications/notifications/mails/templates.js +1 -1
- package/server/applications/notifications/mails/templates.js.map +1 -1
- package/server/applications/notifications/schemas/notifications.schema.js +2 -1
- package/server/applications/notifications/schemas/notifications.schema.js.map +1 -1
- package/server/applications/notifications/services/notifications-manager.service.js +16 -13
- package/server/applications/notifications/services/notifications-manager.service.js.map +1 -1
- package/server/applications/notifications/services/notifications-manager.service.spec.js +9 -8
- package/server/applications/notifications/services/notifications-manager.service.spec.js.map +1 -1
- package/server/applications/notifications/services/notifications-queries.service.js +1 -1
- package/server/applications/notifications/services/notifications-queries.service.js.map +1 -1
- package/server/applications/shares/services/shares-manager.service.js +3 -2
- package/server/applications/shares/services/shares-manager.service.js.map +1 -1
- package/server/applications/sync/constants/auth.js +2 -2
- package/server/applications/sync/constants/auth.js.map +1 -1
- package/server/applications/sync/dtos/sync-client-registration.dto.js +5 -0
- package/server/applications/sync/dtos/sync-client-registration.dto.js.map +1 -1
- package/server/applications/sync/dtos/sync-operations.dto.js +1 -2
- package/server/applications/sync/dtos/sync-operations.dto.js.map +1 -1
- package/server/applications/sync/schemas/sync-clients.schema.js +2 -1
- package/server/applications/sync/schemas/sync-clients.schema.js.map +1 -1
- package/server/applications/sync/schemas/sync-paths.schema.js +2 -1
- package/server/applications/sync/schemas/sync-paths.schema.js.map +1 -1
- package/server/applications/sync/services/sync-clients-manager.service.js +28 -20
- package/server/applications/sync/services/sync-clients-manager.service.js.map +1 -1
- package/server/applications/sync/services/sync-clients-manager.service.spec.js +24 -18
- package/server/applications/sync/services/sync-clients-manager.service.spec.js.map +1 -1
- package/server/applications/sync/services/sync-queries.service.js +5 -5
- package/server/applications/sync/services/sync-queries.service.js.map +1 -1
- package/server/applications/users/admin-users.controller.js +48 -37
- package/server/applications/users/admin-users.controller.js.map +1 -1
- package/server/applications/users/admin-users.controller.spec.js +15 -0
- package/server/applications/users/admin-users.controller.spec.js.map +1 -1
- package/server/applications/users/constants/routes.js +5 -0
- package/server/applications/users/constants/routes.js.map +1 -1
- package/server/applications/users/constants/user.js +8 -0
- package/server/applications/users/constants/user.js.map +1 -1
- package/server/applications/users/dto/delete-user.dto.js +5 -23
- package/server/applications/users/dto/delete-user.dto.js.map +1 -1
- package/server/applications/users/dto/user-properties.dto.js +38 -3
- package/server/applications/users/dto/user-properties.dto.js.map +1 -1
- package/server/applications/users/interfaces/admin-user.interface.js.map +1 -1
- package/server/applications/users/interfaces/user-secrets.interface.js +10 -0
- package/server/applications/users/interfaces/user-secrets.interface.js.map +1 -0
- package/server/applications/users/models/user.model.js +84 -50
- package/server/applications/users/models/user.model.js.map +1 -1
- package/server/applications/users/schemas/user.interface.js.map +1 -1
- package/server/applications/users/schemas/users.schema.js +2 -0
- package/server/applications/users/schemas/users.schema.js.map +1 -1
- package/server/applications/users/services/admin-users-manager.service.js +7 -19
- package/server/applications/users/services/admin-users-manager.service.js.map +1 -1
- package/server/applications/users/services/admin-users-manager.service.spec.js +7 -26
- package/server/applications/users/services/admin-users-manager.service.spec.js.map +1 -1
- package/server/applications/users/services/admin-users-queries.service.js +1 -0
- package/server/applications/users/services/admin-users-queries.service.js.map +1 -1
- package/server/applications/users/services/users-manager.service.js +138 -28
- package/server/applications/users/services/users-manager.service.js.map +1 -1
- package/server/applications/users/services/users-manager.service.spec.js +11 -9
- package/server/applications/users/services/users-manager.service.spec.js.map +1 -1
- package/server/applications/users/services/users-queries.service.js +63 -57
- package/server/applications/users/services/users-queries.service.js.map +1 -1
- package/server/applications/users/users.controller.js +48 -1
- package/server/applications/users/users.controller.js.map +1 -1
- package/server/applications/users/users.controller.spec.js +8 -1
- package/server/applications/users/users.controller.spec.js.map +1 -1
- package/server/applications/users/users.e2e-spec.js +2 -1
- package/server/applications/users/users.e2e-spec.js.map +1 -1
- package/server/applications/users/utils/avatar.js +48 -0
- package/server/applications/users/utils/avatar.js.map +1 -0
- package/server/authentication/auth.config.js +89 -26
- package/server/authentication/auth.config.js.map +1 -1
- package/server/authentication/auth.controller.js +117 -9
- package/server/authentication/auth.controller.js.map +1 -1
- package/server/authentication/auth.controller.spec.js +16 -1
- package/server/authentication/auth.controller.spec.js.map +1 -1
- package/server/authentication/auth.e2e-spec.js +4 -3
- package/server/authentication/auth.e2e-spec.js.map +1 -1
- package/server/authentication/auth.module.js +4 -1
- package/server/authentication/auth.module.js.map +1 -1
- package/server/authentication/constants/auth-ldap.js +44 -0
- package/server/authentication/constants/auth-ldap.js.map +1 -0
- package/server/authentication/constants/auth.js +37 -4
- package/server/authentication/constants/auth.js.map +1 -1
- package/server/authentication/constants/routes.js +21 -0
- package/server/authentication/constants/routes.js.map +1 -1
- package/server/authentication/constants/scope.js +20 -0
- package/server/authentication/constants/scope.js.map +1 -0
- package/server/authentication/dto/login-response.dto.js +27 -4
- package/server/authentication/dto/login-response.dto.js.map +1 -1
- package/server/authentication/dto/token-response.dto.js +5 -0
- package/server/authentication/dto/token-response.dto.js.map +1 -1
- package/server/{applications/users/dto/user-password.dto.js → authentication/dto/two-fa-verify.dto.js} +27 -9
- package/server/authentication/dto/two-fa-verify.dto.js.map +1 -0
- package/server/authentication/guards/auth-basic.strategy.js +6 -5
- package/server/authentication/guards/auth-basic.strategy.js.map +1 -1
- package/server/authentication/guards/auth-token-access.strategy.js +3 -2
- package/server/authentication/guards/auth-token-access.strategy.js.map +1 -1
- package/server/authentication/guards/auth-token-refresh.strategy.js +3 -2
- package/server/authentication/guards/auth-token-refresh.strategy.js.map +1 -1
- package/server/authentication/guards/auth-two-fa-guard.js +81 -0
- package/server/authentication/guards/auth-two-fa-guard.js.map +1 -0
- package/server/authentication/interfaces/jwt-payload.interface.js +5 -0
- package/server/authentication/interfaces/jwt-payload.interface.js.map +1 -1
- package/server/authentication/interfaces/token.interface.js +2 -0
- package/server/authentication/interfaces/token.interface.js.map +1 -1
- package/server/authentication/interfaces/two-fa-setup.interface.js +10 -0
- package/server/authentication/interfaces/two-fa-setup.interface.js.map +1 -0
- package/server/authentication/models/auth-method.js.map +1 -1
- package/server/authentication/services/auth-manager.service.js +72 -49
- package/server/authentication/services/auth-manager.service.js.map +1 -1
- package/server/authentication/services/auth-methods/auth-method-database.service.js +3 -3
- package/server/authentication/services/auth-methods/auth-method-database.service.js.map +1 -1
- package/server/authentication/services/auth-methods/auth-method-database.service.spec.js +5 -0
- package/server/authentication/services/auth-methods/auth-method-database.service.spec.js.map +1 -1
- package/server/authentication/services/auth-methods/auth-method-ldap.service.js +151 -66
- package/server/authentication/services/auth-methods/auth-method-ldap.service.js.map +1 -1
- package/server/authentication/services/auth-methods/auth-method-ldap.service.spec.js +52 -50
- package/server/authentication/services/auth-methods/auth-method-ldap.service.spec.js.map +1 -1
- package/server/authentication/services/auth-methods/auth-method-two-fa.service.js +251 -0
- package/server/authentication/services/auth-methods/auth-method-two-fa.service.js.map +1 -0
- package/server/authentication/services/auth-methods/auth-method-two-fa.service.spec.js +41 -0
- package/server/authentication/services/auth-methods/auth-method-two-fa.service.spec.js.map +1 -0
- package/server/authentication/utils/crypt-secret.js +68 -0
- package/server/authentication/utils/crypt-secret.js.map +1 -0
- package/server/common/functions.js +18 -2
- package/server/common/functions.js.map +1 -1
- package/server/common/qrcode.js +34 -0
- package/server/common/qrcode.js.map +1 -0
- package/server/common/shared.js +18 -0
- package/server/common/shared.js.map +1 -1
- package/server/configuration/config.environment.js +23 -6
- package/server/configuration/config.environment.js.map +1 -1
- package/server/configuration/config.interfaces.js +10 -0
- package/server/configuration/config.interfaces.js.map +1 -0
- package/server/configuration/config.loader.js.map +1 -1
- package/server/configuration/config.validation.js +13 -13
- package/server/configuration/config.validation.js.map +1 -1
- package/server/infrastructure/cache/adapters/mysql-cache.adapter.js +6 -6
- package/server/infrastructure/cache/adapters/mysql-cache.adapter.js.map +1 -1
- package/server/infrastructure/cache/schemas/mysql-cache.schema.js +2 -1
- package/server/infrastructure/cache/schemas/mysql-cache.schema.js.map +1 -1
- package/server/infrastructure/cache/services/cache.service.js.map +1 -1
- package/server/infrastructure/database/columns.js +39 -0
- package/server/infrastructure/database/columns.js.map +1 -0
- package/server/infrastructure/database/database.config.js +0 -1
- package/server/infrastructure/database/database.config.js.map +1 -1
- package/server/infrastructure/mailer/interfaces/mail.interface.js.map +1 -1
- package/server/infrastructure/mailer/mailer.config.js +12 -0
- package/server/infrastructure/mailer/mailer.config.js.map +1 -1
- package/server/infrastructure/mailer/mailer.service.js +2 -1
- package/server/infrastructure/mailer/mailer.service.js.map +1 -1
- package/static/assets/mimes/text-x-c.svg +1 -0
- package/static/assets/pdfjs/build/pdf.mjs +2522 -914
- package/static/assets/pdfjs/build/pdf.mjs.map +1 -1
- package/static/assets/pdfjs/build/pdf.sandbox.mjs +2 -2
- package/static/assets/pdfjs/build/pdf.worker.mjs +1024 -566
- package/static/assets/pdfjs/build/pdf.worker.mjs.map +1 -1
- package/static/assets/pdfjs/version +1 -1
- package/static/assets/pdfjs/web/debugger.mjs +116 -37
- package/static/assets/pdfjs/web/images/comment-popup-editButton.svg +5 -0
- package/static/assets/pdfjs/web/locale/ach/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/af/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/an/viewer.ftl +0 -16
- package/static/assets/pdfjs/web/locale/ar/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/ast/viewer.ftl +0 -19
- package/static/assets/pdfjs/web/locale/az/viewer.ftl +0 -16
- package/static/assets/pdfjs/web/locale/be/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/bg/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/bn/viewer.ftl +0 -16
- package/static/assets/pdfjs/web/locale/bo/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/br/viewer.ftl +0 -22
- package/static/assets/pdfjs/web/locale/brx/viewer.ftl +0 -16
- package/static/assets/pdfjs/web/locale/bs/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/ca/viewer.ftl +12 -23
- package/static/assets/pdfjs/web/locale/cak/viewer.ftl +0 -23
- package/static/assets/pdfjs/web/locale/ckb/viewer.ftl +0 -16
- package/static/assets/pdfjs/web/locale/cs/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/cy/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/da/viewer.ftl +3 -35
- package/static/assets/pdfjs/web/locale/de/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/dsb/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/el/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/en-CA/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/en-GB/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/en-US/viewer.ftl +25 -13
- package/static/assets/pdfjs/web/locale/eo/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/es-AR/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/es-CL/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/es-ES/viewer.ftl +5 -32
- package/static/assets/pdfjs/web/locale/es-MX/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/et/viewer.ftl +0 -16
- package/static/assets/pdfjs/web/locale/eu/viewer.ftl +38 -32
- package/static/assets/pdfjs/web/locale/fa/viewer.ftl +0 -19
- package/static/assets/pdfjs/web/locale/ff/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/fi/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/fr/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/fur/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/fy-NL/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/ga-IE/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/gd/viewer.ftl +0 -23
- package/static/assets/pdfjs/web/locale/gl/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/gn/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/gu-IN/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/he/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/hi-IN/viewer.ftl +0 -16
- package/static/assets/pdfjs/web/locale/hr/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/hsb/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/hu/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/hy-AM/viewer.ftl +372 -16
- package/static/assets/pdfjs/web/locale/hye/viewer.ftl +0 -16
- package/static/assets/pdfjs/web/locale/ia/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/id/viewer.ftl +38 -32
- package/static/assets/pdfjs/web/locale/is/viewer.ftl +27 -32
- package/static/assets/pdfjs/web/locale/it/viewer.ftl +0 -33
- package/static/assets/pdfjs/web/locale/ja/viewer.ftl +31 -33
- package/static/assets/pdfjs/web/locale/ka/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/kab/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/kk/viewer.ftl +31 -32
- package/static/assets/pdfjs/web/locale/km/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/kn/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/ko/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/lij/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/lo/viewer.ftl +0 -23
- package/static/assets/pdfjs/web/locale/lt/viewer.ftl +0 -16
- package/static/assets/pdfjs/web/locale/ltg/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/lv/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/meh/viewer.ftl +0 -14
- package/static/assets/pdfjs/web/locale/mk/viewer.ftl +0 -19
- package/static/assets/pdfjs/web/locale/ml/viewer.ftl +0 -31
- package/static/assets/pdfjs/web/locale/mr/viewer.ftl +0 -16
- package/static/assets/pdfjs/web/locale/ms/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/my/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/nb-NO/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/ne-NP/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/nl/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/nn-NO/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/oc/viewer.ftl +0 -24
- package/static/assets/pdfjs/web/locale/pa-IN/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/pl/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/pt-BR/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/pt-PT/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/rm/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/ro/viewer.ftl +5 -37
- package/static/assets/pdfjs/web/locale/ru/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/sat/viewer.ftl +0 -23
- package/static/assets/pdfjs/web/locale/sc/viewer.ftl +8 -27
- package/static/assets/pdfjs/web/locale/sco/viewer.ftl +0 -16
- package/static/assets/pdfjs/web/locale/si/viewer.ftl +0 -22
- package/static/assets/pdfjs/web/locale/sk/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/skr/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/sl/viewer.ftl +30 -32
- package/static/assets/pdfjs/web/locale/son/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/sq/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/sr/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/sv-SE/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/szl/viewer.ftl +0 -16
- package/static/assets/pdfjs/web/locale/ta/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/te/viewer.ftl +0 -16
- package/static/assets/pdfjs/web/locale/tg/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/th/viewer.ftl +38 -32
- package/static/assets/pdfjs/web/locale/tl/viewer.ftl +0 -16
- package/static/assets/pdfjs/web/locale/tr/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/trs/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/uk/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/ur/viewer.ftl +0 -16
- package/static/assets/pdfjs/web/locale/uz/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/vi/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/xh/viewer.ftl +0 -12
- package/static/assets/pdfjs/web/locale/zh-CN/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/locale/zh-TW/viewer.ftl +0 -32
- package/static/assets/pdfjs/web/viewer.css +586 -437
- package/static/assets/pdfjs/web/viewer.html +12 -23
- package/static/assets/pdfjs/web/viewer.mjs +955 -514
- package/static/assets/pdfjs/web/viewer.mjs.map +1 -1
- package/static/assets/pdfjs/web/wasm/openjpeg.wasm +0 -0
- package/static/assets/pdfjs/web/wasm/openjpeg_nowasm_fallback.js +10 -22
- package/static/{chunk-SPTF6FSM.js → chunk-27YQB3TE.js} +1 -1
- package/static/chunk-2I4CUFUA.js +1 -0
- package/static/chunk-2MTM6SWN.js +4 -0
- package/static/{chunk-7VRUZRJG.js → chunk-34MKICK5.js} +2 -2
- package/static/chunk-5O3DIUU3.js +1 -0
- package/static/{chunk-VJRTMDEJ.js → chunk-6NMVZIIT.js} +1 -1
- package/static/{chunk-L6MU6S2V.js → chunk-7DN7ZAPU.js} +1 -1
- package/static/{chunk-MVO4WZLK.js → chunk-7FUM3JGM.js} +1 -1
- package/static/{chunk-RSS6GYNE.js → chunk-7ITZXYYJ.js} +1 -1
- package/static/chunk-7P27WBGC.js +4 -0
- package/static/chunk-ATP3BFHV.js +562 -0
- package/static/chunk-AWQ2YTVC.js +1 -0
- package/static/chunk-DSOE3FEP.js +1 -0
- package/static/{chunk-2R6HHGUR.js → chunk-EFKMBLRE.js} +1 -1
- package/static/chunk-FUFKVHPU.js +1 -0
- package/static/{chunk-MRSWNAVB.js → chunk-HCDLWTMW.js} +1 -1
- package/static/chunk-IPAC4VAF.js +1 -0
- package/static/{chunk-ZC5NIT55.js → chunk-IQOALFYU.js} +1 -1
- package/static/chunk-JASU3CIH.js +1 -0
- package/static/{chunk-6OJZWYRZ.js → chunk-JQ5FTO2M.js} +1 -1
- package/static/chunk-JUNZFADM.js +1 -0
- package/static/{chunk-LLWSLOSX.js → chunk-LJUKI4SQ.js} +1 -1
- package/static/{chunk-WI7FOANP.js → chunk-LUWQFIWR.js} +1 -1
- package/static/{chunk-BIUNUYZ5.js → chunk-ORMRCEGT.js} +1 -1
- package/static/{chunk-IZL7JPTS.js → chunk-Q7D6RN4N.js} +1 -1
- package/static/{chunk-JYXLQRHG.js → chunk-QJX6ITLW.js} +1 -1
- package/static/{chunk-YJMN3B4N.js → chunk-QQ6UQQBR.js} +1 -1
- package/static/chunk-S2HDY3OL.js +1 -0
- package/static/{chunk-NE4NDO45.js → chunk-S75P2FFI.js} +1 -1
- package/static/{chunk-CRQNEHTX.js → chunk-T3EYFSVZ.js} +1 -1
- package/static/{chunk-MCLQFZ3S.js → chunk-U34OZUZ7.js} +1 -1
- package/static/chunk-Y7EH7G5K.js +1 -0
- package/static/{chunk-MGGT6MIJ.js → chunk-ZQQPUYLU.js} +1 -1
- package/static/index.html +2 -2
- package/static/main-7SQDDVMD.js +9 -0
- package/static/{styles-FYUSO6OJ.css → styles-A5VYX3CE.css} +1 -1
- package/server/applications/users/dto/user-password.dto.js.map +0 -1
- package/static/chunk-4U5A2DEP.js +0 -4
- package/static/chunk-54EAZ2UD.js +0 -1
- package/static/chunk-7ZRXJONB.js +0 -1
- package/static/chunk-F2J2IIJE.js +0 -1
- package/static/chunk-FNFGUIQH.js +0 -4
- package/static/chunk-GGLK52CG.js +0 -1
- package/static/chunk-HW2H3ISM.js +0 -559
- package/static/chunk-HX6BBYVD.js +0 -1
- package/static/chunk-JF7S3UYQ.js +0 -1
- package/static/chunk-KSHPKI4G.js +0 -1
- package/static/chunk-VPJ2V27B.js +0 -1
- package/static/chunk-VUI3KV7V.js +0 -1
- package/static/chunk-ZXS4V7J2.js +0 -1
- 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,
|
|
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,
|
|
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
|
|
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('
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
116
|
+
user.clientId = client.id;
|
|
106
117
|
// update accesses
|
|
107
|
-
this.
|
|
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(
|
|
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(
|
|
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,
|
|
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,
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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:
|
|
181
|
-
useValue:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 === '
|
|
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 +
|
|
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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"}
|